/* */ /* d3controls.js */ /* */ if (typeof require === "function") { var d3 = require('./d3.v4.0.0-alpha.40.js') } (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3lanesControls = global.d3lanesControls || {}))); }(this, function (exports) { 'use strict'; /* ------------- */ /* stepControls */ /* ------------- */ function stepControls(store) { var store = store var currentListeners = [] var nextListeners = currentListeners // ______________________________ ensureCanMutateNextListeners function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } // ____________________ tc function tc() {} // ____________________ start tc.start = function start() { var periodFactor = store.getState().configReducer.periodFactor var beatTime = store.getState().configReducer.beatTime var periodTime = periodFactor * beatTime // items added var itemSpan = store.getState().configReducer.itemSpan var tickspan = store.getState().configReducer.tickspan var vLow = store.getState().lanesReducer.messagesCursorLow var vHigh = store.getState().lanesReducer.messagesCursorHigh var tf = setInterval(function() { var currentMode = store.getState().courtReducer.currentMode var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() } }, periodTime) return tc } // ______________________________ subscribe tc.subscribe = function subscribe (listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return tc } return tc } /* ------------- */ /* tickControls */ /* ------------- */ function tickControls(store) { var store = store var currentListeners = [] var nextListeners = currentListeners // ______________________________ ensureCanMutateNextListeners function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } // ____________________ tc function tc() {} // ____________________ start tc.start = function start() { // Anatomy of a video game // Misuse of the requestAnimationFrme() var started = false var rfps = 60 var last = performance.now() var timestamp = 0 var ticker = function(timestamp) { window.requestAnimationFrame(ticker) if (timestamp != undefined) rfps = rfps * 0.9 + (1000/(timestamp-last)) * 0.1 if (timestamp != undefined) last = timestamp while( performance.now() - timestamp < 17 ) {} var fps = parseFloat(Math.round(rfps * 100) / 100).toFixed(0) store.dispatch(actions.setFps(fps)) var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() } } if (!started) { started = true ticker() } return tc } // ______________________________ subscribe tc.subscribe = function subscribe (listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return tc } return tc } /* ------------- */ /* mouseControls */ /* ------------- */ function mouseControls(store) { var store = store // ____________________ mouseEventsActions var mousedown = function mousedown(svg) { // console.log("event: ", d3.event) // var e = window.event; var e = d3.event pauseEvent(e); function pauseEvent(e){ if(e.stopPropagation) e.stopPropagation(); if(e.preventDefault) e.preventDefault(); e.cancelBubble=true; e.returnValue=false; return false; } var coords = d3.mouse(svg); store.dispatch(actions.updateMousePos(coords[0], coords[1])) store.dispatch(actions.startParticles()) store.dispatch(actions.createParticles({ particlesPerTick: store.getState().particlesReducer.particlesPerTick, x: coords[0], y: coords[1], xInit: 0, xEnd: store.getState().courtReducer.svgWidth, randNormal: store.getState().configReducer.randNormal, randNormal2: store.getState().configReducer.randNormal2, lanes: store.getState().lanesReducer.lanes, })) } var touchstart = function touchstart(svg) { var coords = d3.mouse(svg); store.dispatch(actions.updateTouchPos(coords[0], coords[1])) store.dispatch(actions.startParticles()) store.dispatch(actions.createParticles({ particlesPerTick: store.getState().particlesReducer.particlesPerTick, x: coords[0], y: coords[1], xInit: 0, xEnd: store.getState().courtReducer.svgWidth, randNormal: store.getState().configReducer.randNormal, randNormal2: store.getState().configReducer.randNormal2, lanes: store.getState().lanesReducer.lanes, })) } var mousemove = function mousemove(svg) { var coords = d3.mouse(svg); store.dispatch(actions.updateMousePos(coords[0], coords[1])) var generating = store.getState().particlesReducer.particlesGenerating if (generating === true) { store.dispatch(actions.createParticles({ particlesPerTick: store.getState().particlesReducer.particlesPerTick, x: coords[0], y: coords[1], xInit: 0, xEnd: store.getState().courtReducer.svgWidth, randNormal: store.getState().configReducer.randNormal, randNormal2: store.getState().configReducer.randNormal2, lanes: store.getState().lanesReducer.lanes, })) } } var touchmove = function touchmove(svg) { var coords = d3.mouse(svg); store.dispatch(actions.updateTouchPos(coords[0], coords[1])) } var mouseup = function mouseup(svg) { store.dispatch(actions.stopParticles()) var generating = store.getState().particlesReducer.particlesGenerating } var touchend = function touchend(svg) { store.dispatch(actions.stopParticles()) } var mouseleave = function mouseleave(svg) { store.dispatch(actions.stopParticles()) } // ____________________ controlfn function controlfn() { } // ____________________ startMouseEvents controlfn.startMouseEvents = function startMouseEvents(svg) { svg.on('mousedown', function() {mousedown(this)}) svg.on('touchstart', function() {touchstart(this)}) svg.on('mousemove', function() {mousemove(this)}) svg.on('touchmove', function() {touchmove(this)}) svg.on('mouseup', function() {mouseup(this)}) svg.on('mouseleave', function() {mouseleave(this)}) svg.on('mouseleave', function() {mouseleave(this)}) } return controlfn } /* ------------- */ /* kbdControls */ /* ------------- */ function kbdControls(store) { var store = store // ____________________ handleKeyDown // https://www.kirupa.com/html5/keyboard_events_in_javascript.htm // https://github.com/gaearon/redux-devtools-dock-monitor var handleKeyDown = function handleKeyDown(e) { e.stopPropagation(); e.preventDefault(); store.dispatch(actions.setKeybKey(e.keyCode)) var keys = store.getState().courtReducer.keys // keys[e.keyCode] = true; if (keys[70] && keys[17]) fKeyCtrl() // change currentView else if (keys[68] && keys[17]) dKeyCtrl() // change debugMode else if (e.keyCode == '37' && !keys[17]) leftArrow() // change currentMode autoMode/walkMode else if (e.keyCode == '37' && keys[17]) leftArrowCtrl() // change width else if (e.keyCode == '39' && !keys[17]) rightArrow() // change currentMode else if (e.keyCode == '39' && keys[17]) rightArrowCtrl() // change width else if (e.keyCode == '38' && !keys[17]) upArrow() // change currentMode nextWalk else if (e.keyCode == '38' && keys[17]) upArrowCtrl() // change height else if (e.keyCode == '40' && !keys[17]) downArrow() // change currentMode else if (e.keyCode == '40' && keys[17]) downArrowCtrl() // change height } // ____________________ downArrowCtrl var downArrowCtrl = function downArrowCtrl() { store.dispatch(actions.resizeHeight(+10)) } // ____________________ fKeyCtrl var fKeyCtrl = function fKeyCtrl() { // change view // // Ctrl 17 + Shift 16 + f 70 var views = Object.keys(store.getState().configReducer.views) var idx = views.indexOf(store.getState().courtReducer.currentView) var newIdx = idx + 1 % views.length var newview = store.getState().configReducer.views[views[newIdx]] store.dispatch(actions.setView(newview)) } // ____________________ dKeyCtrl var dKeyCtrl = function dKeyCtrl() { // change debug mode // // Ctrl 17 + Shift 16 + d 68 store.dispatch(actions.switchDebugMode()) } // ____________________ matchesKey function matchesKey(key, event) { if (!key) return false const charCode = event.keyCode || event.which; const char = String.fromCharCode(charCode); return key.name.toUpperCase() === char.toUpperCase() && key.alt === event.altKey && key.ctrl === event.ctrlKey && key.meta === event.metaKey && key.shift === event.shiftKey; } // ____________________ handleKeyPressed var handleKeyPressed = function handleKeyPressed(e) { } // ____________________ handleKeyReleased var handleKeyReleased = function handleKeyReleased(e) { store.dispatch(actions.releaseKeybKey(e.keyCode)) } // ____________________ keysEventsActions // arrows up/down => currentMode walkMode // arrow right => currentMode autoMode // arrow left => currentMode walkMode // ____________________ leftArrow var leftArrow = function leftArrow() { // set currentMode walkMode var currentMode = 'walkMode' store.dispatch(actions.setMode(currentMode)) } // ____________________ rightArrow var rightArrow = function rightArrow() { // set currentMode autoMode var currentMode = 'autoMode' store.dispatch(actions.setMode(currentMode)) } // ____________________ upArrow var upArrow = function upArrow() { var currentMode = store.getState().courtReducer.currentMode if (currentMode == 'autoMode') { var newMode = 'walkMode' store.dispatch(actions.setMode(newMode)) } else if (currentMode == 'walkMode') { var itemSpan = store.getState().configReducer.itemSpan store.dispatch(actions.walkUpRecords(itemSpan, currentMode)) } } // ____________________ downArrow var downArrow = function downArrow() { var currentMode = store.getState().courtReducer.currentMode if (currentMode == 'autoMode') { var newMode = 'walkMode' store.dispatch(actions.setMode(newMode)) } else if (currentMode == 'walkMode') { var itemSpan = store.getState().configReducer.itemSpan store.dispatch(actions.walkDownRecords(itemSpan, currentMode)) } } // ____________________ leftArrowCtrl var leftArrowCtrl = function leftArrowCtrl() { console.log("leftArrowCtrlFn") store.dispatch(actions.resizeWidth(-10)) } // ____________________ rightArrowCtrl var rightArrowCtrl = function rightArrowCtrl() { console.log("rightArrowCtrlFn") store.dispatch(actions.resizeWidth(10)) } // ____________________ upArrowCtrl var upArrowCtrl = function upArrowCtrl() { console.log("upArrowCtrlFn") store.dispatch(actions.resizeWidth(-10)) } // ____________________ controlfn function controlfn() { } // ____________________ startKeysEvents controlfn.startKeybKeyEvents = function startKeybKeyEvents() { store.dispatch(actions.startKeybKeyEvents()) document.addEventListener("keydown", handleKeyDown, false); document.addEventListener("keypress", handleKeyPressed, false); document.addEventListener("keyup", handleKeyReleased, false); } return controlfn } /* ================================= */ /* posControls */ /* ================================= */ function posControls (scope) { // selection var dName = 'pos' var aTypes = { start: dName + 'start', // tipstart move: dName + 'move', // tipmove end: dName + 'end' // tipend } var aTypesList = Object.keys(aTypes).map(function(k){return aTypes[k]}) var qaTypesList = aTypesList.map(function(k){return k + "." + dName}) // ______________________________ fnCallbacks var fnCallbacks = {} for (var i = 0; i < aTypesList.length; i++) { fnCallbacks[aTypesList[i]] = function(action) {} } // ______________________________ start pos fnCallbacks[aTypes.start] = function(action) { var node = d3.select(this) // selection var datum = node.datum() // datum } // ______________________________ move pos fnCallbacks[aTypes.move] = function(action) { var node = d3.select(this) // selection // node.on("mousemove.tip", null) createTextPad(action) displayTextPad(action) moveTextPad(action) function createTextPad(a) { var textPadDiv = d3.select("body") .selectAll("div.postip") .data(['divMousePos']) .enter() .append("div") .attr("class", "postip") .attr("viewBox", "0 0 10 10") .style("top", "-5px") .style("position", "absolute") .style("padding", "10px") .style("background", "rgba(255, 255, 255, .90)") .style("border", "1px solid lightgray") .style("pointer-events", "none") .style("z-index", "100") .style('border', '1px solid red') .style('color', 'grey') .classed('postip-hidden', true) .style("opacity", 0) } function textPadFn (a) { var s = String("|_____" + a.ox + " " + a.oy + "_____|") return s } // https://github.com/1wheel/swoopy-drag/blob/master/lib/d3-jetpack.js function displayTextPad(a) { d3.select('.postip') .classed('postip-hidden', false) .style('opacity', 1) .html('') .selectAll('div') .data([textPadFn]).enter() .append('div') .html(function(textPadFn) { return (textPadFn(a)) }) } function moveTextPad() { var postip = d3.select('div.postip') if (!postip.size()) return var e = d3.event, x = e.clientX, y = e.clientY, doctop = (window.scrollY)? window.scrollY : (document.documentElement && document.documentElement.scrollTop)? document.documentElement.scrollTop : document.body.scrollTop, n = postip.node(), nBB = n.getBoundingClientRect() postip.style('top', (y+doctop-nBB.height-18)+"px"); postip.style('left', Math.min(Math.max(0, (x-nBB.width/2)), window.innerWidth - nBB.width)+"px"); } } // ______________________________ end pos fnCallbacks[aTypes.end] = function(action) { var node = d3.select(this) // selection var datum = node.datum() // datum d3.select('div.postip') .classed('postip-hidden', true) .style('opacity', 0) d3.selectAll('.postipped') .classed('postipped', false) } // ______________________________ dispatcher var d3_event = d3.dispatch.apply(null, aTypesList) for (var i=0; i < qaTypesList.length; i++) { d3_event.on(qaTypesList[i], fnCallbacks[aTypesList[i]]) } d3_event.of = function(thiz, argumentz) { return function(e1) { d3_event.call(e1.type, thiz, e1) } } // ______________________________ started function started(d, i, nodes) { var datum = d, // d datum node = this, // elem parent = node.parentNode, origin = d3.mouse(parent), ox = origin[0], oy = origin[1] var context = d3.select(d3_window(node)) // selection var a = { type: aTypes.start, ox: ox, oy: oy } d3_event.of(node)(a) } // ______________________________ moved function moved(d, i, nodes) { var datum = d, // d datum node = this, // elem parent = node.parentNode, origin = d3.mouse(parent), ox = origin[0], oy = origin[1] var context = d3.select(d3_window(node)) // selection var a = { type: aTypes.move, ox: ox, oy: oy } d3_event.of(node)(a) } // ______________________________ ended function ended(d, i, nodes) { var datum = d, // d datum node = this, // elem parent = node.parentNode, origin = d3.mouse(parent) var a = { type: aTypes.end } d3_event.of(node)(a) } // ______________________________ lib function prevent() { event.preventDefault(); } function d3_window(node) { return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); } // ______________________________ function d3Control(scope) { scope.on("mouseenter.pos", started) scope.on("mousemove.pos", moved) scope.on("mouseout.pos", ended) } d3Control.on = function() { var value = d3_event.on.apply(d3_event, arguments); return value === d3_event ? d3Control : value; } return d3Control } /* ================================= */ /* tipControls */ /* ================================= */ function tipControls (scope) { // selection var dName = 'tip' var aTypes = { start: dName + 'start', // tipstart move: dName + 'move', // tipmove end: dName + 'end' // tipend } var aTypesList = Object.keys(aTypes).map(function(k){return aTypes[k]}) var qaTypesList = aTypesList.map(function(k){return k + "." + dName}) // ______________________________ fnCallbacks var fnCallbacks = {} for (var i = 0; i < aTypesList.length; i++) { fnCallbacks[aTypesList[i]] = function(action) {} } // ______________________________ start tip fnCallbacks[aTypes.start] = function(action) { var node = d3.select(this) // selection var datum = node.datum() // datum createTooltip(action) displayTooltip(action) moveMoveTooltip(action) function createTooltip() { var tipDiv = d3.select("body") .selectAll("div.tooltip") .data(['divTooltip']) .enter() .append("div") .attr("class", "tooltip") .attr("viewBox", "0 0 10 10") .style("top", "-5px") .style("position", "absolute") .style("padding", "10px") .style("background", "rgba(255, 255, 255, .90)") .style("border", "1px solid lightgray") .style("pointer-events", "none") .style("z-index", "100") .style('border', '1px solid red') .style('color', 'grey') .classed('tooltip-hidden', true) .style("opacity", 0) } // https://github.com/1wheel/swoopy-drag/blob/master/lib/d3-jetpack.js function displayTooltip() { var d = action.datum var fieldFns = d3.keys(d) .filter(function(str){ var r = (typeof d[str] != 'object') && (d[str] != 'array') return r }) .map(function(str){ return function (d) { return str + ': ' + '' + d[str] + '' }}) var tipfn = function(action) { var s = String(action.datum.tip || action.datum.id || 'tip') return wordwrap(s, 20) } d3.select('.tooltip') .classed('tooltip-hidden', false) .style('opacity', 1) .html('') .selectAll('div') .data(fieldFns).enter() .append('div') .html(function(fieldFns) { return (fieldFns(d)) }) } function moveMoveTooltip() { var tooltip = d3.select('.tooltip') if (!tooltip.size()) return var e = d3.event, x = e.clientX, y = e.clientY, doctop = (window.scrollY)? window.scrollY : (document.documentElement && document.documentElement.scrollTop)? document.documentElement.scrollTop : document.body.scrollTop, n = tooltip.node(), nBB = n.getBoundingClientRect() tooltip.style('top', (y+doctop-nBB.height-18)+"px"); tooltip.style('left', Math.min(Math.max(0, (x-nBB.width/2)), window.innerWidth - nBB.width)+"px"); } function wordwrap (line, maxCharactersPerLine) { var w = line.split(' '), lines = [], words = [], maxChars = maxCharactersPerLine || 40, l = 0; w.forEach(function(d) { if (l+d.length > maxChars) { lines.push(words.join(' ')); words.length = 0; l = 0; } l += d.length; words.push(d); }); if (words.length) { lines.push(words.join(' ')); } return lines; } } // ______________________________ move tip fnCallbacks[aTypes.move] = function(action) { var node = d3.select(this) // selection var datum = node.datum() // datum } // ______________________________ end tip fnCallbacks[aTypes.end] = function(action) { var node = d3.select(this) // selection var datum = node.datum() // datum d3.select('.tooltip') .classed('tooltip-hidden', true) .style('opacity', 0) d3.selectAll('.tooltipped') .classed('tooltipped', false) } // ______________________________ dispatcher var d3_event = d3.dispatch.apply(null, aTypesList) for (var i=0; i < qaTypesList.length; i++) { d3_event.on(qaTypesList[i], fnCallbacks[aTypesList[i]]) } d3_event.of = function(thiz, argumentz) { return function(e1) { d3_event.call(e1.type, thiz, e1) } } // ______________________________ started tip function started(d, i, nodes) { var datum = d, // d datum node = this, // elem parent = node.parentNode, origin = d3.mouse(parent), ox = d.x - origin[0] || 0, oy = d.y - origin[1] || 0, tiped = false var context = d3.select(d3_window(node)) // selection var a = { type: aTypes.start, datum: d } d3_event.of(node)(a) } // ______________________________ ended tip function ended(d, i, nodes) { var datum = d, // d datum node = this, // elem parent = node.parentNode, origin = d3.mouse(parent), ox = d.x - origin[0] || 0, oy = d.y - origin[1] || 0, tiped = false var a = { type: aTypes.end, } d3_event.of(node)(a) } // ______________________________ lib function prevent() { event.preventDefault(); } function d3_window(node) { return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); } // ______________________________ function d3Control(scope) { scope.each(function() { scope.on("mousemove.tip", started) scope.on("mouseout.tip", ended) }) } d3Control.on = function() { var value = d3_event.on.apply(d3_event, arguments); return value === d3_event ? d3Control : value; } return d3Control } /* ================================= */ /* dragControls */ /* ================================= */ // https://github.com/d3/d3-drag/blob/master/src/drag.js function dragControls (scope) { function prevent() { event.preventDefault(); } function d3_window(node) { return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); } var scope = scope // selection var dName = 'drag' var aTypes = { start: dName + 'start', // dragstart move: dName + 'move', // dragmove end: dName + 'end' // dragend } var aTypesList = Object.keys(aTypes).map(function(k){return aTypes[k]}) var qaTypesList = aTypesList.map(function(k){return k + "." + dName}) var fnCallbacks = {} for (var i = 0; i < aTypesList.length; i++) { fnCallbacks[aTypesList[i]] = function(action) {} } // ______________________________ dragend fnCallbacks[aTypes.end] = function(action) { var node = d3.select(this) node.datum().dx1 = action.dx1 node.datum().dy1 = action.dy1 } // ______________________________ dragmove fnCallbacks[aTypes.move] = function(action) { var node = d3.select(this) node .attr("transform", "translate(" + action.dx1 + "," + action.dy1 + ")") } // ______________________________ dispatcher var d3_event = d3.dispatch.apply(null, aTypesList) for (var i=0; i < qaTypesList.length; i++) { d3_event.on(qaTypesList[i], fnCallbacks[aTypesList[i]]) } d3_event.of = function(thiz, argumentz) { return function(e1) { d3_event.call(e1.type, thiz, e1) } } // ______________________________ listener function started(d, i, nodes) { var node = this, parent = node.parentNode, origin = d3.mouse(parent), ox = d.x - origin[0] || 0, oy = d.y - origin[1] || 0, dragged = false var context = d3.select(d3_window(node)) .on("dragstart.drag", prevent) .on("selectstart.drag", prevent) .on("mouseup", ended) .on("mousemove", moved) var emit = d3_event.of(node, arguments) // ______________________________ when moved function moved() { var p = d3.mouse(parent) var a = { type: aTypes.move, x0: origin[0] + ox, // first x y0: origin[1] + oy, x1: p[0] + ox, // new x position y1: p[1] + oy, dx: p[0] - origin[0], // delta x dy: p[1] - origin[1], dx1: (d.dx1 || 0) + p[0] - origin[0], // aggregated delta x dy1: (d.dy1 || 0) + p[1] - origin[1] } emit(a) } // ______________________________ when ended function ended() { context.on("mousemove", null) .on("mouseup", null); var p = d3.mouse(parent) var a = { type: aTypes.end, x0: origin[0] + ox, // first x y0: origin[1] + oy, x1: p[0] + ox, // new x position y1: p[1] + oy, dx: p[0] - origin[0], // delta x dy: p[1] - origin[1], dx1: (d.dx1 || 0) + p[0] - origin[0], // aggregated delta x dy1: (d.dy1 || 0) + p[1] - origin[1] } emit(a) } function afterended() { context.on("click.drag", null); } } function drag(selection) { selection.on("mousedown.drag", started) } drag.on = function() { var value = d3_event.on.apply(d3_event, arguments); return value === d3_event ? drag : value; }; return drag; } exports.stepControls = stepControls exports.tickControls = tickControls exports.mouseControls = mouseControls exports.kbdControls = kbdControls exports.dragControls = dragControls exports.posControls = posControls exports.tipControls = tipControls })); /* */ /* d3lanes-actions.js */ /* */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3lanesActions = global.d3lanesActions || {}))); }(this, function (exports) { 'use strict'; // ____________________ merge_objects function merge_objects(ctt1,ctt2){ var i, obj = {} for (i = 0; i < arguments.length; i++) { Object.assign(obj, arguments[i]) } return obj; } // ____________________ keyMirror // https://github.com/STRML/keyMirror var keyMirror = function(obj, prefix) { var ret = {}; var key; if (!(obj instanceof Object && !Array.isArray(obj))) { throw new Error('keyMirror(...): Argument must be an object.'); } for (key in obj) { if (obj.hasOwnProperty(key)) { ret[key] = prefix + key; } } return ret; }; // ____________________ action TYPES var cttsCourt = { ADD_KEYS_EVENT: '', FETCH_RECORDS: '', RELEASE_KEYBKEY: '', RESET_KEYS_EVENTS: '', RESIZE_SCREEN: '', RESIZE_HEIGHT: '', RESIZE_WIDTH: '', SET_KEYBKEY: '', SET_MODE: '', SET_NOTICE: '', SET_VIEW: '', START_KEYS_EVENTS: '', START_KEYBKEY_EVENTS: '', UPDATE_MOUSE_POS: '', } var cttsLanes = { DELETE_LANE: '', SET_LANE: '', SET_LANES: '', SET_MESSAGES: '', SET_RECORDS: '', WALK_DOWN_RECORDS: '', WALK_UP_RECORDS: '', SET_RECORDS_COLLECTION: '', SET_RECORDS_FETCHED: '', UPDATE_MESSAGES: '', } var cttsDebug = { SET_DEBUGMODE: '', SET_FPS: '', SWITCH_DEBUGMODE: '', } var cttsParticles = { CREATE_PARTICLES: '', START_PARTICLES: '', START_TICKER: '', STOP_PARTICLES: '', STOP_TICKER: '', TICK_PARTICLES: '', } // ____________________ actions COURT var ActionCreatorsCourt = { resizeHeight(height) { return { type: ActionTypes.RESIZE_HEIGHT, delta: height } }, resizeScreen(width, height) { return { type: ActionTypes.RESIZE_SCREEN, width: width, height: height } }, resizeWidth(width) { return { type: ActionTypes.RESIZE_WIDTH, delta: width } }, setKeybKey(keyCode) { return { type: ActionTypes.SET_KEYBKEY, keyCode: keyCode, } }, releaseKeybKey(keyCode) { return { type: ActionTypes.RELEASE_KEYBKEY, keyCode: keyCode, } }, setMode(currentMode) { return { type: ActionTypes.SET_MODE, currentMode: currentMode, } }, setView(currentView) { return { type: ActionTypes.SET_VIEW, currentView: currentView, } }, setNotice(notice) { return { type: ActionTypes.SET_NOTICE, notice: notice, } }, startKeybKeyEvents() { return { type: ActionTypes.START_KEYBKEY_EVENTS } }, updateMousePos(x, y) { return { type: ActionTypes.UPDATE_MOUSE_POS, x: x, y: y } }, } // ____________________ actions LANES var ActionCreatorsLanes = { setRecordsFetched(areRecordsFetched) { return { type: ActionTypes.SET_RECORDS_FETCHED, areRecordsFetched: areRecordsFetched, } }, setRecordsCollection(recordsCollection) { return { type: ActionTypes.SET_RECORDS_COLLECTION, recordsCollection: recordsCollection, } }, setRecords(argObj) { // SET_RECORDS return { type: ActionTypes.SET_RECORDS, itemSpan: argObj.itemSpan, mode: argObj.currentMode, } }, walkDownRecords(itemSpan, mode) { // WALK_DOWN_RECORDS return { type: ActionTypes.WALK_DOWN_RECORDS, itemSpan: itemSpan, mode: mode, } }, walkUpRecords(itemSpan, mode) { // WALK_UP_RECORDS return { type: ActionTypes.WALK_UP_RECORDS, itemSpan: itemSpan, mode: mode, } }, increaseCursorLow() { // INCREASE_CURSOR_LOW return { type: ActionTypes.INCREASE_CURSOR_LOW, } }, decreaseCursorLow() { return { type: ActionTypes.DECREASE_CURSOR_LOW, } }, increaseCursorHigh() { return { type: ActionTypes.INCREASE_CURSOR_HIGH, } }, decreaseCursorHigh() { return { type: ActionTypes.DECREASE_CURSOR_HIGH, } }, deleteLane(lane) { return { type: ActionTypes.DELETE_LANE, lane: lane, } }, setLane(lane) { return { type: ActionTypes.SET_LANE, lane: lane, } }, setLanes(lanes) { return { type: ActionTypes.SET_LANES, lanes: lanes, } }, setMessages(messages) { return { type: ActionTypes.SET_MESSAGES, messages: messages, } }, updateMessages(messages) { return { type: ActionTypes.UPDATE_MESSAGES, cursorLow: cursorLow, cursorHigh: cursorHigh, } }, } // ____________________ actions DEBUG var ActionCreatorsDebug = { setDebugMode(debugMode) { return { type: ActionTypes.SET_DEBUG_MODE, debugMode: debugMode, } }, setFps(fps) { return { type: ActionTypes.SET_FPS, fps: fps, } }, switchDebugMode() { return { type: ActionTypes.SWITCH_DEBUGMODE }; }, } // ____________________ actions PARTICLES var ActionCreatorsParticles = { createParticles(obj) { return { type: ActionTypes.CREATE_PARTICLES, // createParticles N: obj.particlesPerTick, x: obj.x, y: obj.y, randNormal: obj.randNormal, randNormal2: obj.randNormal2, xInit: obj.xInit, xEnd: obj.xEnd, lanes: obj.lanes, } }, startParticles() { return { type: ActionTypes.START_PARTICLES } }, startTicker() { return { type: ActionTypes.START_TICKER }; }, stopTicker() { return { type: ActionTypes.STOP_TICKER }; }, tickParticles(arg) { // console.log("arg: ", JSON.stringify(arg.lanes, null, 2)) return { type: ActionTypes.TICK_PARTICLES, // tickParticles width: arg.width, height: arg.height, gravity: arg.gravity, lanes: arg.lanes, } }, stopParticles() { return { type: ActionTypes.STOP_PARTICLES } }, } var ctts = merge_objects(cttsLanes, cttsCourt, cttsDebug, cttsParticles) var ActionTypes = keyMirror(ctts, '') var ActionCreators = merge_objects(ActionCreatorsCourt, ActionCreatorsParticles, ActionCreatorsDebug, ActionCreatorsLanes) exports.ActionTypes = ActionTypes; exports.ActionCreators = ActionCreators; }));/* */ /* d3lanes-component-court.js */ /* */ if (typeof require === "function") { var d3 = require('./d3.v4.0.0-alpha.40.js') } (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3lanesComponentCourt = global.d3lanesComponentCourt || {}))); }(this, function (exports) { 'use strict'; function render(newState) { var state doRender(newState) function doRender(newState) { if (state === newState) return state = newState var svgContainer = d3.select(state.configReducer.containerElem) .selectAll('svg') .data(['svg']) var svgContainerNew = svgContainer.enter() .append("svg") .attr("id", state.configReducer.containerId) .style('width', state.courtReducer.svgWidth) .style('height', state.courtReducer.svgHeight) .attr('class', 'bar-chart') // .style('border', '1px solid red') .style('color', 'blue') .attr('viewbox',"0 0 3 2") var svg = d3.select('svg') var itemsGroup = d3.select('svg') .selectAll('g.notices') // items .data(['items']) itemsGroup.enter() .append("g") .classed("notices", true) // items // _________________________________ render Notice Update var errorNotice = (state.courtReducer.notice) ? state.courtReducer.notice : "" var noticeToShow = " " + "click particles arrow mode" + " - " + state.configReducer.modeLabels[state.configReducer.modes[state.courtReducer.currentMode]] + " - " + parseInt(svg.style("width")) + " x " + parseInt(svg.style("height")) + " - N: " + state.particlesReducer.particleIndex + " - fps: " + state.debugReducer.fps var winWidthPixels = parseInt(svg.style("width")) var winHeightPixels = parseInt(svg.style("height")) var fontSizeHw = 3 + "hw" var fontSize = winWidthPixels * 3/100 var fontname = 'sans-serif' var c=document.createElement('canvas'); var ctx=c.getContext('2d'); ctx.font = fontSize + 'px ' + fontname; var noticeLength = ctx.measureText(noticeToShow).width var vlocation = winHeightPixels - fontSize var hlocation = winWidthPixels - noticeLength // items elems var itemsElems = svgContainer .select("g.notices") .selectAll("g.notice") .data([noticeToShow]); // items elems enter var itemsElemsNew = itemsElems.enter() .append("g") .classed("notice", true) itemsElemsNew.each(function(d, i) { var itemElemNew = d3.select(this) .append("text") .classed("info", true) .style("font-family", fontname) .attr("x", function(d) { return hlocation; }) .attr("y", function(d) { return vlocation }) .style("font-size", function(d, i) { return fontSize }) .text(function(d) { return d }) .style("fill-opacity", 1) }) // items elems update itemsElems .select('text') .text(function(d) { return d }) .attr("x", function(d) { return hlocation; }) .attr("y", function(d) { return vlocation; }) // items elems exit itemsElems.exit() .select('text') .remove() } // render } exports.render = render; }))/* */ /* d3lanes-component-lanes.js */ /* */ if (typeof require === "function") { var d3 = require('./d3.v4.0.0-alpha.40.js') } (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3lanesComponentLanes = global.d3lanesComponentLanes || {}))); }(this, function (exports) { 'use strict'; // _____________ coordsUtils function coordsUtils () { function index_hcoord_pct(arr, index) { return (index+1) * (100/(arr.length+1)); } function index_hcoord_pct_with_symbol(arr, index) { return index_hcoord_pct(arr, index) + "%"; } function horizontal_center(x1, x2) { if (x1 > x2) return (x2 - x1)/2 + x1 else return (x1 - x2)/2 + x2 } function horizontal_percent_to_coord(svg, percent) { // https://bugzilla.mozilla.org/show_bug.cgi?id=874811 // _e_ if(navigator.userAgent.toLowerCase().indexOf('firefox') > -1) { var xScrollWidth = svg[0].parentNode.scrollWidth; } else { var xScrollWidth = svg.property("scrollWidth"); } return xScrollWidth * Number.parseFloat(percent)/100; } function horizontal_coord_to_percent(svg, length) { var xScrollWidth = svg.property("scrollWidth"); return Number.parseFloat(length) / xScrollWidth; } var publicAPI = { hcoord_tagged_pct: function hcoord_tagged_pct(arr, val) { return index_hcoord_pct_with_symbol(arr, arr.indexOf(val)); }, hcoord_pct: function hcoord_pct(arr, val) { return index_hcoord_pct(arr, arr.indexOf(val)); }, hcenter_tagged_pct: function hcenter_tagged_pct(x1, x2) { return horizontal_center(x1, x2) + "%"; } } return publicAPI } // _____________ arrayUtils function arrayUtils () { function flattenArrByObjProp(a, p) { return a.reduce(function(prevArr, currVal, i, a) { if (prevArr.indexOf(currVal[p]) < 0) var r = prevArr.push(currVal[p]) return prevArr }, []); } function union_arrays (x, y) { var obj = {}; for (var i = x.length-1; i >= 0; -- i) obj[x[i]] = x[i]; for (var i = y.length-1; i >= 0; -- i) obj[y[i]] = y[i]; var res = [] for (var k in obj) { if (obj.hasOwnProperty(k)) res.push(obj[k]); } return res; } function array_names_from_props (arr, props) { var r = props.reduce( function(prevArr, currVal, i, a) { var q1 = flattenArrByObjProp(arr, currVal) var q = union_arrays(prevArr, q1) return q }, []) return r } var publicAPI = { array_names_from_props: function array_names_from_props (arr, props) { var r = props.reduce( function(prevArr, currVal, i, a) { var q1 = flattenArrByObjProp(arr, currVal) var q = union_arrays(prevArr, q1) return q }, []) return r }, } return publicAPI } // _____________ context var stateLanes = { lanesReducer: {} } var intransition = false // _____________ render function render(newState) { if (intransition == true) { return } if (JSON.stringify(stateLanes.lanesReducer.records) === JSON.stringify(newState.lanesReducer.records)) { return } // DATA // store previous - will not change during render var _messages0 = stateLanes.lanesReducer.records || [] var state = stateLanes = newState var _messages1 = state.lanesReducer.records var _fadeTime = state.configReducer.fadeFactor * state.configReducer.beatTime var _itemProps = state.configReducer.itemProps // SVG var svgContainer = d3.select('body') .selectAll('svg') .data(['svgContainer']) svgContainer .enter() .append("svg") .attr("id", state.configReducer.container) svgContainer .style('width', state.courtReducer.svgWidth) .style('height', state.courtReducer.svgHeight) var messagesGroup = d3.select('svg') .selectAll('g.messages') // items .data(['messages']) messagesGroup.enter() .append("g") .classed("messages", true) // items var actorsGroup = d3.select('svg') .selectAll('g.lanes') // items .data(['lanes']) actorsGroup.enter() .append("g") .classed("lanes", true) // items var marker = svgContainer.append("marker") .attr("id", "message-marker") .attr("viewBox", "0 0 10 10") .attr("refX", "10") .attr("refY", "5") .attr("markerWidth", "5") .attr("markerHeight", "4") .attr("orient", "auto") .append("path") .attr("class", "message-arrow") .attr("d", "M 0 0 L 10 5 L 0 10 z") // DATA var _laneItems0 = arrayUtils() .array_names_from_props(_messages0, _itemProps) var _laneObjs0 = _laneItems0.map(function(d, i) { return ({id: d, name: d, x0: parseFloat(coordsUtils().hcoord_pct(_laneItems0, d) * parseInt(svgContainer.style("width")) / 100).toFixed(0)})}) var _lanesObj0 = _laneItems0.reduce(function(total,d,currentIndex,arr) { var o = {} o[d] = {name: d, x0: parseFloat(coordsUtils().hcoord_pct(_laneItems0, d) * parseInt(svgContainer.style("width")) / 100).toFixed(0)} return (Object.assign({}, total, o))}, {}) var _laneItems1 = arrayUtils() .array_names_from_props(_messages1, _itemProps) var _laneObjs1 = _laneItems1.map(function(d, i) { var x0 = 0 if ( _lanesObj0.hasOwnProperty( d) ) { x0 = _lanesObj0[d].x0 } return ({id: d, name: d, x0: x0})}) // lane elems trasition var laneElemsTransition = d3.transition() .duration(_fadeTime) .ease(d3.easeLinear) // laneElems DATA var laneElems = svgContainer .select("g.lanes") .selectAll("g.actor") .data(_laneObjs1, function(d) { return d.id }) // laneElems EXIT laneElems.exit() .transition(laneElemsTransition) .style("opacity", function(d) { store.dispatch(actions.deleteLane(d)) return 0 }) .remove(function(){console.log("++++++++++++++++++")}) // laneElems UPDATE texts var actorTexts = laneElems.select("text") .attr("text-anchor", "middle") .attr("alignment-baseline", "middle") .style("font-size", function(d, i) { return parseInt(svgContainer.style("width")) * 2/100 }) .text(function(d) { return d.name }) .attr("dy", "20") .transition(laneElemsTransition) .attr("x", function(d, i) { var r = coordsUtils().hcoord_tagged_pct(_laneItems1, d.name) return r }) .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) // laneElems UPDATE lines var actorLines = laneElems.select("line") .attr("x0", function(d, i) { var r = parseFloat(coordsUtils().hcoord_pct(_laneItems0, d.name) * parseInt(svgContainer.style("width")) / 100).toFixed(0) return r }) .attr("y1", function() { var text_bbox = this.parentNode.querySelector("text").getBBox(); return text_bbox.y + text_bbox.height; }) .attr("y2", "100%") .transition(laneElemsTransition) .attrTween("x1", function(d, i, a) { return function (t) { var r = parseFloat(coordsUtils().hcoord_pct(_laneItems1, d.name) * parseInt(svgContainer.style("width")) / 100).toFixed(0) var x = parseFloat(parseInt(d.x0) + t * (r - parseInt(d.x0))).toFixed(0) // dispatch lanes abscissa var l = {name: d.name, id: d.id, x: x } store.dispatch(actions.setLane(l)) return x } }) .attr("x2", function(d, i) { var r = coordsUtils().hcoord_tagged_pct(_laneItems1, d.name) return r }) .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) // laneElems ENTER var newActorElements = laneElems .enter() .append("g") .classed("actor", true) // laneElems ENTER text newActorElements.append("text") .attr("class", "actor") .attr("text-anchor", "middle") .attr("alignment-baseline", "middle") .style("font-family", "sans-serif") .style("fill", "transparent") .style("font-size", function(d, i) { return parseInt(svgContainer.style("width")) * 2/100 }) .text(function(d) { return d.name }) .attr("dy", "20") .attr("x", function(d, i, a) { var r = coordsUtils().hcoord_tagged_pct(_laneItems1, d.name) return r }) .transition(laneElemsTransition) .style("fill", "black") .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) // laneElems ENTER lines newActorElements.append("line") .attr("class", "actor") .attr("stroke", "lightgray") .style("stroke-width", "1px") .attr("stroke-width", 1) .attr("x0", function(d, i) { var r = parseFloat(coordsUtils().hcoord_pct(_laneItems0, d.name) * parseInt(svgContainer.style("width")) / 100).toFixed(0) return r }) .attr("x1", function(d, i, a) { var r = coordsUtils().hcoord_tagged_pct(_laneItems1, d.name) var x = parseFloat(coordsUtils().hcoord_pct(_laneItems1, d.name) * parseInt(svgContainer.style("width")) / 100).toFixed(0) var l = {name: d.name, id: d.id, x: x } store.dispatch(actions.setLane(l)) return r }) .attr("x2", function(d, i, a) { var r = coordsUtils().hcoord_tagged_pct(_laneItems1, d.name) return r }) .attr("y1", function(_d, i) { var text_bbox = this.parentNode.querySelector("text").getBBox(); return text_bbox.y + text_bbox.height; }) .attr("y2", function(d, i, a) { var r = "100%" return r }) // messageElements var messageElements = svgContainer .select("g.messages") .selectAll("g.message") .data(_messages1, function(d, i) { return d.id || (d.id = ++i); }) // message elems UPDATE texts messageElements.select('text') .transition(laneElemsTransition) .attr("x", function(d, i) { var r1 = coordsUtils().hcoord_pct(_laneItems1, d.from) var r2 = coordsUtils().hcoord_pct(_laneItems1, d.to) var r = coordsUtils().hcenter_tagged_pct(r1, r2) return r }) .attr("y", function(d, i, s) { var r = (i + 2) * state.configReducer.vstep - 10 return r // (i+1)*10 }) .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) // message elems UPDATE lines messageElements.select('line') .transition(laneElemsTransition) .attr("x1", function(d, i, a) { var r = coordsUtils().hcoord_tagged_pct(_laneItems1, d.from) return r }) .attr("x2", function(d, i, a) { var r = coordsUtils().hcoord_tagged_pct(_laneItems1, d.to) return r }) .attr("y1", function(d, i) { var r = (i + 2) * state.configReducer.vstep; return r }) .attr("y2", function(d, i) { var r = (i + 2) * state.configReducer.vstep; return r }) .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) // message elems UPDATE paths messageElements.select("path") .transition(laneElemsTransition) .attr("d", function(d, i) { var x_pc = coordsUtils().hcoord_tagged_pct(_laneItems1, d.from) var xScrollWidth = parseInt(svgContainer.style("width")) var t = xScrollWidth * Number.parseFloat(x_pc)/100 var x = t var rx = 40 var ry = 20 var y = (i + 2)*50 - ry var sweep_flag = 1 var r = [ "M", x, y, "a", rx, ry, 0, 1, sweep_flag, 0, ry*2, ].join(" "); return r }) .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) // message elems ENTER var newMessageElements = messageElements .enter() .append("g") .classed("message", true) // messageElems ENTER TEXTs newMessageElements.each(function(d, i) { var new_message = d3.select(this) .append("text") .attr("class", "message") .style("fill", "transparent") .style("font-size", function(d, i) { return parseInt(svgContainer.style("width")) * 2/100 }) .attr("dy", ".15em") .attr("text-anchor", d.from == d.to ? "end" : "middle") .attr("alignment-baseline", d.from == d.to ? "middle" : "autoMode") .text(d.msg) .attr("y", (i + 2) * state.configReducer.vstep - 10) .attr("x", function() { var x1 = coordsUtils().hcoord_pct(_laneItems1, d.from) var x2 = coordsUtils().hcoord_pct(_laneItems1, d.to) var r = coordsUtils().hcenter_tagged_pct(x1, x2) return r }) .transition(laneElemsTransition) .style("fill", "grey") .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) }) // messageElems ENTER PATHs newMessageElements.each(function(d, i) { var new_message = d3.select(this) if (d.from == d.to) { new_message.append("path") // new mPATHs .attr("fill-opacity", 0) .attr("stroke", "transparent") .each(function(d) { // this._current = d_to_arc(d, i); // store initial state }) .attr("d", function() { var x_pc_to = coordsUtils().hcoord_pct(_laneItems1, d.to) var xScrollWidth = parseInt(svgContainer.style("width")) var t = xScrollWidth * Number.parseFloat(x_pc_to)/100 var x = t var rx = 40 var ry = 20 var y = (i + 2)*50 - ry var sweep_flag = 1 var r = [ "M", x, y, "a", rx, ry, 0, 1, sweep_flag, 0, ry*2, ].join(" "); return r }) .transition(laneElemsTransition) .attr("stroke", "grey") .attr("fill", "grey") .attrTween("marker-end", function(d) { return function (t) { if (t != 1) { return null } else { return "url(#message-marker)" } } }) .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) // messageElems ENTER LINEs } else { var line = new_message.append("line") .attr("class", "message") .attr("stroke", "transparent") .attr("stroke-width", 1) .attr("y1", function() { var r = (i + 2) * state.configReducer.vstep ; return r }) .attr("y2", function() { var r = (i + 2) * state.configReducer.vstep ; return r }) .attr("x1", coordsUtils().hcoord_tagged_pct(_laneItems1, d.from)) .attr("x2", coordsUtils().hcoord_tagged_pct(_laneItems1, d.to)) .transition(laneElemsTransition) .attr("stroke", "gray") .attr("fill", "grey") .attrTween("marker-end", function() { return function (t) { if (t != 1) { return null } else { return "url(#message-marker)" } } }) .on("start", function start() { intransition = true }) .on("end", function end() { intransition = false }) } }); // message elems EXIT messageElements.exit() .transition() .style("opacity", 0) .remove() } exports.render = render; }))/* */ /* d3lanes-component-particles.js */ /* */ if (typeof require === "function") { var d3 = require('./d3.v4.0.0-alpha.40.js') } (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3lanesComponentParticles = global.d3lanesComponentParticles || {}))); }(this, function (exports) { 'use strict'; // _____________ context var stateParticles = { particlesReducer: {} } var rendering = false var intransition = false // _______________________ render function render(newState) { if (rendering == true) return if (newState.particlesReducer.particles.length == 0) return rendering = true var state = stateParticles= newState var particleRadio = state.particlesReducer.particleRadio || 6.33 var svgContainer = d3.select('body') .selectAll('svg') .data(['svgContainer']) svgContainer .enter() .append("svg") .attr("id", state.configReducer.container) svgContainer .style('width', state.courtReducer.svgWidth) .style('height', state.courtReducer.svgHeight) var itemsGroup = d3.select('svg') .selectAll('g.particles') // items .data(['items']) itemsGroup.enter() .append("g") .classed("particles", true) // items // _________________________________ render Particles var color = d3.scalePlasma() .domain([0, 1]) var particleElements = svgContainer .select("g.particles") .selectAll("circle") .data(state.particlesReducer.particles) .attr('cx', function(d, i, a) { return d.x }) .attr('cy', function(d, i, a) { return d.y }) .attr('r', function(d, i, a) { return particleRadio }) .style("fill", function (d) { var r = d.closestLaneUp.x / (d.closestLaneUp.x - d.closestLaneDown.x) return color( ((3*r)%10 / 10) + (Math.random()* 3 /10)) }) .style("fill-opacity", 0.2) .style("stroke", "none") var newParticleElements = particleElements .enter() .append("circle") .attr('cx', function(d, i, a) { return d.x }) .attr('cy', function(d, i, a) { return d.y }) .attr('r', function(d, i, a) { return particleRadio }) .style("fill", function (d) { var r = d.closestLaneUp.x / (d.closestLaneUp.x - d.closestLaneDown.x) return color( ((3*r)%10 / 10) + (Math.random()/10)) }) .style("stroke", "none") particleElements.exit() .remove() rendering = false } // render exports.render = render; }))/* */ /* d3lanes-reducer.js */ /* */ if (typeof require === "function") { var d3 = require('./d3.v4.0.0-alpha.40.js') var d3lanesActions = require('./d3lanes-actions.js') } (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3lanesReducer = global.d3lanesReducer || {}))); }(this, function (exports) { 'use strict'; // _____________ adapted from redux combineReducers function combineReducers(reducers) { var reducerKeys = Object.keys(reducers) var finalReducers = {} for (var i = 0; i < reducerKeys.length; i++) { var key = reducerKeys[i] if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } var finalReducerKeys = Object.keys(finalReducers) return function combination(state = {}, action) { var hasChanged = false var nextState = {} for (var i = 0; i < finalReducerKeys.length; i++) { var key = finalReducerKeys[i] var reducer = finalReducers[key] var previousStateForKey = state[key] var nextStateForKey = reducer(previousStateForKey, action) if (typeof nextStateForKey === 'undefined') { var errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state } } // _____________ CONFIG var initialStateConfig = { modes: {autoMode: 'autoMode', walkMode: 'walkMode'}, modeLabels: {autoMode: 'auto', walkMode: 'walk'}, views: {lanesView: 'lanesView'}, gravity: 0.5, randNormal: d3.randomNormal(1.3, 2), randNormal2: d3.randomNormal(0.5, 1.8), containerElem: '#container', containerId: 'svgid', tickspan: 60, beatTime: 500, fadeFactor: 3, // times beat - fade items periodFactor: 4, // times beat - add items vstep: 50, itemSpan: 6, itemProps: ['to', 'from'], itemVal: 'msg', messageCollection_000: [ {id: "1", from: "customer", to: "barrista1", msg: "place order"}, {id: "2", from: "barrista1", to: "register", msg: "enter order"}, {id: "3", from: "register", to: "barrista1", msg: "give total"}, {id: "4", from: "barrista1", to: "barrista1", msg: "get cup making sure that it is fine for purpose"}, {id: "5", from: "barrista1", to: "barrista2", msg: "give cup"}, {id: "6", from: "barrista1", to: "customer", msg: "request money"}, {id: "7", from: "customer", to: "barrista1", msg: "pay order"}, {id: "8", from: "barrista2", to: "barrista2", msg: "get chai mix"}, {id: "9", from: "barrista2", to: "barrista2", msg: "add flavor"}, {id: "10", from: "barrista2", to: "barrista2", msg: "add milk"}, {id: "11", from: "barrista2", to: "barrista2", msg: "add ice"}, {id: "12", from: "barrista2", to: "barrista2", msg: "swirl"}, {id: "13", from: "barrista2", to: "customer", msg: "give tasty beverage"}, {id: "14", from: "customer", to: "tasty beverage", msg: "sip"}, {id: "15", from: "tasty beverage", to: "customer", msg: "burn"}, {id: "16", from: "customer", to: "customer", msg: "cry"}, {id: "17", from: "customer", to: "manager", msg: "complain"}, {id: "18", from: "manager", to: "barrista1", msg: "fire"}, {id: "19", from: "manager", to: "barrista2", msg: "fire"}, ], messageCollection: [ {id: "1", from: "app", to: "store", msg: "create store"}, {id: "2", from: "store", to: "store", msg: "subscribe lanes listener"}, {id: "3", from: "store", to: "store", msg: "subscribe particles listener"}, {id: "4", from: "app", to: "app", msg: "start kbd controller"}, {id: "5", from: "app", to: "app", msg: "start mouse controller"}, {id: "6", from: "ticker", to: "ticker", msg: "subscribe tickParticles"}, {id: "7", from: "ticker", to: "ticker", msg: "subscribe setRecords"}, {id: "8", from: "ticker", to: "ticker", msg: "start auto"}, {id: "9", from: "store", to: "reducer", msg: "dispatch setRecords action"}, {id: "10", from: "reducer", to: "reducer", msg: "apply action logic"}, {id: "11", from: "reducer", to: "store", msg: "return new state"}, {id: "12", from: "ticker", to: "ticker", msg: "run listeners"}, {id: "13", from: "component", to: "UI", msg: "render lanes"}, {id: "14", from: "UI", to: "app", msg: "trigger left arrow event"}, {id: "15", from: "store", to: "reducer", msg: "dispatch setMode action"}, {id: "16", from: "reducer", to: "reducer", msg: "run action"}, {id: "17", from: "reducer", to: "store", msg: "return new state"}, {id: "18", from: "ticker", to: "ticker", msg: "run listeners"}, {id: "19", from: "UI", to: "app", msg: "send down arrow event"}, {id: "20", from: "store", to: "reducer", msg: "dispatch setRecods action"}, {id: "21", from: "reducer", to: "reducer", msg: "run action and get record"}, {id: "22", from: "reducer", to: "reducer", msg: "return new set"}, {id: "23", from: "ticker", to: "ticker", msg: "run listeners"}, {id: "24", from: "component", to: "UI", msg: "render lanes"}, {id: "25", from: "UI", to: "app", msg: "send right arrow event"}, {id: "26", from: "store", to: "reducer", msg: "dispatch setMode action"}, {id: "27", from: "reducer", to: "reducer", msg: "run action"}, {id: "28", from: "reducer", to: "reducer", msg: "return new mode auto"}, {id: "29", from: "ticker", to: "ticker", msg: "run listeners with new state"}, {id: "30", from: "component", to: "UI", msg: "render auto lanes"}, {id: "31", from: "store", to: "reducer", msg: "dispatch createParticles action"}, {id: "32", from: "reducer", to: "reducer", msg: "run action"}, {id: "33", from: "reducer", to: "store", msg: "return new state with particles"}, {id: "34", from: "ticker", to: "ticker", msg: "run particles listeners"}, {id: "35", from: "component", to: "UI", msg: "render particles"}, ], } function configReducer(state = initialStateConfig, action) { if (actions == null) return state var ActionTypes = d3lanesActions.ActionTypes switch (action.type) { default: return state; } } // _____________ DEBUG var initialStateDebug = { debugMode: true, fps: 0 } function debugReducer(state = initialStateDebug, action) { if (actions == null) return state var ActionTypes = d3lanesActions.ActionTypes switch (action.type) { case ActionTypes.SET_FPS: return setFps(state, action) case ActionTypes.SWITCH_DEBUGMODE: console.log('SWITCH_DEBUGMODE') return switchDebugMode(state, action) default: return state; } } function setFps(state, action) { return Object.assign({}, state, { fps: action.fps }) } function switchDebugMode(state, action) { return Object.assign({}, state, { debugMode: !state.debugMode }) } // _____________ COURT var initialStateCourt = { svgHeight: 400, // svgWidth: 600, // keys: [], notice: 'auto lanes', currentMode: 'autoMode', currentView: 'lanesView', arrowKeysStarted: false, keybKeyEventsStarted: false, tickerStarted: false, lastTick: 0, mousePos: [null, null], } function courtReducer(state = initialStateCourt, action) { if (actions == null) return state var ActionTypes = d3lanesActions.ActionTypes switch (action.type) { case ActionTypes.SET_KEYBKEY: console.log('SET_KEYBKEY') var ks = state.keys ks[action.keyCode] = true return Object.assign({}, state, { keys: ks }); case ActionTypes.RELEASE_KEYBKEY: console.log('RELEASE_KEYBKEY') var ks = state.keys ks[action.keyCode] = false return Object.assign({}, state, { keys: ks }); case ActionTypes.START_KEYBKEY_EVENTS: console.log('START_KEYBKEY_EVENTS') return Object.assign({}, state, { keybKeyEventsStarted: true }); case ActionTypes.SET_MODE: console.log('SET_MODE') return Object.assign({}, state, { currentMode: action.currentMode, }); case ActionTypes.SET_VIEW: console.log('SET_VIEW') return Object.assign({}, state, { currentView: action.currentView, }); case ActionTypes.SET_NOTICE: console.log('SET_NOTICE') return Object.assign({}, state, { notice: action.notice, }); case ActionTypes.START_TICKER: console.log('START_TICKER') return Object.assign({}, state, { tickerStarted: true }); case ActionTypes.STOP_TICKER: console.log('STOP_TICKER') return Object.assign({}, state, { tickerStarted: false }); case ActionTypes.UPDATE_MOUSE_POS: return Object.assign({}, state, { mousePos: [action.x, action.y] }); case ActionTypes.RESIZE_SCREEN: console.log('RESIZE_SCREEN') return Object.assign({}, state, { svgWidth: action.width, svgHeight: action.height }); case ActionTypes.RESIZE_WIDTH: console.log('RESIZE_WIDTH') return Object.assign({}, state, { svgWidth: state.svgWidth + action.delta }); case ActionTypes.RESIZE_HEIGHT: console.log('RESIZE_HEIGHT') return Object.assign({}, state, { svgHeight: state.svgHeight + action.delta }); default: return state; } } // _____________ LANES var initialStateLanes = { lanes: [], lanesIndex: 0, messages: [], records: [], recordsCollection: [], areRecordsFetched: false, messagesCursorLow: 0, messagesCursorHigh: 0, } function lanesReducer(state = initialStateLanes, action) { if (actions == null) return state var ActionTypes = d3lanesActions.ActionTypes switch (action.type) { case ActionTypes.INCREASE_CURSOR_LOW: var r = Object.assign({}, state, {messagesCursorLow: ++state.messagesCursorLow} ); return r case ActionTypes.REDUCE_CURSOR_LOW: var r = Object.assign({}, state, {messagesCursorLow: --state.messagesCursorLow} ); return r case ActionTypes.INCREASE_CURSOR_HIGH: var r = Object.assign({}, state, {messagesCursorHigh: ++state.messagesCursorHigh} ); return r case ActionTypes.REDUCE_CURSOR_HIGH: var r = Object.assign({}, state, {messagesCursorHigh: --state.messagesCursorHigh} ); return r case ActionTypes.DELETE_LANE: // setLane var lanes = state.lanes var ls = lanes.filter(function( obj ) { return obj.id !== action.lane.id; }); var r = Object.assign({}, state, {lanes: ls}, {lanesIndex: ls.length} ); return r case ActionTypes.SET_LANE: // setLane var lanes = state.lanes var ls = {} var result = lanes.filter(function( obj ) { return obj.id == action.lane.id; }); if (result.length === 0) { // add ls = {lanes: [ { id: action.lane.id, name: action.lane.name, x: action.lane.x }, ...lanes ]} } else { // edit ls = {lanes: lanes.map(lane => lane.id === action.lane.id ? Object.assign({}, lane, { id: action.lane.id, name: action.lane.name, x: action.lane.x }) : lane )} } var r = Object.assign({}, state, ls, { lanesIndex: ls.lanes.length }); return r case ActionTypes.SET_LANES: console.log('SET_LANES') return Object.assign({}, state, { lanes: action.lanes, lanesIndex: Object.keys(action.lanes).length }); case ActionTypes.FETCH_RECORDS: console.log('FETCH_RECORDS') var processRecord = function processRecord(d) { d.amount = +d.amount; d.risk = +d.risk; d.valueOf = function value() { return this.amount; } return d; } var processData = function processData(error, dataCsv) { if (store.getState().court.currentMode == 0) { // _tbd_ ++timeTick ++vLast store.dispatch(actions.setMessages(store.getState().configReducer.messageCollection.slice(0, store.getState().configReducer.messageCollection.length))) } } d3.queue() .defer(d3.csv, action.src, processRecord) .await(processData) return Object.assign({}, state); case ActionTypes.SET_MESSAGES: console.log('SET_MESSAGES') return Object.assign({}, state, { messages: action.messages, }); case ActionTypes.SET_RECORDS_FETCHED: console.log('SET_RECORDS_FETCHED') return Object.assign({}, state, { areRecordsFetched: action.areRecordsFetched }) case ActionTypes.SET_RECORDS_COLLECTION: console.log('SET_RECORDS_COLLECTION') return Object.assign({}, state, { recordsCollection: action.recordsCollection }) case ActionTypes.SET_RECORDS: console.log('SET_RECORDS') var vLow = state.messagesCursorLow var vHigh = state.messagesCursorHigh var itemSpan = action.itemSpan var mode = action.mode var r = state if (mode == 'autoMode') { var records = state.recordsCollection var numRecords = records.length if (vHigh >= vLow) vHigh = vHigh + 1 // add one to upper border if (vHigh > numRecords) vHigh = -1 // upper border if (((vHigh - vLow) > itemSpan) // all spteps full || (vHigh == -1) // infinitum with vLow active || (vLow == -1) // get always from reset ) vLow = vLow + 1 // increase lower border if (vLow > numRecords) vLow = -1 // reset at end of cycle r = Object.assign({}, state, { records: state.recordsCollection.slice(vLow, vHigh), messagesCursorLow: vLow, messagesCursorHigh: vHigh, }) } return r case ActionTypes.WALK_UP_RECORDS: console.log('WALK_UP_RECORDS') var vLow = state.messagesCursorLow var vHigh = state.messagesCursorHigh var itemSpan = action.itemSpan var mode = action.mode var r = state if (mode == 'walkMode') { vLow = Math.max(0, --vLow) r = Object.assign({}, state, { records: state.recordsCollection.slice(vLow, vHigh), messagesCursorLow: vLow, messagesCursorHigh: vHigh, }) } return r case ActionTypes.WALK_DOWN_RECORDS: console.log('WALK_DOWN_RECORDS') var vLow = state.messagesCursorLow var vHigh = state.messagesCursorHigh var itemSpan = action.itemSpan var mode = action.mode var r = state if (mode == 'walkMode') { if ((vHigh - vLow) > itemSpan) ++vLow ++vHigh r = Object.assign({}, state, { records: state.recordsCollection.slice(vLow, vHigh), messagesCursorLow: vLow, messagesCursorHigh: vHigh, }) } return r default: return state; } } // _____________ PARTICLES var initialStateParticles = { particles: [], particleIndex: 0, particlesGenerating: false, particlesPerTick: 33, particleRadio: 9, } function particlesReducer(state = initialStateParticles, action) { if (actions == null) return state var ActionTypes = d3lanesActions.ActionTypes switch (action.type) { case ActionTypes.START_PARTICLES: // startParticles return Object.assign({}, state, { particlesGenerating: true }); case ActionTypes.STOP_PARTICLES: // stopParticles return Object.assign({}, state, { particlesGenerating: false }); case ActionTypes.CREATE_PARTICLES: // createParticles var newParticles = state.particles.slice(0) var i for (i = 0; i < action.N; i++) { var ref = parseInt(action.x) var closestLaneUp = action.lanes .filter(function (d) {return d.x >= ref} ) .reduce(function (prev, curr) { return (Math.abs(curr.x - ref) < Math.abs(prev.x - ref) ? curr : prev); }, {id: 'end', x: action.xEnd}) var closestLaneDown = action.lanes .filter(function (d) {return d.x <= ref} ) .reduce(function (prev, curr) { return (Math.abs(curr.x - ref) < Math.abs(prev.x - ref) ? curr : prev); }, {id: 'init', x: action.xInit}) var particle = {id: state.particleIndex+i, x: action.x, y: action.y, closestLaneDown: closestLaneDown, closestLaneUp: closestLaneUp, }; particle.vector = [particle.id%2 ? - action.randNormal() : action.randNormal(), - action.randNormal2()*3.3]; newParticles.unshift(particle); } return Object.assign({}, state, { particles: newParticles, particleIndex: state.particleIndex+i+1 }); case ActionTypes.TICK_PARTICLES: // tickParticles var laneXs = action.lanes .map(function(l) { var x = parseInt(l.x) return x}) var svgWidth = action.width var svgHeight = action.height var gravity = action.gravity var movedParticles = state.particles .filter(function (p) { return (!(p.y > svgHeight)) }) .filter(function (p) { return (!(p.y < 0)) }) .map(function (p) { var vx = p.vector[0] var vy = p.vector[1] p.x += vx p.y += vy var ref = parseInt(p.x) var laneUp = action.lanes .filter(function(l) { return (l.id == p.closestLaneUp.id) }) p.closestLaneUp.x = (laneUp.length > 0 ) ? +laneUp[0].x : +p.closestLaneUp.x var laneDown = action.lanes .filter(function(l) { return (l.id == p.closestLaneDown.id) }) p.closestLaneDown.x = (laneDown.length > 0 ) ? +laneDown[0].x : +p.closestLaneDown.x if (ref < (p.closestLaneDown.x + state.particleRadio) || ref > (p.closestLaneUp.x - state.particleRadio)) { p.vector[0] = -p.vector[0] } p.vector[1] += gravity + 2 * gravity * (p.y - svgHeight) / svgHeight return p }); return Object.assign({}, state, { particles: movedParticles, particleIndex: movedParticles.length, }); default: return state; } } // _____________ combined reducer var reducer = combineReducers({ debugReducer: debugReducer, configReducer: configReducer, courtReducer: courtReducer, lanesReducer: lanesReducer, particlesReducer: particlesReducer, }) exports.reducer = reducer; }));/* */ /* d3lanes-store.js */ /* */ /* adapted from REDUX http://redux.js.org/ */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.d3lanesStore = global.d3lanesStore || {}))); }(this, function (exports) { 'use strict'; var createStore = function createStore(reducer, initialState) { var currentReducer = reducer var currentState = initialState var currentListeners = [] var nextListeners = currentListeners var isDispatching = false // ______________________________ ensureCanMutateNextListeners function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } // ______________________________ getState function getState() { return currentState } // redux compose function compose(...funcs) { if (funcs.length === 0) { return arg => arg } else { const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args)) } } // ______________________________ subscribe function subscribe(listener) { if (typeof listener !== 'function') { throw new Error('Expected listener to be a function.') } var isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } isSubscribed = false ensureCanMutateNextListeners() var index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) } } // ______________________________ dispatch function dispatch(action) { if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() } return action } return { compose: compose, dispatch: dispatch, subscribe: subscribe, getState: getState } } exports.createStore = createStore; })); /* */ /* index.js */ /* */ if (typeof require === "function") { var d3 = require('./d3.v4.0.0-alpha.40.js') var d3lanesComponentLanes = require('./d3lanes-component-lanes.js') var d3lanesComponentCourt = require('./d3lanes-component-court.js') var d3lanesComponentParticles = require('./d3lanes-component-particles.js') var d3lanesReducer = require('./d3lanes-reducer.js') var d3lanesStore = require('./d3lanes-store.js') var d3lanesActions = require('./d3lanes-actions.js') var d3lanesControls = require('./d3lanes-controls.js') } var store = d3lanesStore.createStore(d3lanesReducer.reducer, d3lanesReducer.reducer()) store.subscribe(store.compose(d3lanesComponentCourt.render, store.getState)) store.subscribe(store.compose(d3lanesComponentLanes.render, store.getState)) store.subscribe(store.compose(d3lanesComponentParticles.render, store.getState)) var actions = d3lanesActions.ActionCreators var svgContainer = d3.select(store.getState().configReducer.containerElem) .selectAll('svg') .data(['svg']) var svgContainerNew = svgContainer.enter() .append("svg") .attr("id", store.getState().configReducer.containerId) .style('width', store.getState().courtReducer.svgWidth) .style('height', store.getState().courtReducer.svgHeight) .style('background', 'oldlace') .attr('class', 'bar-chart') // .style('border', '1px solid darkgrey') .attr('viewbox',"0 0 3 2") d3lanesControls.kbdControls(store, d3.select('svg')).startKeybKeyEvents() d3lanesControls.mouseControls(store).startMouseEvents(d3.select('svg')) store.dispatch(actions.setRecordsCollection( store.getState().configReducer.messageCollection)) store.dispatch(actions.setRecordsFetched(true)) // jff store.dispatch(actions.startParticles()) store.dispatch(actions.createParticles({ particlesPerTick: store.getState().particlesReducer.particlesPerTick * 5, x: store.getState().courtReducer.svgWidth / 2, y: store.getState().courtReducer.svgWidth / 2, xInit: 0, xEnd: store.getState().courtReducer.svgWidth, randNormal: store.getState().configReducer.randNormal, randNormal2:store.getState().configReducer.randNormal2, lanes: [], })) store.dispatch(actions.stopParticles()) var ticker = d3lanesControls.tickControls(store) .subscribe( store.compose( store.dispatch, actions.tickParticles, function() { return { width: store.getState().courtReducer.svgWidth, height: store.getState().courtReducer.svgHeight, gravity: store.getState().configReducer.gravity, lanes: store.getState().lanesReducer.lanes } } )) .start() var walker = d3lanesControls.stepControls(store) .subscribe( store.compose( store.dispatch, actions.setRecords, function() { return { itemSpan: store.getState().configReducer.itemSpan, currentMode: store.getState().courtReducer.currentMode } } )) .start()