Manipulating data point in chart.js external tooltip - javascript

I'm using Chart.js to display some financial data in a pie chart, along with external tooltips. The data in the tooltip is displayed currently like this:
"Invoiceable: 1,202.5"
What I want is to firstly round the number to 1,200, and then add a '£' sign before the data point so it reads:
"Invoiceable: £1,200"
I'm struggling to work out how to do this in this code below. Can anyone help?
tooltip: {
// Disable the on-canvas tooltip
enabled: false,
external: function(context) {
// Tooltip Element
let tooltipEl = document.getElementById('chartjs-tooltip');
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = '<table></table>';
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
const tooltipModel = context.tooltip;
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
// Set caret Position
tooltipEl.classList.remove('above', 'below', 'no-transform');
if (tooltipModel.yAlign) {
tooltipEl.classList.add(tooltipModel.yAlign);
} else {
tooltipEl.classList.add('no-transform');
}
function getBody(bodyItem) {
return bodyItem.lines;
}
// Set Text
if (tooltipModel.body) {
const titleLines = tooltipModel.title || [];
const bodyLines = tooltipModel.body.map(getBody);
let innerHtml = '<thead>';
titleLines.forEach(function(title) {
innerHtml += '<tr><th>' + title + '</th></tr>';
});
innerHtml += '</thead><tbody>';
bodyLines.forEach(function(body, i) {
innerHtml += '<tr><td><div class="tooltip">'+ body + '</div></td></tr>';
});
innerHtml += '</tbody>';
let tableRoot = tooltipEl.querySelector('table');
tableRoot.innerHTML = innerHtml;
}
const position = context.chart.canvas.getBoundingClientRect();
const bodyFont = Chart.helpers.toFont(tooltipModel.options.bodyFont);
// Display, position, and set styles for font
tooltipEl.style.opacity = .8;
tooltipEl.style.position = 'absolute';
tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX + 'px';
tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + 'px';
tooltipEl.style.font = bodyFont.string;
tooltipEl.style.padding = tooltipModel.padding + 'px ' + tooltipModel.padding + 'px';
tooltipEl.style.pointerEvents = 'none';
}
}
FTR - I copied the above code from chart.js, which made it work, but I don't presume to understand it for a second!

You can adjust the bodyLines.forEach loop to alter the value it shows like so:
bodyLines.forEach(function(body, i) {
let [label, value] = body[0].split(': ');
value = `£${value.split('.')[0]}`
innerHtml += '<tr><td><div class="tooltip">' + `${label}: ${value}` + '</div></td></tr>';
});
const options = {
type: 'line',
data: {
labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
datasets: [{
label: 'Invoicable',
data: [120000.5, 190000.7, 30000.2, 50000.3, 20000.9, 30000.1],
borderColor: 'pink',
backgroundColor: 'pink',
}]
},
options: {
locale: 'en-EN',
plugins: {
tooltip: {
// Disable the on-canvas tooltip
enabled: false,
external: function(context) {
// Tooltip Element
let tooltipEl = document.getElementById('chartjs-tooltip');
// Create element on first render
if (!tooltipEl) {
tooltipEl = document.createElement('div');
tooltipEl.id = 'chartjs-tooltip';
tooltipEl.innerHTML = '<table></table>';
document.body.appendChild(tooltipEl);
}
// Hide if no tooltip
const tooltipModel = context.tooltip;
if (tooltipModel.opacity === 0) {
tooltipEl.style.opacity = 0;
return;
}
// Set caret Position
tooltipEl.classList.remove('above', 'below', 'no-transform');
if (tooltipModel.yAlign) {
tooltipEl.classList.add(tooltipModel.yAlign);
} else {
tooltipEl.classList.add('no-transform');
}
function getBody(bodyItem) {
return bodyItem.lines;
}
// Set Text
if (tooltipModel.body) {
const titleLines = tooltipModel.title || [];
const bodyLines = tooltipModel.body.map(getBody);
let innerHtml = '<thead>';
titleLines.forEach(function(title) {
innerHtml += '<tr><th>' + title + '</th></tr>';
});
innerHtml += '</thead><tbody>';
bodyLines.forEach(function(body, i) {
let [label, value] = body[0].split(': ');
value = `£${value.split('.')[0]}`
innerHtml += '<tr><td><div class="tooltip">' + `${label}: ${value}` + '</div></td></tr>';
});
innerHtml += '</tbody>';
let tableRoot = tooltipEl.querySelector('table');
tableRoot.innerHTML = innerHtml;
}
const position = context.chart.canvas.getBoundingClientRect();
const bodyFont = Chart.helpers.toFont(tooltipModel.options.bodyFont);
// Display, position, and set styles for font
tooltipEl.style.opacity = .8;
tooltipEl.style.position = 'absolute';
tooltipEl.style.left = position.left + window.pageXOffset + tooltipModel.caretX + 'px';
tooltipEl.style.top = position.top + window.pageYOffset + tooltipModel.caretY + 'px';
tooltipEl.style.font = bodyFont.string;
tooltipEl.style.padding = tooltipModel.padding + 'px ' + tooltipModel.padding + 'px';
tooltipEl.style.pointerEvents = 'none';
}
}
}
}
}
const ctx = document.getElementById('chartJSContainer').getContext('2d');
const chart = new Chart(ctx, options);
<body>
<canvas id="chartJSContainer" width="600" height="400"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.js"></script>
</body>

Related

HTML & Js Color Picker: What am I doing wrong?

