diff options
| author | Alon Levy <alon@pobox.com> | 2014-12-16 17:37:42 +0200 |
|---|---|---|
| committer | Alon Levy <alon@pobox.com> | 2014-12-16 17:37:42 +0200 |
| commit | ade19de410e0a789a921ec1666b278b2a64176d6 (patch) | |
| tree | c4c54b106de93609ca7bb70a20fd4be696715dbe /src/client/view | |
| parent | c7d026b1d504323a8c61d51b726e5e8d2337fc4b (diff) | |
moving files around after repository merger
Diffstat (limited to 'src/client/view')
| -rw-r--r-- | src/client/view/completer.js | 229 | ||||
| -rw-r--r-- | src/client/view/edge_info.js | 36 | ||||
| -rw-r--r-- | src/client/view/helpers.js | 87 | ||||
| -rw-r--r-- | src/client/view/internal.js | 14 | ||||
| -rw-r--r-- | src/client/view/node_info.js | 117 | ||||
| -rw-r--r-- | src/client/view/selection.js | 102 | ||||
| -rw-r--r-- | src/client/view/tab.js | 57 | ||||
| -rw-r--r-- | src/client/view/timeline.js | 180 | ||||
| -rw-r--r-- | src/client/view/view.js | 12 |
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(); + }, +}; +}); |
