diff --git a/js/dos/ext/vis/examples/graph3d/playground/playground.js b/js/dos/ext/vis/examples/graph3d/playground/playground.js
new file mode 100644
index 0000000..9e3cc0b
--- /dev/null
+++ b/js/dos/ext/vis/examples/graph3d/playground/playground.js
@@ -0,0 +1,544 @@
+
+var query = null;
+
+
+function load() {
+ selectDataType();
+
+ loadCsvExample();
+ loadJsonExample();
+ loadJavascriptExample();
+ loadGooglespreadsheetExample();
+ loadDatasourceExample();
+
+ draw();
+}
+
+
+
+/**
+ * Upate the UI based on the currently selected datatype
+ */
+function selectDataType() {
+}
+
+
+function round(value, decimals) {
+ return parseFloat(value.toFixed(decimals));
+}
+
+function loadCsvExample() {
+ var csv = "";
+
+ // headers
+ csv += '"x", "y", "value"\n';
+
+ // create some nice looking data with sin/cos
+ var steps = 30;
+ var axisMax = 314;
+ var axisStep = axisMax / steps;
+ for (var x = 0; x < axisMax; x+=axisStep) {
+ for (var y = 0; y < axisMax; y+=axisStep) {
+ var value = Math.sin(x/50) * Math.cos(y/50) * 50 + 50;
+
+ csv += round(x, 2) + ', ' + round(y, 2) + ', ' + round(value, 2) + '\n';
+ }
+ }
+
+ document.getElementById("csvTextarea").innerHTML = csv;
+
+ // also adjust some settings
+ document.getElementById("style").value = "surface";
+ document.getElementById("verticalRatio").value = "0.5";
+
+ document.getElementById("xLabel").value = "x";
+ document.getElementById("yLabel").value = "y";
+ document.getElementById("zLabel").value = "value";
+ document.getElementById("filterLabel").value = "";
+ document.getElementById("legendLabel").value = "";
+ drawCsv();
+}
+
+
+function loadCsvAnimationExample() {
+ var csv = "";
+
+ // headers
+ csv += '"x", "y", "value", "time"\n';
+
+ // create some nice looking data with sin/cos
+ var steps = 20;
+ var axisMax = 314;
+ var tMax = 31;
+ var axisStep = axisMax / steps;
+ for (var t = 0; t < tMax; t++) {
+ for (var x = 0; x < axisMax; x+=axisStep) {
+ for (var y = 0; y < axisMax; y+=axisStep) {
+ var value = Math.sin(x/50 + t/10) * Math.cos(y/50 + t/10) * 50 + 50;
+ csv += round(x, 2) + ', ' + round(y, 2) + ', ' + round(value, 2) + ', ' + t + '\n';
+ }
+ }
+ }
+
+ document.getElementById("csvTextarea").innerHTML = csv;
+
+ // also adjust some settings
+ document.getElementById("style").value = "surface";
+ document.getElementById("verticalRatio").value = "0.5";
+ document.getElementById("animationInterval").value = 100;
+
+ document.getElementById("xLabel").value = "x";
+ document.getElementById("yLabel").value = "y";
+ document.getElementById("zLabel").value = "value";
+ document.getElementById("filterLabel").value = "time";
+ document.getElementById("legendLabel").value = "";
+
+ drawCsv();
+}
+
+
+function loadCsvLineExample() {
+ var csv = "";
+
+ // headers
+ csv += '"sin(t)", "cos(t)", "t"\n';
+
+ // create some nice looking data with sin/cos
+ var steps = 100;
+ var axisMax = 314;
+ var tmax = 4 * 2 * Math.PI;
+ var axisStep = axisMax / steps;
+ for (t = 0; t < tmax; t += tmax / steps) {
+ var r = 1;
+ var x = r * Math.sin(t);
+ var y = r * Math.cos(t);
+ var z = t;
+ csv += round(x, 2) + ', ' + round(y, 2) + ', ' + round(z, 2) + '\n';
+ }
+
+ document.getElementById("csvTextarea").innerHTML = csv;
+
+ // also adjust some settings
+ document.getElementById("style").value = "line";
+ document.getElementById("verticalRatio").value = "1.0";
+ document.getElementById("showPerspective").checked = false;
+
+ document.getElementById("xLabel").value = "sin(t)";
+ document.getElementById("yLabel").value = "cos(t)";
+ document.getElementById("zLabel").value = "t";
+ document.getElementById("filterLabel").value = "";
+ document.getElementById("legendLabel").value = "";
+
+ drawCsv();
+}
+
+function loadCsvMovingDotsExample() {
+ var csv = "";
+
+ // headers
+ csv += '"x", "y", "z", "color value", "time"\n';
+
+ // create some shortcuts to math functions
+ var sin = Math.sin;
+ var cos = Math.cos;
+ var pi = Math.PI;
+
+ // create the animation data
+ var tmax = 2.0 * pi;
+ var tstep = tmax / 75;
+ var dotCount = 1; // set this to 1, 2, 3, 4, ...
+ for (var t = 0; t < tmax; t += tstep) {
+ var tgroup = parseFloat(t.toFixed(2));
+ var value = t;
+
+ // a dot in the center
+ var x = 0;
+ var y = 0;
+ var z = 0;
+ csv += round(x, 2) + ', ' + round(y, 2) + ', ' + round(z, 2) + ', ' + round(value, 2)+ ', ' + round(tgroup, 2) + '\n';
+
+ // one or multiple dots moving around the center
+ for (var dot = 0; dot < dotCount; dot++) {
+ var tdot = t + 2*pi * dot / dotCount;
+ //data.addRow([sin(tdot), cos(tdot), sin(tdot), value, tgroup]);
+ //data.addRow([sin(tdot), -cos(tdot), sin(tdot + tmax*1/2), value, tgroup]);
+
+ var x = sin(tdot);
+ var y = cos(tdot);
+ var z = sin(tdot);
+ csv += round(x, 2) + ', ' + round(y, 2) + ', ' + round(z, 2) + ', ' + round(value, 2)+ ', ' + round(tgroup, 2) + '\n';
+
+ var x = sin(tdot);
+ var y = -cos(tdot);
+ var z = sin(tdot + tmax*1/2);
+ csv += round(x, 2) + ', ' + round(y, 2) + ', ' + round(z, 2) + ', ' + round(value, 2)+ ', ' + round(tgroup, 2) + '\n';
+
+ }
+ }
+
+ document.getElementById("csvTextarea").innerHTML = csv;
+
+ // also adjust some settings
+ document.getElementById("style").value = "dot-color";
+ document.getElementById("verticalRatio").value = "1.0";
+ document.getElementById("animationInterval").value = "35";
+ document.getElementById("animationAutoStart").checked = true;
+ document.getElementById("showPerspective").checked = true;
+
+ document.getElementById("xLabel").value = "x";
+ document.getElementById("yLabel").value = "y";
+ document.getElementById("zLabel").value = "z";
+ document.getElementById("filterLabel").value = "time";
+ document.getElementById("legendLabel").value = "color value";
+
+ drawCsv();
+}
+
+function loadCsvColoredDotsExample() {
+ var csv = "";
+
+ // headers
+ csv += '"x", "y", "z", "distance"\n';
+
+ // create some shortcuts to math functions
+ var sqrt = Math.sqrt;
+ var pow = Math.pow;
+ var random = Math.random;
+
+ // create the animation data
+ var imax = 200;
+ for (var i = 0; i < imax; i++) {
+ var x = pow(random(), 2);
+ var y = pow(random(), 2);
+ var z = pow(random(), 2);
+ var dist = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));
+
+ csv += round(x, 2) + ', ' + round(y, 2) + ', ' + round(z, 2) + ', ' + round(dist, 2)+ '\n';
+ }
+
+ document.getElementById("csvTextarea").innerHTML = csv;
+
+ // also adjust some settings
+ document.getElementById("style").value = "dot-color";
+ document.getElementById("verticalRatio").value = "1.0";
+ document.getElementById("showPerspective").checked = true;
+
+ document.getElementById("xLabel").value = "x";
+ document.getElementById("yLabel").value = "y";
+ document.getElementById("zLabel").value = "value";
+ document.getElementById("legendLabel").value = "distance"
+ document.getElementById("filterLabel").value = "";
+
+ drawCsv();
+}
+
+function loadCsvSizedDotsExample() {
+ var csv = "";
+
+ // headers
+ csv += '"x", "y", "z", "range"\n';
+
+ // create some shortcuts to math functions
+ var sqrt = Math.sqrt;
+ var pow = Math.pow;
+ var random = Math.random;
+
+ // create the animation data
+ var imax = 200;
+ for (var i = 0; i < imax; i++) {
+ var x = pow(random(), 2);
+ var y = pow(random(), 2);
+ var z = pow(random(), 2);
+ var dist = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));
+ var range = sqrt(2) - dist;
+
+ csv += round(x, 2) + ', ' + round(y, 2) + ', ' + round(z, 2) + ', ' + round(range, 2)+ '\n';
+ }
+
+ document.getElementById("csvTextarea").innerHTML = csv;
+
+ // also adjust some settings
+ document.getElementById("style").value = "dot-size";
+ document.getElementById("verticalRatio").value = "1.0";
+ document.getElementById("showPerspective").checked = true;
+
+ document.getElementById("xLabel").value = "x";
+ document.getElementById("yLabel").value = "y";
+ document.getElementById("zLabel").value = "z";
+ document.getElementById("legendLabel").value = "range";
+ document.getElementById("filterLabel").value = "";
+
+ drawCsv();
+}
+
+
+function loadJsonExample() {
+}
+
+
+function loadJavascriptExample() {
+}
+
+function loadJavascriptFunctionExample() {
+}
+
+function loadGooglespreadsheetExample() {
+
+}
+
+
+function loadDatasourceExample() {
+}
+
+
+
+/**
+ * Retrieve teh currently selected datatype
+ * @return {string} datatype
+ */
+function getDataType() {
+ return "csv";
+}
+
+
+/**
+ * Retrieve the datatable from the entered contents of the csv text
+ * @param {boolean} [skipValue] | if true, the 4th element is a filter value
+ * @return {vis DataSet}
+ */
+function getDataCsv() {
+ var csv = document.getElementById("csvTextarea").value;
+
+ // parse the csv content
+ var csvArray = csv2array(csv);
+
+ var data = new vis.DataSet();
+
+ var skipValue = false;
+ if (document.getElementById("filterLabel").value != "" && document.getElementById("legendLabel").value == "") {
+ skipValue = true;
+ }
+
+ // read all data
+ for (var row = 1; row < csvArray.length; row++) {
+ if (csvArray[row].length == 4 && skipValue == false) {
+ data.add({x:parseFloat(csvArray[row][0]),
+ y:parseFloat(csvArray[row][1]),
+ z:parseFloat(csvArray[row][2]),
+ style:parseFloat(csvArray[row][3])});
+ }
+ else if (csvArray[row].length == 4 && skipValue == true) {
+ data.add({x:parseFloat(csvArray[row][0]),
+ y:parseFloat(csvArray[row][1]),
+ z:parseFloat(csvArray[row][2]),
+ filter:parseFloat(csvArray[row][3])});
+ }
+ else if (csvArray[row].length == 5) {
+ data.add({x:parseFloat(csvArray[row][0]),
+ y:parseFloat(csvArray[row][1]),
+ z:parseFloat(csvArray[row][2]),
+ style:parseFloat(csvArray[row][3]),
+ filter:parseFloat(csvArray[row][4])});
+ }
+ else {
+ data.add({x:parseFloat(csvArray[row][0]),
+ y:parseFloat(csvArray[row][1]),
+ z:parseFloat(csvArray[row][2]),
+ style:parseFloat(csvArray[row][2])});
+ }
+ }
+
+ return data;
+}
+
+/**
+ * remove leading and trailing spaces
+ */
+function trim(text) {
+ while (text.length && text.charAt(0) == ' ')
+ text = text.substr(1);
+
+ while (text.length && text.charAt(text.length-1) == ' ')
+ text = text.substr(0, text.length-1);
+
+ return text;
+}
+
+/**
+ * Retrieve the datatable from the entered contents of the javascript text
+ * @return {vis Dataset}
+ */
+function getDataJson() {
+ var json = document.getElementById("jsonTextarea").value;
+ var data = new google.visualization.DataTable(json);
+
+ return data;
+}
+
+
+/**
+ * Retrieve the datatable from the entered contents of the javascript text
+ * @return {vis Dataset}
+ */
+function getDataJavascript() {
+ var js = document.getElementById("javascriptTextarea").value;
+
+ eval(js);
+
+ return data;
+}
+
+
+/**
+ * Retrieve the datatable from the entered contents of the datasource text
+ * @return {vis Dataset}
+ */
+function getDataDatasource() {
+}
+
+/**
+ * Retrieve a JSON object with all options
+ */
+function getOptions() {
+ return {
+ width: document.getElementById("width").value,
+ height: document.getElementById("height").value,
+ style: document.getElementById("style").value,
+ showAnimationControls: (document.getElementById("showAnimationControls").checked != false),
+ showGrid: (document.getElementById("showGrid").checked != false),
+ showPerspective: (document.getElementById("showPerspective").checked != false),
+ showShadow: (document.getElementById("showShadow").checked != false),
+ keepAspectRatio: (document.getElementById("keepAspectRatio").checked != false),
+ verticalRatio: document.getElementById("verticalRatio").value,
+ animationInterval: document.getElementById("animationInterval").value,
+ xLabel: document.getElementById("xLabel").value,
+ yLabel: document.getElementById("yLabel").value,
+ zLabel: document.getElementById("zLabel").value,
+ filterLabel: document.getElementById("filterLabel").value,
+ legendLabel: document.getElementById("legendLabel").value,
+ animationPreload: (document.getElementById("animationPreload").checked != false),
+ animationAutoStart:(document.getElementById("animationAutoStart").checked != false),
+
+ xCenter: Number(document.getElementById("xCenter").value) || undefined,
+ yCenter: Number(document.getElementById("yCenter").value) || undefined,
+
+ xMin: Number(document.getElementById("xMin").value) || undefined,
+ xMax: Number(document.getElementById("xMax").value) || undefined,
+ xStep: Number(document.getElementById("xStep").value) || undefined,
+ yMin: Number(document.getElementById("yMin").value) || undefined,
+ yMax: Number(document.getElementById("yMax").value) || undefined,
+ yStep: Number(document.getElementById("yStep").value) || undefined,
+ zMin: Number(document.getElementById("zMin").value) || undefined,
+ zMax: Number(document.getElementById("zMax").value) || undefined,
+ zStep: Number(document.getElementById("zStep").value) || undefined,
+
+ valueMin: Number(document.getElementById("valueMin").value) || undefined,
+ valueMax: Number(document.getElementById("valueMax").value) || undefined,
+
+ xBarWidth: Number(document.getElementById("xBarWidth").value) || undefined,
+ yBarWidth: Number(document.getElementById("yBarWidth").value) || undefined
+ };
+}
+
+/**
+ * Redraw the graph with the entered data and options
+ */
+function draw() {
+ return drawCsv();
+}
+
+
+function drawCsv() {
+ // retrieve data and options
+ var data = getDataCsv();
+ var options = getOptions();
+
+ // Creat a graph
+ var graph = new vis.Graph3d(document.getElementById('graph'), data, options);
+}
+
+function drawJson() {
+ // retrieve data and options
+ var data = getDataJson();
+ var options = getOptions();
+
+ // Creat a graph
+ var graph = new vis.Graph3d(document.getElementById('graph'), data, options);
+}
+
+function drawJavascript() {
+ // retrieve data and options
+ var data = getDataJavascript();
+ var options = getOptions();
+
+ // Creat a graph
+ var graph = new vis.Graph3d(document.getElementById('graph'), data, options);
+}
+
+
+function drawGooglespreadsheet() {
+ // Instantiate our graph object.
+ drawGraph = function(response) {
+ document.getElementById("draw").disabled = "";
+
+ if (response.isError()) {
+ error = 'Error: ' + response.getMessage();
+ document.getElementById('graph').innerHTML =
+ "" + error + ""; ;
+ }
+
+ // retrieve the data from the query response
+ data = response.getDataTable();
+
+ // specify options
+ options = getOptions();
+
+ // Instantiate our graph object.
+ var graph = new vis.Graph3d(document.getElementById('graph'), data, options);
+ }
+
+ url = document.getElementById("googlespreadsheetText").value;
+ document.getElementById("draw").disabled = "disabled";
+
+ // send the request
+ query && query.abort();
+ query = new google.visualization.Query(url);
+ query.send(drawGraph);
+}
+
+
+function drawDatasource() {
+ // Instantiate our graph object.
+ drawGraph = function(response) {
+ document.getElementById("draw").disabled = "";
+
+ if (response.isError()) {
+ error = 'Error: ' + response.getMessage();
+ document.getElementById('graph').innerHTML =
+ "" + error + ""; ;
+ }
+
+ // retrieve the data from the query response
+ data = response.getDataTable();
+
+ // specify options
+ options = getOptions();
+
+ // Instantiate our graph object.
+ var graph = new vis.Graph3d(document.getElementById('graph'), data, options);
+ };
+
+ url = document.getElementById("datasourceText").value;
+ document.getElementById("draw").disabled = "disabled";
+
+ // if the entered url is a google spreadsheet url, replace the part
+ // "/ccc?" with "/tq?" in order to retrieve a neat data query result
+ if (url.indexOf("/ccc?")) {
+ url.replace("/ccc?", "/tq?");
+ }
+
+ // send the request
+ query && query.abort();
+ query = new google.visualization.Query(url);
+ query.send(drawGraph);
+}