summaryrefslogtreecommitdiff
path: root/src/client/view
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/client/view
parentc7d026b1d504323a8c61d51b726e5e8d2337fc4b (diff)
moving files around after repository merger
Diffstat (limited to 'src/client/view')
-rw-r--r--src/client/view/completer.js229
-rw-r--r--src/client/view/edge_info.js36
-rw-r--r--src/client/view/helpers.js87
-rw-r--r--src/client/view/internal.js14
-rw-r--r--src/client/view/node_info.js117
-rw-r--r--src/client/view/selection.js102
-rw-r--r--src/client/view/tab.js57
-rw-r--r--src/client/view/timeline.js180
-rw-r--r--src/client/view/view.js12
9 files changed, 834 insertions, 0 deletions
diff --git a/src/client/view/completer.js b/src/client/view/completer.js
new file mode 100644
index 00000000..d2593321
--- /dev/null
+++ b/src/client/view/completer.js
@@ -0,0 +1,229 @@
+define(
+['jquery', 'Bacon'],
+function($, Bacon) {
+
+function unquoted(name)
+{
+ var start = 0,
+ end = name.length;
+
+ if (name.length >= 1) {
+ if (name.charAt(0) == '"') {
+ start = 1;
+ if (name.length > 1 && name.charAt(name.length - 1) == '"') {
+ end = name.length - 1;
+ }
+ }
+ return name.substring(start, end);
+ }
+ return name;
+}
+
+function setCaret(e, num)
+{
+ e.selectionStart = e.selectionEnd = num;
+}
+
+var completer = (function (input_element, dropdown, base_config) {
+ var config = get_config(base_config),
+ dropdown_raw = dropdown[0],
+ options_bus = new Bacon.Bus(),
+ options = [],
+ selected_index = -1,
+ input_element_raw = input_element[0],
+ completion_start = 0,
+ completion_end = 0,
+ minimum_length = 1;
+
+ // turn off the browser's autocomplete
+ input_element.attr('autocomplete', 'off');
+
+ //$('.ui-autocomplete').css('width', '10px');
+ options_bus.onValue(function update_options(new_options) {
+ options = new_options;
+ });
+
+ input_element.keyup(function(e) {
+ var ret = undefined;
+ switch (e.keyCode) {
+ case 38: //UP
+ prev_option();
+ ret = false;
+ break;
+ case 40: //DOWN
+ next_option();
+ ret = false;
+ break;
+ case 27: // Escape
+ hide();
+ ret = false;
+ break;
+ default:
+ // This catches cursor move due to keyboard events. no event for cursor movement itself
+ // below we catch cursor moves due to mouse click
+ oninput(input_element_raw.value, input_element_raw.selectionStart);
+ }
+ return ret;
+ });
+ input_element.keydown(function(e) {
+ switch (e.keyCode) {
+ case 38:
+ case 40:
+ return false;
+ case 9: // Tab
+ if (config.hideOnTab) {
+ hide();
+ }
+ break;
+ }
+ });
+
+ function get_config(base) {
+ return {
+ triggerStart: base && base.triggerStart || '#',
+ triggerEnd: base && base.triggerEnd || ' ',
+ hideOnTab: base && base.hasOwnProperty('hideOnTab') ? base.hideOnTab : true,
+ };
+ }
+
+ function completions(text)
+ {
+ var ret = [],
+ noquotes = unquoted(text.toLowerCase());
+
+ for (var name in options) {
+ if (name.toLowerCase().indexOf(noquotes) === 0) {
+ ret.push(name);
+ }
+ }
+ return ret;
+ }
+
+ function show() {
+ if (dropdown.children().length > 0) {
+ dropdown.show();
+ }
+ }
+ function hide()
+ {
+ dropdown.hide();
+ }
+
+ /***
+ * #this is a #
+ * ^
+ *
+ * #this is a #t
+ * ^
+ *
+ * #this and #that then #he
+ * ^
+ */
+ function oninput(text, cursor) {
+ var hash = text.slice(0, cursor).lastIndexOf(config.triggerStart);
+ // TODO check if current completion has been invalidated
+ _invalidateSelection();
+ hide();
+ dropdown_raw.innerHTML = ""; // remove all elements
+ if (hash == -1 && config.triggerStart != ' ') { // space matches start of string too
+ return;
+ }
+ var space = text.slice(hash + 1).indexOf(config.triggerEnd);
+ space = space == -1 ? text.length : space;
+ if (space < cursor) {
+ return;
+ }
+ completion_start = hash + 1;
+ completion_end = space;
+ var string = text.slice(completion_start, completion_end);
+ if (string.length < minimum_length) {
+ return;
+ }
+ completions(string).forEach(function(name) {
+ var suggestion = $('<div class="suggestion-item">' + name + '</div>');
+ suggestion.on('click', function(e) {
+ _applySuggestion(name);
+ input_element.focus();
+ });
+ dropdown.append(suggestion);
+ });
+ show();
+ }
+
+ function _invalidateSelection() {
+ update_highlighting(-1);
+ }
+
+ function _move_option(change, default_value) {
+ var next,
+ n = dropdown.children().length;
+
+ if (n == 0) {
+ return;
+ }
+ show();
+ if (selected_index == -1) {
+ next = default_value;
+ } else {
+ next = (selected_index + change) % n;
+ }
+ update_highlighting(next);
+ }
+ function next_option() {
+ _move_option(1, 0);
+ }
+ function prev_option() {
+ _move_option(dropdown.children().length - 1, dropdown.children().length - 1);
+ }
+ function _get_option(index) {
+ if (dropdown.children().length <= index) {
+ console.log('error: dropdown does not contain index ' + index +
+ ', it has ' + dropdown.children().length + ' elements');
+ return '';
+ }
+ var e = dropdown.children()[index],
+ s = e.innerText || e.textContent;
+ if (s.indexOf(' ') != -1) {
+ return '"' + s + '"';
+ }
+ return s;
+ }
+ function _choice(i) {
+ return dropdown.children().eq(i);
+ }
+ function update_highlighting(new_index) {
+ if (selected_index != -1) {
+ _choice(selected_index).removeClass('selected');
+ }
+ if (new_index != -1) {
+ _choice(new_index).addClass('selected');
+ }
+ selected_index = new_index;
+ }
+ function _applySuggestion(str) {
+ var cur = input_element.val(),
+ start = cur.slice(0, completion_start) + str + ' ';
+ input_element.val(start + cur.slice(completion_end));
+ setCaret(input_element, start.length);
+ oninput('', 0);
+ }
+ function handleEnter() {
+ if (selected_index == -1) {
+ return false;
+ }
+ _applySuggestion(_get_option(selected_index));
+ return true;
+ }
+
+ return {
+ options: options_bus,
+ oninput: oninput,
+ next_option: next_option,
+ prev_option: prev_option,
+ handleEnter: handleEnter,
+ };
+});
+
+return completer;
+
+});
diff --git a/src/client/view/edge_info.js b/src/client/view/edge_info.js
new file mode 100644
index 00000000..d711e200
--- /dev/null
+++ b/src/client/view/edge_info.js
@@ -0,0 +1,36 @@
+"use strict"
+
+define(['view/internal'],
+function(internal) {
+
+var delete_button = internal.edit_tab.get('edge', '#deleteedge'),
+ delete_callback = undefined;
+
+delete_button.on('click', function() {
+ if (delete_callback) {
+ delete_callback();
+ }
+});
+
+function show(link)
+{
+ internal.edit_tab.show('edge');
+ internal.edit_tab.get('edge', '#edgetitle').html(link.name);
+}
+
+function hide()
+{
+ internal.edit_tab.hide();
+}
+
+function on_delete(f)
+{
+ delete_callback = f;
+}
+
+return {
+ show: show,
+ hide: hide,
+ on_delete: on_delete,
+};
+});
diff --git a/src/client/view/helpers.js b/src/client/view/helpers.js
new file mode 100644
index 00000000..ad11763a
--- /dev/null
+++ b/src/client/view/helpers.js
@@ -0,0 +1,87 @@
+"use strict"
+
+define(function() {
+function customColor(type) {
+ var color;
+ switch (type) {
+ case "person":
+ color = '#FCB924'; //blue
+ break;
+ case "club":
+ color = '#ee3654'; //magenta
+ break;
+ case "skill":
+ color = '#fad900'; //yellow
+ break;
+ case "third-internship-proposal":
+ color = '#33c2e0'; //cyan
+ break;
+ case "internship":
+ color = '#ff8b11'; //orange
+ break;
+ case "interest":
+ color = '#8b3ab0'; //purple
+ break;
+ case "project":
+ color = "#40C200"; //green
+ break;
+ case "empty":
+ color = "#919095"; //mid-grey
+ break;
+ case "chainlink":
+ color = "#363636"; //dark-grey
+ break;
+ case "bubble":
+ color = "rgba(255,255,255,0.2)"; // white, 0.2 opaque
+ break;
+ default:
+ console.log('bug: unknown type ' + type);
+ color = '#d4d4d9'; //mid-light grey
+ break;
+ }
+ return color;
+}
+
+function customSize(type) {
+ var size;
+ switch (type) {
+ case "person":
+ size = 12;
+ break;
+ case "club":
+ size = 12;
+ break;
+ case "skill":
+ size = 12;
+ break;
+ case "third-internship-proposal":
+ size = 12;
+ break;
+ case "internship":
+ size = 12;
+ break;
+ case "interest":
+ size = 12;
+ break;
+ case "empty":
+ size = 9;
+ break;
+ case "chainlink":
+ size = 8;
+ break;
+ case "bubble":
+ size = 180;
+ break;
+ default:
+ size = 9;
+ break;
+ }
+ return size;
+}
+
+
+return {
+ customSize: customSize,
+ customColor: customColor,
+};
+});
diff --git a/src/client/view/internal.js b/src/client/view/internal.js
new file mode 100644
index 00000000..bc18bd75
--- /dev/null
+++ b/src/client/view/internal.js
@@ -0,0 +1,14 @@
+define(['view/tab'],
+function(tab) {
+
+var EDGE_INFO_SELECTOR = '.edge_info',
+ NODE_INFO_SELECTOR = '.info',
+ edit_tab = new tab.Tab({edge: EDGE_INFO_SELECTOR, node: NODE_INFO_SELECTOR});
+
+return {
+ EDGE_INFO_SELECTOR: EDGE_INFO_SELECTOR,
+ NODE_INFO_SELECTOR: NODE_INFO_SELECTOR,
+ edit_tab: edit_tab,
+};
+
+});
diff --git a/src/client/view/node_info.js b/src/client/view/node_info.js
new file mode 100644
index 00000000..44f2fab8
--- /dev/null
+++ b/src/client/view/node_info.js
@@ -0,0 +1,117 @@
+define(['jquery', 'jquery-ui', 'view/helpers', 'view/internal'],
+function($, _unused_jquery_ui, view_helpers, internal) {
+
+var d = null,
+ submit_callback = null,
+ delete_callback = null;
+
+function _get_form_data() {
+ return {
+ name: $('.info #editformname').val(),
+ type: $('.info #edittype').val(),
+ url: $('.info #editurl').val(),
+ status: $('.info #editstatus').val(),
+ startdate: $("#editstartdate").val(),
+ enddate: $("#editenddate").val(),
+ };
+}
+
+//internal.edit_tab.get('node', "#editbox").submit(function(e) {
+// if (submit_callback) {
+// return submit_callback(e, _get_form_data());
+// }
+// console.log('bug: edit tab submit called with no callback set');
+// e.preventDefault();
+//})
+
+//internal.edit_tab.get('node', "#deletenode").click(function(e) {
+// if (delete_callback) {
+// return delete_callback(e, _get_form_data());
+// }
+// console.log('bug: edit tab delete called with no callback set');
+// e.preventDefault();
+//});
+
+function show(d) {
+ var info = $('.info'),
+ f = false,
+ t = true,
+ visible = {
+ "third-internship-proposal": [t, t, t, f, f],
+ "chainlink": [f, f, f, f, f],
+ "skill": [f, f, f, f, t],
+ "interest": [f, f, f, f, t],
+ "_defaults": [f, f, f, f, t],
+ },
+ fields = ["#status", "#startdate", "#enddate", "#desc", "#url"],
+ flags = visible.hasOwnProperty(d.type) ? visible[d.type] : visible._defaults,
+ i;
+
+ internal.edit_tab.show('node');
+
+ for (i = 0 ; i < flags.length; ++i) {
+ var elem = info.find(fields[i]);
+ elem[flags[i] ? 'show' : 'hide']();
+ }
+
+ $('.info').attr('class', 'info');
+ $('.info').addClass('type-' + d.type); // Add a class to distinguish types for css
+
+ $('.info').find('#editformname').val(d.name);
+ $("#editenddate").datepicker({
+ inline: true,
+ showOtherMonths: true,
+ dayNamesMin: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ });
+ $("#editstartdate").datepicker({
+ inline: true,
+ showOtherMonths: true,
+ dayNamesMin: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
+ });
+
+ $('#editdescription').val(d.type);
+
+ $('#edittype').val(d.type);
+
+ $('#editurl').val(d.url);
+
+ $('#editstatus').val(d.status);
+
+ if (d.type === "third-internship-proposal") {
+ $('#editstartdate').val(d.start);
+ $('#editenddate').val(d.end);
+ }
+}
+
+function hide()
+{
+ internal.edit_tab.hide();
+}
+
+function on_save(f) {
+ $('#edit-node-dialog__save').click(function(e) {
+ return f(e, _get_form_data());
+ });
+}
+
+function on_delete(f) {
+ $('#edit-node-dialog__delete').click(function(e) {
+ return f(e, _get_form_data());
+ });
+}
+
+function on_keyup(f) {
+ $('.info').keyup(function(e) {
+ return f(e, _get_form_data());
+ });
+}
+
+return {
+ show: show,
+ hide: hide,
+ on_save: on_save,
+ on_delete: on_delete,
+ on_keyup: on_keyup,
+};
+
+});
diff --git a/src/client/view/selection.js b/src/client/view/selection.js
new file mode 100644
index 00000000..d51d1a4b
--- /dev/null
+++ b/src/client/view/selection.js
@@ -0,0 +1,102 @@
+define(['rz_core'],
+function(rz_core) {
+
+function get_rz_core()
+{
+ // circular dependency on rz_core, so require.js cannot solve it.
+ if (rz_core === undefined) {
+ rz_core = require('rz_core');
+ }
+ return rz_core;
+}
+
+var selected_nodes = [];
+
+function byVisitors(node_selector, link_selector) {
+ var new_selected_nodes = get_rz_core().graph.findByVisitors(node_selector, link_selector);
+
+ clear();
+ connectedComponent(new_selected_nodes);
+}
+
+function connectedComponent(nodes) {
+ var connected = get_rz_core().graph.getConnectedNodesAndLinks(nodes, 1),
+ i,
+ node,
+ link,
+ data;
+
+ selected_nodes = nodes.map(function(x) { return x; });
+
+ for (i = 0 ; i < connected.nodes.length ; ++i) {
+ data = connected.nodes[i];
+ node = data.node;
+ switch (data.type) {
+ case 'exit':
+ node.state = 'exit';
+ break;
+ case 'enter':
+ node.state = 'enter';
+ break;
+ };
+ }
+ for (i = 0 ; i < connected.links.length ; ++i) {
+ data = connected.links[i];
+ link = data.link;
+ switch (data.type) {
+ case 'exit':
+ link.state = 'exit';
+ break;
+ case 'enter':
+ link.state = 'enter';
+ break;
+ };
+ }
+ nodes.forEach(function (n) { n.state = 'chosen'; });
+}
+
+var node_selected = function(node) {
+ return node.state == 'chosen' || node.state == 'enter' || node.state == 'exit' || node.state == 'selected'
+ || node.state == 'temp' || node.state == 'related';
+}
+
+var selected_class = function(node) {
+ return selected_nodes.length > 0 ? (node_selected(node) ? "selected" : "notselected") : "";
+}
+
+var clear = function() {
+ selected_nodes.length = 0;
+ get_rz_core().graph.setRegularState();
+}
+
+function arr_compare(a1, a2)
+{
+ if (a1.length != a2.length) {
+ return false;
+ }
+ for (var i = 0 ; i < a1.length ; ++i) {
+ if (a1[i] != a2[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+var update = function(nodes) {
+ var set = !arr_compare(nodes, selected_nodes);
+ clear();
+ if (set) {
+ connectedComponent(nodes);
+ }
+}
+
+return {
+ byVisitors: byVisitors,
+ connectedComponent: connectedComponent,
+ clear: clear,
+ update: update,
+ selected_class: selected_class,
+ node_selected: node_selected,
+};
+
+});
diff --git a/src/client/view/tab.js b/src/client/view/tab.js
new file mode 100644
index 00000000..97ba1351
--- /dev/null
+++ b/src/client/view/tab.js
@@ -0,0 +1,57 @@
+"use strict"
+
+define(['jquery'],
+function($) {
+
+function Tab(dict) {
+ var k,
+ selector = {},
+ name = [];
+
+ for (k in dict) {
+ if (dict.hasOwnProperty(k) == false) {
+ continue;
+ }
+ selector[k] = dict[k];
+ name.push(k);
+ }
+ this._selector = selector;
+ this._name = name;
+}
+
+Tab.prototype.show = function(shown_name) {
+ var i,
+ name,
+ element;
+
+ for (i = 0 ; i < this._name.length ; ++i) {
+ name = this._name[i];
+ element = $(this._selector[name]);
+ if (name === shown_name) {
+ element.fadeIn(300);
+ } else {
+ element.hide();
+ }
+ }
+}
+
+Tab.prototype.hide = function() {
+ var i;
+
+ for (i = 0 ; i < this._name.length ; ++i) {
+ $(this._selector[this._name[i]]).fadeOut(300);
+ }
+}
+
+Tab.prototype.get = function(name, sel) {
+ // selector concatenation
+ var e = $(this._selector[name] + ' ' + sel);
+
+ return e;
+}
+
+return {
+ Tab: Tab
+};
+
+});
diff --git a/src/client/view/timeline.js b/src/client/view/timeline.js
new file mode 100644
index 00000000..0a26c0a7
--- /dev/null
+++ b/src/client/view/timeline.js
@@ -0,0 +1,180 @@
+"use strict"
+
+define(['jquery', 'rz_core'],
+function ($, rz_core) {
+
+function checkSwitch(checkswitch) {
+
+ if (checkswitch.checked) {
+ vis.selectAll(".timeline").remove();
+ $('.missingdates').fadeOut(300);
+ scrollValue = $('body').scrollLeft();
+
+ $('body').scrollLeft(0);
+ graphstate = "GRAPH";
+ rz_core.update_view__graph();
+
+ $('.status').fadeOut(600);
+
+ //boxedin=false;
+
+ } else {
+ timelineTimer=0;
+ $('.missingdates').fadeIn(300);
+
+ graph.recenterZoom();
+
+ $('body').scrollLeft(scrollValue);
+
+ graphstate = "TIMELINE";
+
+ rz_core.update_view__graph();
+
+ $('.status').fadeIn(600);
+
+
+ initAxis();
+
+ //boxedin=true;
+ }
+
+}
+
+///For some reason JQuery's $('body').scroll never worked, so I found something else.
+$('body').bind('DOMMouseScroll', function(e){
+ if(graphstate==="TIMELINE"){
+ if(e.originalEvent.detail !== 0) {
+ $('.overlay').hide();
+ }else{
+ $('.overlay').show();
+ }
+ }else{
+ return false;
+ }
+ });
+
+ //IE, Opera, Safari
+ $('body').bind('mousewheel', function(e){
+ if(graphstate==="TIMELINE"){
+ if(e.originalEvent.wheelDelta !== 0) {
+ $('.overlay').hide();
+ }else{
+ $('.overlay').show();
+ var left = $('body').offset().left;
+ $('.overlay').scrollLeft(left);
+ }
+ }else{
+ return false;
+ }
+ });
+
+function initAxis() {
+ var bar_height = 20;
+ var row_height = bar_height + 10;
+ var vertical_padding = 150
+ var bar_start_offset = 40;
+ var h = 15 * row_height + vertical_padding;
+
+
+ var min = deliverables[0].startdate;
+ var max = deliverables[0].enddate;
+
+ for (var i = 0; i < deliverables.length; i++) {
+ var deliv = deliverables[i];
+ if (min > deliv.startdate) min = deliv.startdate;
+ if (end < deliv.enddate) end = deliv.enddate;
+ }
+ min = new Date(min); ///with min
+ max = new Date(max);
+ console.log("min "+min+" max "+max);
+
+ ///update interval
+ var timeDiff = Math.abs(max.getTime() - min.getTime());
+ var diffDays = timeDiff / (1000 * 3600 * 24);
+ var w = Math.round(diffDays * 15 - 100);
+ graphinterval = w / diffDays;
+
+ var svg=vis;
+ vis.attr("width",w);
+
+ var paddingLeft = 150;
+ var paddingTop = 120;
+
+ var xScale = d3.time.scale()
+ .domain([min, max]).nice()
+ .range([paddingLeft, w]);
+
+ var xAxis = d3.svg.axis()
+ .scale(xScale)
+ .orient("bottom");
+
+ // Lines
+ var line = svg.append("g")
+ .selectAll("line")
+ .data(xScale.ticks(40))
+ .enter().append("line")
+ .attr("x1", xScale)
+ .attr("x2", xScale)
+ .attr("y1", paddingTop + 30)
+ .attr("y2", h - 50)
+ .attr("class", "timeline")
+ .style("stroke", "#ccc");
+
+ var y = function (i) {
+ return i * row_height + paddingTop + bar_start_offset;
+ }
+
+ var labelY = function (i) {
+ return i * row_height + paddingTop + bar_start_offset + 13;
+ }
+
+ // Company bars
+ var bar = svg.selectAll("rect")
+ .data(function (d) {
+ return Math.random() * 5;
+ });
+
+ bar.enter().append("rect")
+ .attr("y", -100)
+ .attr("x", -1000)
+ .attr("width", 100)
+ .attr("height", bar_height)
+ .attr("class", "company-bar timeline")
+ .on("mouseover", function (d) {
+ d3.select(this).style("fill", "#F5AF00");
+ getCompanyData(String(d.uid))
+ })
+ .on("mouseout", function () {
+ d3.select(this).style("fill", "#fc0");
+ });
+
+ var label = svg.selectAll("text")
+ .data(deliverables, function (key) {
+ return key.id
+ });;
+
+ label.enter().append("text")
+ .attr("class", "bar-label timeline")
+ // .attr("text-anchor","end")
+ .attr("x", paddingLeft - 10)
+ .attr("y", function (d, i) {
+ return labelY(i);
+ })
+ .text(function (d) {
+ });
+
+ // Bottom Axis
+ var btmAxis = svg.append("g")
+ .attr("transform", "translate(0," + (h - 25) + ")")
+ .attr("class", "axis timeline")
+ .call(xAxis);
+
+ // Top Axis
+ var topAxis = svg.append("g")
+ .attr("transform", "translate(0," + paddingTop + ")")
+ .attr("class", "axis timeline")
+ .call(xAxis);
+}
+
+return {};
+});
diff --git a/src/client/view/view.js b/src/client/view/view.js
new file mode 100644
index 00000000..95bfc09b
--- /dev/null
+++ b/src/client/view/view.js
@@ -0,0 +1,12 @@
+"use strict"
+
+define(['view/node_info', 'view/edge_info', 'view/internal'],
+function(view_node_info, view_edge_info, view_internal) {
+return {
+ 'node_info': view_node_info,
+ 'edge_info': view_edge_info,
+ 'hide': function() {
+ view_internal.edit_tab.hide();
+ },
+};
+});