API Docs for: 2.0.0

public/src/widgetHelperFunctions.js

// SAGE2 is available for use under the SAGE2 Software License
//
// University of Illinois at Chicago's Electronic Visualization Laboratory (EVL)
// and University of Hawai'i at Manoa's Laboratory for Advanced Visualization and
// Applications (LAVA)
//
// See full text, terms and conditions in the LICENSE.txt included file
//
// Copyright (c) 2014

/* global controlObjects, controlItems */

"use strict";

/**
 * Provides widget controls and helper functionality for custom application user interface
 *
 * @module client
 * @submodule widgets
 */
var dynamicStyleSheets = {};
var svgBackgroundForWidgetConnectors = null;
var svgForegroundForWidgetConnectors = null;

function drawSpokeForRadialLayout(instanceID, paper, center, point) {
	var spoke = paper.line(center.x, center.y, point.x, point.y);
	spoke.attr({
		stroke: "rgba(250,250,250,1.0)",
		strokeWidth: 3,
		fill: "none"
	});
	spoke.data("instanceID", instanceID);
}

function drawBackgroundForWidgetRadialDial(instanceID, paper, center, radius) {
	var backGroundFill = paper.circle(center.x, center.y, radius);
	var backGroundStroke = paper.circle(center.x, center.y, radius);
	var grad = paper.gradient("r(0.5, 0.5, 0.40)rgba(150,166,189,0.8)-rgba(150,166,189,0.67)");
	backGroundFill.attr({
		id: instanceID + "backGround",
		fill: grad,
		stroke: "none"
	});
	var shadow = svgBackgroundForWidgetConnectors.filter(Snap.filter.shadow(0, 0, radius * 0.03, "rgb(220,220,220)", 1));
	backGroundStroke.attr({
		id: instanceID + "backGroundEdge",
		fill: "none",
		stroke: "rgba(220,220,220,0.8)",
		filter: shadow,
		strokeDasharray: "12,2",
		strokeWidth: radius * 0.03
	});
	backGroundFill.data("paper", paper);
	backGroundFill.data("instanceID", instanceID);
}

function drawWidgetControlCenter(instanceID, paper, center, radius) {
	var controlCenter = paper.circle(center.x, center.y, radius);
	controlCenter.attr({
		fill: "rgba(110,110,110,1.0)",
		stroke: "rgba(200,200,200,0.8)",
		id: instanceID + "menuCenter"
	});
	controlCenter.data("paper", paper);
	controlCenter.data("instanceID", instanceID);
}

function drawPieSlice(paper, start, end, innerR, outerR, center) {
	var pointA = polarToCartesian(innerR, start, center);
	var pointB = polarToCartesian(outerR, start, center);
	var pointC = polarToCartesian(outerR, end, center);
	var pointD = polarToCartesian(innerR, end, center);

	var d = "M " + pointA.x + " " + pointA.y + "L " + pointB.x + " " + pointB.y +
		"A " + outerR + " " + outerR + " " + 0 + " " + 0 + " " + 0 + " " + pointC.x +
		" " + pointC.y + "L " + pointD.x + " " + pointD.y +
		"A " + innerR + " " + innerR + " " + 0 + " " + 0 + " " + 1 + " " + pointA.x +
		" " + pointA.y + "";

	var groupBoundaryPath = paper.path(d);
	groupBoundaryPath.attr("class", "widgetBackground");
}

function makeWidgetBarOutlinePath(start, end, innerR, center, width, offset) {
	var center2 = {x: center.x + width, y: center.y};
	var pointA = polarToCartesian(innerR, start, center);
	var pointB = polarToCartesian(innerR, start, center2);
	var pointC = polarToCartesian(innerR, end, center2);
	var pointD = polarToCartesian(innerR, end, center);
	pointA.x += offset;
	pointB.x += offset;
	pointC.x += offset;
	pointD.x += offset;
	var d = "M " + pointA.x + " " + pointA.y + "L " + pointB.x + " " + pointB.y +
		"A " + innerR + " " + innerR + " " + 0 + " " + 0 + " " + 0 + " " + pointC.x +
		" " + pointC.y + "L " + pointD.x + " " + pointD.y +
		"A " + innerR + " " + innerR + " " + 0 + " " + 0 + " " + 1 + " " + pointA.x +
		" " + pointA.y + "";

	return {d: d, leftTop: pointA, leftBottom: pointD, rightTop: pointB, rightBottom: pointC};
}

