| /* |
| * Copyright 2014 Open Networking Laboratory |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| ONOS GUI -- Quick Help Layer |
| |
| Defines the key-map layer for the UI. Used to give user a list of |
| key bindings; both global, and for the current view. |
| |
| @author Simon Hunt |
| */ |
| |
| (function (onos){ |
| 'use strict'; |
| |
| // API's |
| var api = onos.api; |
| |
| // Config variables |
| var w = '100%', |
| h = '80%', |
| fade = 500, |
| vb = '-200 0 400 400'; |
| |
| // State variables |
| var data = []; |
| |
| // DOM elements and the like |
| var qhdiv = d3.select('#quickhelp'), |
| svg = qhdiv.select('svg'), |
| pane, |
| rect, |
| items, |
| keyAgg; |
| |
| // General functions |
| function isA(a) { |
| return $.isArray(a) ? a : null; |
| } |
| |
| var keyDisp = { |
| equals: '=', |
| dash: '-', |
| slash: '/', |
| backQuote: '`', |
| leftArrow: 'L-arrow', |
| upArrow: 'U-arrow', |
| rightArrow: 'R-arrow', |
| downArrow: 'D-arrow' |
| }; |
| |
| function cap(s) { |
| return s.replace(/^[a-z]/, function (m) { return m.toUpperCase(); }); |
| } |
| |
| function mkKeyDisp(id) { |
| var v = keyDisp[id] || id; |
| return cap(v); |
| } |
| |
| // layout configuration |
| var pad = 8, |
| offy = 45, |
| dy = 14, |
| offDesc = 8; |
| |
| // D3 magic |
| function updateKeyItems() { |
| var keyItems = items.selectAll('.keyItem') |
| .data(data); |
| |
| var entering = keyItems.enter() |
| .append('g') |
| .attr({ |
| id: function (d) { return d.id; }, |
| class: 'keyItem' |
| }); |
| |
| entering.each(function (d, i) { |
| var el = d3.select(this), |
| y = offy + dy * i; |
| |
| if (d.id[0] === '_') { |
| el.append('line') |
| .attr({ x1: 0, y1: y, x2: 1, y2: y}); |
| } else { |
| el.append('text') |
| .text(d.key) |
| .attr({ |
| class: 'key', |
| x: 0, |
| y: y |
| }); |
| // NOTE: used for sizing column width... |
| keyAgg.append('text').text(d.key).attr('class', 'key'); |
| |
| el.append('text') |
| .text(d.desc) |
| .attr({ |
| class: 'desc', |
| x: offDesc, |
| y: y |
| }); |
| } |
| }); |
| |
| var kbox = keyAgg.node().getBBox(); |
| items.selectAll('.desc').attr('x', kbox.width + offDesc); |
| |
| var box = items.node().getBBox(), |
| paneW = box.width + pad * 2, |
| paneH = box.height + offy; |
| |
| items.selectAll('line').attr('x2', box.width); |
| items.attr('transform', translate(-paneW/2, -pad)); |
| rect.attr({ |
| width: paneW, |
| height: paneH, |
| transform: translate(-paneW/2-pad, 0) |
| }); |
| } |
| |
| function translate(x, y) { |
| return 'translate(' + x + ',' + y + ')'; |
| } |
| |
| function aggregateData(bindings) { |
| var gmap = d3.map(bindings.globalKeys), |
| vmap = d3.map(bindings.viewKeys), |
| gkeys = gmap.keys(), |
| vkeys = vmap.keys(), |
| vgest = bindings.viewGestures, |
| sep = 0; |
| |
| gkeys.sort(); |
| vkeys.sort(); |
| |
| data = []; |
| gkeys.forEach(function (k) { |
| addItem('glob', k, gmap.get(k)); |
| }); |
| addItem('sep'); |
| vkeys.forEach(function (k) { |
| addItem('view', k, vmap.get(k)); |
| }); |
| addItem('sep'); |
| vgest.forEach(function (g) { |
| if (g.length === 2) { |
| addItem('gest', g[0], g[1]); |
| } |
| }); |
| |
| |
| function addItem(type, k, d) { |
| var id = type + '-' + k, |
| a = isA(d), |
| desc = a && a[1]; |
| |
| if (type === 'sep') { |
| data.push({ |
| id: '_' + sep++, |
| type: type |
| }); |
| } else if (type === 'gest') { |
| data.push({ |
| id: id, |
| type: type, |
| key: k, |
| desc: d |
| }); |
| } else if (desc) { |
| data.push( |
| { |
| id: id, |
| type: type, |
| key: mkKeyDisp(k), |
| desc: desc |
| } |
| ); |
| } |
| } |
| } |
| |
| function popBind(bindings) { |
| pane = svg.append('g') |
| .attr({ |
| class: 'help', |
| opacity: 0 |
| }); |
| |
| rect = pane.append('rect') |
| .attr('rx', 8); |
| |
| pane.append('text') |
| .text('Quick Help') |
| .attr({ |
| class: 'title', |
| dy: '1.2em', |
| transform: translate(-pad,0) |
| }); |
| |
| items = pane.append('g'); |
| keyAgg = pane.append('g').style('visibility', 'hidden'); |
| |
| aggregateData(bindings); |
| updateKeyItems(); |
| |
| _fade(1); |
| } |
| |
| function fadeBindings() { |
| _fade(0); |
| } |
| |
| function _fade(o) { |
| svg.selectAll('g.help') |
| .transition() |
| .duration(fade) |
| .attr('opacity', o); |
| } |
| |
| function addSvg() { |
| svg = qhdiv.append('svg') |
| .attr({ |
| width: w, |
| height: h, |
| viewBox: vb |
| }); |
| } |
| |
| function removeSvg() { |
| svg.transition() |
| .delay(fade + 20) |
| .remove(); |
| } |
| |
| function showQuickHelp(bindings) { |
| svg = qhdiv.select('svg'); |
| if (svg.empty()) { |
| addSvg(); |
| popBind(bindings); |
| } else { |
| hideQuickHelp(); |
| } |
| } |
| |
| function hideQuickHelp() { |
| svg = qhdiv.select('svg'); |
| if (!svg.empty()) { |
| fadeBindings(); |
| removeSvg(); |
| return true; |
| } |
| return false; |
| } |
| |
| onos.ui.addLib('quickHelp', { |
| show: showQuickHelp, |
| hide: hideQuickHelp |
| }); |
| }(ONOS)); |