The problem is, no matter what control you're on, it will just affect the blue color.
I'm guessing it might have something to do with the fact that I'm declaring the controls and assigning their handlers in a loop, but I honestly don't know what I'm doing wrong or if the solution is to just do that manually, one by one.
Here's a copy of the project.
let
// This one will contain all the elements
picker = document.createElement("div")
// And this one the color controls
, values = document.createElement("form")
// This' the color preview
, preview = document.createElement("div")
// The preview initializes and updates based on this values
, colors = { red : 200, green : 0, blue : 0 }
// This validates if a value is between 0 and 255
, vv = { min : 0, max : 255 }
, validVal = (n) => vv.min <= n && n <= vv.max
// And this' just a style string
, style = ""
;
// This one changes preview's bg color and shows the
// value inside it
function updatePreview() {
let rgbString =
"rgb("
+ [colors.red, colors.green, colors.blue].join(",")
+ ")";
preview.style["background-color"] = rgbString;
preview.innerHTML = rgbString;
}
// Now we define the elements' class names
picker.className += " color-picker";
values.className += " rgb-values";
preview.className += " preview";
// And their appearance
style += "display : inline-block;";
values.style = style;
style += "width: 200px; height: 200px; border: 1px solid #000;";
preview.style = style;
// Then we add'em to the screen
picker.appendChild(values);
picker.appendChild(preview);
document.body.appendChild(picker);
// And, "finally", we add the controls and their handlers
// One for each color
for (var color in colors) {
// This are text and slide controls
let
label = document.createElement("label")
, span = document.createElement("span")
, slide = document.createElement("input")
, text = document.createElement("input")
;
// We define their general attributes
label.style = "display: block";
slide.name = color + "-slide";
slide.type = "range";
slide.min = vv.min;
slide.max = vv.max;
slide.step = "1";
text.name = color + "-text";
text.type = "text";
text.size = "3";
span.innerHTML = " " + color;
// And set their initial values
slide.value = text.value = colors[color];
// We add'em to screen also
label.appendChild(slide);
label.appendChild(text);
label.appendChild(span);
values.appendChild(label);
// And now the handlers
/*
This is the tricky part.
I must be doing something wrong here. I guess.
Pls, help!
*/
function slideHandler(e) {
text.value = slide.value;
colors[color] = slide.value;
updatePreview();
}
slide.oninput = slideHandler;
function textHandler(e) {
if (validVal(text.value)) slide.value = text.value;
colors[color] = slide.value;
updatePreview();
}
text.onchange = textHandler;
}
// And... Showtime!
updatePreview();
Change the var color for slide.name.split('-')[0]
CODE:
function slideHandler(e) {(
text.value = slide.value;
colors[slide.name.split('-')[0]] = slide.value;
updatePreview();)}
(function() {
window.onload = function() {
let
// This one will contain all the elements
picker = document.createElement("div")
// And this one the color controls
,
values = document.createElement("form")
// This' the color preview
,
preview = document.createElement("div")
// The preview initializes and updates based on this values
,
colors = {
red: 200,
green: 0,
blue: 0
}
// This validates if a value is between 0 and 255
,
vv = {
min: 0,
max: 255
},
validVal = (n) => vv.min <= n && n <= vv.max
// And this' just a style string
,
style = "";
// This one changes preview's bg color and shows the
// value inside it
function updatePreview() {
let rgbString =
"rgb(" +
[colors.red, colors.green, colors.blue].join(",") +
")";
preview.style["background-color"] = rgbString;
preview.innerHTML = rgbString;
}
// Now we define the elements' class names
picker.className += " color-picker";
values.className += " rgb-values";
preview.className += " preview";
// And their appearance
style += "display : inline-block;";
values.style = style;
style += "width: 200px; height: 200px; border: 1px solid #000;";
preview.style = style;
// Then we add'em to the screen
picker.appendChild(values);
picker.appendChild(preview);
document.body.appendChild(picker);
// And, "finally", we add the controls and their handlers
// One for each color
for (var color in colors) {
// This are text and slide controls
let
label = document.createElement("label"),
span = document.createElement("span"),
slide = document.createElement("input"),
text = document.createElement("input");
// We define their general attributes
label.style = "display: block";
slide.name = color + "-slide";
slide.type = "range";
slide.min = vv.min;
slide.max = vv.max;
slide.step = "1";
text.name = color + "-text";
text.type = "text";
text.size = "3";
span.innerHTML = " " + color;
// And set their initial values
slide.value = text.value = colors[color];
// We add'em to screen also
label.appendChild(slide);
label.appendChild(text);
label.appendChild(span);
values.appendChild(label);
// And now the handlers
/*
This is the tricky part.
I must be doing something wrong here. I guess.
Pls, help!
*/
function slideHandler(e) {
text.value = slide.value;
colors[slide.name.split('-')[0]] = slide.value;
updatePreview();
}
slide.oninput = slideHandler;
function textHandler(e) {
if (validVal(text.value)) slide.value = text.value;
colors[slide.name.split('-')[0]] = slide.value;
updatePreview();
}
text.onchange = textHandler;
}
// And... Showtime!
updatePreview();
};
})();
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
</body>

Trying to make a marquee