function mapMoveToSlider(sliderKnob, position) {
	var slider     = sliderKnob.parent();
	var sliderLine = slider.select("line");
	var knobWidth  = sliderKnob.attr("width");
	var bound = sliderLine.getBBox();
	var left  = bound.x + knobWidth / 2.0;
	var right = bound.x2 - knobWidth / 2.0;
	var begin = slider.data('begin');
	// var end   = slider.data('end');
	var steps = slider.data('steps');
	var increments = slider.data('increments');

	if (position < left) {
		position = left;
	} else if (position > right) {
		position = right;
	}

	var deltaX = (right - left) / steps;
	var n = Math.floor(0.5 + (position - left) / deltaX);
	if (isNaN(n) === true) {
		n = 0;
	}
	return {sliderValue: begin + n * increments, newPosition: left + n * deltaX};
}

function insertTextIntoTextInputWidget(textInput, code, printable) {
	var textBox = textInput.select("rect");
	var boxWidth = textBox.attr("width");
	// var tAxVal = textInput.data("left");
	// var rightEnd = tAxVal + parseInt(textBox.attr("width"));
	// var position = textInput.data("blinkerPosition");
	var displayText = '';
	var ctrl = textInput.select("text");
	// var buf = textInput.data("text") || '';

	var head = textInput.data("head");
	var prefix = textInput.data("prefix");
	var suffix = textInput.data("suffix");
	var tail = textInput.data("tail");

	if (printable) {
		prefix = prefix + String.fromCharCode(code);
	} else {
		switch (code) {
			case 37: // left
				if (prefix.length > 0) {
					suffix = prefix.slice(-1) + suffix;
					prefix = prefix.slice(0, -1);
				} else if (head.length > 0) {
					suffix = head.slice(-1) + suffix;
					head = head.slice(0, -1);
				}
				break;
			case 39: // right
				if (suffix.length > 0) {
					prefix = prefix + suffix.slice(0, 1);
					suffix = suffix.slice(1);
				} else if (tail.length > 0) {
					prefix = prefix + tail.slice(0, 1);
					tail = tail.slice(1);
				}
				break;
			case 8: // backspace
				if (prefix.length > 0) {
					prefix = prefix.slice(0, -1);
				} else {
					head = head.slice(0, -1);
				}
				suffix = suffix + tail.slice(0, 1);
				tail = tail.slice(1);
				break;
			case 46: // delete
				if (suffix.length > 0) {
					suffix = suffix.slice(1) + tail.slice(0, 1);
				}
				tail = tail.slice(1);
				break;
		}
	}
	displayText = prefix + suffix;
	ctrl.attr("text", displayText);
	var textWidth = (displayText.length > 0) ? ctrl.getBBox().width : 0;
	while (textWidth > boxWidth - 5) {
		if (suffix.length > 0) {
			tail = suffix.slice(-1) + tail;
			suffix = suffix.slice(0, -1);
		} else {
			head = head + prefix.slice(0, 1);
			prefix = prefix.slice(1);
		}
		displayText = prefix + suffix;
		ctrl.attr("text", displayText);
		textWidth = (displayText.length > 0) ? ctrl.getBBox().width : 0;
	}
	ctrl.attr("text", "l");
	var extraspace = ctrl.getBBox().width;
	ctrl.attr("text", prefix + "l");
	// Trailing space is not considered to BBbox width, hence extraspace is a work around
	var bposition = (prefix.length > 0) ? ctrl.getBBox().width - extraspace : 0;
	var pth = "M " + (textInput.data("left") + bposition) + textInput.data("blinkerSuf");
	textInput.select("path").attr({path: pth});
	ctrl.attr("text", prefix + suffix);
	textInput.data("head", head);
	textInput.data("prefix", prefix);
	textInput.data("suffix", suffix);
	textInput.data("tail", tail);
}

function getTextFromTextInputWidget(textInput) {
	return textInput.data("head") + textInput.data("prefix") + textInput.data("suffix") + textInput.data("tail");
}

