UNPKG

aframe-babia-components

Version:

A data visualization set of components for A-Frame.

1,390 lines (1,261 loc) 6.76 MB
/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./index.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./common/noti-buffer.js": /*!*******************************!*\ !*** ./common/noti-buffer.js ***! \*******************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { /* * Notifying buffer * * This buffer maintains an object (data). Its value is set with 'set', * and when set, it triggers notications to registered consumers * Registration is done via 'register'. */ class NotiBuffer { /* * notifiers: object with registered notifier objects * - key: identifier (unique integer) * - value: notify function */ constructor(func1, func2) { this.currentId = 0; this.notifiers = {}; this.data; // Optional: Functions to be executed by producer if details are received when registering this.function1 = func1; this.function2 = func2; } /* * Set data in the buffer */ set(data) { console.log("produced", data); this.data = data; for (const notify of Object.values(this.notifiers)) { notify(this.data); } } /* * Register a notifier for the buffer * Notifiers have the following signature: * function notifier (data) * data is the data stored in the buffer * Returns the id of the notifier */ register(notify, details) { if (this.data !== undefined) { console.log("Data was ready"); notify(this.data); }; let id = this.currentId; this.notifiers[id] = notify; this.currentId ++; // Optional: Details from consumer and function to be executed by producer if (details && this.function1) { this.function1(details); } return id; } /* * Unregister a notifier for the buffer * id is the the identifier returned when registering */ unregister(id, details) { delete(this.notifiers[id]); // Optional: Details from consumer and function to be executed by producer if (details && this.function2) { this.function2(details); } } } // Export (only if 'module' exists, that is, we're not in the browser) if (true) { module.exports.NotiBuffer = NotiBuffer; }; /***/ }), /***/ "./components/filters/babia-filter.js": /*!********************************************!*\ !*** ./components/filters/babia-filter.js ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { let findProdComponent = __webpack_require__(/*! ../others/common */ "./components/others/common.js").findProdComponent; let parseJson = __webpack_require__(/*! ../others/common */ "./components/others/common.js").parseJson; /* global AFRAME */ if (typeof AFRAME === 'undefined') { throw new Error('Component attempted to register before AFRAME was available.'); } const NotiBuffer = __webpack_require__(/*! ../../common/noti-buffer */ "./common/noti-buffer.js").NotiBuffer; /** * A-Charts component for A-Frame. */ AFRAME.registerComponent('babia-filter', { schema: { from: { type: 'string' }, filter: { type: 'string' }, // data, for debugging, highest priority data: { type: 'string' } }, /** * Set if component needs multiple instancing. */ multiple: false, /** * Called once when component is attached. Generally for initial setup. */ init: function () { this.notiBuffer = new NotiBuffer(); }, /** * Called when component is attached and when component data changes. * Generally modifies the entity based on the data. */ update: function (oldData) { let data = this.data; let el = this.el; // Highest priority to data if (data.data && (oldData.data !== data.data || data.filter !== oldData.filter)) { let _data = parseJson(data.data); this.processData(_data); } else if (data.from !== oldData.from || data.filter !== oldData.filter) { // Unregister from old notiBuffer if (this.prodComponent) { this.prodComponent.notiBuffer.unregister(this.notiBufferId); }; // Register for the new one // (It will also invoke processData once if there is already data) this.prodComponent = findProdComponent(data, el, "babia-filter"); if (this.prodComponent.notiBuffer) { this.notiBufferId = this.prodComponent.notiBuffer.register(this.processData.bind(this)); } } }, /** * Called when a component is removed (e.g., via removeAttribute). * Generally undoes all modifications to the entity. */ remove: function () {}, /** * Called when entity pauses. * Use to stop or remove any dynamic or background behavior such as events. */ pause: function () {}, /** * Called when entity resumes. * Use to continue or add any dynamic or background behavior such as events. */ play: function () {}, /** * Producer component */ prodComponent: undefined, /** * NotiBuffer identifier */ notiBufferId: undefined, processData: function (data) { // Filter data and save it let filter = this.data.filter.split('='); let dataFiltered; if (filter[0] && filter[1]) { dataFiltered = data.filter(key => key[filter[0]] == filter[1]); this.notiBuffer.set(dataFiltered); } else { console.error("Error on filter, please use key=value syntax"); } } }); /***/ }), /***/ "./components/filters/babia-selector.js": /*!**********************************************!*\ !*** ./components/filters/babia-selector.js ***! \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { let findProdComponent = __webpack_require__(/*! ../others/common */ "./components/others/common.js").findProdComponent; let findNavComponent = __webpack_require__(/*! ../others/common */ "./components/others/common.js").findNavComponent; let parseJson = __webpack_require__(/*! ../others/common */ "./components/others/common.js").parseJson; class Selectable { /* * @param list {array} List of items to select from * @param field {string} Field name used to select (present in all items) */ constructor(list, field, current, step, speed, state, direction) { this.data = {}; for (let item of list) { let selector = item[field]; if (this.data[selector]) { this.data[selector].push(item); } else { this.data[selector] = [item]; }; } this.selectors = Object.keys(this.data).sort(); this.length = this.selectors.length; if (!step) { this.step = 1; } else { this.step = step; } if (!speed) { this.speed = 1; } else { this.speed = speed; } if (current == -2) { this.current = 0; } else { this.current = current; } if (!state) { this.state = 'play'; } else { this.state = state; } if (!direction) { this.direction = 'forward'; } else { this.direction = direction; } } next() { let selected = this.data[this.selectors[this.current]]; if (this.current + this.step <= this.length - 1) { this.current += this.step; } else if (this.current = this.length - 1) { this.current = this.length; } else { this.current = this.length; } return selected; } prev() { let selected = this.data[this.selectors[this.current]]; if (this.current - this.step >= 0) { this.current -= this.step; } else { this.current = -1; } return selected; } setValue(value) { let selected = this.data[this.selectors[value]]; if (this.direction != 'rewind') { if (value > this.length) { this.current = this.length; } else if (value < 1) { this.current = 1; } else { this.current = value + 1; } } else { if (value > this.length - 2) { this.current = this.length - 2; } else if (value < -1) { this.current = -1; } else { this.current = value - 1; } } return selected; } }; /* global AFRAME */ if (typeof AFRAME === 'undefined') { throw new Error('Component attempted to register before AFRAME was available.'); } const NotiBuffer = __webpack_require__(/*! ../../common/noti-buffer */ "./common/noti-buffer.js").NotiBuffer; /** * Selector component for BabiaXR. */ AFRAME.registerComponent('babia-selector', { schema: { // Id of the querier where the data comes from from: { type: 'string' }, // Id of the querier where the data comes from controller: { type: 'string' }, // Field to use as selector select: { type: 'string', default: 'date' }, // Timeout for moving to the next selection timeout: { type: 'number', default: 6000 }, // data, for debugging, highest priority data: { type: 'string' }, // Current value of timeline current_value: { type: 'number', default: -2 }, // Current speed speed: { type: 'number', default: 0 }, // Current step step: { type: 'number', default: 0 }, // Current direction direction: { type: 'string', default: '' }, // Current state state: { type: 'string', default: '' } }, multiple: false, interval: undefined, /** * Called once when component is attached. Generally for initial setup. */ init: function () { this.notiBuffer = new NotiBuffer(); this.navNotiBuffer = new NotiBuffer(); }, /** * Called when component is attached and when component data changes. * Generally modifies the entity based on the data. */ babiaMetadata: { id: 0 }, update: function (oldData) { let data = this.data; let el = this.el; // Highest priority to data if (data.data && oldData.data !== data.data) { let _data = parseJson(data.data); this.processData(_data); } else { if (data.from !== oldData.from) { // Unregister for old producer if (this.prodComponent) { this.prodComponent.notiBuffer.unregister(this.notiBufferId); }; // Register for the new one this.prodComponent = findProdComponent(data, el, 'babia-selector'); if (this.prodComponent.notiBuffer) { this.notiBufferId = this.prodComponent.notiBuffer.register(this.processData.bind(this)); } }; } // find controller if (data.controller != oldData.controller) { this.selectorController = document.querySelector('#' + data.controller); // Unregister for old navigator if (this.navComponent) { this.navComponent.notiBuffer.unregister(this.navNotiBufferId, this); if (el.components.networked) { this.removeOldNavListener(this.navComponent.el); } }; // Register for the new one this.navComponent = findNavComponent(data, el); if (this.navComponent.notiBuffer) { this.navNotiBufferId = this.navComponent.notiBuffer.register(this.processEvent.bind(this), this); } if (el.components.networked) { this.addMultiuserMode(this.navComponent.el); } } if (data.direction && data.direction != oldData.direction) { if (data.direction != 'rewind') { if (this.selectable.current != this.selectable.length) { this.selectable.current += 2; } if (this.navNotiBuffer) { this.navNotiBuffer.set('forward'); } } else { if (this.selectable.current < 1) { this.selectable.current = 0; } else { this.selectable.current -= 2; } if (this.navNotiBuffer) { this.navNotiBuffer.set('rewind'); } } } if (data.step && data.step != this.selectable.step) { this.selectable.step = data.step; this.navNotiBuffer.set({ type: 'step', value: data.step }); if (this.data.direction != 'rewind') { if (this.selectable.current + data.step > this.selectable.length) { this.selectable.current = this.selectable.length; } else { this.selectable.current += data.step - 1; } } else { if (this.selectable.current - data.step <= 0) { this.selectable.current = -1; } else { this.selectable.current -= data.step + 1; } } } if (data.speed && data.speed != this.selectable.speed) { this.selectable.speed = data.speed; let timeout = this.data.timeout / data.speed; this.navNotiBuffer.set({ type: 'speed', value: data.speed }); let self = this; clearInterval(this.interval); this.interval = window.setInterval(function () { self.loop(); }, timeout); } // Only in multiuser if (el.components.networked) { // You are not the owner and you are not alone in the scene if (el.components.networked.data.owner != NAF.clientId && el.components.networked.data.owner != 'scene') { if (data.current_value != -2 && data.current_value != oldData.current_value) { // Initialize with the proper value if (data.direction != 'rewind') { this.navNotiBuffer.set({ type: 'position', value: data.current_value - 1, label: this.selectable.selectors[data.current_value - 1] }); this.setSelect(data.current_value - 1); } else { this.navNotiBuffer.set({ type: 'position', value: data.current_value + 1, label: this.selectable.selectors[data.current_value + 1] }); this.setSelect(data.current_value + 1); } } if (data.state == 'pause' && data.state != oldData.state) { this.navNotiBuffer.set('pause'); } } } }, nextSelect: function () { if (this.selectable.current > this.selectable.length - 1) { this.selectable.current = this.selectable.length; this.selectable.state = 'pause'; this.el.setAttribute('babia-selector', 'state', 'pause'); this.navNotiBuffer.set('pause'); } else { this.newData = this.selectable.next(); this.el.setAttribute('babia-selector', 'current_value', this.selectable.current); this.notiBuffer.set(this.newData); this.navNotiBuffer.set({ type: 'position', value: this.selectable.current, label: this.selectable.selectors[this.selectable.current - 1] }); this.babiaMetadata = { id: this.selectable.current }; } }, prevSelect: function () { if (this.selectable.current >= 0) { this.newData = this.selectable.prev(); this.el.setAttribute('babia-selector', 'current_value', this.selectable.current); this.notiBuffer.set(this.newData); this.navNotiBuffer.set({ type: 'position', value: this.selectable.current, label: this.selectable.selectors[this.selectable.current + 1] }); this.babiaMetadata = { id: this.selectable.current }; } else { this.selectable.current = -1; this.selectable.state = 'pause'; this.el.setAttribute('babia-selector', 'state', 'pause'); this.navNotiBuffer.set('pause'); } }, setSelect: function (value) { if (value != this.selectable.current - 1 && this.data.direction != 'rewind' || value != this.selectable.current + 1 && this.data.direction === 'rewind') { let label; this.newData = this.selectable.setValue(value); if (this.data.direction != 'rewind') { this.babiaMetadata = { id: value++ }; label = this.selectable.selectors[value - 1]; } else { this.babiaMetadata = { id: value-- }; this.selectable.current -= 2; label = this.selectable.selectors[value + 1]; } this.notiBuffer.set(this.newData); this.navNotiBuffer.set({ type: 'position', value: value, label: label }); } }, loop: function () { if (this.data.state != 'pause') { if (this.data.direction != 'rewind') { this.nextSelect(); } else { this.prevSelect(); } } }, /** * Producer component */ prodComponent: undefined, /** * Navigation component */ navComponent: undefined, /** * Producer NotiBuffer identifier */ prodNotiBufferId: undefined, /** * Navigation NotiBuffer identifier */ navNotiBufferId: undefined, /** * Where new data is stored */ newData: undefined, processData: function (_data) { // Create a Selectable object, and set the updating interval if (!this.selectable) { this.selectable = new Selectable(_data, this.data.select, this.data.current_value, this.data.step, this.data.speed); } this.navNotiBuffer.set({ type: 'position', value: this.selectable.current, label: this.selectable.selectors[this.selectable.current - 1] }); let self = this; this.nextSelect(); this.interval = window.setInterval(function () { self.loop(); }, self.data.timeout * self.selectable.speed); }, processEvent: function (event) { if (event.includes('pause')) { this.selectable.state = 'pause'; this.el.setAttribute('babia-selector', 'state', 'pause'); } else if (event.includes('play')) { this.selectable.state = 'play'; this.el.setAttribute('babia-selector', 'state', 'play'); } else if (event.includes('forward')) { this.selectable.direction = 'forward'; this.el.setAttribute('babia-selector', 'direction', 'forward'); } else if (event.includes('rewind')) { this.selectable.direction = 'rewind'; this.el.setAttribute('babia-selector', 'direction', 'rewind'); } else if (event.includes('babiaSetPosition')) { this.selectable.state = 'pause'; let value = parseInt(event.substring(16), 10); if (this.data.direction != 'rewind') { this.el.setAttribute('babia-selector', { 'state': 'pause', 'current_value': value + 1 }); } else { this.el.setAttribute('babia-selector', { 'state': 'pause', 'current_value': value - 1 }); } this.setSelect(value); this.navNotiBuffer.set('pause'); } else if (event.includes('babiaSetStep')) { this.el.setAttribute('babia-selector', 'step', parseInt(event.substring(12), 10)); } else if (event.includes('babiaSetSpeed')) { this.el.setAttribute('babia-selector', 'speed', parseInt(event.substring(13), 10)); } }, removeOldNavListener: function (nav) { nav.removeEventListener('click', function () { if (!NAF.utils.isMine(selector) && selector.components.networked.data.owner != 'scene') { if (Object.keys(NAF.connection.connectedClients).length > 0) { NAF.utils.takeOwnership(selector); } } }); }, addMultiuserMode: function (nav) { let selector = this.el; document.body.addEventListener('clientConnected', function (event) { let clientId = event.detail.clientId; console.log('clientConnected event. clientId =', clientId); console.log("Selector owner: ", selector.components.networked.data.owner); let imFirst = true; if (selector.components.networked.data.owner == 'scene') { for (let client in NAF.connection.getConnectedClients()) { let otherTime = NAF.connection.getConnectedClients()[client].roomJoinTime; let myTime = NAF.connection.adapter._myRoomJoinTime; console.log("Other: ", otherTime); console.log("Mine: ", myTime); if (myTime > otherTime) { imFirst = false; } } if (imFirst) { NAF.utils.takeOwnership(selector); } else { console.log("I'm not first"); makeInvisible(); } } else if (selector.components.networked.data.owner != NAF.clientId) { makeInvisible(); } }); nav.addEventListener('click', function () { if (!NAF.utils.isMine(selector) && selector.components.networked.data.owner != 'scene') { if (Object.keys(NAF.connection.connectedClients).length > 0) { NAF.utils.takeOwnership(selector); } } }); selector.addEventListener('ownership-gained', e => { console.log("Selector ownership gained"); makeVisible(); }); selector.addEventListener('ownership-lost', e => { console.log("Selector ownership lost"); makeInvisible(); }); function makeInvisible() { nav.children[1].setAttribute('visible', false); nav.children[2].setAttribute('visible', false); nav.children[3].setAttribute('visible', false); } function makeVisible() { nav.children[1].setAttribute('visible', true); nav.children[2].setAttribute('visible', true); nav.children[3].setAttribute('visible', true); } } }); /***/ }), /***/ "./components/filters/babia-treebuilder.js": /*!*************************************************!*\ !*** ./components/filters/babia-treebuilder.js ***! \*************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { let findProdComponent = __webpack_require__(/*! ../others/common */ "./components/others/common.js").findProdComponent; let parseJson = __webpack_require__(/*! ../others/common */ "./components/others/common.js").parseJson; const NotiBuffer = __webpack_require__(/*! ../../common/noti-buffer */ "./common/noti-buffer.js").NotiBuffer; /* global AFRAME */ if (typeof AFRAME === 'undefined') { throw new Error('Component attempted to register before AFRAME was available.'); } /** * A-Charts component for A-Frame. */ AFRAME.registerComponent('babia-treebuilder', { schema: { from: { type: 'string' }, field: { type: 'string' }, split_by: { type: 'string' }, // data, for debugging, highest priority data: { type: 'string' } }, /** * Set if component needs multiple instancing. */ multiple: false, /** * Producer component */ prodComponent: undefined, /** * NotiBuffer identifier */ notiBufferId: undefined, /** * Called once when component is attached. Generally for initial setup. */ init: function () { this.notiBuffer = new NotiBuffer(); }, /** * Called when component is attached and when component data changes. * Generally modifies the entity based on the data. */ update: function (oldData) { let data = this.data; let el = this.el; if (data.data && (oldData.data !== data.data || data.field !== oldData.field || data.split_by !== oldData.split_by)) { let _data = parseJson(data.data); this.processData(_data); } else if (data.from !== oldData.from || data.field !== oldData.field || data.split_by !== oldData.split_by) { // Unregister from old notiBuffer if (this.prodComponent) { this.prodComponent.notiBuffer.unregister(this.notiBufferId); }; // Register for the new one // (It will also invoke processData once if there is already data) this.prodComponent = findProdComponent(data, el, "babia-treebuilder"); if (this.prodComponent.notiBuffer) { this.notiBufferId = this.prodComponent.notiBuffer.register(this.processData.bind(this)); } } }, // Generate the datatree and save it processData: function (paths) { let data = this.data; let tree = []; for (let i = 0; i < paths.length; i++) { let path = paths[i][data.field].split(data.split_by); let currentLevel = tree; for (let j = 0; j < path.length; j++) { // Check if starts with the split char if (!path[j]) { continue; } let part = path[j]; let existingPath = findWhere(currentLevel, 'name', part); if (existingPath) { currentLevel = existingPath.children; } else { let newPart = {}; if (j === path.length - 1) { newPart = paths[i]; } else { newPart['children'] = []; } let findUid = paths[i][data.field].split(data.split_by + part + data.split_by); // No pop if it is a building beacuse it breaks if (findUid.length > 1) { let rest = findUid.pop(); } newPart['uid'] = findUid.join(data.split_by + part + data.split_by) + data.split_by + part; newPart['name'] = part; currentLevel.push(newPart); currentLevel = newPart.children; } } } //console.log(tree) this.notiBuffer.set(tree); } }); function findWhere(array, key, value) { t = 0; // find the index where the id is the as the a value while (t < array.length && array[t][key] !== value) { t++; }; if (t < array.length) { return array[t]; } else { return false; } } /***/ }), /***/ "./components/geometries/babia-bar.js": /*!********************************************!*\ !*** ./components/geometries/babia-bar.js ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { const colors = __webpack_require__(/*! ../others/common */ "./components/others/common.js").colors; /* * BabiaXR Bar component * * Builds a bar (usually for the bar chart) */ AFRAME.registerComponent('babia-bar', { schema: { // Height of the bar height: { type: 'number' }, // Width of the bar width: { type: 'number' }, // Depth of the bar depth: { type: 'number' }, // Color for axis and labels color: { type: 'color', default: '#000' }, // Should this be animated animation: { type: 'boolean', default: true }, // Duration of animations dur: { type: 'number', default: 2000 }, // Label for the bar ('', 'fixed', 'events') label: { type: 'string', default: '' }, // Label text (valid if label is not empty) labelText: { type: 'string', default: '' }, // Label lookat for following labelLookat: { type: 'string', default: "[camera]" }, // Scale for the label labelScale: { type: 'number', default: 1 } }, init: function () { console.log("Starting bar:", this.data.height, this.data.color); let data = this.data; this.box = document.createElement('a-entity'); this.box.classList.add("babiaxraycasterclass"); this.el.appendChild(this.box); let props = {}; if (data.animation) { props = { 'height': 0, 'width': data.width, 'depth': data.depth }; } else { props = { 'height': data.height, 'width': data.width, 'depth': data.depth }; } this.box.setAttribute('geometry', { 'primitive': 'box', 'height': props.height, 'width': props.width, 'depth': props.depth }); this.box.setAttribute('material', { 'color': data.color }); }, update: function (oldData) { let data = this.data; let box = this.box; this.updateProperty(box, 'geometry', 'height', data.animation, data['height'], oldData.height); if (data.height != oldData.height) { // If there is change in height, update position if (data.animation) { box.setAttribute('animation__pos', { 'property': 'position', 'to': { x: 0, y: data.height / 2, z: 0 }, 'dur': data.dur }); if (data.height <= 0 && oldData.height > 0) { box.setAttribute('animation__opacity', { 'property': 'material.opacity', 'to': 0, 'dur': data.dur }); } else if (oldData.height <= 0 && data.height > 0) { box.setAttribute('animation__opacity', { 'property': 'material.opacity', 'to': 100, 'dur': data.dur }); } } else { box.setAttribute('position', { x: 0, y: data.height / 2, z: 0 }); if (data.height <= 0 && oldData.height > 0) { box.setAttribute('material', 'opacity', 0); } else if (oldData.height <= 0 && data.height > 0) { box.setAttribute('material', 'opacity', 100); } }; }; this.updateProperty(box, 'geometry', 'width', data.animation, data.width, oldData.width); this.updateProperty(box, 'geometry', 'depth', data.animation, data.depth, oldData.depth); this.updateProperty(box, 'material', 'color', data.animation, data.color, oldData.color); if (this.data.label === 'events') { box.addEventListener('mouseenter', this.showLabel.bind(this)); box.addEventListener('mouseleave', this.hideLabel.bind(this)); } else if (this.data.label === 'fixed') { this.showLabel(oldData); }; }, /* * Update a property in an element, having animation into account * * @param el Element * @param component Component in which to update property * @param property Property to update in element * @param name: Property name in data, oldData * @param oldValue: Old value */ updateProperty: function (el, component, property, anim, newValue, oldValue) { let data = this.data; if (newValue !== oldValue) { if (anim) { let prop = component; if (property) { prop = prop + '.' + property; }; el.setAttribute('animation__' + component + '_' + property, { 'property': prop, 'to': newValue, 'dur': data.dur }); } else { if (property) { el.setAttribute(component, { [property]: newValue }); } else { el.setAttribute(component, newValue); } }; }; }, showLabel: function (oldData) { let data = this.data; if (data.label === 'events') { this.el.setAttribute('scale', { x: 1.1, y: 1.1, z: 1.1 }); }; text = data.labelText; let width = 2; if (text.length > 16) { width = text.length / 8; }; let height = 1; oldHeight = oldData.height || 0; oldDepth = oldData.depth || 0; let oldPosition = { x: 0, y: oldHeight + 0.6 * height, z: 0 }; if (!this.labelEl) { this.labelEl = document.createElement('a-entity'); this.labelEl.setAttribute('babia-label', { 'width': width, 'textWidth': 6 }); if (data.animation && data.label === 'fixed') { this.labelEl.setAttribute('position', oldPosition); }; this.el.appendChild(this.labelEl); }; let position = { x: 0, y: data.height + 0.6 * height, z: 0.7 * data.depth }; let anim = data.animation && data.label === 'fixed'; this.updateProperty(this.labelEl, 'position', '', anim, position, oldPosition); this.labelEl.setAttribute('rotation', { x: 0, y: 0, z: 0 }); if (text != oldData.labelText) { this.labelEl.setAttribute('babia-label', { 'text': data.labelText }); } if (data.labelLookat && oldData.labelLookat !== data.labelLookat) { this.labelEl.setAttribute('babia-lookat', data.labelLookat); } if (data.labelScale && oldData.labelScale !== data.labelScale) { this.labelEl.setAttribute('scale', { x: data.labelScale, y: data.labelScale, z: data.labelScale }); } }, hideLabel: function () { if (this.data.label === 'events') { this.el.setAttribute('scale', { x: 1, y: 1, z: 1 }); }; if (this.labelEl) { this.el.removeChild(this.labelEl); this.labelEl = null; } } }); /***/ }), /***/ "./components/geometries/babia-cyl.js": /*!********************************************!*\ !*** ./components/geometries/babia-cyl.js ***! \********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { const colors = __webpack_require__(/*! ../others/common */ "./components/others/common.js").colors; /* * BabiaXR Cyl component * * Builds a cylinder (usually for the cyls chart) */ AFRAME.registerComponent('babia-cyl', { schema: { // Height of the cylinder height: { type: 'number' }, // Width of the cylinder radius: { type: 'number' }, // Color for axis and labels color: { type: 'color', default: '#000' }, // Should this be animated animation: { type: 'boolean', default: true }, // Duration of animations dur: { type: 'number', default: 2000 }, // Label for the cylinder ('', 'fixed', 'events') label: { type: 'string', default: '' }, // Label text (valid if label is not empty) labelText: { type: 'string', default: '' }, // Label lookat for following labelLookat: { type: 'string', default: "[camera]" } }, init: function () { console.log("Starting cyl:", this.data.height, this.data.color); let data = this.data; this.cylinder = document.createElement('a-entity'); this.cylinder.classList.add("babiaxraycasterclass"); this.el.appendChild(this.cylinder); let props = {}; if (data.animation) { props = { 'height': 0, 'radius': data.radius }; } else { props = { 'height': data.height, 'radius': data.radius }; } this.cylinder.setAttribute('geometry', { 'primitive': 'cylinder', 'height': props.height, 'radius': props.radius }); this.cylinder.setAttribute('material', { 'color': data.color }); }, update: function (oldData) { let data = this.data; let cylinder = this.cylinder; this.updateProperty(cylinder, 'geometry', 'height', data.animation, data['height'], oldData.height); // Update height if (data.height != oldData.height) { // If there is change in height, update position if (data.animation) { cylinder.setAttribute('animation__pos', { 'property': 'position', 'to': { x: 0, y: data.height / 2, z: 0 }, 'dur': data.dur }); if (data.height <= 0 && oldData.height > 0) { cylinder.setAttribute('animation__opacity', { 'property': 'material.opacity', 'to': 0, 'dur': data.dur }); } else if (oldData.height <= 0 && data.height > 0) { cylinder.setAttribute('animation__opacity', { 'property': 'material.opacity', 'to': 100, 'dur': data.dur }); } } else { cylinder.setAttribute('position', { x: 0, y: data.height / 2, z: 0 }); if (data.height <= 0 && oldData.height > 0) { cylinder.setAttribute('material', 'opacity', 0); } else if (oldData.height <= 0 && data.height > 0) { cylinder.setAttribute('material', 'opacity', 100); } }; }; this.updateProperty(cylinder, 'geometry', 'radius', data.animation, data.radius, oldData.radius); this.updateProperty(cylinder, 'material', 'color', data.animation, data.color, oldData.color); if (this.data.label === 'events') { cylinder.addEventListener('mouseenter', this.showLabel.bind(this)); cylinder.addEventListener('mouseleave', this.hideLabel.bind(this)); } else if (this.data.label === 'fixed') { this.showLabel(oldData); }; }, /* * Update a property in an element, having animation into account * * @param el Element * @param component Component in which to update property * @param property Property to update in element * @param name: Property name in data, oldData * @param oldValue: Old value */ updateProperty: function (el, component, property, anim, newValue, oldValue) { let data = this.data; if (newValue !== oldValue) { if (anim) { let prop = component; if (property) { prop = prop + '.' + property; }; el.setAttribute('animation__' + component + '_' + property, { 'property': prop, 'to': newValue, 'dur': data.dur }); } else { if (property) { el.setAttribute(component, { [property]: newValue }); } else { el.setAttribute(component, newValue); } }; }; }, showLabel: function (oldData) { let data = this.data; if (data.label === 'events') { this.el.setAttribute('scale', { x: 1.1, y: 1.1, z: 1.1 }); }; text = data.labelText; let width = 2; if (text.length > 16) { width = text.length / 8; }; let height = 1; oldHeight = oldData.height || 0; let oldPosition = { x: 0, y: oldHeight + 0.6 * height, z: 0 }; if (!this.labelEl) { this.labelEl = document.createElement('a-entity'); this.labelEl.setAttribute('babia-label', { 'width': width, 'textWidth': 6 }); if (data.animation && data.label === 'fixed') { this.labelEl.setAttribute('position', oldPosition); }; this.el.appendChild(this.labelEl); }; let position = { x: 0, y: data.height + 0.7 * height, z: 0.7 * data.radius }; let anim = data.animation && data.label === 'fixed'; this.updateProperty(this.labelEl, 'position', '', anim, position, oldPosition); this.labelEl.setAttribute('rotation', { x: 0, y: 0, z: 0 }); if (text != oldData.labelText) { this.labelEl.setAttribute('babia-label', { 'text': data.labelText }); } if (data.labelLookat && oldData.labelLookat !== data.labelLookat) { this.labelEl.setAttribute('babia-lookat', data.labelLookat); } if (data.labelScale && oldData.labelScale !== data.labelScale) { this.labelEl.setAttribute('scale', { x: data.labelScale, y: data.labelScale, z: data.labelScale }); } }, hideLabel: function () { if (this.data.label === 'events') { this.el.setAttribute('scale', { x: 1, y: 1, z: 1 }); }; if (this.labelEl) { this.el.removeChild(this.labelEl); this.labelEl = null; } } }); /***/ }), /***/ "./components/others/assets/tooltip.png": /*!**********************************************!*\ !*** ./components/others/assets/tooltip.png ***! \**********************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = ("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAC70lEQVR42u3by04TURzH8W+nFx0kMUBbE6eNiSCXjQlxZQKU2qUbSERfwUQQ0ATfwATYGCPxHSwoLtzZ9ObWGIMRSDDRlFlIW1xIUuxtXHQ0gC1MTRM6nfNPupmeaef3OTNnziRzbJqmcUy5gDH9cx24qG9r5toDtoEPwCvgNZCv1dh2DMA4sAD0YO7aAh4BL6t9KVXZZgfm9R3MHh49w4qeyX4SgB14DMzRejWnZ7PXAmjl8DURDo4B8y0e/mAt6OPCX4BbQBhr1QSwbNM0zQV8AXwWA1CBy5IuYbXwAApwW9InOVatMZumaV+BS0b3KBSK5XgyqcYSSXXt01o2k87k8sVi+TSOvk2WHV6vV+7r7e0IBoaVkaFhxel0SHX8xDebpmn7wBkjrWPxhPp06fnHlLq914zd6Vd87VP37l4NBgJGL+m8TTvhYeBgz49P3HnzPb2Ta+Zz2uv2yKvLL24aPRMMny6RaDTV7OEBdjLp3NtIJGW0vWGAWDyhmmVkiyXfqQ0H+LyxvmsWgPU6jtUwQDa7u28WgGwmu99wgHyhUDYLQD23ZQmLlwAQAAJAAAgAASAABIAAEAACQAAIAAEgAASAABAAAkAACAABIAAEgAAQAAJAAByqc22ywyyhXA7jL0oZbtjV6ZbNAtDl7jrbcIArPd3nzQIw0D/Q2XCA0cCIYhaA0eEhpeEAoWDQf8HjbfrLwOv2yKHQDX89AD+NNHQ6HdLs9NSgX/G1N2t4v+Jrfzhzf9DldBrt2LxN07RNoNfonxQKxXIkGk1F4wl1Y3PjR3onkyuUTudVWafdIXm8brm/r78jGBhRQsGg/39elQ1TWS9gxVqRgFULz4NWrb5goluisqjwgQV7fxb49WfACAOLFgq/qGc+tGrMDjwBJls8/BIwDZSOToRKwAzwzCrhq80EWxnhn/C1psIlYEqfG2y1QPAtKivjJo+GPzoGVH201nceB65RWWrmbPLABf0W957K8vkwxyyf/w0kIOOe2pS1LAAAAABJRU5ErkJggg=="); /***/ }), /***/ "./components/others/babia-axis.js": /*!*****************************************!*\ !*** ./components/others/babia-axis.js ***! \*****************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { const colors = __webpack_require__(/*! ../others/common */ "./components/others/common.js").colors; /* global AFRAME */ if (typeof AFRAME === 'undefined') { throw new Error('Component attempted to register before AFRAME was available.'); } /* * Class for building axis (x, y, or z), with labels and everything */ class Axis { /* * @param el Element * @param axis Axis ('x', 'y', 'z') * @param anim Is done with animation? * @param dur Duration of the animation */ constructor(el, axis, anim, dur) { this.el = el; this.axis = axis; this.anim = anim; this.dur = dur; } /* * Update axis line * * @param length Lenght of axis line * @param color Color of axis line */ updateLine(length, color) { const axis = this.axis; const el = this.el; const anim = this.anim; const dur = this.dur; const comp = `line__${axis}axis`; let lineEl = el.components[comp]; let end = { x: 0, y: 0, z: 0 }; end[axis] = length; if (anim && lineEl) { el.setAttribute(`animation__${axis}axis`, { 'property': `${comp}.end`, 'to': end, 'dur': dur }); el.setAttribute(comp, { 'color': color }); } else { el.setAttribute(comp, { 'start': { x: 0, y: 0, z: 0 }, 'end': end, 'color': color }); }; } /* * Remove labels which are children of an element * * @param el Element * @param anim Is done with animation? * @param dur Duration of the animation */ removeLabels() { const el = this.el; const anim = this.anim; const dur = this.dur; let labels = el.querySelectorAll('[text]'); for (const label of labels) { if (anim) { label.addEventListener('animationcomplete', function (e) { e.target.remove(); }); label.setAttribute('animation', { 'property': 'text.opacity', 'to': 0, 'dur': dur }); } else