summaryrefslogtreecommitdiff
path: root/src/rz_core.js
diff options
context:
space:
mode:
authorAlon Levy <alon@pobox.com>2014-12-16 17:37:42 +0200
committerAlon Levy <alon@pobox.com>2014-12-16 17:37:42 +0200
commitade19de410e0a789a921ec1666b278b2a64176d6 (patch)
treec4c54b106de93609ca7bb70a20fd4be696715dbe /src/rz_core.js
parentc7d026b1d504323a8c61d51b726e5e8d2337fc4b (diff)
moving files around after repository merger
Diffstat (limited to 'src/rz_core.js')
-rw-r--r--src/rz_core.js756
1 files changed, 0 insertions, 756 deletions
diff --git a/src/rz_core.js b/src/rz_core.js
deleted file mode 100644
index 7ec19692..00000000
--- a/src/rz_core.js
+++ /dev/null
@@ -1,756 +0,0 @@
-"use strict"
-
-define(['jquery', 'd3', 'consts', 'rz_bus', 'util', 'model/graph', 'model/core', 'view/helpers', 'view/view', 'rz_observer', 'view/selection', 'rz_config'],
-function($, d3, consts, rz_bus, util, model_graph, model_core, view_helpers, view, rz_observer, selection, rz_config) {
-
-var addednodes = [],
- vis,
- graphinterval = 0,
- timeline_timer = 0,
- deliverables = [],
- circle, // <-- should not be module globals.
- scrollValue = 0,
- graph,
- drag,
- force;
-
-// "CSS" for SVG elements. Reused for editing elements.
-var node_text_dx = 15,
- node_text_dy = '.30em',
- svg_input_fo_node_x = node_text_dx,
- svg_input_fo_node_y = '-.70em',
- svg_input_fo_height = '30px';
-
-/**
- * svgInput - creates an embedded input element under a given
- *
- * edit_node(@sibling, @node)
- * edit_link(@sibling, @link)
- */
-var svgInput = (function() {
- var measure_node = $('#measure-node')[0],
- measure_link = $('#measure-link')[0],
- original_element,
- is_link;
-
- function appendForeignElementInputWithID(base, elemid, width, height)
- {
- var input = document.createElement('input'),
- body = document.createElement('body'),
- fo = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
-
- body.appendChild(input);
-
- fo.setAttribute('height', height || svg_input_fo_height);
- fo.style.pointerEvents = 'none';
- input.style.pointerEvents = 'all';
- fo.appendChild(body);
- base.appendChild(fo);
- input.setAttribute('id', elemid);
- return input;
- }
-
- function measure(text)
- {
- var span;
-
- span = is_link ? measure_link : measure_node;
- span.innerHTML = text;
- return span.getBoundingClientRect().width; // $().width() works too
- }
-
- function onkeydown(e) {
- var ret = undefined,
- jelement = createOrGetSvgInput(),
- element = jelement[0],
- newname = jelement.val(),
- fo = createOrGetSvgInputFO(),
- d;
-
- if (element != this) {
- console.log('unexpected editname_on_keypress this should be the svg-input element');
- }
-
- if (e.which == 13 || e.which == 27) {
- ret = false;
- d = jelement.data().d;
- if (e.which == 13 && newname != d.name) {
- if (d.hasOwnProperty('__src')) {
- graph.editLink(d.__src.id, d.__dst.id, newname);
- } else {
- graph.editName(d.id, newname);
- }
- rz_bus.names.push([newname]);
- update_view__graph(true);
- }
- hide();
- }
- rz_bus.ui_key.push({where: consts.KEYSTROKE_WHERE_EDIT_NODE, keys: [e.which]});
- return ret;
- };
-
- function resize_measure(e) {
- resize(measure($(e.target).val()) + 30);
- }
-
- function resize(new_width) {
- var svg_input = createOrGetSvgInput(),
- fo = createOrGetSvgInputFO();
-
- svg_input.css('width', new_width);
- fo.attr('width', new_width);
- }
-
- // FIXME: element being deleted. Some delete is legit - removal of related element. Some isn't (a click).
- // Instead of investigating (time constraint) reparenting as sibling, and introducing
- // this function. Cost of creation of element is negligble, it's just ugly..
- function createOrGetSvgInput()
- {
- var svg_input_name = 'svg-input',
- svg_input_selector = '#' + svg_input_name,
- svg_input = $(svg_input_selector);
-
- if (svg_input.length == 0) {
- console.log('creating new svg-input');
- svg_input = $(appendForeignElementInputWithID(vis[0][0], svg_input_name));
- svg_input.on('keydown', onkeydown);
- svg_input.bind('change keypress', resize_measure);
- }
- return svg_input;
- }
-
- function createOrGetSvgInputFO()
- {
- return createOrGetSvgInput().parent().parent();
- }
-
- /*
- * @param e visual node element
- * @param n node model object
- */
- function enable(e, n) {
- var oldname = n.name,
- svg_input = createOrGetSvgInput(),
- fo = createOrGetSvgInputFO();
-
- is_link = n.hasOwnProperty('__src');
-
- e.parentNode.appendChild(fo[0]); // This will unparent from the old parent
- if (is_link) {
- fo.attr('transform', e.getAttribute('transform'));
- // XXX links set the text-anchor middle attribute. no idea how to do that
- fo.attr('x', -$(e).width() / 2);
- fo.attr('y', -$(e).height() / 2 - 3); // XXX This minus 3 is only kinda ok.
- fo.attr('class', 'svg-input-fo-link');
- } else {
- fo.attr('x', svg_input_fo_node_x);
- fo.attr('y', svg_input_fo_node_y);
- fo.attr('transform', null);
- fo.attr('class', 'svg-input-fo-node');
- }
- // Set width correctly
- resize(measure(oldname) + 30);
- fo.show();
- svg_input.val(oldname);
- svg_input.data().d = n;
- svg_input.focus();
- if (original_element) {
- original_element.show();
- }
- original_element = $(e);
- original_element.hide();
- // TODO: set cursor to correct location in text
- }
-
- function hide() {
- createOrGetSvgInputFO().hide();
- if (original_element && original_element.show) {
- original_element.show();
- }
- }
-
- return {
- enable: enable,
- hide: hide,
- };
-}());
-
-
-
-function recenterZoom() {
- vis.attr("transform", "translate(0,0)scale(1)");
-}
-
-// zoom or drag
-var zoomInProgress = false;
-
-var initDrawingArea = function () {
-
- function zoom() {
- zoomInProgress = true;
- vis.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
- d3.event.sourceEvent.stopPropagation();
- }
-
- function dragstarted(d) {
- d3.event.sourceEvent.stopPropagation();
- d3.select(this).classed("dragging", true);
- d.dragstart = {clientX:d3.event.sourceEvent.clientX, clientY:d3.event.sourceEvent.clientY};
- force.stop();
- }
-
- function dragged(d) {
- d.x = d3.event.x;
- d.y = d3.event.y;
- tick();
- }
-
- function dragended(d) {
- d3.select(this).classed("dragging", false);
- d3.select(this).classed("fixed", true); // TODO: this is broken since we override all the classes. Need to switch to class addition/removal (i.e. use classed for everything) or set class in one location (so here just set a value on the node, not the element)
- if (d.dragstart.clientX - d3.event.sourceEvent.clientX != 0 ||
- d.dragstart.clientY - d3.event.sourceEvent.clientY != 0) {
- tick();
- force.resume();
- }
- }
-
- graph = new model_graph.Graph();
-
- var user_id = $('#user_id'),
- user = user_id.text();
-
- if (user_id.length > 0) {
- console.log('found user ID: \'' + user + '\'');
- graph.set_user(user);
- }
-
- var el = document.body;
- vis = d3.select(el).append("svg:svg")
- .attr('id', 'canvas_d3')
- .attr("width", '100%')
- .attr("height", '100%')
- .attr("pointer-events", "all")
- .append("g")
- .attr("class", "zoom");
-
- d3.select(el).select("svg").append("svg:defs")
- .data(["end"]) // Different link/path types can be defined here
- .append("svg:marker") // This section adds in the arrows
- .attr("id", String)
- .attr("viewBox", "0 -5 10 10")
- .attr("refX", 22)
- .attr("refY", -1.5)
- .attr("markerWidth", 4)
- .attr("markerHeight", 4)
- .attr("orient", "auto")
- .append("svg:path")
- .attr("d", "M0,-5L10,0L0,5");
-
- /*
- * init zoom behavior
- */
- var zoom_obj = d3.behavior.zoom().scaleExtent([0.1, 3]).on("zoom", zoom);
- zoom_obj(d3.select('#canvas_d3'))
- d3.select("svg").on("dblclick.zoom", null); // disable zoom on double click
-
- $('svg').click(svg_click_handler);
-
- // SVG rendering order is last rendered on top, so to make sure
- // all links are below the nodes we group them under a single g
- vis.append("g").attr("id", "link-group");
- vis.append("g").attr("id", "selected-link-group");
-
- drag = d3.behavior.drag()
- .origin(function(d) { return d; })
- .on("dragstart", dragstarted)
- .on("drag", dragged)
- .on("dragend", dragended);
-
- // $('#canvas_d3').dblclick(canvas_handler_dblclick); - see #138
- if (rz_config.backend_enabled){
- graph.load_from_backend( function(){
- update_view__graph(false);
- });
- }
-}
-
-function init_force_layout(){
- var el = document.body;
- var w = $(el).innerWidth(),
- h = $(el).innerHeight();
-
- force = d3.layout.force()
- .distance(120)
- .gravity(0.12)
- .charge(-1800)
- .size([w, h])
- .on("tick", tick)
- .start();
-}
-
-initDrawingArea();
-init_force_layout();
-
-/**
- * find the visual element counterpart of a given model object. This relies on
- * the visual element having an id attribute equal to the object's id.
- *
- * @return null if visual element is not found
- */
-function locate_visual_element(model_obj){
- var id_sel = $('#' + model_obj.id);
- if (0 == id_sel.length){
- console.warn('unable to find visual element for model object: object id: ' + model_obj.id.toString())
- return null;
- }
- return id_sel[0];
-}
-
-/**
- * add node on canvas double click
- */
-function canvas_handler_dblclick(){
- var n = model_core.create_node__set_random_id();
- n.name = ''; // will be set by user
-
- graph.addNode(n);
- update_view__graph();
-
- var n_ve = locate_visual_element(n); // locate visual element
-
- var on_slowdown_cb = function(){
- svgInput.enable($(n_ve).find('.nodetext'), n);
- observer.disconnect();
- }
- var mutation_handler = rz_observer.new_Mutation_Handler__on_dxy_slowdown(on_slowdown_cb);
- var observer = rz_observer.new_MutationObserver(mutation_handler);
- mutation_handler.on_slowdown_threshold_reached;
-
- observer.observe(n_ve, {
- subtree: false,
- childList : false,
- attributes: true,
- attributeOldValue : true,
- });
-}
-
-/**
- * update view: graph
- */
-function update_view__graph(no_relayout) {
- var node,
- link,
- link_g,
- linktext,
- nodetext,
- unselected_link_group = document.querySelector('#link-group'),
- selected_link_group = document.querySelector('#selected-link-group');
-
- link = vis.selectAll("g.link")
- .data(graph.links(), function(d) { return d.id; });
-
- link_g = link.enter().append('g')
- .attr('id', function(d){ return d.id; }) // append link id to enable data->visual mapping
- .attr('class', 'link graph')
-
- link_g.append("path")
- .attr("class", function(d) {
- return d.state + ' link graph';
- })
- .attr('id', function(d){ return d.id; }) // append link id to enable data->visual mapping
- .attr("marker-end", "url(#end)");
-
- // second path for larger click area
- link_g.append("path")
- .attr("class", "ghostlink")
- .on("click", function(d, i) {
- if (zoomInProgress) {
- // don't disable zoomInProgress, it will be disabled by the svg_click_handler
- // after this events bubbles to the svg element
- return;
- }
- var that = this,
- src = this.link.__src,
- dst = this.link.__dst;
-
- view.edge_info.on_delete(function () {
- graph.removeLink(that.link);
- update_view__graph(true);
- view.edge_info.hide();
- });
- view.edge_info.show(d);
- selection.update([src, dst]);
- update_view__graph(true);
- });
-
- link.attr("class", function(d, i){
- var temp_and = (d.name && d.name.replace(/ /g,"")=="and" && d.state==="temp") ? "temp_and" : "";
-
- return ["graph link", temp_and, selection.selected_class(d)].join(' ');
- });
-
- link.selectAll('path.link')
- .attr('class', function(d) {
- return [d.state, selection.selected_class(d), "link graph"].join(' ');
- });
-
- link.exit().remove();
-
- vis.selectAll('.ghostlink')
- .data(graph.links())
- .each(function (d) {
- this.link = d;
- });
-
- linktext = vis.selectAll(".linklabel")
- .data(graph.links(), function(d) { return d.id; });
- linktext.enter()
- .append("text")
- .attr('id', function(d){ return d.id; }) // append link id to enable data->visual mapping
- .attr("class", function(d) {
- return ["linklabel graph", selection.selected_class(d)].join(' ');
- })
- .attr("text-anchor", "middle")
- .on("click", function(d, i) {
- if (d.state !== "temp") {
- svgInput.enable(this, d);
- }
- });
-
- linktext
- .text(function(d) {
- var name = d.name || "";
- if (!(d.__dst.state === "temp" ||
- d.__src.state === "chosen" || d.__dst.state === "chosen")) {
- return "";
- }
- if (name.length < 25 || d.__src.state === "chosen" ||
- d.__dst.state === "chosen" || d.state==="temp") {
- return name;
- } else {
- return name.substring(0, 14) + "...";
- }
- });
-
- linktext.exit().remove();
-
- node = vis.selectAll(".node")
- .data(graph.nodes(), function(d) {
- return d.id;
- });
-
- var nodeEnter = node.enter()
- .append("g")
- .attr('id', function(d){ return d.id; }) // append node id to enable data->visual mapping
- .attr('visibility', 'hidden') // made visible on first tick
- .call(drag);
-
- // reorder nodes so selected are last, and so rendered last, and so on top.
- (function () {
- var ontop = [],
- bubble;
-
- node.each(function (d) {
- this.node = d;
- })
- .attr('class', function(d) {
- if (selection.node_selected(d)) {
- if (d.type == 'bubble') {
- bubble = this;
- } else {
- ontop.push(this);
- }
- }
- return ['node', selection.selected_class(d)].join(' ');
- });
- if (bubble === undefined) {
- // nothing to do if there is no bubble
- return;
- }
- function reparent(new_parent, element) {
- if (element.parentNode == new_parent) {
- return;
- }
- new_parent.appendChild(element);
- }
- // move link to correct group
- // O(|links|*|ontop|)
- link.each(function (d) {
- if (ontop.some(function (node) {
- var d_node = node.node;
- return d.__src == d_node || d.__dst == d_node;
- }))
- {
- reparent(selected_link_group, this);
- } else {
- reparent(unselected_link_group, this);
- }
- });
- linktext.each(function (d) {
- if (selection.node_selected(d)) {
- ontop.push(this);
- }
- });
- function moveToEnd(e) {
- e.parentNode.appendChild(e);
- }
- moveToEnd(bubble);
- ontop.reverse().forEach(function (e) {
- moveToEnd(e);
- });
- var count_links = function() {
- return selected_link_group.childElementCount + unselected_link_group.childElementCount;
- };
- // put back on top link group on top
- selected_link_group.parentNode.insertBefore(selected_link_group, bubble.nextSibling);
- })();
-
- nodetext = nodeEnter.insert("text")
- .attr("class", "nodetext graph")
- .attr("dx", node_text_dx)
- .attr("dy", node_text_dy)
- .on("click", function(d, i) {
- if (d3.event.defaultPrevented) {
- // drag happened, ignore click https://github.com/mbostock/d3/wiki/Drag-Behavior#on
- return;
- }
- if (d.state !== "temp") {
- svgInput.enable(this, d);
- selection.update([d]);
- showNodeInfo(this.parentNode.node, i);
- }
- d3.event.stopPropagation();
- });
-
- node.select('g.node text')
- .text(function(d) {
- if (!d.name) {
- return d.type == 'bubble' ? "" : "_";
- }
- if (d.state === "temp" || d.state === 'chosen'
- || d.state === "enter" || d.state === "exit") {
- return d.name;
- } else {
- if (d.name.length < 28) {
- return d.name;
- } else {
- return d.name.substring(0, 25) + "...";
- }
- }
- });
-
- circle = nodeEnter.insert("circle");
- node.select('g.node circle')
- .attr("class", function(d) {
- return d.type + " " + d.state + " circle graph";
- })
- .attr("r", function(d) {
- return view_helpers.customSize(d.type) - 2;
- })
- .on("click", function(d, i) {
- if (d3.event.defaultPrevented) {
- // drag happened, ignore click https://github.com/mbostock/d3/wiki/Drag-Behavior#on
- return;
- }
- d3.event.stopPropagation();
- selection.update([d]);
- if(d.state !== "temp") {
- showNodeInfo(d, i);
- }
- });
- circle.append("svg:image")
- .attr("class", "status graph")
- .attr('x', -7)
- .attr('y', -8)
- .attr('width', 15)
- .attr('height', 15)
- .attr("xlink:href", function(d) {
- switch (d.status) {
- case "done":
- return "res/img/check.png";
- break;
- case "current":
- return "res/img/wait.png";
- break;
- case "waiting":
- return "res/img/cross.png";
- break;
- }
- });
-
- node.exit().remove();
-
- //update deliverables
- deliverables = [];
- var nodes = graph.nodes();
- for (var i = 0; i < nodes.length; i++) {
- var current = nodes[i];
- if (current.type === "third-internship-proposal") {
- deliverables.push({
- "id": nodes[i].id,
- "startdate": nodes[i].start,
- "enddate": nodes[i].end
- });
- }
- //Do something
- }
-
- force.nodes(graph.nodes())
- .links(graph.links())
-
- if (no_relayout) {
- // XXX If we are stopped we need to update the text of the links at least,
- // and this is the simplest way
- tick();
- } else {
- force.alpha(0.1).start();
- }
-}
-
-
-
-var debug_print = function(message) {
- var element = $(".debug");
- if (element.length == 1) {
- element.html(message);
- } else {
- console.log(message);
- }
-}
-
-function check_for_nan(x) {
- if (Number.isNaN(x)) {
- console.log('nan problem');
- force.stop();
- }
- return Number.isNaN(x);
-}
-
-var newnodes=1;
-function tick(e) {
- //console.log(e);
- //$(".debug").html(force.alpha());
- var node = vis.selectAll(".node")
- .data(force.nodes(), function(d) {
- return d.id;
- });
- var link = vis.selectAll("path.link")
- .data(graph.links(), function(d) {
- return d.id;
- });
- var linktext = vis.selectAll(".linklabel").data(graph.links());
-
- function transform(d) {
- if (check_for_nan(d.x) || check_for_nan(d.y)) {
- return;
- }
- return "translate(" + d.x + "," + d.y + ")";
- }
-
- //circles animation
- var tempcounter = 0,
- temptotal = graph.nodes().filter(function(d){
- return d.state === "temp" && d.type !== "chainlink" && d.type !== "bubble";
- }).length;
- if (temptotal !== newnodes) {
- newnodes += temptotal / 15 / (newnodes * newnodes);
- }
- newnodes = Math.max(1, Math.min(newnodes, temptotal));
- graph.nodes().forEach(function(d, i) {
- var r, a;
- if (d.state === "temp") {
- tempcounter++;
- if (d.type==="chainlink" || d.type==="bubble") {
- d.x = window.innerWidth / 2;
- d.y = window.innerHeight / 2;
- } else {
- r = 60 + newnodes * 20;
- a = -Math.PI + Math.PI * 2 * (tempcounter-1) / newnodes + 0.3;
- d.x = window.innerWidth / 2 + r * Math.cos(a);
- d.y = window.innerHeight / 2 + r * Math.sin(a);
- }
- check_for_nan(d.x);
- check_for_nan(d.y);
- }
- });
-
- link.attr("d", function(d, i) {
- var d_val,
- ghost;
-
- var dx = d.__dst.x - d.__src.x,
- dy = d.__dst.y - d.__src.y,
- dr = Math.sqrt(dx * dx + dy * dy);
- d_val = "M" + d.__src.x + "," + d.__src.y + "L" + d.__dst.x + "," + d.__dst.y;
- // update ghostlink position
- ghost = $(this.nextElementSibling);
- ghost.attr("d", d_val);
- return d_val;
- });
-
- linktext.attr("transform", function(d) {
- return "translate(" + (d.__src.x + d.__dst.x) / 2 + "," + (d.__src.y + d.__dst.y) / 2 + ")";
- });
-
- node.attr("transform", transform);
-
- // After initial placement we can make the nodes visible.
- //links.attr('visibility', 'visible');
- node.attr('visibility', 'visible');
-}
-
-function showNodeInfo(d, i) {
- view.node_info.on_save(function(e, form_data) {
-
- graph.update_node(d, form_data, function(){
- var old_type = d.type,
- new_type = form_data.type;
-
- if (new_type != old_type) {
- view.node_info.show(d);
- }
-
- view.node_info.hide();
- update_view__graph(true);
- });
-
- return false;
- });
-
- view.node_info.on_delete(function() {
- if (confirm('This node and all its connections will be deleted, are you sure?')) {
- graph.removeNode(d.id);
- update_view__graph(false);
- view.node_info.hide();
- }
- });
-
- view.node_info.show(d);
-}
-
-function svg_click_handler(e) {
- if (zoomInProgress) {
- zoomInProgress = false;
- return;
- }
- if (e.originalEvent.target.nodeName != 'svg') {
- return;
- }
- svgInput.hide();
- selection.clear();
- view.hide();
- update_view__graph(true);
-}
-
-return {
- graph: graph,
- force: force,
- load_from_json: function(result) {
- graph.load_from_json(result);
- recenterZoom();
- update_view__graph(false);
- },
- update_view__graph : update_view__graph,
-}
-}); /* close define call */