function getWidgetControlInstanceById(ctrl) {
	var svgElements = Snap.selectAll('*');
	var requestedSvgElement = null;
	for (var l = 0; l < svgElements.length; l++) {
		var parent = svgElements[l].parent();
		// dummy value to guard against undefined entries
		var id = svgElements[l].attr("id") || "id";
		if (id.indexOf(ctrl.ctrlId) > -1 && svgElements[l].data("appId") === ctrl.appId &&
				parent.data("instanceID") === ctrl.instanceID) {
			requestedSvgElement = svgElements[l];
			break;
		}
	}
	return requestedSvgElement;
}

function getPropertyHandle(objectHandle, property) {
	var names = property.split('.');
	var handle  = objectHandle;
	var i = 1;
	for (; i < names.length - 1; i++) {
		handle = handle[names[i]];
	}
	return {handle: handle, property: names[i]};
}

function getWidgetControlInstanceUnderPointer(data, offsetX, offsetY) {
	var pointerElement = document.getElementById(data.ptrId);
	pointerElement.style.left = (parseInt(pointerElement.style.left) + 10000) + "px";
	var widgetControlUnderPointer = Snap.getElementByPoint(data.x - offsetX, data.y - offsetY);
	pointerElement.style.left = (parseInt(pointerElement.style.left) - 10000) + "px";
	return widgetControlUnderPointer;
}


function polarToCartesian(radius, theta, center) {
	theta = theta * Math.PI / 180.0;
	if (center === undefined || center === null) {
		center = {x: 0, y: 0};
	}
	var x = center.x + radius * Math.cos(theta);
	var y = center.y - radius * Math.sin(theta);
	return {x: x, y: y};
}

function cartesianToPolar(x, y, center) {
	if (center === undefined || center === null) {
		center = {x: 0, y: 0};
	}
	var radius = Math.sqrt((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y));
	var theta = Math.acos((x - center.x) / radius) * 180.0 / Math.PI;
	return {r: radius, theta: theta};
}

function thetaFromY(y, radius, center) {
	if (center === undefined || center === null) {
		center = {x: 0, y: 0};
	}
	return Math.asin((center.y - y) / radius) * 180.0 / Math.PI;
}

function createWidgetToAppConnector(instanceID) {
	var paper = svgBackgroundForWidgetConnectors;
	var connector = paper.line(0, 0, 0, 0);
	var shadow = svgBackgroundForWidgetConnectors.filter(Snap.filter.shadow(0, 0, 8, "rgb(220,220,220)", 1));
	connector.attr({
		id: instanceID + "link",
		strokeWidth: ui.widgetControlSize * 0.18,
		filter: shadow
	});
}

function addStyleElementForTitleColor(caption, color) {
	if (color !== null && color !== undefined) {
		dynamicStyleSheets[caption] = caption;
		var sheet = document.createElement('style');
		sheet.id = "title" + caption;
		var percent = 10;
		if (typeof color !== 'string'  && !(color instanceof String)) {
			color = '#666666';
		}
		sheet.innerHTML = ".title" + caption +
			" { position:absolute;	border: solid 1px #000000; overflow: hidden; box-shadow: 8px 0px 15px #222222;" +
			"background-image: -webkit-linear-gradient(left," + color + " " + percent + "%, #666666 100%); " +
			"background-image:    -moz-linear-gradient(left," + color + " " + percent + "%, #666666 100%); " +
			"background-image:     -ms-linear-gradient(left," + color + " " + percent + "%, #666666 100%); " +
			"background-image:      -o-linear-gradient(left," + color + " " + percent + "%, #666666 100%); " +
			"background-image:         linear-gradient(left," + color + " " + percent + "%, #666666 100%); }";
		document.body.appendChild(sheet);
	}
}

function removeStyleElementForTitleColor(caption) {
	var sheet = document.getElementById("title" + caption);
	if (sheet) {
		sheet.parentNode.removeChild(sheet);
		delete dynamicStyleSheets[caption];
	}
}

function hideWidgetToAppConnectors(appId) {
	var selectedAppTitle;
	if (appId in controlObjects) {
		selectedAppTitle = document.getElementById(appId + "_title");
		selectedAppTitle.className = "windowTitle";
		for (var item in controlItems) {
			if (item.indexOf(appId) > -1) {
				clearConnectorColor(item, appId);
			}
		}
	}
}