I am currently generating the elements putting them into a container and I want that container to move from right to left of the marquee tag.
HTML:
<div class="news_wrapper">
<div class="news_start"></div>
<div class="news_end"></div>
<div id="marquee" class="news_text">
<!--<p>Consider a small donation btc: 114t1q7Fro4JCANagYMF55BynAGhvsk39z</p>-->
</div>
</div>
JavaScript:
function createElement(type, attributes, someElement) {
var element = document.createElement(type);
for (var key in attributes) {
if (key === "class") {
var cls = attributes[key];
for (var c in cls)
element.classList.add(cls[c]);
} else {
element[key] = attributes[key];
}
}
someElement.appendChild(element);
}
window.addEventListener('load', function () {
var news_marquee = document.getElementById("marquee"),
news_marquee_width,
news_volatility,
news_class,
news_name,
news_track,
news_track_width,
lcal_coinsss,
find_volatility;
add_news();
function move_news() {
news_track_width = news_track_width + 1;
console.log(news_track_width);
news_track.style.right = "-" + news_track_width + "px";
setInterval(move_news, 1000);
}
function add_news() {
lcal_coinsss = JSON.parse(localStorage.getItem('coinss'));
find_volatility = findVolatility(lcal_coinsss);
var newElement = createElement("div", {
"id": "news_track",
"class": ["news_track"]
}, news_marquee);
news_track = document.getElementById("news_track");
for (var i in find_volatility) {
news_volatility = find_volatility[i][1];
if (news_volatility >= 1) {
news_class = "positive_number_text";
} else if (news_volatility <= -1) {
news_class = "negative_number_text";
}
var newElement = createElement("p", {
"id": "news_name" + find_volatility[i][0],
"class": ["news_name"]
}, news_track);
news_name = document.getElementById("news_name" + find_volatility[i][0]);
news_name.textContent = find_volatility[i][0];
var newElement = createElement("span", {
"id": "news_volatility" + find_volatility[i][0],
"class": [news_class]
}, news_track);
news_volatility = document.getElementById("news_volatility" + find_volatility[i][0]);
news_volatility.textContent = find_volatility[i][1];
}
news_marquee_width = news_marquee.offsetWidth;
news_track_width = news_track.offsetWidth;
news_track.style.right = "-" + news_track_width + "px";
move_news();
// console.log(news_marquee_width);
// console.log(news_track_width);
}
I am generating the elements, and they are in a wrapper inside the marquee but I am not able to move it as I want to, when I run the move_news it will add right CSS expotentional.
Basically I want the generated div with content to move in a marquee style.
If anyone has any suggestions towards the whole script please let me know.

chrome extensions, trying to make minimize to tray button

I'm trying to use chrome app samples to make a chrome extensions that has a top custom top bar. I might have butcher the code but could someone help make it work?
https://github.com/GoogleChrome/chrome-app-samples/tree/master/samples/frameless-window
I can't make the minimize to tray button to work
function closeWindow() {
window.close();
}
function minimizeWindow() {
window.minimize();
}
function updateImageUrl(image_id, new_image_url) {
var image = document.getElementById(image_id);
if (image)
image.src = new_image_url;
}
function createImage(image_id, image_url) {
var image = document.createElement("img");
image.setAttribute("id", image_id);
image.src = image_url;
return image;
}
function createButton(button_id, button_name, normal_image_url,
hover_image_url, click_func) {
var button = document.createElement("div");
button.setAttribute("class", button_name);
var button_img = createImage(button_id, normal_image_url);
button.appendChild(button_img);
button.onmouseover = function() {
updateImageUrl(button_id, hover_image_url);
}
button.onmouseout = function() {
updateImageUrl(button_id, normal_image_url);
}
button.onclick = click_func;
return button;
}
function focusTitlebars(focus) {
var bg_color = focus ? "#3a3d3d" : "#000000";
var titlebar = document.getElementById("top-titlebar");
if (titlebar)
titlebar.style.backgroundColor = bg_color;
}
function addTitlebar(titlebar_name, titlebar_icon_url, titlebar_text) {
var titlebar = document.createElement("div");
var titlebar_name = "top-titlebar";
titlebar.setAttribute("id", titlebar_name);
titlebar.setAttribute("class", titlebar_name);
var icon = document.createElement("div");
icon.setAttribute("class", titlebar_name + "-icon");
icon.appendChild(createImage(titlebar_name + "icon", "16.png"));
titlebar.appendChild(icon);
var title = document.createElement("div");
title.setAttribute("class", titlebar_name + "-text");
title.innerText = document.title; // titlebar name
titlebar.appendChild(title);
var win = chrome.app.window.current();
var closeButton = createButton(titlebar_name + "-close-button",
titlebar_name + "-close-button",
"img/button_close.png",
"img/button_close_hover.png",
closeWindow);
titlebar.appendChild(closeButton);
// Create minimize button
var minimizeButton = createButton(titlebar_name + "-minimize-button",
titlebar_name + "-minimize-button",
"img/button_minimize.png",
"img/button_minimize_hover.png",
minimizeWindow);
titlebar.appendChild(minimizeButton);
//
var divider = document.createElement("div");
divider.setAttribute("class", titlebar_name + "-divider");
titlebar.appendChild(divider);
document.body.appendChild(titlebar);
}
function removeTitlebar(titlebar_name) {
var titlebar = document.getElementById(titlebar_name);
if (titlebar)
document.body.removeChild(titlebar);
}
function updateContentStyle() {
var content = document.getElementById("content");
if (!content)
return;
var left = 0;
var top = 0;
var width = window.outerWidth;
var height = window.outerHeight;
var titlebar = document.getElementById("top-titlebar");
if (titlebar) {
height -= titlebar.offsetHeight;
top += titlebar.offsetHeight;
}
if (titlebar) {
width -= titlebar.offsetWidth;
}
var contentStyle = "position: absolute; ";
contentStyle += "left: " + left + "px; ";
contentStyle += "top: " + top + "px; ";
contentStyle += "width: " + width + "px; ";
contentStyle += "height: " + height + "px; ";
content.setAttribute("style", contentStyle);
}
This will fix the problem
function minimizeWindow() {
var window = chrome.app.window.current();
window.minimize();
}

How to change selection area for path in SVG using Javascript

I am working in SVG editor 2.7 version. Here I need to change selection area for path tags in SVG using Javascript.
For example refer this image below:
Here I analysis about SVG editor, <g> tags will generated automatically and create only one selection area which is provided only rectangular shape only.
Here I need to change selection area for path section.
Below code is based on select.js :
(function() {'use strict';
if (!svgedit.select) {
svgedit.select = {};
}
var svgFactory_;
var config_;
var selectorManager_; // A Singleton
var gripRadius = svgedit.browser.isTouch() ? 5 : 4;
// Class: svgedit.select.Selector
// Private class for DOM element selection boxes
//
// Parameters:
// id - integer to internally indentify the selector
// elem - DOM element associated with this selector
svgedit.select.Selector = function(id, elem) {
// this is the selector's unique number
this.id = id;
// this holds a reference to the element for which this selector is being used
this.selectedElement = elem;
// this is a flag used internally to track whether the selector is being used or not
this.locked = true;
// this holds a reference to the <g> element that holds all visual elements of the selector
this.selectorGroup = svgFactory_.createSVGElement({
'element': 'g',
'attr': {'id': ('selectorGroup' + this.id)}
});
// this holds a reference to the path rect
this.selectorRect = this.selectorGroup.appendChild(
svgFactory_.createSVGElement({
'element': 'path',
'attr': {
'id': ('selectedBox' + this.id),
'fill': 'none',
'stroke': '#22C',
'stroke-width': '1',
'stroke-dasharray': '5,5',
// need to specify this so that the rect is not selectable
'style': 'pointer-events:none'
}
})
);
// this holds a reference to the grip coordinates for this selector
this.gripCoords = {
'nw': null,
'n' : null,
'ne': null,
'e' : null,
'se': null,
's' : null,
'sw': null,
'w' : null
};
this.reset(this.selectedElement);
};
// Function: svgedit.select.Selector.reset
// Used to reset the id and element that the selector is attached to
//
// Parameters:
// e - DOM element associated with this selector
svgedit.select.Selector.prototype.reset = function(e) {
this.locked = true;
this.selectedElement = e;
this.resize();
this.selectorGroup.setAttribute('display', 'inline');
};
// Function: svgedit.select.Selector.updateGripCursors
// Updates cursors for corner grips on rotation so arrows point the right way
//
// Parameters:
// angle - Float indicating current rotation angle in degrees
svgedit.select.Selector.prototype.updateGripCursors = function(angle) {
var dir,
dir_arr = [],
steps = Math.round(angle / 45);
if (steps < 0) {steps += 8;}
for (dir in selectorManager_.selectorGrips) {
dir_arr.push(dir);
}
while (steps > 0) {
dir_arr.push(dir_arr.shift());
steps--;
}
var i = 0;
for (dir in selectorManager_.selectorGrips) {
selectorManager_.selectorGrips[dir].setAttribute('style', ('cursor:' + dir_arr[i] + '-resize'));
i++;
}
};
// Function: svgedit.select.Selector.showGrips
// Show the resize grips of this selector
//
// Parameters:
// show - boolean indicating whether grips should be shown or not
svgedit.select.Selector.prototype.showGrips = function(show) {
// TODO: use suspendRedraw() here
var bShow = show ? 'inline' : 'none';
selectorManager_.selectorGripsGroup.setAttribute('display', bShow);
var elem = this.selectedElement;
this.hasGrips = show;
if (elem && show) {
this.selectorGroup.appendChild(selectorManager_.selectorGripsGroup);
this.updateGripCursors(svgedit.utilities.getRotationAngle(elem));
}
};
// Function: svgedit.select.Selector.resize
// Updates the selector to match the element's size
svgedit.select.Selector.prototype.resize = function() {
console.log(this.selectorGroup);
var selectedBox = this.selectorRect,
selectorRubberBand = this.getRubberBandBox,
mgr = selectorManager_,
selectedGrips = mgr.selectorGrips,
selected = this.selectedElement,
sw = selected.getAttribute('stroke-width'),
current_zoom = svgFactory_.currentZoom();
$('#current_zoom').val(current_zoom);
console.log(mgr);
var offset = 1/current_zoom;
if (selected.getAttribute('stroke') !== 'none' && !isNaN(sw)) {
offset += (sw/2);
}
var tagName = selected.tagName;
if (tagName === 'text') {
offset += 2/current_zoom;
}
// loop and transform our bounding box until we reach our first rotation
var tlist = svgedit.transformlist.getTransformList(selected);
var m = svgedit.math.transformListToTransform(tlist).matrix;
// This should probably be handled somewhere else, but for now
// it keeps the selection box correctly positioned when zoomed
m.e *= current_zoom;
m.f *= current_zoom;
var bbox = svgedit.utilities.getBBox(selected);
if (tagName === 'g' && !$.data(selected, 'gsvg')) {
// The bbox for a group does not include stroke vals, so we
// get the bbox based on its children.
var stroked_bbox = svgFactory_.getStrokedBBox(selected.childNodes);
if (stroked_bbox) {
bbox = stroked_bbox;
}
}
// apply the transforms
var l = bbox.x, t = bbox.y, w = bbox.width, h = bbox.height;
bbox = {x:l, y:t, width:w, height:h};
var x_varadd=70;
var y_varadd=40;
var cen_x=l;
var cen_y=t;
var dummyvarrr=0;
var gethide_curtselid=$("#hide_curtselid").val();
var gethide_curtselwidth=$("#hide_curtdrawx_width").val(w);
var gethide_curtselheight=$("#hide_curtdrawy_height").val(h);
$("#hide_curtdrawx_id").val(l);
$("#hide_curtdrawy_id").val(t);
var gethide_curtselid12=$("#hide_curtdrawx_id").val();
var gethide_curtselid13=$("#hide_curtdrawy_id").val();
offset *= current_zoom;
var nbox = svgedit.math.transformBox(l*current_zoom, t*current_zoom, w*current_zoom, h*current_zoom, m),
aabox = nbox.aabox,
nbax = aabox.x - offset,
nbay = aabox.y - offset,
nbaw = aabox.width + (offset * 2),
nbah = aabox.height + (offset * 2);
var gethide_curtselwidth=$("#hide_curtdrawx_width").val(nbaw);
var gethide_curtselheight=$("#hide_curtdrawy_height").val(nbah);
$("#hide_curtdrawx_id").val(nbax);
$("#hide_curtdrawy_id").val(nbay);
// now if the shape is rotated, un-rotate it
var cx = nbax + nbaw/2,
cy = nbay + nbah/2;
var angle = svgedit.utilities.getRotationAngle(selected);
if (angle) {
var rot = svgFactory_.svgRoot().createSVGTransform();
rot.setRotate(-angle, cx, cy);
var rotm = rot.matrix;
nbox.tl = svgedit.math.transformPoint(nbox.tl.x, nbox.tl.y, rotm);
nbox.tr = svgedit.math.transformPoint(nbox.tr.x, nbox.tr.y, rotm);
nbox.bl = svgedit.math.transformPoint(nbox.bl.x, nbox.bl.y, rotm);
nbox.br = svgedit.math.transformPoint(nbox.br.x, nbox.br.y, rotm);
// calculate the axis-aligned bbox
var tl = nbox.tl;
var minx = tl.x,
miny = tl.y,
maxx = tl.x,
maxy = tl.y;
var min = Math.min, max = Math.max;
minx = min(minx, min(nbox.tr.x, min(nbox.bl.x, nbox.br.x) ) ) - offset;
miny = min(miny, min(nbox.tr.y, min(nbox.bl.y, nbox.br.y) ) ) - offset;
maxx = max(maxx, max(nbox.tr.x, max(nbox.bl.x, nbox.br.x) ) ) + offset;
maxy = max(maxy, max(nbox.tr.y, max(nbox.bl.y, nbox.br.y) ) ) + offset;
nbax = minx;
nbay = miny;
nbaw = (maxx-minx);
nbah = (maxy-miny);
}
var sr_handle = svgFactory_.svgRoot().suspendRedraw(100);
var dstr = 'M' + nbax + ',' + nbay
+ ' L' + (nbax+nbaw) + ',' + nbay
+ ' ' + (nbax+nbaw) + ',' + (nbay+nbah)
+ ' ' + nbax + ',' + (nbay+nbah) + 'z';
// alert(dstr);
selectedBox.setAttribute('d', dstr);
var xform = angle ? 'rotate(' + [angle, cx, cy].join(',') + ')' : '';
this.selectorGroup.setAttribute('transform', xform);
// TODO(codedread): Is this if needed?
// if (selected === selectedElements[0]) {
this.gripCoords = {
'nw': [nbax, nbay],
'ne': [nbax+nbaw, nbay],
'sw': [nbax, nbay+nbah],
'se': [nbax+nbaw, nbay+nbah],
'n': [nbax + (nbaw)/2, nbay],
'w': [nbax, nbay + (nbah)/2],
'e': [nbax + nbaw, nbay + (nbah)/2],
's': [nbax + (nbaw)/2, nbay + nbah]
};
var width_rect = nbaw-offset;
var height_rect = nbah-offset;
var conversation =$('#measurement_det').val();
// alert(conversation);
if(conversation =='Meter') {
var width_gets = (parseFloat(width_rect)/100).toFixed(2)+'m';
var height_gets = (parseFloat(height_rect)/100).toFixed(2)+'m';
}
if(conversation =='Feet') {
var width_gets = (parseFloat(width_rect)/100)*3.2808399;
var feet_w = Math.floor(width_gets);
var inches_w = Math.round((width_gets - feet_w) * 12);
var width_gets = feet_w + "'" + inches_w + '"';
var width_gets = width_gets+'ft';
var height_gets = (parseFloat(height_rect)/100)*3.2808399;
var feet_h = Math.floor(height_gets);
var inches_h = Math.round((height_gets - feet_h) * 12);
var height_gets = feet_h + "'" + inches_h + '"';
var height_gets = height_gets+'ft';
}
if(selected.tagName=='rect')
{
var rect_text_id=selected.parentNode.id;
var rect_text_id1=rect_text_id.split('_');
}
if(selected.tagName=='g')
{
var sslice = selected.childNodes;
if(sslice[0].tagName=='rect')
{
var rect_text_id=sslice[0].parentNode.id;
var rect_text_id1=rect_text_id.split('_');
}
}
var dir;
for (dir in this.gripCoords) {
var coords = this.gripCoords[dir];
if((dir =='n') || (dir =='e') || (dir =='s') || (dir =='w')) {
/* selectedGrips[dir].setAttribute('cx', coords[0]);
selectedGrips[dir].setAttribute('cy', coords[1]); */
/* $('#selectorGrip_resize_n').html('');
$('#selectorGrip_resize_s').html(''); */
if(selected.childNodes[0] && selected.childNodes[0].tagName=='rect')
{
$('#meter_range_rect'+rect_text_id1[3]+'_n').text(width_gets);
$('#meter_range_rect'+rect_text_id1[3]+'_n').attr({'font-weight':''});
$('#meter_range_rect'+rect_text_id1[3]+'_s').text(width_gets);
$('#meter_range_rect'+rect_text_id1[3]+'_s').attr({'font-weight':''});
}
if(dir =='w') {
selectedGrips[dir].setAttribute('transform', 'rotate(-90,'+coords[0]+','+coords[1]+')');
// $('#selectorGrip_resize_w').html('');
if(selected.childNodes[0] && selected.childNodes[0].tagName=='rect')
{
$('#meter_range_rect'+rect_text_id1[3]+'_w').text(height_gets);
$('#meter_range_rect'+rect_text_id1[3]+'_w').attr({'font-weight':''});
}
}
if(dir =='e') {
selectedGrips[dir].setAttribute('transform', 'rotate(-90,'+coords[0]+','+coords[1]+')');
// $('#selectorGrip_resize_e').html('');
if(selected.childNodes[0] && selected.childNodes[0].tagName=='rect')
{
$('#meter_range_rect'+rect_text_id1[3]+'_e').text(height_gets);
$('#meter_range_rect'+rect_text_id1[3]+'_e').attr({'font-weight':''});
}
}
selectedGrips[dir].setAttribute('cx', coords[0]);
selectedGrips[dir].setAttribute('cy', coords[1]);
}
else {
selectedGrips[dir].setAttribute('cx', coords[0]);
selectedGrips[dir].setAttribute('cy', coords[1]);
}
}
// we want to go 20 pixels in the negative transformed y direction, ignoring scale
mgr.rotateGripConnector.setAttribute('x1', nbax + (nbaw)/2);
mgr.rotateGripConnector.setAttribute('y1', nbay);
mgr.rotateGripConnector.setAttribute('x2', nbax + (nbaw)/2);
mgr.rotateGripConnector.setAttribute('y2', nbay - (gripRadius*5));
mgr.rotateGrip.setAttribute('cx', nbax + (nbaw)/2);
mgr.rotateGrip.setAttribute('cy', nbay - (gripRadius*5));
// }
svgFactory_.svgRoot().unsuspendRedraw(sr_handle);
};
// Class: svgedit.select.SelectorManager
svgedit.select.SelectorManager = function() {
// this will hold the <g> element that contains all selector rects/grips
this.selectorParentGroup = null;
// this is a special rect that is used for multi-select
this.rubberBandBox = null;
// this will hold objects of type svgedit.select.Selector (see above)
this.selectors = [];
// this holds a map of SVG elements to their Selector object
this.selectorMap = {};
// this holds a reference to the grip elements
this.selectorGrips = {
'nw': null,
'n' : null,
'ne': null,
'e' : null,
'se': null,
's' : null,
'sw': null,
'w' : null
};
this.selectorGripsGroup = null;
this.rotateGripConnector = null;
this.rotateGrip = null;
this.initGroup();
};
// Function: svgedit.select.SelectorManager.initGroup
// Resets the parent selector group element
svgedit.select.SelectorManager.prototype.initGroup = function() {
// remove old selector parent group if it existed
if (this.selectorParentGroup && this.selectorParentGroup.parentNode) {
this.selectorParentGroup.parentNode.removeChild(this.selectorParentGroup);
}
// create parent selector group and add it to svgroot
this.selectorParentGroup = svgFactory_.createSVGElement({
'element': 'g',
'attr': {'id': 'selectorParentGroup'}
});
this.selectorGripsGroup = svgFactory_.createSVGElement({
'element': 'g',
'attr': {'display': 'none'}
});
this.selectorParentGroup.appendChild(this.selectorGripsGroup);
svgFactory_.svgRoot().appendChild(this.selectorParentGroup);
this.selectorMap = {};
this.selectors = [];
this.rubberBandBox = null;
// add the corner grips
var dir;
for (dir in this.selectorGrips) {
var grip = svgFactory_.createSVGElement({
'element': 'circle',
'attr': {
'id': ('selectorGrip_resize_' + dir),
'fill': '#04739E',
'r': gripRadius,
'style': ('cursor:' + dir + '-resize'),
// This expands the mouse-able area of the grips making them
// easier to grab with the mouse.
// This works in Opera and WebKit, but does not work in Firefox
// see https://bugzilla.mozilla.org/show_bug.cgi?id=500174
'stroke-width': 2,
'pointer-events': 'all'
}
});
/* if((dir =='n') || (dir =='e') || (dir =='s') || (dir =='w')) {
var grip = svgFactory_.createSVGElement({
'element': 'text',
'attr': {
'id': ('selectorGrip_resize_' + dir),
'fill': '#3F7F00',
// 'r': gripRadius,
'style': ('cursor:' + dir + '-resize'),
// This expands the mouse-able area of the grips making them
// easier to grab with the mouse.
// This works in Opera and WebKit, but does not work in Firefox
// see https://bugzilla.mozilla.org/show_bug.cgi?id=500174
'stroke-width': 2,
'pointer-events': 'all'
}
});
grip.textContent = '';
}else {
var grip = svgFactory_.createSVGElement({
'element': 'circle',
'attr': {
'id': ('selectorGrip_resize_' + dir),
'fill': '#04739E',
'r': gripRadius,
'style': ('cursor:' + dir + '-resize'),
// This expands the mouse-able area of the grips making them
// easier to grab with the mouse.
// This works in Opera and WebKit, but does not work in Firefox
// see https://bugzilla.mozilla.org/show_bug.cgi?id=500174
'stroke-width': 2,
'pointer-events': 'all'
}
});
}
*/
$.data(grip, 'dir', dir);
$.data(grip, 'type', 'resize');
this.selectorGrips[dir] = this.selectorGripsGroup.appendChild(grip);
}
// add rotator elems
this.rotateGripConnector = this.selectorGripsGroup.appendChild(
svgFactory_.createSVGElement({
'element': 'line',
'attr': {
'id': ('selectorGrip_rotateconnector'),
'stroke': '#22C',
'stroke-width': '0'
}
})
);
this.rotateGrip = this.selectorGripsGroup.appendChild(
svgFactory_.createSVGElement({
'element': 'circle',
'attr': {
'id': 'selectorGrip_rotate',
'fill': 'url(#rotate)',
'r': '15',
'stroke': '#22C',
'stroke-width': 2,
'style': "cursor:url(" + config_.imgPath + "rotate.png) 12 12, auto;"
}
})
);
$.data(this.rotateGrip, 'type', 'rotate');
if ($('#canvasBackground').length) {return;}
var dims = config_.dimensions;
var canvasbg = svgFactory_.createSVGElement({
'element': 'svg',
'attr': {
'id': 'canvasBackground',
'width': dims[0],
'height': dims[1],
'x': 0,
'y': 0,
'overflow': (svgedit.browser.isWebkit() ? 'none' : 'visible'), // Chrome 7 has a problem with this when zooming out
'style': 'pointer-events:none'
}
});
var rect = svgFactory_.createSVGElement({
'element': 'rect',
'attr': {
'width': '100%',
'height': '100%',
'x': 0,
'y': 0,
'stroke-width': 1,
'stroke': '#000',
'fill': '#FFF',
'style': 'pointer-events:none'
}
});
// Both Firefox and WebKit are too slow with this filter region (especially at higher
// zoom levels) and Opera has at least one bug
// if (!svgedit.browser.isOpera()) rect.setAttribute('filter', 'url(#canvashadow)');
canvasbg.appendChild(rect);
svgFactory_.svgRoot().insertBefore(canvasbg, svgFactory_.svgContent());
};
// Function: svgedit.select.SelectorManager.requestSelector
// Returns the selector based on the given element
//
// Parameters:
// elem - DOM element to get the selector for
svgedit.select.SelectorManager.prototype.requestSelector = function(elem) {
if (elem == null) {return null;}
var i,
N = this.selectors.length;
// If we've already acquired one for this element, return it.
if (typeof(this.selectorMap[elem.id]) == 'object') {
this.selectorMap[elem.id].locked = true;
$("#hide_curtselid").val(elem.id);
return this.selectorMap[elem.id];
}
for (i = 0; i < N; ++i) {
if (this.selectors[i] && !this.selectors[i].locked) {
this.selectors[i].locked = true;
this.selectors[i].reset(elem);
this.selectorMap[elem.id] = this.selectors[i];
return this.selectors[i];
}
}
// if we reached here, no available selectors were found, we create one
this.selectors[N] = new svgedit.select.Selector(N, elem);
this.selectorParentGroup.appendChild(this.selectors[N].selectorGroup);
this.selectorMap[elem.id] = this.selectors[N];
return this.selectors[N];
};
// Function: svgedit.select.SelectorManager.requestSelectors
// Returns the selector based on the given Rect and line and Path
//
// Parameters:
// elem - DOM element to get the selector for
svgedit.select.SelectorManager.prototype.requestSelectors = function(elem) {
if (elem == null) {return null;}
var i,
N = this.selectors.length;
// console.log(elem);
// If we've already acquired one for this element, return it.
if (typeof(this.selectorMap[elem.id]) == 'object') {
this.selectorMap[elem.id].locked = true;
$("#hide_curtselid").val(elem.id);
return this.selectorMap[elem.id];
}
for (i = 0; i < N; ++i) {
if (this.selectors[i] && !this.selectors[i].locked) {
this.selectors[i].locked = true;
this.selectors[i].reset(elem);
this.selectorMap[elem.id] = this.selectors[i];
return this.selectors[i];
}
}
// if we reached here, no available selectors were found, we create one
this.selectors[N] = new svgedit.select.Selector(N, elem);
this.selectorParentGroup.appendChild(this.selectors[N].selectorGroup);
this.selectorMap[elem.id] = this.selectors[N];
return this.selectors[N];
};
// Function: svgedit.select.SelectorManager.releaseSelector
// Removes the selector of the given element (hides selection box)
//
// Parameters:
// elem - DOM element to remove the selector for
svgedit.select.SelectorManager.prototype.releaseSelector = function(elem) {
if (elem == null) {return;}
var i,
N = this.selectors.length,
sel = this.selectorMap[elem.id];
for (i = 0; i < N; ++i) {
if (this.selectors[i] && this.selectors[i] == sel) {
if (sel.locked == false) {
// TODO(codedread): Ensure this exists in this module.
console.log('WARNING! selector was released but was already unlocked');
}
delete this.selectorMap[elem.id];
sel.locked = false;
sel.selectedElement = null;
sel.showGrips(false);
// remove from DOM and store reference in JS but only if it exists in the DOM
try {
sel.selectorGroup.setAttribute('display', 'none');
} catch(e) { }
break;
}
}
};
// Function: svgedit.select.SelectorManager.getRubberBandBox
// Returns the rubberBandBox DOM element. This is the rectangle drawn by the user for selecting/zooming
svgedit.select.SelectorManager.prototype.getRubberBandBox = function() {
if (!this.rubberBandBox) {
this.rubberBandBox = this.selectorParentGroup.appendChild(
svgFactory_.createSVGElement({
'element': 'path',
'attr': {
'id': 'selectorRubberBand',
'fill': '#22C',
'fill-opacity': 0.15,
'stroke': '#22C',
'stroke-width': 10.5,
'display': 'block',
'style': 'pointer-events:none'
}
})
);
}
return this.rubberBandBox;
};
/**
* Interface: svgedit.select.SVGFactory
* An object that creates SVG elements for the canvas.
*
* interface svgedit.select.SVGFactory {
* SVGElement createSVGElement(jsonMap);
* SVGSVGElement svgRoot();
* SVGSVGElement svgContent();
*
* Number currentZoom();
* Object getStrokedBBox(Element[]); // TODO(codedread): Remove when getStrokedBBox() has been put into svgutils.js
* }
*/
/**
* Function: svgedit.select.init()
* Initializes this module.
*
* Parameters:
* config - an object containing configurable parameters (imgPath)
* svgFactory - an object implementing the SVGFactory interface (see above).
*/
svgedit.select.init = function(config, svgFactory) {
config_ = config;
svgFactory_ = svgFactory;
selectorManager_ = new svgedit.select.SelectorManager();
};
/**
* Function: svgedit.select.getSelectorManager
*
* Returns:
* The SelectorManager instance.
*/
svgedit.select.getSelectorManager = function() {
return selectorManager_;
};
}());
Dynamic create <g> tags based
<g id="selectorParentGroup" >
<rect id="selectorRubberBand" fill="#22C" fill-opacity="0.15" stroke="#22C" stroke-width="10.5" display="none" style="pointer-events:none" x="2034" y="909.800048828125" width="0" height="0"/>
<path id="selectedBox0" fill="none" stroke="#22C" stroke-dasharray="5,5" style="pointer-events:none" d="M2057,948.716552734375 L2280.5,948.716552734375 2280.5,1092 2057,1092z"/>
</g>
Just check both attached images, we are on the first stage (image) we to make it us the second stage - Selection area should be in over boundary as in secondary image (should be as image right side). Kindly assist me.
All suggestion will be highly appreciated.

Apply drag and drop/attach feature to JSTreegraph using Jquery draggable

I am using JSTreegraph plugin to draw a tree structure.
But now I need a drag, drop and attach feature wherein I can drag any node of the tree and attach to any other node and subsequently all the children of the first node will be now the grand-children of the new node (to which it is attached).
As far as I know this plugin doesnt seem to have this feature. It simply draws the structure based on the data object passed to it.
The plugin basically assigns a class Node to all the nodes(divs) of the tree and another class NodeHover to a node on hover. No id is assigned to these divs.
So I tried using JQuery Draggable just to see if any of the node can be moved by doing this
$('.Node').draggable();
$('.NodeHover').draggable();
But it doesnt seem to work. So can anyone help me with this.
How can I get drag and attach functionality?
*EDIT:: Pardon me am not so great with using Fiddle, so am sharing a sample code here for your use:*
HTML file: which will draw the sample tree
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link href="css/JSTreeGraph.css" rel="stylesheet" type="text/css" />
<script src="js/JSTreeGraph.js" type="text/javascript"></script>
<script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>
<script src="js/jquery.ui.position.js" type="text/javascript"></script>
<style type="text/css">
.Container {
position: absolute;
top: 100px;
left: 50px;
id: Container;
}
</style>
</head>
<body>
<div id="tree">
Ctrl+Click to Add Node
<br />
Double Click to Expand or Collapse
<br />
Shift+Click to Delete Node
<br />
<select id="dlLayout" onchange="ChangeLayout()">
<option value="Horizontal">
Horizontal
</option>
<option value="Vertical" selected>
Vertical
</option>
</select>
<div class="Container" id="dvTreeContainer"></div>
<script type="text/javascript">
var selectedNode;
// Root node
var rootNode = { Content: "Onida", Nodes:[] };
// First Level
rootNode.Nodes[0] = { Content: "Employee Code", navigationType: "0"};
rootNode.Nodes[1] = { Content: "Problem Area", navigationType: "1" };
// Second Level
rootNode.Nodes[1].Nodes = [{ Content : "ACC-HO", Collapsed: true /* This node renders collapsed */ },
{ Content : "ACC-SALES" },
{ Content : "BUSI. HEAD", /*This node looks different*/ ToolTip: "Click ME!" },
{ Content : "CEO"},
{ Content : "HO-ADMIN"},
{ Content : "HO-FACTORY"},
{ Content : "SALES"}];
// Third Level
rootNode.Nodes[1].Nodes[0].Nodes = [{ Content: "Billing" },
{ Content: "Credit Limit" },
{ Content: "Reconciliation" }];
rootNode.Nodes[1].Nodes[1].Nodes = [{ Content: "Billing" },
{ Content: "Others" }];
rootNode.Nodes[1].Nodes[2].Nodes = [{ Content: "AC" },
{ Content: "CTV" },
{ Content: "DVD" },
{ Content: "Washing Machine" }];
rootNode.Nodes[1].Nodes[6].Nodes = [{ Content: "Appointments" },
{ Content: "Resignations" },
{ Content: "Others" }];
// Draw the tree for the first time
RefreshTree();
function RefreshTree() {
DrawTree({ Container: document.getElementById("dvTreeContainer"),
RootNode: rootNode,
Layout: document.getElementById("dlLayout").value,
OnNodeClickFirefox: NodeClickFF,
OnNodeClickIE: NodeClickIE,
OnNodeDoubleClick: NodeDoubleClick
});
}
//function
function NodeClickFF(e) {
if (e.shiftKey){
// Delete Node
if (!this.Node.Collapsed) {
for(var index=0; index<this.Node.ParentNode.Nodes.length; index++) {
if(this.Node.ParentNode.Nodes[index].Content == this.Node.Content) {
this.Node.ParentNode.Nodes.splice(index, 1);
break;
}
}
RefreshTree();
}
// return false;
}
else if (e.ctrlKey) {
// Add new Child if Expanded
if (!this.Node.Collapsed) {
if (!this.Node.Nodes) this.Node.Nodes = new Array();
var newNodeIndex = this.Node.Nodes.length;
this.Node.Nodes[newNodeIndex] = new Object();
this.Node.Nodes[newNodeIndex].Content = this.Node.Content + "." + (newNodeIndex + 1);
RefreshTree();
}
// return false;
}
else{
fnNodeProperties(this.Node);
}
}
function NodeClickIE() {
if (typeof(event) == "undefined" && event.ctrlKey) {
// Add new Child if Expanded
if (!this.Node.Collapsed) {
if (!this.Node.Nodes) this.Node.Nodes = new Array();
var newNodeIndex = this.Node.Nodes.length;
this.Node.Nodes[newNodeIndex] = new Object();
this.Node.Nodes[newNodeIndex].Content = this.Node.Content + "." + (newNodeIndex + 1);
RefreshTree();
}
}
else if (typeof(event) == "undefined" && event.shiftKey) {
// Delete Node
if (!this.Node.Collapsed) {
for(var index=0; index<this.Node.ParentNode.Nodes.length; index++) {
if(this.Node.ParentNode.Nodes[index].Content == this.Node.Content) {
this.Node.ParentNode.Nodes.splice(index, 1);
break;
}
}
RefreshTree();
}
}
else{
fnNodeProperties(this.Node);
}
}
function NodeDoubleClick() {
if (this.Node.Nodes && this.Node.Nodes.length > 0) { // If has children
this.Node.Collapsed = !this.Node.Collapsed;
RefreshTree();
}
}
function ChangeLayout() {
RefreshTree();
}
</script>
</div>
</body>
</html>
JSTreeGraph JS file: plugin js file
function DrawTree(options) {
// Prepare Nodes
PrepareNode(options.RootNode);
// Calculate Boxes Positions
if (options.Layout == "Vertical") {
PerformLayoutV(options.RootNode);
} else {
PerformLayoutH(options.RootNode);
}
// Draw Boxes
options.Container.innerHTML = "";
DrawNode(options.RootNode, options.Container, options);
// Draw Lines
DrawLines(options.RootNode, options.Container);
}
function DrawLines(node, container) {
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has children and Is Expanded
for (var j = 0; j < node.Nodes.length; j++) {
if(node.ChildrenConnectorPoint.Layout=="Vertical")
DrawLineV(container, node.ChildrenConnectorPoint, node.Nodes[j].ParentConnectorPoint);
else
DrawLineH(container, node.ChildrenConnectorPoint, node.Nodes[j].ParentConnectorPoint);
// Children
DrawLines(node.Nodes[j], container);
}
}
}
function DrawLineH(container, startPoint, endPoint) {
var midY = (startPoint.Y + ((endPoint.Y - startPoint.Y) / 2)); // Half path between start en end Y point
// Start segment
DrawLineSegment(container, startPoint.X, startPoint.Y, startPoint.X, midY, 1);
// Intermidiate segment
var imsStartX = startPoint.X < endPoint.X ? startPoint.X : endPoint.X; // The lower value will be the starting point
var imsEndX = startPoint.X > endPoint.X ? startPoint.X : endPoint.X; // The higher value will be the ending point
DrawLineSegment(container, imsStartX, midY, imsEndX, midY, 1);
// End segment
DrawLineSegment(container, endPoint.X, midY, endPoint.X, endPoint.Y, 1);
}
function DrawLineV(container, startPoint, endPoint) {
var midX = (startPoint.X + ((endPoint.X - startPoint.X) / 2)); // Half path between start en end X point
// Start segment
DrawLineSegment(container, startPoint.X, startPoint.Y, midX, startPoint.Y, 1);
// Intermidiate segment
var imsStartY = startPoint.Y < endPoint.Y ? startPoint.Y : endPoint.Y; // The lower value will be the starting point
var imsEndY = startPoint.Y > endPoint.Y ? startPoint.Y : endPoint.Y; // The higher value will be the ending point
DrawLineSegment(container, midX, imsStartY, midX, imsEndY, 1);
// End segment
DrawLineSegment(container, midX, endPoint.Y, endPoint.X, endPoint.Y, 1);
}
function DrawLineSegment(container, startX, startY, endX, endY, lineWidth) {
var lineDiv = document.createElement("div");
lineDiv.style.top = startY + "px";
lineDiv.style.left = startX + "px";
if (startX == endX) { // Vertical Line
lineDiv.style.width = lineWidth + "px";
lineDiv.style.height = (endY - startY) + "px";
}
else{ // Horizontal Line
lineDiv.style.width = (endX - startX) + "px";
lineDiv.style.height = lineWidth + "px";
}
lineDiv.className = "NodeLine";
container.appendChild(lineDiv);
}
function DrawNode(node, container, options) {
var nodeDiv = document.createElement("div");
nodeDiv.style.top = node.Top + "px";
nodeDiv.style.left = node.Left + "px";
nodeDiv.style.width = node.Width + "px";
nodeDiv.style.height = node.Height + "px";
if (node.Collapsed) {
nodeDiv.className = "NodeCollapsed";
} else {
nodeDiv.className = "Node";
}
if (node.Class)
nodeDiv.className = node.Class;
if (node.Content)
nodeDiv.innerHTML = "<div class='NodeContent'>" + node.Content + "</div>";
if (node.ToolTip)
nodeDiv.setAttribute("title", node.ToolTip);
nodeDiv.Node = node;
// Events
if (options.OnNodeClickIE){
//alert('OnNodeClick');
nodeDiv.onclick = options.OnNodeClickIE;
}
// Events
if (options.OnNodeClickFirefox){
//alert('OnNodeClick');
nodeDiv.onmousedown = options.OnNodeClickFirefox;
}
//on right click
if (options.OnContextMenu){
//alert('OnContextMenu');
nodeDiv.oncontextmenu = options.OnContextMenu;
}
if (options.OnNodeDoubleClick)
nodeDiv.ondblclick = options.OnNodeDoubleClick;
nodeDiv.onmouseover = function () { // In
this.PrevClassName = this.className;
this.className = "NodeHover";
};
nodeDiv.onmouseout = function () { // Out
if (this.PrevClassName) {
this.className = this.PrevClassName;
this.PrevClassName = null;
}
};
container.appendChild(nodeDiv);
// Draw children
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has Children and is Expanded
for (var i = 0; i < node.Nodes.length; i++) {
DrawNode(node.Nodes[i], container, options);
}
}
}
function PerformLayoutV(node) {
var nodeHeight = 30;
var nodeWidth = 100;
var nodeMarginLeft = 40;
var nodeMarginTop = 20;
var nodeTop = 0; // defaultValue
// Before Layout this Node, Layout its children
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) {
for (var i = 0; i < node.Nodes.length; i++) {
PerformLayoutV(node.Nodes[i]);
}
}
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // If Has Children and Is Expanded
// My Top is in the center of my children
var childrenHeight = (node.Nodes[node.Nodes.length - 1].Top + node.Nodes[node.Nodes.length - 1].Height) - node.Nodes[0].Top;
nodeTop = (node.Nodes[0].Top + (childrenHeight / 2)) - (nodeHeight / 2);
// Is my top over my previous sibling?
// Move it to the bottom
if (node.LeftNode && ((node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop) > nodeTop)) {
var newTop = node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop;
var diff = newTop - nodeTop;
/// Move also my children
MoveBottom(node.Nodes, diff);
nodeTop = newTop;
}
} else {
// My top is next to my top sibling
if (node.LeftNode)
nodeTop = node.LeftNode.Top + node.LeftNode.Height + nodeMarginTop;
}
node.Top = nodeTop;
// The Left depends only on the level
node.Left = (nodeMarginLeft * (node.Level + 1)) + (nodeWidth * (node.Level + 1));
// Size is constant
node.Height = nodeHeight;
node.Width = nodeWidth;
// Calculate Connector Points
// Child: Where the lines get out from to connect this node with its children
var pointX = node.Left + nodeWidth;
var pointY = nodeTop + (nodeHeight/2);
node.ChildrenConnectorPoint = { X: pointX, Y: pointY, Layout: "Vertical" };
// Parent: Where the line that connect this node with its parent end
pointX = node.Left;
pointY = nodeTop + (nodeHeight/2);
node.ParentConnectorPoint = { X: pointX, Y: pointY, Layout: "Vertical" };
}
function PerformLayoutH(node) {
var nodeHeight = 30;
var nodeWidth = 100;
var nodeMarginLeft = 20;
var nodeMarginTop = 50;
var nodeLeft = 0; // defaultValue
// Before Layout this Node, Layout its children
if ((!node.Collapsed) && node.Nodes && node.Nodes.length>0) {
for (var i = 0; i < node.Nodes.length; i++) {
PerformLayoutH(node.Nodes[i]);
}
}
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // If Has Children and Is Expanded
// My left is in the center of my children
var childrenWidth = (node.Nodes[node.Nodes.length-1].Left + node.Nodes[node.Nodes.length-1].Width) - node.Nodes[0].Left;
nodeLeft = (node.Nodes[0].Left + (childrenWidth / 2)) - (nodeWidth / 2);
// Is my left over my left node?
// Move it to the right
if(node.LeftNode&&((node.LeftNode.Left+node.LeftNode.Width+nodeMarginLeft)>nodeLeft)) {
var newLeft = node.LeftNode.Left + node.LeftNode.Width + nodeMarginLeft;
var diff = newLeft - nodeLeft;
/// Move also my children
MoveRigth(node.Nodes, diff);
nodeLeft = newLeft;
}
} else {
// My left is next to my left sibling
if (node.LeftNode)
nodeLeft = node.LeftNode.Left + node.LeftNode.Width + nodeMarginLeft;
}
node.Left = nodeLeft;
// The top depends only on the level
node.Top = (nodeMarginTop * (node.Level + 1)) + (nodeHeight * (node.Level + 1));
// Size is constant
node.Height = nodeHeight;
node.Width = nodeWidth;
// Calculate Connector Points
// Child: Where the lines get out from to connect this node with its children
var pointX = nodeLeft + (nodeWidth / 2);
var pointY = node.Top + nodeHeight;
node.ChildrenConnectorPoint = { X: pointX, Y: pointY, Layout:"Horizontal" };
// Parent: Where the line that connect this node with its parent end
pointX = nodeLeft + (nodeWidth / 2);
pointY = node.Top;
node.ParentConnectorPoint = { X: pointX, Y: pointY, Layout: "Horizontal" };
}
function MoveRigth(nodes, distance) {
for (var i = 0; i < nodes.length; i++) {
nodes[i].Left += distance;
if (nodes[i].ParentConnectorPoint) nodes[i].ParentConnectorPoint.X += distance;
if (nodes[i].ChildrenConnectorPoint) nodes[i].ChildrenConnectorPoint.X += distance;
if (nodes[i].Nodes) {
MoveRigth(nodes[i].Nodes, distance);
}
}
}
function MoveBottom(nodes, distance) {
for (var i = 0; i < nodes.length; i++) {
nodes[i].Top += distance;
if (nodes[i].ParentConnectorPoint) nodes[i].ParentConnectorPoint.Y += distance;
if (nodes[i].ChildrenConnectorPoint) nodes[i].ChildrenConnectorPoint.Y += distance;
if (nodes[i].Nodes) {
MoveBottom(nodes[i].Nodes, distance);
}
}
}
function PrepareNode(node, level, parentNode, leftNode, rightLimits) {
if (level == undefined) level = 0;
if (parentNode == undefined) parentNode = null;
if (leftNode == undefined) leftNode = null;
if (rightLimits == undefined) rightLimits = new Array();
node.Level = level;
node.ParentNode = parentNode;
node.LeftNode = leftNode;
if ((!node.Collapsed) && node.Nodes && node.Nodes.length > 0) { // Has children and is expanded
for (var i = 0; i < node.Nodes.length; i++) {
var left = null;
if (i == 0 && rightLimits[level]!=undefined) left = rightLimits[level];
if (i > 0) left = node.Nodes[i - 1];
if (i == (node.Nodes.length-1)) rightLimits[level] = node.Nodes[i];
PrepareNode(node.Nodes[i], level + 1, node, left, rightLimits);
}
}
}
JSTreeGraph CSS file:
.NodeContent
{
font-family:Verdana;
font-size:small;
}
.Node
{
position:absolute;
background-color: #CCDAFF;
border: 1px solid #5280FF;
text-align:center;
vertical-align:middle;
cursor:pointer;
overflow:hidden;
}
.NodeHover
{
position:absolute;
background-color: #8FADFF;
border: 1px solid #5280FF;
text-align:center;
vertical-align:middle;
cursor:pointer;
overflow:hidden;
}
.NodeCollapsed
{
position:absolute;
background-color: #8FADFF;
border: 2px solid black;
text-align:center;
vertical-align:middle;
cursor:pointer;
overflow:hidden;
}
.NodeLine
{
background-color: #000066;
position:absolute;
overflow:hidden;
}
I supose that what you need is to stablish a mechanism to modify your tree logic structure on dragging and then reload the whole tree element. This way the plugin will render your new modified structure.
This is not an easy problem and you have not outlined the exact specifications.
When you move a node, should all child nodes move with it?
What happens to a node and its child elements when a node is dropped/attached?
The plugin that you are using works well for drawing static trees, but it is not written in such a way to allow for dynamically editing the tree.
I have to agree with #Bardo in that the easiest way to make this work for your needs is to understand how the tree has been manipulated. (Thankfully the plugin seems to provide an onNodeClick option which will allow you to understand which node you are intending to manipulate). Once the tree has been manipulated then it must be completely redrawn. (There doesn't appear to be a good way to partially draw a tree).

Categories

Resources