function clearConnectorColor(instanceID, appId) {
	var connector = Snap.select("[id*=\"" + instanceID + "link\"]");
	if (connector) {
		connector.attr({
			stroke: "none",
			fill: "none"
		});
	}
	var selectedControl = Snap.select("[id*=\"" + instanceID + "backGroundEdge\"]");
	if (selectedControl) {
		selectedControl.attr({
			stroke: "rgba(220,220,220,0.8)"
		});
	}
	if (appId in controlObjects) {

		var selectedAppTitle = document.getElementById(appId + "_title");
		selectedAppTitle.className = "windowTitle";
	}
}

function moveWidgetToAppConnector(instanceID, x1, y1, x2, y2, cutLength) {
	var a = Math.abs(x1 - x2);
	var b = Math.abs(y1 - y2);
	var width = Math.sqrt(a * a + b * b);
	if (parseInt(width) === 0) {
		return;
	}
	var alpha = (cutLength) / width;
	x1 = alpha * x2 + (1 - alpha) * x1;
	y1 = alpha * y2 + (1 - alpha) * y1;

	var connector = Snap.select("[id*=\"" + instanceID + "link\"]");
	if (connector) {
		connector.attr({
			x1: x1,
			y1: y1,
			x2: x2,
			y2: y2
		});
	}
}

function removeWidgetToAppConnector(instanceID) {
	var connector = Snap.select("[id*=\"" + instanceID + "link\"]");
	if (connector) {
		connector.remove();
	}
}

function setConnectorColor(instanceID, color) {
	if (!color) {
		color = '#666666';
	}
	var connector = Snap.select("[id*=\"" + instanceID + "link\"]");
	if (connector) {
		connector.attr({
			stroke: color
		});
	}
	var selectedControl = Snap.select("[id*=\"" + instanceID + "backGroundEdge\"]");
	if (selectedControl) {
		selectedControl.attr("stroke", color);
	}
}

function showWidgetToAppConnectors(data) {
	var selectedAppTitle, re, styleCaption;
	selectedAppTitle = document.getElementById(data.id + "_title");
	if (!selectedAppTitle) {
		return;
	}
	re = /\.|:/g;
	styleCaption = data.user_id.split(re).join("");
	selectedAppTitle.className = dynamicStyleSheets[styleCaption] ? "title" + styleCaption : "windowTitle";
	for (var item in controlItems) {
		if (item.indexOf(data.id) > -1 && controlItems[item].show) {
			setConnectorColor(item, data.user_color);
		}
	}
}

function makeSvgBackgroundForWidgetConnectors(width, height) {
	var backDrop = new Snap(parseInt(width), parseInt(height));
	backDrop.node.style.zIndex = "0";
	backDrop.node.style.left = "0";
	backDrop.node.style.top = "0";
	backDrop.node.style.position = "absolute";
	ui.main.appendChild(backDrop.node);
	svgBackgroundForWidgetConnectors = backDrop;
	// testing with a foreground
	var foreground = new Snap(parseInt(width), parseInt(height));
	foreground.node.style.zIndex = "10000";
	foreground.node.style.left = "0";
	foreground.node.style.top = "0";
	foreground.node.style.position = "absolute";
	foreground.attr("class", "svgForegroundDraw");
	ui.main.appendChild(foreground.node);
	svgForegroundForWidgetConnectors = foreground;

	return backDrop;
}

function createButtonShape(paper, cx, cy, buttonRad, buttonShape) {
	var buttonBack;
	var point;
	var polygonPts = [];
	var theta;
	switch (buttonShape) {
		case "hexagon":
			for (theta = 0; theta <= 360; theta += 60) {
				point = polarToCartesian(buttonRad, theta + 30, {x: cx, y: cy});
				polygonPts.push(point.x);
				polygonPts.push(point.y);
			}
			buttonBack = paper.polygon(polygonPts);
			break;
		case "octagon":
			for (theta = 0; theta <= 360; theta += 45) {
				point = polarToCartesian(buttonRad, theta + 22.5, {x: cx, y: cy});
				polygonPts.push(point.x);
				polygonPts.push(point.y);
			}
			buttonBack = paper.polygon(polygonPts);
			break;
		case "circle":
		default:
			buttonBack = paper.circle(cx, cy, buttonRad);
			break;
	}
	return buttonBack;
}