vega-view
Advanced tools
Sorry, the diff of this file is too big to display
| import { isString, hasOwnProperty, error, truthy, constant, extend, isArray, toSet, isObject, debounce, isDate, inherits, stringValue } from 'vega-util'; | ||
| import { changeset, isChangeSet, EventStream, transforms, Dataflow, asyncCallback } from 'vega-dataflow'; | ||
| import { point, renderModule, CanvasHandler, RenderType, Scenegraph } from 'vega-scenegraph'; | ||
| import { tickStep } from 'd3-array'; | ||
| import { functionContext } from 'vega-functions'; | ||
| import { context } from 'vega-runtime'; | ||
| import { interval } from 'd3-timer'; | ||
| import { locale } from 'vega-format'; | ||
| // initialize aria role and label attributes | ||
| function initializeAria(view) { | ||
| const el = view.container(); | ||
| if (el) { | ||
| el.setAttribute('role', 'graphics-document'); | ||
| el.setAttribute('aria-roleDescription', 'visualization'); | ||
| ariaLabel(el, view.description()); | ||
| } | ||
| } // update aria-label if we have a DOM container element | ||
| function ariaLabel(el, desc) { | ||
| if (el) desc == null ? el.removeAttribute('aria-label') : el.setAttribute('aria-label', desc); | ||
| } | ||
| function background (view) { | ||
| // respond to background signal | ||
| view.add(null, _ => { | ||
| view._background = _.bg; | ||
| view._resize = 1; | ||
| return _.bg; | ||
| }, { | ||
| bg: view._signals.background | ||
| }); | ||
| } | ||
| const Default = 'default'; | ||
| function cursor (view) { | ||
| // get cursor signal, add to dataflow if needed | ||
| const cursor = view._signals.cursor || (view._signals.cursor = view.add({ | ||
| user: Default, | ||
| item: null | ||
| })); // evaluate cursor on each mousemove event | ||
| view.on(view.events('view', 'mousemove'), cursor, (_, event) => { | ||
| const value = cursor.value, | ||
| user = value ? isString(value) ? value : value.user : Default, | ||
| item = event.item && event.item.cursor || null; | ||
| return value && user === value.user && item == value.item ? value : { | ||
| user: user, | ||
| item: item | ||
| }; | ||
| }); // when cursor signal updates, set visible cursor | ||
| view.add(null, function (_) { | ||
| let user = _.cursor, | ||
| item = this.value; | ||
| if (!isString(user)) { | ||
| item = user.item; | ||
| user = user.user; | ||
| } | ||
| setCursor(view, user && user !== Default ? user : item || user); | ||
| return item; | ||
| }, { | ||
| cursor: cursor | ||
| }); | ||
| } | ||
| function setCursor(view, cursor) { | ||
| const el = view.globalCursor() ? typeof document !== 'undefined' && document.body : view.container(); | ||
| if (el) { | ||
| return cursor == null ? el.style.removeProperty('cursor') : el.style.cursor = cursor; | ||
| } | ||
| } | ||
| function dataref(view, name) { | ||
| var data = view._runtime.data; | ||
| if (!hasOwnProperty(data, name)) { | ||
| error('Unrecognized data set: ' + name); | ||
| } | ||
| return data[name]; | ||
| } | ||
| function data(name, values) { | ||
| return arguments.length < 2 ? dataref(this, name).values.value : change.call(this, name, changeset().remove(truthy).insert(values)); | ||
| } | ||
| function change(name, changes) { | ||
| if (!isChangeSet(changes)) { | ||
| error('Second argument to changes must be a changeset.'); | ||
| } | ||
| const dataset = dataref(this, name); | ||
| dataset.modified = true; | ||
| return this.pulse(dataset.input, changes); | ||
| } | ||
| function insert(name, _) { | ||
| return change.call(this, name, changeset().insert(_)); | ||
| } | ||
| function remove(name, _) { | ||
| return change.call(this, name, changeset().remove(_)); | ||
| } | ||
| function width(view) { | ||
| var padding = view.padding(); | ||
| return Math.max(0, view._viewWidth + padding.left + padding.right); | ||
| } | ||
| function height(view) { | ||
| var padding = view.padding(); | ||
| return Math.max(0, view._viewHeight + padding.top + padding.bottom); | ||
| } | ||
| function offset(view) { | ||
| var padding = view.padding(), | ||
| origin = view._origin; | ||
| return [padding.left + origin[0], padding.top + origin[1]]; | ||
| } | ||
| function resizeRenderer(view) { | ||
| var origin = offset(view), | ||
| w = width(view), | ||
| h = height(view); | ||
| view._renderer.background(view.background()); | ||
| view._renderer.resize(w, h, origin); | ||
| view._handler.origin(origin); | ||
| view._resizeListeners.forEach(handler => { | ||
| try { | ||
| handler(w, h); | ||
| } catch (error) { | ||
| view.error(error); | ||
| } | ||
| }); | ||
| } | ||
| /** | ||
| * Extend an event with additional view-specific methods. | ||
| * Adds a new property ('vega') to an event that provides a number | ||
| * of methods for querying information about the current interaction. | ||
| * The vega object provides the following methods: | ||
| * view - Returns the backing View instance. | ||
| * item - Returns the currently active scenegraph item (if any). | ||
| * group - Returns the currently active scenegraph group (if any). | ||
| * This method accepts a single string-typed argument indicating the name | ||
| * of the desired parent group. The scenegraph will be traversed from | ||
| * the item up towards the root to search for a matching group. If no | ||
| * argument is provided the enclosing group for the active item is | ||
| * returned, unless the item it itself a group, in which case it is | ||
| * returned directly. | ||
| * xy - Returns a two-element array containing the x and y coordinates for | ||
| * mouse or touch events. For touch events, this is based on the first | ||
| * elements in the changedTouches array. This method accepts a single | ||
| * argument: either an item instance or mark name that should serve as | ||
| * the reference coordinate system. If no argument is provided the | ||
| * top-level view coordinate system is assumed. | ||
| * x - Returns the current x-coordinate, accepts the same arguments as xy. | ||
| * y - Returns the current y-coordinate, accepts the same arguments as xy. | ||
| * @param {Event} event - The input event to extend. | ||
| * @param {Item} item - The currently active scenegraph item (if any). | ||
| * @return {Event} - The extended input event. | ||
| */ | ||
| function eventExtend (view, event, item) { | ||
| var r = view._renderer, | ||
| el = r && r.canvas(), | ||
| p, | ||
| e, | ||
| translate; | ||
| if (el) { | ||
| translate = offset(view); | ||
| e = event.changedTouches ? event.changedTouches[0] : event; | ||
| p = point(e, el); | ||
| p[0] -= translate[0]; | ||
| p[1] -= translate[1]; | ||
| } | ||
| event.dataflow = view; | ||
| event.item = item; | ||
| event.vega = extension(view, item, p); | ||
| return event; | ||
| } | ||
| function extension(view, item, point) { | ||
| const itemGroup = item ? item.mark.marktype === 'group' ? item : item.mark.group : null; | ||
| function group(name) { | ||
| var g = itemGroup, | ||
| i; | ||
| if (name) for (i = item; i; i = i.mark.group) { | ||
| if (i.mark.name === name) { | ||
| g = i; | ||
| break; | ||
| } | ||
| } | ||
| return g && g.mark && g.mark.interactive ? g : {}; | ||
| } | ||
| function xy(item) { | ||
| if (!item) return point; | ||
| if (isString(item)) item = group(item); | ||
| const p = point.slice(); | ||
| while (item) { | ||
| p[0] -= item.x || 0; | ||
| p[1] -= item.y || 0; | ||
| item = item.mark && item.mark.group; | ||
| } | ||
| return p; | ||
| } | ||
| return { | ||
| view: constant(view), | ||
| item: constant(item || {}), | ||
| group: group, | ||
| xy: xy, | ||
| x: function (item) { | ||
| return xy(item)[0]; | ||
| }, | ||
| y: function (item) { | ||
| return xy(item)[1]; | ||
| } | ||
| }; | ||
| } | ||
| const VIEW = 'view', | ||
| TIMER = 'timer', | ||
| WINDOW = 'window', | ||
| NO_TRAP = { | ||
| trap: false | ||
| }; | ||
| /** | ||
| * Initialize event handling configuration. | ||
| * @param {object} config - The configuration settings. | ||
| * @return {object} | ||
| */ | ||
| function initializeEventConfig(config) { | ||
| const events = extend({ | ||
| defaults: {} | ||
| }, config); | ||
| const unpack = (obj, keys) => { | ||
| keys.forEach(k => { | ||
| if (isArray(obj[k])) obj[k] = toSet(obj[k]); | ||
| }); | ||
| }; | ||
| unpack(events.defaults, ['prevent', 'allow']); | ||
| unpack(events, ['view', 'window', 'selector']); | ||
| return events; | ||
| } | ||
| function prevent(view, type) { | ||
| var def = view._eventConfig.defaults, | ||
| prevent = def.prevent, | ||
| allow = def.allow; | ||
| return prevent === false || allow === true ? false : prevent === true || allow === false ? true : prevent ? prevent[type] : allow ? !allow[type] : view.preventDefault(); | ||
| } | ||
| function permit(view, key, type) { | ||
| const rule = view._eventConfig && view._eventConfig[key]; | ||
| if (rule === false || isObject(rule) && !rule[type]) { | ||
| view.warn("Blocked ".concat(key, " ").concat(type, " event listener.")); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| /** | ||
| * Create a new event stream from an event source. | ||
| * @param {object} source - The event source to monitor. | ||
| * @param {string} type - The event type. | ||
| * @param {function(object): boolean} [filter] - Event filter function. | ||
| * @return {EventStream} | ||
| */ | ||
| function events(source, type, filter) { | ||
| var view = this, | ||
| s = new EventStream(filter), | ||
| send = function (e, item) { | ||
| view.runAsync(null, () => { | ||
| if (source === VIEW && prevent(view, type)) { | ||
| e.preventDefault(); | ||
| } | ||
| s.receive(eventExtend(view, e, item)); | ||
| }); | ||
| }, | ||
| sources; | ||
| if (source === TIMER) { | ||
| if (permit(view, 'timer', type)) { | ||
| view.timer(send, type); | ||
| } | ||
| } else if (source === VIEW) { | ||
| if (permit(view, 'view', type)) { | ||
| // send traps errors, so use {trap: false} option | ||
| view.addEventListener(type, send, NO_TRAP); | ||
| } | ||
| } else { | ||
| if (source === WINDOW) { | ||
| if (permit(view, 'window', type) && typeof window !== 'undefined') { | ||
| sources = [window]; | ||
| } | ||
| } else if (typeof document !== 'undefined') { | ||
| if (permit(view, 'selector', type)) { | ||
| sources = document.querySelectorAll(source); | ||
| } | ||
| } | ||
| if (!sources) { | ||
| view.warn('Can not resolve event source: ' + source); | ||
| } else { | ||
| for (var i = 0, n = sources.length; i < n; ++i) { | ||
| sources[i].addEventListener(type, send); | ||
| } | ||
| view._eventListeners.push({ | ||
| type: type, | ||
| sources: sources, | ||
| handler: send | ||
| }); | ||
| } | ||
| } | ||
| return s; | ||
| } | ||
| function itemFilter(event) { | ||
| return event.item; | ||
| } | ||
| function markTarget(event) { | ||
| // grab upstream collector feeding the mark operator | ||
| return event.item.mark.source; | ||
| } | ||
| function invoke(name) { | ||
| return function (_, event) { | ||
| return event.vega.view().changeset().encode(event.item, name); | ||
| }; | ||
| } | ||
| function hover (hoverSet, leaveSet) { | ||
| hoverSet = [hoverSet || 'hover']; | ||
| leaveSet = [leaveSet || 'update', hoverSet[0]]; // invoke hover set upon mouseover | ||
| this.on(this.events('view', 'mouseover', itemFilter), markTarget, invoke(hoverSet)); // invoke leave set upon mouseout | ||
| this.on(this.events('view', 'mouseout', itemFilter), markTarget, invoke(leaveSet)); | ||
| return this; | ||
| } | ||
| /** | ||
| * Finalize a View instance that is being removed. | ||
| * Cancel any running timers. | ||
| * Remove all external event listeners. | ||
| * Remove any currently displayed tooltip. | ||
| */ | ||
| function finalize () { | ||
| var tooltip = this._tooltip, | ||
| timers = this._timers, | ||
| listeners = this._eventListeners, | ||
| n, | ||
| m, | ||
| e; | ||
| n = timers.length; | ||
| while (--n >= 0) { | ||
| timers[n].stop(); | ||
| } | ||
| n = listeners.length; | ||
| while (--n >= 0) { | ||
| e = listeners[n]; | ||
| m = e.sources.length; | ||
| while (--m >= 0) { | ||
| e.sources[m].removeEventListener(e.type, e.handler); | ||
| } | ||
| } | ||
| if (tooltip) { | ||
| tooltip.call(this, this._handler, null, null, null); | ||
| } | ||
| return this; | ||
| } | ||
| function element (tag, attr, text) { | ||
| const el = document.createElement(tag); | ||
| for (const key in attr) el.setAttribute(key, attr[key]); | ||
| if (text != null) el.textContent = text; | ||
| return el; | ||
| } | ||
| const BindClass = 'vega-bind', | ||
| NameClass = 'vega-bind-name', | ||
| RadioClass = 'vega-bind-radio'; | ||
| /** | ||
| * Bind a signal to an external HTML input element. The resulting two-way | ||
| * binding will propagate input changes to signals, and propagate signal | ||
| * changes to the input element state. If this view instance has no parent | ||
| * element, we assume the view is headless and no bindings are created. | ||
| * @param {Element|string} el - The parent DOM element to which the input | ||
| * element should be appended as a child. If string-valued, this argument | ||
| * will be treated as a CSS selector. If null or undefined, the parent | ||
| * element of this view will be used as the element. | ||
| * @param {object} param - The binding parameters which specify the signal | ||
| * to bind to, the input element type, and type-specific configuration. | ||
| * @return {View} - This view instance. | ||
| */ | ||
| function bind (view, el, binding) { | ||
| if (!el) return; | ||
| const param = binding.param; | ||
| let bind = binding.state; | ||
| if (!bind) { | ||
| bind = binding.state = { | ||
| elements: null, | ||
| active: false, | ||
| set: null, | ||
| update: value => { | ||
| if (value !== view.signal(param.signal)) { | ||
| view.runAsync(null, () => { | ||
| bind.source = true; | ||
| view.signal(param.signal, value); | ||
| }); | ||
| } | ||
| } | ||
| }; | ||
| if (param.debounce) { | ||
| bind.update = debounce(param.debounce, bind.update); | ||
| } | ||
| } | ||
| generate(bind, el, param, view.signal(param.signal)); | ||
| if (!bind.active) { | ||
| view.on(view._signals[param.signal], null, () => { | ||
| bind.source ? bind.source = false : bind.set(view.signal(param.signal)); | ||
| }); | ||
| bind.active = true; | ||
| } | ||
| return bind; | ||
| } | ||
| /** | ||
| * Generate an HTML input form element and bind it to a signal. | ||
| */ | ||
| function generate(bind, el, param, value) { | ||
| const div = element('div', { | ||
| 'class': BindClass | ||
| }); | ||
| const wrapper = param.input === 'radio' ? div : div.appendChild(element('label')); | ||
| wrapper.appendChild(element('span', { | ||
| 'class': NameClass | ||
| }, param.name || param.signal)); | ||
| el.appendChild(div); | ||
| let input = form; | ||
| switch (param.input) { | ||
| case 'checkbox': | ||
| input = checkbox; | ||
| break; | ||
| case 'select': | ||
| input = select; | ||
| break; | ||
| case 'radio': | ||
| input = radio; | ||
| break; | ||
| case 'range': | ||
| input = range; | ||
| break; | ||
| } | ||
| input(bind, wrapper, param, value); | ||
| } | ||
| /** | ||
| * Generates an arbitrary input form element. | ||
| * The input type is controlled via user-provided parameters. | ||
| */ | ||
| function form(bind, el, param, value) { | ||
| const node = element('input'); | ||
| for (const key in param) { | ||
| if (key !== 'signal' && key !== 'element') { | ||
| node.setAttribute(key === 'input' ? 'type' : key, param[key]); | ||
| } | ||
| } | ||
| node.setAttribute('name', param.signal); | ||
| node.value = value; | ||
| el.appendChild(node); | ||
| node.addEventListener('input', () => bind.update(node.value)); | ||
| bind.elements = [node]; | ||
| bind.set = value => node.value = value; | ||
| } | ||
| /** | ||
| * Generates a checkbox input element. | ||
| */ | ||
| function checkbox(bind, el, param, value) { | ||
| const attr = { | ||
| type: 'checkbox', | ||
| name: param.signal | ||
| }; | ||
| if (value) attr.checked = true; | ||
| const node = element('input', attr); | ||
| el.appendChild(node); | ||
| node.addEventListener('change', () => bind.update(node.checked)); | ||
| bind.elements = [node]; | ||
| bind.set = value => node.checked = !!value || null; | ||
| } | ||
| /** | ||
| * Generates a selection list input element. | ||
| */ | ||
| function select(bind, el, param, value) { | ||
| const node = element('select', { | ||
| name: param.signal | ||
| }), | ||
| labels = param.labels || []; | ||
| param.options.forEach((option, i) => { | ||
| const attr = { | ||
| value: option | ||
| }; | ||
| if (valuesEqual(option, value)) attr.selected = true; | ||
| node.appendChild(element('option', attr, (labels[i] || option) + '')); | ||
| }); | ||
| el.appendChild(node); | ||
| node.addEventListener('change', () => { | ||
| bind.update(param.options[node.selectedIndex]); | ||
| }); | ||
| bind.elements = [node]; | ||
| bind.set = value => { | ||
| for (let i = 0, n = param.options.length; i < n; ++i) { | ||
| if (valuesEqual(param.options[i], value)) { | ||
| node.selectedIndex = i; | ||
| return; | ||
| } | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Generates a radio button group. | ||
| */ | ||
| function radio(bind, el, param, value) { | ||
| const group = element('span', { | ||
| 'class': RadioClass | ||
| }), | ||
| labels = param.labels || []; | ||
| el.appendChild(group); | ||
| bind.elements = param.options.map((option, i) => { | ||
| const attr = { | ||
| type: 'radio', | ||
| name: param.signal, | ||
| value: option | ||
| }; | ||
| if (valuesEqual(option, value)) attr.checked = true; | ||
| const input = element('input', attr); | ||
| input.addEventListener('change', () => bind.update(option)); | ||
| const label = element('label', {}, (labels[i] || option) + ''); | ||
| label.prepend(input); | ||
| group.appendChild(label); | ||
| return input; | ||
| }); | ||
| bind.set = value => { | ||
| const nodes = bind.elements, | ||
| n = nodes.length; | ||
| for (let i = 0; i < n; ++i) { | ||
| if (valuesEqual(nodes[i].value, value)) nodes[i].checked = true; | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Generates a slider input element. | ||
| */ | ||
| function range(bind, el, param, value) { | ||
| value = value !== undefined ? value : (+param.max + +param.min) / 2; | ||
| const max = param.max != null ? param.max : Math.max(100, +value) || 100, | ||
| min = param.min || Math.min(0, max, +value) || 0, | ||
| step = param.step || tickStep(min, max, 100); | ||
| const node = element('input', { | ||
| type: 'range', | ||
| name: param.signal, | ||
| min: min, | ||
| max: max, | ||
| step: step | ||
| }); | ||
| node.value = value; | ||
| const span = element('span', {}, +value); | ||
| el.appendChild(node); | ||
| el.appendChild(span); | ||
| const update = () => { | ||
| span.textContent = node.value; | ||
| bind.update(+node.value); | ||
| }; // subscribe to both input and change | ||
| node.addEventListener('input', update); | ||
| node.addEventListener('change', update); | ||
| bind.elements = [node]; | ||
| bind.set = value => { | ||
| node.value = value; | ||
| span.textContent = value; | ||
| }; | ||
| } | ||
| function valuesEqual(a, b) { | ||
| return a === b || a + '' === b + ''; | ||
| } | ||
| function initializeRenderer (view, r, el, constructor, scaleFactor, opt) { | ||
| r = r || new constructor(view.loader()); | ||
| return r.initialize(el, width(view), height(view), offset(view), scaleFactor, opt).background(view.background()); | ||
| } | ||
| function trap (view, fn) { | ||
| return !fn ? null : function () { | ||
| try { | ||
| fn.apply(this, arguments); | ||
| } catch (error) { | ||
| view.error(error); | ||
| } | ||
| }; | ||
| } | ||
| function initializeHandler (view, prevHandler, el, constructor) { | ||
| // instantiate scenegraph handler | ||
| const handler = new constructor(view.loader(), trap(view, view.tooltip())).scene(view.scenegraph().root).initialize(el, offset(view), view); // transfer event handlers | ||
| if (prevHandler) { | ||
| prevHandler.handlers().forEach(h => { | ||
| handler.on(h.type, h.handler); | ||
| }); | ||
| } | ||
| return handler; | ||
| } | ||
| function initialize (el, elBind) { | ||
| const view = this, | ||
| type = view._renderType, | ||
| config = view._eventConfig.bind, | ||
| module = renderModule(type); // containing dom element | ||
| el = view._el = el ? lookup(view, el) : null; // initialize aria attributes | ||
| initializeAria(view); // select appropriate renderer & handler | ||
| if (!module) view.error('Unrecognized renderer type: ' + type); | ||
| const Handler = module.handler || CanvasHandler, | ||
| Renderer = el ? module.renderer : module.headless; // initialize renderer and input handler | ||
| view._renderer = !Renderer ? null : initializeRenderer(view, view._renderer, el, Renderer); | ||
| view._handler = initializeHandler(view, view._handler, el, Handler); | ||
| view._redraw = true; // initialize signal bindings | ||
| if (el && config !== 'none') { | ||
| elBind = elBind ? view._elBind = lookup(view, elBind) : el.appendChild(element('form', { | ||
| 'class': 'vega-bindings' | ||
| })); | ||
| view._bind.forEach(_ => { | ||
| if (_.param.element && config !== 'container') { | ||
| _.element = lookup(view, _.param.element); | ||
| } | ||
| }); | ||
| view._bind.forEach(_ => { | ||
| bind(view, _.element || elBind, _); | ||
| }); | ||
| } | ||
| return view; | ||
| } | ||
| function lookup(view, el) { | ||
| if (typeof el === 'string') { | ||
| if (typeof document !== 'undefined') { | ||
| el = document.querySelector(el); | ||
| if (!el) { | ||
| view.error('Signal bind element not found: ' + el); | ||
| return null; | ||
| } | ||
| } else { | ||
| view.error('DOM document instance not found.'); | ||
| return null; | ||
| } | ||
| } | ||
| if (el) { | ||
| try { | ||
| el.innerHTML = ''; | ||
| } catch (e) { | ||
| el = null; | ||
| view.error(e); | ||
| } | ||
| } | ||
| return el; | ||
| } | ||
| const number = _ => +_ || 0; | ||
| const paddingObject = _ => ({ | ||
| top: _, | ||
| bottom: _, | ||
| left: _, | ||
| right: _ | ||
| }); | ||
| function padding (_) { | ||
| return isObject(_) ? { | ||
| top: number(_.top), | ||
| bottom: number(_.bottom), | ||
| left: number(_.left), | ||
| right: number(_.right) | ||
| } : paddingObject(number(_)); | ||
| } | ||
| /** | ||
| * Render the current scene in a headless fashion. | ||
| * This method is asynchronous, returning a Promise instance. | ||
| * @return {Promise} - A Promise that resolves to a renderer. | ||
| */ | ||
| async function renderHeadless (view, type, scaleFactor, opt) { | ||
| const module = renderModule(type), | ||
| ctr = module && module.headless; | ||
| if (!ctr) error('Unrecognized renderer type: ' + type); | ||
| await view.runAsync(); | ||
| return initializeRenderer(view, null, null, ctr, scaleFactor, opt).renderAsync(view._scenegraph.root); | ||
| } | ||
| /** | ||
| * Produce an image URL for the visualization. Depending on the type | ||
| * parameter, the generated URL contains data for either a PNG or SVG image. | ||
| * The URL can be used (for example) to download images of the visualization. | ||
| * This method is asynchronous, returning a Promise instance. | ||
| * @param {string} type - The image type. One of 'svg', 'png' or 'canvas'. | ||
| * The 'canvas' and 'png' types are synonyms for a PNG image. | ||
| * @return {Promise} - A promise that resolves to an image URL. | ||
| */ | ||
| async function renderToImageURL (type, scaleFactor) { | ||
| if (type !== RenderType.Canvas && type !== RenderType.SVG && type !== RenderType.PNG) { | ||
| error('Unrecognized image type: ' + type); | ||
| } | ||
| const r = await renderHeadless(this, type, scaleFactor); | ||
| return type === RenderType.SVG ? toBlobURL(r.svg(), 'image/svg+xml') : r.canvas().toDataURL('image/png'); | ||
| } | ||
| function toBlobURL(data, mime) { | ||
| const blob = new Blob([data], { | ||
| type: mime | ||
| }); | ||
| return window.URL.createObjectURL(blob); | ||
| } | ||
| /** | ||
| * Produce a Canvas instance containing a rendered visualization. | ||
| * This method is asynchronous, returning a Promise instance. | ||
| * @return {Promise} - A promise that resolves to a Canvas instance. | ||
| */ | ||
| async function renderToCanvas (scaleFactor, opt) { | ||
| const r = await renderHeadless(this, RenderType.Canvas, scaleFactor, opt); | ||
| return r.canvas(); | ||
| } | ||
| /** | ||
| * Produce a rendered SVG string of the visualization. | ||
| * This method is asynchronous, returning a Promise instance. | ||
| * @return {Promise} - A promise that resolves to an SVG string. | ||
| */ | ||
| async function renderToSVG (scaleFactor) { | ||
| const r = await renderHeadless(this, RenderType.SVG, scaleFactor); | ||
| return r.svg(); | ||
| } | ||
| function runtime (view, spec, expr) { | ||
| return context(view, transforms, functionContext, expr).parse(spec); | ||
| } | ||
| function scale(name) { | ||
| var scales = this._runtime.scales; | ||
| if (!hasOwnProperty(scales, name)) { | ||
| error('Unrecognized scale or projection: ' + name); | ||
| } | ||
| return scales[name].value; | ||
| } | ||
| var Width = 'width', | ||
| Height = 'height', | ||
| Padding = 'padding', | ||
| Skip = { | ||
| skip: true | ||
| }; | ||
| function viewWidth(view, width) { | ||
| var a = view.autosize(), | ||
| p = view.padding(); | ||
| return width - (a && a.contains === Padding ? p.left + p.right : 0); | ||
| } | ||
| function viewHeight(view, height) { | ||
| var a = view.autosize(), | ||
| p = view.padding(); | ||
| return height - (a && a.contains === Padding ? p.top + p.bottom : 0); | ||
| } | ||
| function initializeResize(view) { | ||
| var s = view._signals, | ||
| w = s[Width], | ||
| h = s[Height], | ||
| p = s[Padding]; | ||
| function resetSize() { | ||
| view._autosize = view._resize = 1; | ||
| } // respond to width signal | ||
| view._resizeWidth = view.add(null, _ => { | ||
| view._width = _.size; | ||
| view._viewWidth = viewWidth(view, _.size); | ||
| resetSize(); | ||
| }, { | ||
| size: w | ||
| }); // respond to height signal | ||
| view._resizeHeight = view.add(null, _ => { | ||
| view._height = _.size; | ||
| view._viewHeight = viewHeight(view, _.size); | ||
| resetSize(); | ||
| }, { | ||
| size: h | ||
| }); // respond to padding signal | ||
| const resizePadding = view.add(null, resetSize, { | ||
| pad: p | ||
| }); // set rank to run immediately after source signal | ||
| view._resizeWidth.rank = w.rank + 1; | ||
| view._resizeHeight.rank = h.rank + 1; | ||
| resizePadding.rank = p.rank + 1; | ||
| } | ||
| function resizeView(viewWidth, viewHeight, width, height, origin, auto) { | ||
| this.runAfter(view => { | ||
| let rerun = 0; // reset autosize flag | ||
| view._autosize = 0; // width value changed: update signal, skip resize op | ||
| if (view.width() !== width) { | ||
| rerun = 1; | ||
| view.signal(Width, width, Skip); // set width, skip update calc | ||
| view._resizeWidth.skip(true); // skip width resize handler | ||
| } // height value changed: update signal, skip resize op | ||
| if (view.height() !== height) { | ||
| rerun = 1; | ||
| view.signal(Height, height, Skip); // set height, skip update calc | ||
| view._resizeHeight.skip(true); // skip height resize handler | ||
| } // view width changed: update view property, set resize flag | ||
| if (view._viewWidth !== viewWidth) { | ||
| view._resize = 1; | ||
| view._viewWidth = viewWidth; | ||
| } // view height changed: update view property, set resize flag | ||
| if (view._viewHeight !== viewHeight) { | ||
| view._resize = 1; | ||
| view._viewHeight = viewHeight; | ||
| } // origin changed: update view property, set resize flag | ||
| if (view._origin[0] !== origin[0] || view._origin[1] !== origin[1]) { | ||
| view._resize = 1; | ||
| view._origin = origin; | ||
| } // run dataflow on width/height signal change | ||
| if (rerun) view.run('enter'); | ||
| if (auto) view.runAfter(v => v.resize()); | ||
| }, false, 1); | ||
| } | ||
| /** | ||
| * Get the current view state, consisting of signal values and/or data sets. | ||
| * @param {object} [options] - Options flags indicating which state to export. | ||
| * If unspecified, all signals and data sets will be exported. | ||
| * @param {function(string, Operator):boolean} [options.signals] - Optional | ||
| * predicate function for testing if a signal should be included in the | ||
| * exported state. If unspecified, all signals will be included, except for | ||
| * those named 'parent' or those which refer to a Transform value. | ||
| * @param {function(string, object):boolean} [options.data] - Optional | ||
| * predicate function for testing if a data set's input should be included | ||
| * in the exported state. If unspecified, all data sets that have been | ||
| * explicitly modified will be included. | ||
| * @param {boolean} [options.recurse=true] - Flag indicating if the exported | ||
| * state should recursively include state from group mark sub-contexts. | ||
| * @return {object} - An object containing the exported state values. | ||
| */ | ||
| function getState(options) { | ||
| return this._runtime.getState(options || { | ||
| data: dataTest, | ||
| signals: signalTest, | ||
| recurse: true | ||
| }); | ||
| } | ||
| function dataTest(name, data) { | ||
| return data.modified && isArray(data.input.value) && name.indexOf('_:vega:_'); | ||
| } | ||
| function signalTest(name, op) { | ||
| return !(name === 'parent' || op instanceof transforms.proxy); | ||
| } | ||
| /** | ||
| * Sets the current view state and updates the view by invoking run. | ||
| * @param {object} state - A state object containing signal and/or | ||
| * data set values, following the format used by the getState method. | ||
| * @return {View} - This view instance. | ||
| */ | ||
| function setState(state) { | ||
| this.runAsync(null, v => { | ||
| v._trigger = false; | ||
| v._runtime.setState(state); | ||
| }, v => { | ||
| v._trigger = true; | ||
| }); | ||
| return this; | ||
| } | ||
| function timer (callback, delay) { | ||
| function tick(elapsed) { | ||
| callback({ | ||
| timestamp: Date.now(), | ||
| elapsed: elapsed | ||
| }); | ||
| } | ||
| this._timers.push(interval(tick, delay)); | ||
| } | ||
| function defaultTooltip (handler, event, item, value) { | ||
| const el = handler.element(); | ||
| if (el) el.setAttribute('title', formatTooltip(value)); | ||
| } | ||
| function formatTooltip(value) { | ||
| return value == null ? '' : isArray(value) ? formatArray(value) : isObject(value) && !isDate(value) ? formatObject(value) : value + ''; | ||
| } | ||
| function formatObject(obj) { | ||
| return Object.keys(obj).map(key => { | ||
| const v = obj[key]; | ||
| return key + ': ' + (isArray(v) ? formatArray(v) : formatValue(v)); | ||
| }).join('\n'); | ||
| } | ||
| function formatArray(value) { | ||
| return '[' + value.map(formatValue).join(', ') + ']'; | ||
| } | ||
| function formatValue(value) { | ||
| return isArray(value) ? '[\u2026]' : isObject(value) && !isDate(value) ? '{\u2026}' : value; | ||
| } | ||
| /** | ||
| * Create a new View instance from a Vega dataflow runtime specification. | ||
| * The generated View will not immediately be ready for display. Callers | ||
| * should also invoke the initialize method (e.g., to set the parent | ||
| * DOM element in browser-based deployment) and then invoke the run | ||
| * method to evaluate the dataflow graph. Rendering will automatically | ||
| * be performed upon dataflow runs. | ||
| * @constructor | ||
| * @param {object} spec - The Vega dataflow runtime specification. | ||
| */ | ||
| function View(spec, options) { | ||
| const view = this; | ||
| options = options || {}; | ||
| Dataflow.call(view); | ||
| if (options.loader) view.loader(options.loader); | ||
| if (options.logger) view.logger(options.logger); | ||
| if (options.logLevel != null) view.logLevel(options.logLevel); | ||
| if (options.locale || spec.locale) { | ||
| const loc = extend({}, spec.locale, options.locale); | ||
| view.locale(locale(loc.number, loc.time)); | ||
| } | ||
| view._el = null; | ||
| view._elBind = null; | ||
| view._renderType = options.renderer || RenderType.Canvas; | ||
| view._scenegraph = new Scenegraph(); | ||
| const root = view._scenegraph.root; // initialize renderer, handler and event management | ||
| view._renderer = null; | ||
| view._tooltip = options.tooltip || defaultTooltip, view._redraw = true; | ||
| view._handler = new CanvasHandler().scene(root); | ||
| view._globalCursor = false; | ||
| view._preventDefault = false; | ||
| view._timers = []; | ||
| view._eventListeners = []; | ||
| view._resizeListeners = []; // initialize event configuration | ||
| view._eventConfig = initializeEventConfig(spec.eventConfig); | ||
| view.globalCursor(view._eventConfig.globalCursor); // initialize dataflow graph | ||
| const ctx = runtime(view, spec, options.expr); | ||
| view._runtime = ctx; | ||
| view._signals = ctx.signals; | ||
| view._bind = (spec.bindings || []).map(_ => ({ | ||
| state: null, | ||
| param: extend({}, _) | ||
| })); // initialize scenegraph | ||
| if (ctx.root) ctx.root.set(root); | ||
| root.source = ctx.data.root.input; | ||
| view.pulse(ctx.data.root.input, view.changeset().insert(root.items)); // initialize view size | ||
| view._width = view.width(); | ||
| view._height = view.height(); | ||
| view._viewWidth = viewWidth(view, view._width); | ||
| view._viewHeight = viewHeight(view, view._height); | ||
| view._origin = [0, 0]; | ||
| view._resize = 0; | ||
| view._autosize = 1; | ||
| initializeResize(view); // initialize background color | ||
| background(view); // initialize cursor | ||
| cursor(view); // initialize view description | ||
| view.description(spec.description); // initialize hover proessing, if requested | ||
| if (options.hover) view.hover(); // initialize DOM container(s) and renderer | ||
| if (options.container) view.initialize(options.container, options.bind); | ||
| } | ||
| function lookupSignal(view, name) { | ||
| return hasOwnProperty(view._signals, name) ? view._signals[name] : error('Unrecognized signal name: ' + stringValue(name)); | ||
| } | ||
| function findOperatorHandler(op, handler) { | ||
| const h = (op._targets || []).filter(op => op._update && op._update.handler === handler); | ||
| return h.length ? h[0] : null; | ||
| } | ||
| function addOperatorListener(view, name, op, handler) { | ||
| let h = findOperatorHandler(op, handler); | ||
| if (!h) { | ||
| h = trap(view, () => handler(name, op.value)); | ||
| h.handler = handler; | ||
| view.on(op, null, h); | ||
| } | ||
| return view; | ||
| } | ||
| function removeOperatorListener(view, op, handler) { | ||
| const h = findOperatorHandler(op, handler); | ||
| if (h) op._targets.remove(h); | ||
| return view; | ||
| } | ||
| inherits(View, Dataflow, { | ||
| // -- DATAFLOW / RENDERING ---- | ||
| async evaluate(encode, prerun, postrun) { | ||
| // evaluate dataflow and prerun | ||
| await Dataflow.prototype.evaluate.call(this, encode, prerun); // render as needed | ||
| if (this._redraw || this._resize) { | ||
| try { | ||
| if (this._renderer) { | ||
| if (this._resize) { | ||
| this._resize = 0; | ||
| resizeRenderer(this); | ||
| } | ||
| await this._renderer.renderAsync(this._scenegraph.root); | ||
| } | ||
| this._redraw = false; | ||
| } catch (e) { | ||
| this.error(e); | ||
| } | ||
| } // evaluate postrun | ||
| if (postrun) asyncCallback(this, postrun); | ||
| return this; | ||
| }, | ||
| dirty(item) { | ||
| this._redraw = true; | ||
| this._renderer && this._renderer.dirty(item); | ||
| }, | ||
| // -- GET / SET ---- | ||
| description(text) { | ||
| if (arguments.length) { | ||
| const desc = text != null ? text + '' : null; | ||
| if (desc !== this._desc) ariaLabel(this._el, this._desc = desc); | ||
| return this; | ||
| } | ||
| return this._desc; | ||
| }, | ||
| container() { | ||
| return this._el; | ||
| }, | ||
| scenegraph() { | ||
| return this._scenegraph; | ||
| }, | ||
| origin() { | ||
| return this._origin.slice(); | ||
| }, | ||
| signal(name, value, options) { | ||
| const op = lookupSignal(this, name); | ||
| return arguments.length === 1 ? op.value : this.update(op, value, options); | ||
| }, | ||
| width(_) { | ||
| return arguments.length ? this.signal('width', _) : this.signal('width'); | ||
| }, | ||
| height(_) { | ||
| return arguments.length ? this.signal('height', _) : this.signal('height'); | ||
| }, | ||
| padding(_) { | ||
| return arguments.length ? this.signal('padding', padding(_)) : padding(this.signal('padding')); | ||
| }, | ||
| autosize(_) { | ||
| return arguments.length ? this.signal('autosize', _) : this.signal('autosize'); | ||
| }, | ||
| background(_) { | ||
| return arguments.length ? this.signal('background', _) : this.signal('background'); | ||
| }, | ||
| renderer(type) { | ||
| if (!arguments.length) return this._renderType; | ||
| if (!renderModule(type)) error('Unrecognized renderer type: ' + type); | ||
| if (type !== this._renderType) { | ||
| this._renderType = type; | ||
| this._resetRenderer(); | ||
| } | ||
| return this; | ||
| }, | ||
| tooltip(handler) { | ||
| if (!arguments.length) return this._tooltip; | ||
| if (handler !== this._tooltip) { | ||
| this._tooltip = handler; | ||
| this._resetRenderer(); | ||
| } | ||
| return this; | ||
| }, | ||
| loader(loader) { | ||
| if (!arguments.length) return this._loader; | ||
| if (loader !== this._loader) { | ||
| Dataflow.prototype.loader.call(this, loader); | ||
| this._resetRenderer(); | ||
| } | ||
| return this; | ||
| }, | ||
| resize() { | ||
| // set flag to perform autosize | ||
| this._autosize = 1; // touch autosize signal to ensure top-level ViewLayout runs | ||
| return this.touch(lookupSignal(this, 'autosize')); | ||
| }, | ||
| _resetRenderer() { | ||
| if (this._renderer) { | ||
| this._renderer = null; | ||
| this.initialize(this._el, this._elBind); | ||
| } | ||
| }, | ||
| // -- SIZING ---- | ||
| _resizeView: resizeView, | ||
| // -- EVENT HANDLING ---- | ||
| addEventListener(type, handler, options) { | ||
| let callback = handler; | ||
| if (!(options && options.trap === false)) { | ||
| // wrap callback in error handler | ||
| callback = trap(this, handler); | ||
| callback.raw = handler; | ||
| } | ||
| this._handler.on(type, callback); | ||
| return this; | ||
| }, | ||
| removeEventListener(type, handler) { | ||
| var handlers = this._handler.handlers(type), | ||
| i = handlers.length, | ||
| h, | ||
| t; // search registered handlers, remove if match found | ||
| while (--i >= 0) { | ||
| t = handlers[i].type; | ||
| h = handlers[i].handler; | ||
| if (type === t && (handler === h || handler === h.raw)) { | ||
| this._handler.off(t, h); | ||
| break; | ||
| } | ||
| } | ||
| return this; | ||
| }, | ||
| addResizeListener(handler) { | ||
| const l = this._resizeListeners; | ||
| if (l.indexOf(handler) < 0) { | ||
| // add handler if it isn't already registered | ||
| // note: error trapping handled elsewhere, so | ||
| // no need to wrap handlers here | ||
| l.push(handler); | ||
| } | ||
| return this; | ||
| }, | ||
| removeResizeListener(handler) { | ||
| var l = this._resizeListeners, | ||
| i = l.indexOf(handler); | ||
| if (i >= 0) { | ||
| l.splice(i, 1); | ||
| } | ||
| return this; | ||
| }, | ||
| addSignalListener(name, handler) { | ||
| return addOperatorListener(this, name, lookupSignal(this, name), handler); | ||
| }, | ||
| removeSignalListener(name, handler) { | ||
| return removeOperatorListener(this, lookupSignal(this, name), handler); | ||
| }, | ||
| addDataListener(name, handler) { | ||
| return addOperatorListener(this, name, dataref(this, name).values, handler); | ||
| }, | ||
| removeDataListener(name, handler) { | ||
| return removeOperatorListener(this, dataref(this, name).values, handler); | ||
| }, | ||
| globalCursor(_) { | ||
| if (arguments.length) { | ||
| if (this._globalCursor !== !!_) { | ||
| const prev = setCursor(this, null); // clear previous cursor | ||
| this._globalCursor = !!_; | ||
| if (prev) setCursor(this, prev); // swap cursor | ||
| } | ||
| return this; | ||
| } else { | ||
| return this._globalCursor; | ||
| } | ||
| }, | ||
| preventDefault(_) { | ||
| if (arguments.length) { | ||
| this._preventDefault = _; | ||
| return this; | ||
| } else { | ||
| return this._preventDefault; | ||
| } | ||
| }, | ||
| timer, | ||
| events, | ||
| finalize, | ||
| hover, | ||
| // -- DATA ---- | ||
| data, | ||
| change, | ||
| insert, | ||
| remove, | ||
| // -- SCALES -- | ||
| scale, | ||
| // -- INITIALIZATION ---- | ||
| initialize, | ||
| // -- HEADLESS RENDERING ---- | ||
| toImageURL: renderToImageURL, | ||
| toCanvas: renderToCanvas, | ||
| toSVG: renderToSVG, | ||
| // -- SAVE / RESTORE STATE ---- | ||
| getState, | ||
| setState | ||
| }); | ||
| export { View }; |
| export { default } from '../../rollup.config'; |
+337
-350
@@ -10,2 +10,3 @@ (function (global, factory) { | ||
| const el = view.container(); | ||
| if (el) { | ||
@@ -16,12 +17,9 @@ el.setAttribute('role', 'graphics-document'); | ||
| } | ||
| } | ||
| } // update aria-label if we have a DOM container element | ||
| // update aria-label if we have a DOM container element | ||
| function ariaLabel(el, desc) { | ||
| if (el) desc == null | ||
| ? el.removeAttribute('aria-label') | ||
| : el.setAttribute('aria-label', desc); | ||
| if (el) desc == null ? el.removeAttribute('aria-label') : el.setAttribute('aria-label', desc); | ||
| } | ||
| function background(view) { | ||
| function background (view) { | ||
| // respond to background signal | ||
@@ -32,8 +30,9 @@ view.add(null, _ => { | ||
| return _.bg; | ||
| }, { bg: view._signals.background }); | ||
| }, { | ||
| bg: view._signals.background | ||
| }); | ||
| } | ||
| const Default = 'default'; | ||
| function cursor(view) { | ||
| function cursor (view) { | ||
| // get cursor signal, add to dataflow if needed | ||
@@ -43,19 +42,15 @@ const cursor = view._signals.cursor || (view._signals.cursor = view.add({ | ||
| item: null | ||
| })); | ||
| })); // evaluate cursor on each mousemove event | ||
| // evaluate cursor on each mousemove event | ||
| view.on(view.events('view', 'mousemove'), cursor, | ||
| (_, event) => { | ||
| const value = cursor.value, | ||
| user = value ? (vegaUtil.isString(value) ? value : value.user) : Default, | ||
| item = event.item && event.item.cursor || null; | ||
| view.on(view.events('view', 'mousemove'), cursor, (_, event) => { | ||
| const value = cursor.value, | ||
| user = value ? vegaUtil.isString(value) ? value : value.user : Default, | ||
| item = event.item && event.item.cursor || null; | ||
| return value && user === value.user && item == value.item ? value : { | ||
| user: user, | ||
| item: item | ||
| }; | ||
| }); // when cursor signal updates, set visible cursor | ||
| return (value && user === value.user && item == value.item) | ||
| ? value | ||
| : {user: user, item: item}; | ||
| } | ||
| ); | ||
| // when cursor signal updates, set visible cursor | ||
| view.add(null, function(_) { | ||
| view.add(null, function (_) { | ||
| let user = _.cursor, | ||
@@ -69,17 +64,13 @@ item = this.value; | ||
| setCursor(view, user && user !== Default ? user : (item || user)); | ||
| setCursor(view, user && user !== Default ? user : item || user); | ||
| return item; | ||
| }, {cursor: cursor}); | ||
| }, { | ||
| cursor: cursor | ||
| }); | ||
| } | ||
| function setCursor(view, cursor) { | ||
| const el = view.globalCursor() | ||
| ? (typeof document !== 'undefined' && document.body) | ||
| : view.container(); | ||
| const el = view.globalCursor() ? typeof document !== 'undefined' && document.body : view.container(); | ||
| if (el) { | ||
| return cursor == null | ||
| ? el.style.removeProperty('cursor') | ||
| : (el.style.cursor = cursor); | ||
| return cursor == null ? el.style.removeProperty('cursor') : el.style.cursor = cursor; | ||
| } | ||
@@ -90,14 +81,12 @@ } | ||
| var data = view._runtime.data; | ||
| if (!vegaUtil.hasOwnProperty(data, name)) { | ||
| vegaUtil.error('Unrecognized data set: ' + name); | ||
| } | ||
| return data[name]; | ||
| } | ||
| function data(name, values) { | ||
| return arguments.length < 2 | ||
| ? dataref(this, name).values.value | ||
| : change.call(this, name, vegaDataflow.changeset().remove(vegaUtil.truthy).insert(values)); | ||
| return arguments.length < 2 ? dataref(this, name).values.value : change.call(this, name, vegaDataflow.changeset().remove(vegaUtil.truthy).insert(values)); | ||
| } | ||
| function change(name, changes) { | ||
@@ -107,11 +96,10 @@ if (!vegaDataflow.isChangeSet(changes)) { | ||
| } | ||
| var dataset = dataref(this, name); | ||
| const dataset = dataref(this, name); | ||
| dataset.modified = true; | ||
| return this.pulse(dataset.input, changes); | ||
| } | ||
| function insert(name, _) { | ||
| return change.call(this, name, vegaDataflow.changeset().insert(_)); | ||
| } | ||
| function remove(name, _) { | ||
@@ -125,3 +113,2 @@ return change.call(this, name, vegaDataflow.changeset().remove(_)); | ||
| } | ||
| function height(view) { | ||
@@ -131,12 +118,7 @@ var padding = view.padding(); | ||
| } | ||
| function offset(view) { | ||
| var padding = view.padding(), | ||
| origin = view._origin; | ||
| return [ | ||
| padding.left + origin[0], | ||
| padding.top + origin[1] | ||
| ]; | ||
| return [padding.left + origin[0], padding.top + origin[1]]; | ||
| } | ||
| function resizeRenderer(view) { | ||
@@ -148,3 +130,5 @@ var origin = offset(view), | ||
| view._renderer.background(view.background()); | ||
| view._renderer.resize(w, h, origin); | ||
| view._handler.origin(origin); | ||
@@ -187,6 +171,9 @@ | ||
| */ | ||
| function eventExtend(view, event, item) { | ||
| var r = view._renderer, | ||
| function eventExtend (view, event, item) { | ||
| var r = view._renderer, | ||
| el = r && r.canvas(), | ||
| p, e, translate; | ||
| p, | ||
| e, | ||
| translate; | ||
@@ -208,10 +195,12 @@ if (el) { | ||
| function extension(view, item, point) { | ||
| var itemGroup = item | ||
| ? item.mark.marktype === 'group' ? item : item.mark.group | ||
| : null; | ||
| const itemGroup = item ? item.mark.marktype === 'group' ? item : item.mark.group : null; | ||
| function group(name) { | ||
| var g = itemGroup, i; | ||
| var g = itemGroup, | ||
| i; | ||
| if (name) for (i = item; i; i = i.mark.group) { | ||
| if (i.mark.name === name) { g = i; break; } | ||
| if (i.mark.name === name) { | ||
| g = i; | ||
| break; | ||
| } | ||
| } | ||
@@ -224,4 +213,4 @@ return g && g.mark && g.mark.interactive ? g : {}; | ||
| if (vegaUtil.isString(item)) item = group(item); | ||
| const p = point.slice(); | ||
| var p = point.slice(); | ||
| while (item) { | ||
@@ -232,2 +221,3 @@ p[0] -= item.x || 0; | ||
| } | ||
| return p; | ||
@@ -237,8 +227,12 @@ } | ||
| return { | ||
| view: vegaUtil.constant(view), | ||
| item: vegaUtil.constant(item || {}), | ||
| view: vegaUtil.constant(view), | ||
| item: vegaUtil.constant(item || {}), | ||
| group: group, | ||
| xy: xy, | ||
| x: function(item) { return xy(item)[0]; }, | ||
| y: function(item) { return xy(item)[1]; } | ||
| xy: xy, | ||
| x: function (item) { | ||
| return xy(item)[0]; | ||
| }, | ||
| y: function (item) { | ||
| return xy(item)[1]; | ||
| } | ||
| }; | ||
@@ -250,4 +244,5 @@ } | ||
| WINDOW = 'window', | ||
| NO_TRAP = {trap: false}; | ||
| NO_TRAP = { | ||
| trap: false | ||
| }; | ||
| /** | ||
@@ -258,4 +253,7 @@ * Initialize event handling configuration. | ||
| */ | ||
| function initializeEventConfig(config) { | ||
| const events = vegaUtil.extend({defaults: {}}, config); | ||
| const events = vegaUtil.extend({ | ||
| defaults: {} | ||
| }, config); | ||
@@ -270,3 +268,2 @@ const unpack = (obj, keys) => { | ||
| unpack(events, ['view', 'window', 'selector']); | ||
| return events; | ||
@@ -279,8 +276,3 @@ } | ||
| allow = def.allow; | ||
| return prevent === false || allow === true ? false | ||
| : prevent === true || allow === false ? true | ||
| : prevent ? prevent[type] | ||
| : allow ? !allow[type] | ||
| : view.preventDefault(); | ||
| return prevent === false || allow === true ? false : prevent === true || allow === false ? true : prevent ? prevent[type] : allow ? !allow[type] : view.preventDefault(); | ||
| } | ||
@@ -291,3 +283,3 @@ | ||
| if (rule === false || (vegaUtil.isObject(rule) && !rule[type])) { | ||
| if (rule === false || vegaUtil.isObject(rule) && !rule[type]) { | ||
| view.warn(`Blocked ${key} ${type} event listener.`); | ||
@@ -299,3 +291,2 @@ return false; | ||
| } | ||
| /** | ||
@@ -308,13 +299,16 @@ * Create a new event stream from an event source. | ||
| */ | ||
| function events(source, type, filter) { | ||
| var view = this, | ||
| s = new vegaDataflow.EventStream(filter), | ||
| send = function(e, item) { | ||
| view.runAsync(null, () => { | ||
| if (source === VIEW && prevent(view, type)) { | ||
| e.preventDefault(); | ||
| } | ||
| s.receive(eventExtend(view, e, item)); | ||
| }); | ||
| }, | ||
| send = function (e, item) { | ||
| view.runAsync(null, () => { | ||
| if (source === VIEW && prevent(view, type)) { | ||
| e.preventDefault(); | ||
| } | ||
| s.receive(eventExtend(view, e, item)); | ||
| }); | ||
| }, | ||
| sources; | ||
@@ -326,5 +320,3 @@ | ||
| } | ||
| } | ||
| else if (source === VIEW) { | ||
| } else if (source === VIEW) { | ||
| if (permit(view, 'view', type)) { | ||
@@ -334,5 +326,3 @@ // send traps errors, so use {trap: false} option | ||
| } | ||
| } | ||
| else { | ||
| } else { | ||
| if (source === WINDOW) { | ||
@@ -351,3 +341,3 @@ if (permit(view, 'window', type) && typeof window !== 'undefined') { | ||
| } else { | ||
| for (var i=0, n=sources.length; i<n; ++i) { | ||
| for (var i = 0, n = sources.length; i < n; ++i) { | ||
| sources[i].addEventListener(type, send); | ||
@@ -357,3 +347,3 @@ } | ||
| view._eventListeners.push({ | ||
| type: type, | ||
| type: type, | ||
| sources: sources, | ||
@@ -378,27 +368,14 @@ handler: send | ||
| function invoke(name) { | ||
| return function(_, event) { | ||
| return event.vega.view() | ||
| .changeset() | ||
| .encode(event.item, name); | ||
| return function (_, event) { | ||
| return event.vega.view().changeset().encode(event.item, name); | ||
| }; | ||
| } | ||
| function hover(hoverSet, leaveSet) { | ||
| function hover (hoverSet, leaveSet) { | ||
| hoverSet = [hoverSet || 'hover']; | ||
| leaveSet = [leaveSet || 'update', hoverSet[0]]; | ||
| leaveSet = [leaveSet || 'update', hoverSet[0]]; // invoke hover set upon mouseover | ||
| // invoke hover set upon mouseover | ||
| this.on( | ||
| this.events('view', 'mouseover', itemFilter), | ||
| markTarget, | ||
| invoke(hoverSet) | ||
| ); | ||
| this.on(this.events('view', 'mouseover', itemFilter), markTarget, invoke(hoverSet)); // invoke leave set upon mouseout | ||
| // invoke leave set upon mouseout | ||
| this.on( | ||
| this.events('view', 'mouseout', itemFilter), | ||
| markTarget, | ||
| invoke(leaveSet) | ||
| ); | ||
| this.on(this.events('view', 'mouseout', itemFilter), markTarget, invoke(leaveSet)); | ||
| return this; | ||
@@ -413,9 +390,11 @@ } | ||
| */ | ||
| function finalize() { | ||
| function finalize () { | ||
| var tooltip = this._tooltip, | ||
| timers = this._timers, | ||
| listeners = this._eventListeners, | ||
| n, m, e; | ||
| n, | ||
| m, | ||
| e; | ||
| n = timers.length; | ||
| n = timers.length; | ||
| while (--n >= 0) { | ||
@@ -426,5 +405,7 @@ timers[n].stop(); | ||
| n = listeners.length; | ||
| while (--n >= 0) { | ||
| e = listeners[n]; | ||
| m = e.sources.length; | ||
| while (--m >= 0) { | ||
@@ -442,5 +423,7 @@ e.sources[m].removeEventListener(e.type, e.handler); | ||
| function element(tag, attr, text) { | ||
| var el = document.createElement(tag); | ||
| for (var key in attr) el.setAttribute(key, attr[key]); | ||
| function element (tag, attr, text) { | ||
| const el = document.createElement(tag); | ||
| for (const key in attr) el.setAttribute(key, attr[key]); | ||
| if (text != null) el.textContent = text; | ||
@@ -453,3 +436,2 @@ return el; | ||
| RadioClass = 'vega-bind-radio'; | ||
| /** | ||
@@ -468,5 +450,5 @@ * Bind a signal to an external HTML input element. The resulting two-way | ||
| */ | ||
| function bind(view, el, binding) { | ||
| function bind (view, el, binding) { | ||
| if (!el) return; | ||
| const param = binding.param; | ||
@@ -489,2 +471,3 @@ let bind = binding.state; | ||
| }; | ||
| if (param.debounce) { | ||
@@ -499,5 +482,3 @@ bind.update = vegaUtil.debounce(param.debounce, bind.update); | ||
| view.on(view._signals[param.signal], null, () => { | ||
| bind.source | ||
| ? (bind.source = false) | ||
| : bind.set(view.signal(param.signal)); | ||
| bind.source ? bind.source = false : bind.set(view.signal(param.signal)); | ||
| }); | ||
@@ -509,26 +490,33 @@ bind.active = true; | ||
| } | ||
| /** | ||
| * Generate an HTML input form element and bind it to a signal. | ||
| */ | ||
| function generate(bind, el, param, value) { | ||
| const div = element('div', {'class': BindClass}); | ||
| const div = element('div', { | ||
| 'class': BindClass | ||
| }); | ||
| const wrapper = param.input === 'radio' ? div : div.appendChild(element('label')); | ||
| wrapper.appendChild(element('span', { | ||
| 'class': NameClass | ||
| }, param.name || param.signal)); | ||
| el.appendChild(div); | ||
| let input = form; | ||
| const wrapper = param.input === 'radio' | ||
| ? div | ||
| : div.appendChild(element('label')); | ||
| switch (param.input) { | ||
| case 'checkbox': | ||
| input = checkbox; | ||
| break; | ||
| wrapper.appendChild(element('span', | ||
| {'class': NameClass}, | ||
| (param.name || param.signal) | ||
| )); | ||
| case 'select': | ||
| input = select; | ||
| break; | ||
| el.appendChild(div); | ||
| case 'radio': | ||
| input = radio; | ||
| break; | ||
| let input = form; | ||
| switch (param.input) { | ||
| case 'checkbox': input = checkbox; break; | ||
| case 'select': input = select; break; | ||
| case 'radio': input = radio; break; | ||
| case 'range': input = range; break; | ||
| case 'range': | ||
| input = range; | ||
| break; | ||
| } | ||
@@ -538,3 +526,2 @@ | ||
| } | ||
| /** | ||
@@ -544,2 +531,4 @@ * Generates an arbitrary input form element. | ||
| */ | ||
| function form(bind, el, param, value) { | ||
@@ -553,51 +542,57 @@ const node = element('input'); | ||
| } | ||
| node.setAttribute('name', param.signal); | ||
| node.value = value; | ||
| el.appendChild(node); | ||
| node.addEventListener('input', () => bind.update(node.value)); | ||
| bind.elements = [node]; | ||
| bind.elements = [node]; | ||
| bind.set = value => node.value = value; | ||
| } | ||
| /** | ||
| * Generates a checkbox input element. | ||
| */ | ||
| function checkbox(bind, el, param, value) { | ||
| const attr = {type: 'checkbox', name: param.signal}; | ||
| const attr = { | ||
| type: 'checkbox', | ||
| name: param.signal | ||
| }; | ||
| if (value) attr.checked = true; | ||
| const node = element('input', attr); | ||
| el.appendChild(node); | ||
| node.addEventListener('change', () => bind.update(node.checked)); | ||
| bind.elements = [node]; | ||
| bind.elements = [node]; | ||
| bind.set = value => node.checked = !!value || null; | ||
| } | ||
| /** | ||
| * Generates a selection list input element. | ||
| */ | ||
| function select(bind, el, param, value) { | ||
| const node = element('select', {name: param.signal}), | ||
| const node = element('select', { | ||
| name: param.signal | ||
| }), | ||
| labels = param.labels || []; | ||
| param.options.forEach((option, i) => { | ||
| const attr = {value: option}; | ||
| const attr = { | ||
| value: option | ||
| }; | ||
| if (valuesEqual(option, value)) attr.selected = true; | ||
| node.appendChild(element('option', attr, (labels[i] || option)+'')); | ||
| node.appendChild(element('option', attr, (labels[i] || option) + '')); | ||
| }); | ||
| el.appendChild(node); | ||
| node.addEventListener('change', () => { | ||
| bind.update(param.options[node.selectedIndex]); | ||
| }); | ||
| bind.elements = [node]; | ||
| bind.elements = [node]; | ||
| bind.set = value => { | ||
| for (let i = 0, n = param.options.length; i < n; ++i) { | ||
| if (valuesEqual(param.options[i], value)) { | ||
| node.selectedIndex = i; return; | ||
| node.selectedIndex = i; | ||
| return; | ||
| } | ||
@@ -607,27 +602,25 @@ } | ||
| } | ||
| /** | ||
| * Generates a radio button group. | ||
| */ | ||
| function radio(bind, el, param, value) { | ||
| const group = element('span', {'class': RadioClass}), | ||
| const group = element('span', { | ||
| 'class': RadioClass | ||
| }), | ||
| labels = param.labels || []; | ||
| el.appendChild(group); | ||
| bind.elements = param.options.map((option, i) => { | ||
| const attr = { | ||
| type: 'radio', | ||
| name: param.signal, | ||
| type: 'radio', | ||
| name: param.signal, | ||
| value: option | ||
| }; | ||
| if (valuesEqual(option, value)) attr.checked = true; | ||
| const input = element('input', attr); | ||
| input.addEventListener('change', () => bind.update(option)); | ||
| const label = element('label', {}, (labels[i] || option)+''); | ||
| const label = element('label', {}, (labels[i] || option) + ''); | ||
| label.prepend(input); | ||
| group.appendChild(label); | ||
| return input; | ||
@@ -639,2 +632,3 @@ }); | ||
| n = nodes.length; | ||
| for (let i = 0; i < n; ++i) { | ||
@@ -645,24 +639,21 @@ if (valuesEqual(nodes[i].value, value)) nodes[i].checked = true; | ||
| } | ||
| /** | ||
| * Generates a slider input element. | ||
| */ | ||
| function range(bind, el, param, value) { | ||
| value = value !== undefined ? value : ((+param.max) + (+param.min)) / 2; | ||
| value = value !== undefined ? value : (+param.max + +param.min) / 2; | ||
| const max = param.max != null ? param.max : Math.max(100, +value) || 100, | ||
| min = param.min || Math.min(0, max, +value) || 0, | ||
| step = param.step || d3Array.tickStep(min, max, 100); | ||
| const node = element('input', { | ||
| type: 'range', | ||
| name: param.signal, | ||
| min: min, | ||
| max: max, | ||
| step: step | ||
| type: 'range', | ||
| name: param.signal, | ||
| min: min, | ||
| max: max, | ||
| step: step | ||
| }); | ||
| node.value = value; | ||
| const span = element('span', {}, +value); | ||
| el.appendChild(node); | ||
@@ -674,9 +665,9 @@ el.appendChild(span); | ||
| bind.update(+node.value); | ||
| }; | ||
| }; // subscribe to both input and change | ||
| // subscribe to both input and change | ||
| node.addEventListener('input', update); | ||
| node.addEventListener('change', update); | ||
| bind.elements = [node]; | ||
| bind.elements = [node]; | ||
| bind.set = value => { | ||
@@ -689,14 +680,12 @@ node.value = value; | ||
| function valuesEqual(a, b) { | ||
| return a === b || (a+'' === b+''); | ||
| return a === b || a + '' === b + ''; | ||
| } | ||
| function initializeRenderer(view, r, el, constructor, scaleFactor, opt) { | ||
| function initializeRenderer (view, r, el, constructor, scaleFactor, opt) { | ||
| r = r || new constructor(view.loader()); | ||
| return r | ||
| .initialize(el, width(view), height(view), offset(view), scaleFactor, opt) | ||
| .background(view.background()); | ||
| return r.initialize(el, width(view), height(view), offset(view), scaleFactor, opt).background(view.background()); | ||
| } | ||
| function trap(view, fn) { | ||
| return !fn ? null : function() { | ||
| function trap (view, fn) { | ||
| return !fn ? null : function () { | ||
| try { | ||
@@ -710,9 +699,6 @@ fn.apply(this, arguments); | ||
| function initializeHandler(view, prevHandler, el, constructor) { | ||
| function initializeHandler (view, prevHandler, el, constructor) { | ||
| // instantiate scenegraph handler | ||
| var handler = new constructor(view.loader(), trap(view, view.tooltip())) | ||
| .scene(view.scenegraph().root) | ||
| .initialize(el, offset(view), view); | ||
| const handler = new constructor(view.loader(), trap(view, view.tooltip())).scene(view.scenegraph().root).initialize(el, offset(view), view); // transfer event handlers | ||
| // transfer event handlers | ||
| if (prevHandler) { | ||
@@ -727,29 +713,24 @@ prevHandler.handlers().forEach(h => { | ||
| function initialize(el, elBind) { | ||
| function initialize (el, elBind) { | ||
| const view = this, | ||
| type = view._renderType, | ||
| config = view._eventConfig.bind, | ||
| module = vegaScenegraph.renderModule(type); | ||
| module = vegaScenegraph.renderModule(type); // containing dom element | ||
| // containing dom element | ||
| el = view._el = el ? lookup(view, el) : null; | ||
| el = view._el = el ? lookup(view, el) : null; // initialize aria attributes | ||
| // initialize aria attributes | ||
| initializeAria(view); | ||
| initializeAria(view); // select appropriate renderer & handler | ||
| // select appropriate renderer & handler | ||
| if (!module) view.error('Unrecognized renderer type: ' + type); | ||
| const Handler = module.handler || vegaScenegraph.CanvasHandler, | ||
| Renderer = (el ? module.renderer : module.headless); | ||
| Renderer = el ? module.renderer : module.headless; // initialize renderer and input handler | ||
| // initialize renderer and input handler | ||
| view._renderer = !Renderer ? null | ||
| : initializeRenderer(view, view._renderer, el, Renderer); | ||
| view._renderer = !Renderer ? null : initializeRenderer(view, view._renderer, el, Renderer); | ||
| view._handler = initializeHandler(view, view._handler, el, Handler); | ||
| view._redraw = true; | ||
| view._redraw = true; // initialize signal bindings | ||
| // initialize signal bindings | ||
| if (el && config !== 'none') { | ||
| elBind = elBind ? (view._elBind = lookup(view, elBind)) | ||
| : el.appendChild(element('form', {'class': 'vega-bindings'})); | ||
| elBind = elBind ? view._elBind = lookup(view, elBind) : el.appendChild(element('form', { | ||
| 'class': 'vega-bindings' | ||
| })); | ||
@@ -774,2 +755,3 @@ view._bind.forEach(_ => { | ||
| el = document.querySelector(el); | ||
| if (!el) { | ||
@@ -784,2 +766,3 @@ view.error('Signal bind element not found: ' + el); | ||
| } | ||
| if (el) { | ||
@@ -793,2 +776,3 @@ try { | ||
| } | ||
| return el; | ||
@@ -799,13 +783,16 @@ } | ||
| const paddingObject = _ => ({top: _, bottom: _, left: _, right: _}); | ||
| const paddingObject = _ => ({ | ||
| top: _, | ||
| bottom: _, | ||
| left: _, | ||
| right: _ | ||
| }); | ||
| function padding(_) { | ||
| return vegaUtil.isObject(_) | ||
| ? { | ||
| top: number(_.top), | ||
| bottom: number(_.bottom), | ||
| left: number(_.left), | ||
| right: number(_.right) | ||
| } | ||
| : paddingObject(number(_)); | ||
| function padding (_) { | ||
| return vegaUtil.isObject(_) ? { | ||
| top: number(_.top), | ||
| bottom: number(_.bottom), | ||
| left: number(_.left), | ||
| right: number(_.right) | ||
| } : paddingObject(number(_)); | ||
| } | ||
@@ -818,11 +805,9 @@ | ||
| */ | ||
| async function renderHeadless(view, type, scaleFactor, opt) { | ||
| async function renderHeadless (view, type, scaleFactor, opt) { | ||
| const module = vegaScenegraph.renderModule(type), | ||
| ctr = module && module.headless; | ||
| if (!ctr) vegaUtil.error('Unrecognized renderer type: ' + type); | ||
| await view.runAsync(); | ||
| return initializeRenderer(view, null, null, ctr, scaleFactor, opt) | ||
| .renderAsync(view._scenegraph.root); | ||
| return initializeRenderer(view, null, null, ctr, scaleFactor, opt).renderAsync(view._scenegraph.root); | ||
| } | ||
@@ -839,3 +824,4 @@ | ||
| */ | ||
| async function renderToImageURL(type, scaleFactor) { | ||
| async function renderToImageURL (type, scaleFactor) { | ||
| if (type !== vegaScenegraph.RenderType.Canvas && type !== vegaScenegraph.RenderType.SVG && type !== vegaScenegraph.RenderType.PNG) { | ||
@@ -846,9 +832,9 @@ vegaUtil.error('Unrecognized image type: ' + type); | ||
| const r = await renderHeadless(this, type, scaleFactor); | ||
| return type === vegaScenegraph.RenderType.SVG | ||
| ? toBlobURL(r.svg(), 'image/svg+xml') | ||
| : r.canvas().toDataURL('image/png'); | ||
| return type === vegaScenegraph.RenderType.SVG ? toBlobURL(r.svg(), 'image/svg+xml') : r.canvas().toDataURL('image/png'); | ||
| } | ||
| function toBlobURL(data, mime) { | ||
| var blob = new Blob([data], {type: mime}); | ||
| const blob = new Blob([data], { | ||
| type: mime | ||
| }); | ||
| return window.URL.createObjectURL(blob); | ||
@@ -862,3 +848,4 @@ } | ||
| */ | ||
| async function renderToCanvas(scaleFactor, opt) { | ||
| async function renderToCanvas (scaleFactor, opt) { | ||
| const r = await renderHeadless(this, vegaScenegraph.RenderType.Canvas, scaleFactor, opt); | ||
@@ -873,3 +860,4 @@ return r.canvas(); | ||
| */ | ||
| async function renderToSVG(scaleFactor) { | ||
| async function renderToSVG (scaleFactor) { | ||
| const r = await renderHeadless(this, vegaScenegraph.RenderType.SVG, scaleFactor); | ||
@@ -879,3 +867,3 @@ return r.svg(); | ||
| function runtime(view, spec, expr) { | ||
| function runtime (view, spec, expr) { | ||
| return vegaRuntime.context(view, vegaDataflow.transforms, vegaFunctions.functionContext, expr).parse(spec); | ||
@@ -886,5 +874,7 @@ } | ||
| var scales = this._runtime.scales; | ||
| if (!vegaUtil.hasOwnProperty(scales, name)) { | ||
| vegaUtil.error('Unrecognized scale or projection: ' + name); | ||
| } | ||
| return scales[name].value; | ||
@@ -896,4 +886,5 @@ } | ||
| Padding = 'padding', | ||
| Skip = {skip: true}; | ||
| Skip = { | ||
| skip: true | ||
| }; | ||
| function viewWidth(view, width) { | ||
@@ -904,3 +895,2 @@ var a = view.autosize(), | ||
| } | ||
| function viewHeight(view, height) { | ||
@@ -911,3 +901,2 @@ var a = view.autosize(), | ||
| } | ||
| function initializeResize(view) { | ||
@@ -921,28 +910,25 @@ var s = view._signals, | ||
| view._autosize = view._resize = 1; | ||
| } | ||
| } // respond to width signal | ||
| // respond to width signal | ||
| view._resizeWidth = view.add(null, | ||
| _ => { | ||
| view._width = _.size; | ||
| view._viewWidth = viewWidth(view, _.size); | ||
| resetSize(); | ||
| }, | ||
| {size: w} | ||
| ); | ||
| // respond to height signal | ||
| view._resizeHeight = view.add(null, | ||
| _ => { | ||
| view._height = _.size; | ||
| view._viewHeight = viewHeight(view, _.size); | ||
| resetSize(); | ||
| }, | ||
| {size: h} | ||
| ); | ||
| view._resizeWidth = view.add(null, _ => { | ||
| view._width = _.size; | ||
| view._viewWidth = viewWidth(view, _.size); | ||
| resetSize(); | ||
| }, { | ||
| size: w | ||
| }); // respond to height signal | ||
| // respond to padding signal | ||
| var resizePadding = view.add(null, resetSize, {pad: p}); | ||
| view._resizeHeight = view.add(null, _ => { | ||
| view._height = _.size; | ||
| view._viewHeight = viewHeight(view, _.size); | ||
| resetSize(); | ||
| }, { | ||
| size: h | ||
| }); // respond to padding signal | ||
| // set rank to run immediately after source signal | ||
| const resizePadding = view.add(null, resetSize, { | ||
| pad: p | ||
| }); // set rank to run immediately after source signal | ||
| view._resizeWidth.rank = w.rank + 1; | ||
@@ -952,43 +938,44 @@ view._resizeHeight.rank = h.rank + 1; | ||
| } | ||
| function resizeView(viewWidth, viewHeight, width, height, origin, auto) { | ||
| this.runAfter(view => { | ||
| var rerun = 0; | ||
| let rerun = 0; // reset autosize flag | ||
| // reset autosize flag | ||
| view._autosize = 0; | ||
| view._autosize = 0; // width value changed: update signal, skip resize op | ||
| // width value changed: update signal, skip resize op | ||
| if (view.width() !== width) { | ||
| rerun = 1; | ||
| view.signal(Width, width, Skip); // set width, skip update calc | ||
| view._resizeWidth.skip(true); // skip width resize handler | ||
| } | ||
| // height value changed: update signal, skip resize op | ||
| } // height value changed: update signal, skip resize op | ||
| if (view.height() !== height) { | ||
| rerun = 1; | ||
| view.signal(Height, height, Skip); // set height, skip update calc | ||
| view._resizeHeight.skip(true); // skip height resize handler | ||
| } | ||
| // view width changed: update view property, set resize flag | ||
| } // view width changed: update view property, set resize flag | ||
| if (view._viewWidth !== viewWidth) { | ||
| view._resize = 1; | ||
| view._viewWidth = viewWidth; | ||
| } | ||
| } // view height changed: update view property, set resize flag | ||
| // view height changed: update view property, set resize flag | ||
| if (view._viewHeight !== viewHeight) { | ||
| view._resize = 1; | ||
| view._viewHeight = viewHeight; | ||
| } | ||
| } // origin changed: update view property, set resize flag | ||
| // origin changed: update view property, set resize flag | ||
| if (view._origin[0] !== origin[0] || view._origin[1] !== origin[1]) { | ||
| view._resize = 1; | ||
| view._origin = origin; | ||
| } | ||
| } // run dataflow on width/height signal change | ||
| // run dataflow on width/height signal change | ||
| if (rerun) view.run('enter'); | ||
@@ -1015,5 +1002,6 @@ if (auto) view.runAfter(v => v.resize()); | ||
| */ | ||
| function getState(options) { | ||
| return this._runtime.getState(options || { | ||
| data: dataTest, | ||
| data: dataTest, | ||
| signals: signalTest, | ||
@@ -1025,5 +1013,3 @@ recurse: true | ||
| function dataTest(name, data) { | ||
| return data.modified | ||
| && vegaUtil.isArray(data.input.value) | ||
| && name.indexOf('_:vega:_'); | ||
| return data.modified && vegaUtil.isArray(data.input.value) && name.indexOf('_:vega:_'); | ||
| } | ||
@@ -1034,3 +1020,2 @@ | ||
| } | ||
| /** | ||
@@ -1042,19 +1027,28 @@ * Sets the current view state and updates the view by invoking run. | ||
| */ | ||
| function setState(state) { | ||
| this.runAsync(null, | ||
| v => { v._trigger = false; v._runtime.setState(state); }, | ||
| v => { v._trigger = true; } | ||
| ); | ||
| this.runAsync(null, v => { | ||
| v._trigger = false; | ||
| v._runtime.setState(state); | ||
| }, v => { | ||
| v._trigger = true; | ||
| }); | ||
| return this; | ||
| } | ||
| function timer(callback, delay) { | ||
| function timer (callback, delay) { | ||
| function tick(elapsed) { | ||
| callback({timestamp: Date.now(), elapsed: elapsed}); | ||
| callback({ | ||
| timestamp: Date.now(), | ||
| elapsed: elapsed | ||
| }); | ||
| } | ||
| this._timers.push(d3Timer.interval(tick, delay)); | ||
| } | ||
| function defaultTooltip(handler, event, item, value) { | ||
| var el = handler.element(); | ||
| function defaultTooltip (handler, event, item, value) { | ||
| const el = handler.element(); | ||
| if (el) el.setAttribute('title', formatTooltip(value)); | ||
@@ -1064,6 +1058,3 @@ } | ||
| function formatTooltip(value) { | ||
| return value == null ? '' | ||
| : vegaUtil.isArray(value) ? formatArray(value) | ||
| : vegaUtil.isObject(value) && !vegaUtil.isDate(value) ? formatObject(value) | ||
| : value + ''; | ||
| return value == null ? '' : vegaUtil.isArray(value) ? formatArray(value) : vegaUtil.isObject(value) && !vegaUtil.isDate(value) ? formatObject(value) : value + ''; | ||
| } | ||
@@ -1073,3 +1064,3 @@ | ||
| return Object.keys(obj).map(key => { | ||
| var v = obj[key]; | ||
| const v = obj[key]; | ||
| return key + ': ' + (vegaUtil.isArray(v) ? formatArray(v) : formatValue(v)); | ||
@@ -1084,5 +1075,3 @@ }).join('\n'); | ||
| function formatValue(value) { | ||
| return vegaUtil.isArray(value) ? '[\u2026]' | ||
| : vegaUtil.isObject(value) && !vegaUtil.isDate(value) ? '{\u2026}' | ||
| : value; | ||
| return vegaUtil.isArray(value) ? '[\u2026]' : vegaUtil.isObject(value) && !vegaUtil.isDate(value) ? '{\u2026}' : value; | ||
| } | ||
@@ -1100,6 +1089,6 @@ | ||
| */ | ||
| function View(spec, options) { | ||
| const view = this; | ||
| options = options || {}; | ||
| vegaDataflow.Dataflow.call(view); | ||
@@ -1109,2 +1098,3 @@ if (options.loader) view.loader(options.loader); | ||
| if (options.logLevel != null) view.logLevel(options.logLevel); | ||
| if (options.locale || spec.locale) { | ||
@@ -1119,8 +1109,6 @@ const loc = vegaUtil.extend({}, spec.locale, options.locale); | ||
| view._scenegraph = new vegaScenegraph.Scenegraph(); | ||
| const root = view._scenegraph.root; | ||
| const root = view._scenegraph.root; // initialize renderer, handler and event management | ||
| // initialize renderer, handler and event management | ||
| view._renderer = null; | ||
| view._tooltip = options.tooltip || defaultTooltip, | ||
| view._redraw = true; | ||
| view._tooltip = options.tooltip || defaultTooltip, view._redraw = true; | ||
| view._handler = new vegaScenegraph.CanvasHandler().scene(root); | ||
@@ -1131,9 +1119,7 @@ view._globalCursor = false; | ||
| view._eventListeners = []; | ||
| view._resizeListeners = []; | ||
| view._resizeListeners = []; // initialize event configuration | ||
| // initialize event configuration | ||
| view._eventConfig = initializeEventConfig(spec.eventConfig); | ||
| view.globalCursor(view._eventConfig.globalCursor); | ||
| view.globalCursor(view._eventConfig.globalCursor); // initialize dataflow graph | ||
| // initialize dataflow graph | ||
| const ctx = runtime(view, spec, options.expr); | ||
@@ -1145,13 +1131,8 @@ view._runtime = ctx; | ||
| param: vegaUtil.extend({}, _) | ||
| })); | ||
| })); // initialize scenegraph | ||
| // initialize scenegraph | ||
| if (ctx.root) ctx.root.set(root); | ||
| root.source = ctx.data.root.input; | ||
| view.pulse( | ||
| ctx.data.root.input, | ||
| view.changeset().insert(root.items) | ||
| ); | ||
| view.pulse(ctx.data.root.input, view.changeset().insert(root.items)); // initialize view size | ||
| // initialize view size | ||
| view._width = view.width(); | ||
@@ -1164,17 +1145,12 @@ view._height = view.height(); | ||
| view._autosize = 1; | ||
| initializeResize(view); | ||
| initializeResize(view); // initialize background color | ||
| // initialize background color | ||
| background(view); | ||
| background(view); // initialize cursor | ||
| // initialize cursor | ||
| cursor(view); | ||
| cursor(view); // initialize view description | ||
| // initialize view description | ||
| view.description(spec.description); | ||
| view.description(spec.description); // initialize hover proessing, if requested | ||
| // initialize hover proessing, if requested | ||
| if (options.hover) view.hover(); | ||
| if (options.hover) view.hover(); // initialize DOM container(s) and renderer | ||
| // initialize DOM container(s) and renderer | ||
| if (options.container) view.initialize(options.container, options.bind); | ||
@@ -1184,10 +1160,7 @@ } | ||
| function lookupSignal(view, name) { | ||
| return vegaUtil.hasOwnProperty(view._signals, name) | ||
| ? view._signals[name] | ||
| : vegaUtil.error('Unrecognized signal name: ' + vegaUtil.stringValue(name)); | ||
| return vegaUtil.hasOwnProperty(view._signals, name) ? view._signals[name] : vegaUtil.error('Unrecognized signal name: ' + vegaUtil.stringValue(name)); | ||
| } | ||
| function findOperatorHandler(op, handler) { | ||
| const h = (op._targets || []) | ||
| .filter(op => op._update && op._update.handler === handler); | ||
| const h = (op._targets || []).filter(op => op._update && op._update.handler === handler); | ||
| return h.length ? h[0] : null; | ||
@@ -1197,3 +1170,4 @@ } | ||
| function addOperatorListener(view, name, op, handler) { | ||
| var h = findOperatorHandler(op, handler); | ||
| let h = findOperatorHandler(op, handler); | ||
| if (!h) { | ||
@@ -1204,2 +1178,3 @@ h = trap(view, () => handler(name, op.value)); | ||
| } | ||
| return view; | ||
@@ -1209,3 +1184,3 @@ } | ||
| function removeOperatorListener(view, op, handler) { | ||
| var h = findOperatorHandler(op, handler); | ||
| const h = findOperatorHandler(op, handler); | ||
| if (h) op._targets.remove(h); | ||
@@ -1217,8 +1192,6 @@ return view; | ||
| // -- DATAFLOW / RENDERING ---- | ||
| async evaluate(encode, prerun, postrun) { | ||
| // evaluate dataflow and prerun | ||
| await vegaDataflow.Dataflow.prototype.evaluate.call(this, encode, prerun); | ||
| await vegaDataflow.Dataflow.prototype.evaluate.call(this, encode, prerun); // render as needed | ||
| // render as needed | ||
| if (this._redraw || this._resize) { | ||
@@ -1231,4 +1204,6 @@ try { | ||
| } | ||
| await this._renderer.renderAsync(this._scenegraph.root); | ||
| } | ||
| this._redraw = false; | ||
@@ -1238,7 +1213,6 @@ } catch (e) { | ||
| } | ||
| } | ||
| } // evaluate postrun | ||
| // evaluate postrun | ||
| if (postrun) vegaDataflow.asyncCallback(this, postrun); | ||
| return this; | ||
@@ -1253,9 +1227,9 @@ }, | ||
| // -- GET / SET ---- | ||
| description(text) { | ||
| if (arguments.length) { | ||
| const desc = text != null ? (text + '') : null; | ||
| const desc = text != null ? text + '' : null; | ||
| if (desc !== this._desc) ariaLabel(this._el, this._desc = desc); | ||
| return this; | ||
| } | ||
| return this._desc; | ||
@@ -1277,6 +1251,4 @@ }, | ||
| signal(name, value, options) { | ||
| var op = lookupSignal(this, name); | ||
| return arguments.length === 1 | ||
| ? op.value | ||
| : this.update(op, value, options); | ||
| const op = lookupSignal(this, name); | ||
| return arguments.length === 1 ? op.value : this.update(op, value, options); | ||
| }, | ||
@@ -1293,5 +1265,3 @@ | ||
| padding(_) { | ||
| return arguments.length | ||
| ? this.signal('padding', padding(_)) | ||
| : padding(this.signal('padding')); | ||
| return arguments.length ? this.signal('padding', padding(_)) : padding(this.signal('padding')); | ||
| }, | ||
@@ -1310,6 +1280,9 @@ | ||
| if (!vegaScenegraph.renderModule(type)) vegaUtil.error('Unrecognized renderer type: ' + type); | ||
| if (type !== this._renderType) { | ||
| this._renderType = type; | ||
| this._resetRenderer(); | ||
| } | ||
| return this; | ||
@@ -1320,6 +1293,9 @@ }, | ||
| if (!arguments.length) return this._tooltip; | ||
| if (handler !== this._tooltip) { | ||
| this._tooltip = handler; | ||
| this._resetRenderer(); | ||
| } | ||
| return this; | ||
@@ -1330,6 +1306,9 @@ }, | ||
| if (!arguments.length) return this._loader; | ||
| if (loader !== this._loader) { | ||
| vegaDataflow.Dataflow.prototype.loader.call(this, loader); | ||
| this._resetRenderer(); | ||
| } | ||
| return this; | ||
@@ -1340,4 +1319,4 @@ }, | ||
| // set flag to perform autosize | ||
| this._autosize = 1; | ||
| // touch autosize signal to ensure top-level ViewLayout runs | ||
| this._autosize = 1; // touch autosize signal to ensure top-level ViewLayout runs | ||
| return this.touch(lookupSignal(this, 'autosize')); | ||
@@ -1357,5 +1336,5 @@ }, | ||
| // -- EVENT HANDLING ---- | ||
| addEventListener(type, handler, options) { | ||
| let callback = handler; | ||
| addEventListener(type, handler, options) { | ||
| var callback = handler; | ||
| if (!(options && options.trap === false)) { | ||
@@ -1366,3 +1345,5 @@ // wrap callback in error handler | ||
| } | ||
| this._handler.on(type, callback); | ||
| return this; | ||
@@ -1373,13 +1354,18 @@ }, | ||
| var handlers = this._handler.handlers(type), | ||
| i = handlers.length, h, t; | ||
| i = handlers.length, | ||
| h, | ||
| t; // search registered handlers, remove if match found | ||
| // search registered handlers, remove if match found | ||
| while (--i >= 0) { | ||
| t = handlers[i].type; | ||
| h = handlers[i].handler; | ||
| if (type === t && (handler === h || handler === h.raw)) { | ||
| this._handler.off(t, h); | ||
| break; | ||
| } | ||
| } | ||
| return this; | ||
@@ -1389,3 +1375,4 @@ }, | ||
| addResizeListener(handler) { | ||
| var l = this._resizeListeners; | ||
| const l = this._resizeListeners; | ||
| if (l.indexOf(handler) < 0) { | ||
@@ -1397,2 +1384,3 @@ // add handler if it isn't already registered | ||
| } | ||
| return this; | ||
@@ -1404,5 +1392,7 @@ }, | ||
| i = l.indexOf(handler); | ||
| if (i >= 0) { | ||
| l.splice(i, 1); | ||
| } | ||
| return this; | ||
@@ -1431,5 +1421,7 @@ }, | ||
| const prev = setCursor(this, null); // clear previous cursor | ||
| this._globalCursor = !!_; | ||
| if (prev) setCursor(this, prev); // swap cursor | ||
| } | ||
| return this; | ||
@@ -1454,3 +1446,2 @@ } else { | ||
| hover, | ||
| // -- DATA ---- | ||
@@ -1461,9 +1452,6 @@ data, | ||
| remove, | ||
| // -- SCALES -- | ||
| scale, | ||
| // -- INITIALIZATION ---- | ||
| initialize, | ||
| // -- HEADLESS RENDERING ---- | ||
@@ -1473,3 +1461,2 @@ toImageURL: renderToImageURL, | ||
| toSVG: renderToSVG, | ||
| // -- SAVE / RESTORE STATE ---- | ||
@@ -1476,0 +1463,0 @@ getState, |
@@ -1,1 +0,2 @@ | ||
| !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vega-util"),require("vega-dataflow"),require("vega-scenegraph"),require("d3-array"),require("vega-functions"),require("vega-runtime"),require("d3-timer"),require("vega-format")):"function"==typeof define&&define.amd?define(["exports","vega-util","vega-dataflow","vega-scenegraph","d3-array","vega-functions","vega-runtime","d3-timer","vega-format"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).vega={},e.vega,e.vega,e.vega,e.d3,e.vega,e.vega,e.d3,e.vega)}(this,(function(e,t,n,r,i,a,s,o,u){"use strict";function l(e,t){e&&(null==t?e.removeAttribute("aria-label"):e.setAttribute("aria-label",t))}const d="default";function c(e,t){const n=e.globalCursor()?"undefined"!=typeof document&&document.body:e.container();if(n)return null==t?n.style.removeProperty("cursor"):n.style.cursor=t}function h(e,n){var r=e._runtime.data;return t.hasOwnProperty(r,n)||t.error("Unrecognized data set: "+n),r[n]}function g(e,r){n.isChangeSet(r)||t.error("Second argument to changes must be a changeset.");var i=h(this,e);return i.modified=!0,this.pulse(i.input,r)}function p(e){var t=e.padding();return Math.max(0,e._viewWidth+t.left+t.right)}function f(e){var t=e.padding();return Math.max(0,e._viewHeight+t.top+t.bottom)}function v(e){var t=e.padding(),n=e._origin;return[t.left+n[0],t.top+n[1]]}function _(e,n,i){var a,s,o,u=e._renderer,l=u&&u.canvas();return l&&(o=v(e),s=n.changedTouches?n.changedTouches[0]:n,(a=r.point(s,l))[0]-=o[0],a[1]-=o[1]),n.dataflow=e,n.item=i,n.vega=function(e,n,r){var i=n?"group"===n.mark.marktype?n:n.mark.group:null;function a(e){var t,r=i;if(e)for(t=n;t;t=t.mark.group)if(t.mark.name===e){r=t;break}return r&&r.mark&&r.mark.interactive?r:{}}function s(e){if(!e)return r;t.isString(e)&&(e=a(e));for(var n=r.slice();e;)n[0]-=e.x||0,n[1]-=e.y||0,e=e.mark&&e.mark.group;return n}return{view:t.constant(e),item:t.constant(n||{}),group:a,xy:s,x:function(e){return s(e)[0]},y:function(e){return s(e)[1]}}}(e,i,a),n}const m="view",y={trap:!1};function w(e,n,r){const i=e._eventConfig&&e._eventConfig[n];return!(!1===i||t.isObject(i)&&!i[r])||(e.warn(`Blocked ${n} ${r} event listener.`),!1)}function b(e){return e.item}function z(e){return e.item.mark.source}function k(e){return function(t,n){return n.vega.view().changeset().encode(n.item,e)}}function C(e,t,n){var r=document.createElement(e);for(var i in t)r.setAttribute(i,t[i]);return null!=n&&(r.textContent=n),r}const L="vega-bind",x="vega-bind-name";function A(e,n,r){if(!n)return;const i=r.param;let a=r.state;return a||(a=r.state={elements:null,active:!1,set:null,update:t=>{t!==e.signal(i.signal)&&e.runAsync(null,()=>{a.source=!0,e.signal(i.signal,t)})}},i.debounce&&(a.update=t.debounce(i.debounce,a.update))),function(e,t,n,r){const i=C("div",{class:L}),a="radio"===n.input?i:i.appendChild(C("label"));a.appendChild(C("span",{class:x},n.name||n.signal)),t.appendChild(i);let s=S;switch(n.input){case"checkbox":s=E;break;case"select":s=D;break;case"radio":s=R;break;case"range":s=T}s(e,a,n,r)}(a,n,i,e.signal(i.signal)),a.active||(e.on(e._signals[i.signal],null,()=>{a.source?a.source=!1:a.set(e.signal(i.signal))}),a.active=!0),a}function S(e,t,n,r){const i=C("input");for(const e in n)"signal"!==e&&"element"!==e&&i.setAttribute("input"===e?"type":e,n[e]);i.setAttribute("name",n.signal),i.value=r,t.appendChild(i),i.addEventListener("input",()=>e.update(i.value)),e.elements=[i],e.set=e=>i.value=e}function E(e,t,n,r){const i={type:"checkbox",name:n.signal};r&&(i.checked=!0);const a=C("input",i);t.appendChild(a),a.addEventListener("change",()=>e.update(a.checked)),e.elements=[a],e.set=e=>a.checked=!!e||null}function D(e,t,n,r){const i=C("select",{name:n.signal}),a=n.labels||[];n.options.forEach((e,t)=>{const n={value:e};O(e,r)&&(n.selected=!0),i.appendChild(C("option",n,(a[t]||e)+""))}),t.appendChild(i),i.addEventListener("change",()=>{e.update(n.options[i.selectedIndex])}),e.elements=[i],e.set=e=>{for(let t=0,r=n.options.length;t<r;++t)if(O(n.options[t],e))return void(i.selectedIndex=t)}}function R(e,t,n,r){const i=C("span",{class:"vega-bind-radio"}),a=n.labels||[];t.appendChild(i),e.elements=n.options.map((t,s)=>{const o={type:"radio",name:n.signal,value:t};O(t,r)&&(o.checked=!0);const u=C("input",o);u.addEventListener("change",()=>e.update(t));const l=C("label",{},(a[s]||t)+"");return l.prepend(u),i.appendChild(l),u}),e.set=t=>{const n=e.elements,r=n.length;for(let e=0;e<r;++e)O(n[e].value,t)&&(n[e].checked=!0)}}function T(e,t,n,r){r=void 0!==r?r:(+n.max+ +n.min)/2;const a=null!=n.max?n.max:Math.max(100,+r)||100,s=n.min||Math.min(0,a,+r)||0,o=n.step||i.tickStep(s,a,100),u=C("input",{type:"range",name:n.signal,min:s,max:a,step:o});u.value=r;const l=C("span",{},+r);t.appendChild(u),t.appendChild(l);const d=()=>{l.textContent=u.value,e.update(+u.value)};u.addEventListener("input",d),u.addEventListener("change",d),e.elements=[u],e.set=e=>{u.value=e,l.textContent=e}}function O(e,t){return e===t||e+""==t+""}function j(e,t,n,r,i,a){return(t=t||new r(e.loader())).initialize(n,p(e),f(e),v(e),i,a).background(e.background())}function H(e,t){return t?function(){try{t.apply(this,arguments)}catch(t){e.error(t)}}:null}function U(e,t){if("string"==typeof t){if("undefined"==typeof document)return e.error("DOM document instance not found."),null;if(!(t=document.querySelector(t)))return e.error("Signal bind element not found: "+t),null}if(t)try{t.innerHTML=""}catch(n){t=null,e.error(n)}return t}const q=e=>+e||0;function M(e){return t.isObject(e)?{top:q(e.top),bottom:q(e.bottom),left:q(e.left),right:q(e.right)}:(e=>({top:e,bottom:e,left:e,right:e}))(q(e))}async function W(e,n,i,a){const s=r.renderModule(n),o=s&&s.headless;return o||t.error("Unrecognized renderer type: "+n),await e.runAsync(),j(e,null,null,o,i,a).renderAsync(e._scenegraph.root)}var V="width",P="height",B="padding",G={skip:!0};function I(e,t){var n=e.autosize(),r=e.padding();return t-(n&&n.contains===B?r.left+r.right:0)}function $(e,t){var n=e.autosize(),r=e.padding();return t-(n&&n.contains===B?r.top+r.bottom:0)}function N(e,n){return n.modified&&t.isArray(n.input.value)&&e.indexOf("_:vega:_")}function F(e,t){return!("parent"===e||t instanceof n.transforms.proxy)}function J(e,n,r,i){var a=e.element();a&&a.setAttribute("title",function(e){return null==e?"":t.isArray(e)?K(e):t.isObject(e)&&!t.isDate(e)?(n=e,Object.keys(n).map(e=>{var r=n[e];return e+": "+(t.isArray(r)?K(r):Q(r))}).join("\n")):e+"";var n}(i))}function K(e){return"["+e.map(Q).join(", ")+"]"}function Q(e){return t.isArray(e)?"[…]":t.isObject(e)&&!t.isDate(e)?"{…}":e}function X(e,i){const o=this;if(i=i||{},n.Dataflow.call(o),i.loader&&o.loader(i.loader),i.logger&&o.logger(i.logger),null!=i.logLevel&&o.logLevel(i.logLevel),i.locale||e.locale){const n=t.extend({},e.locale,i.locale);o.locale(u.locale(n.number,n.time))}o._el=null,o._elBind=null,o._renderType=i.renderer||r.RenderType.Canvas,o._scenegraph=new r.Scenegraph;const l=o._scenegraph.root;o._renderer=null,o._tooltip=i.tooltip||J,o._redraw=!0,o._handler=(new r.CanvasHandler).scene(l),o._globalCursor=!1,o._preventDefault=!1,o._timers=[],o._eventListeners=[],o._resizeListeners=[],o._eventConfig=function(e){const n=t.extend({defaults:{}},e),r=(e,n)=>{n.forEach(n=>{t.isArray(e[n])&&(e[n]=t.toSet(e[n]))})};return r(n.defaults,["prevent","allow"]),r(n,["view","window","selector"]),n}(e.eventConfig),o.globalCursor(o._eventConfig.globalCursor);const h=function(e,t,r){return s.context(e,n.transforms,a.functionContext,r).parse(t)}(o,e,i.expr);o._runtime=h,o._signals=h.signals,o._bind=(e.bindings||[]).map(e=>({state:null,param:t.extend({},e)})),h.root&&h.root.set(l),l.source=h.data.root.input,o.pulse(h.data.root.input,o.changeset().insert(l.items)),o._width=o.width(),o._height=o.height(),o._viewWidth=I(o,o._width),o._viewHeight=$(o,o._height),o._origin=[0,0],o._resize=0,o._autosize=1,function(e){var t=e._signals,n=t.width,r=t.height,i=t.padding;function a(){e._autosize=e._resize=1}e._resizeWidth=e.add(null,t=>{e._width=t.size,e._viewWidth=I(e,t.size),a()},{size:n}),e._resizeHeight=e.add(null,t=>{e._height=t.size,e._viewHeight=$(e,t.size),a()},{size:r});var s=e.add(null,a,{pad:i});e._resizeWidth.rank=n.rank+1,e._resizeHeight.rank=r.rank+1,s.rank=i.rank+1}(o),function(e){e.add(null,t=>(e._background=t.bg,e._resize=1,t.bg),{bg:e._signals.background})}(o),function(e){const n=e._signals.cursor||(e._signals.cursor=e.add({user:d,item:null}));e.on(e.events("view","mousemove"),n,(e,r)=>{const i=n.value,a=i?t.isString(i)?i:i.user:d,s=r.item&&r.item.cursor||null;return i&&a===i.user&&s==i.item?i:{user:a,item:s}}),e.add(null,(function(n){let r=n.cursor,i=this.value;return t.isString(r)||(i=r.item,r=r.user),c(e,r&&r!==d?r:i||r),i}),{cursor:n})}(o),o.description(e.description),i.hover&&o.hover(),i.container&&o.initialize(i.container,i.bind)}function Y(e,n){return t.hasOwnProperty(e._signals,n)?e._signals[n]:t.error("Unrecognized signal name: "+t.stringValue(n))}function Z(e,t){const n=(e._targets||[]).filter(e=>e._update&&e._update.handler===t);return n.length?n[0]:null}function ee(e,t,n,r){var i=Z(n,r);return i||((i=H(e,()=>r(t,n.value))).handler=r,e.on(n,null,i)),e}function te(e,t,n){var r=Z(t,n);return r&&t._targets.remove(r),e}t.inherits(X,n.Dataflow,{async evaluate(e,t,r){if(await n.Dataflow.prototype.evaluate.call(this,e,t),this._redraw||this._resize)try{this._renderer&&(this._resize&&(this._resize=0,a=v(i=this),s=p(i),o=f(i),i._renderer.background(i.background()),i._renderer.resize(s,o,a),i._handler.origin(a),i._resizeListeners.forEach(e=>{try{e(s,o)}catch(e){i.error(e)}})),await this._renderer.renderAsync(this._scenegraph.root)),this._redraw=!1}catch(e){this.error(e)}var i,a,s,o;return r&&n.asyncCallback(this,r),this},dirty(e){this._redraw=!0,this._renderer&&this._renderer.dirty(e)},description(e){if(arguments.length){const t=null!=e?e+"":null;return t!==this._desc&&l(this._el,this._desc=t),this}return this._desc},container(){return this._el},scenegraph(){return this._scenegraph},origin(){return this._origin.slice()},signal(e,t,n){var r=Y(this,e);return 1===arguments.length?r.value:this.update(r,t,n)},width(e){return arguments.length?this.signal("width",e):this.signal("width")},height(e){return arguments.length?this.signal("height",e):this.signal("height")},padding(e){return arguments.length?this.signal("padding",M(e)):M(this.signal("padding"))},autosize(e){return arguments.length?this.signal("autosize",e):this.signal("autosize")},background(e){return arguments.length?this.signal("background",e):this.signal("background")},renderer(e){return arguments.length?(r.renderModule(e)||t.error("Unrecognized renderer type: "+e),e!==this._renderType&&(this._renderType=e,this._resetRenderer()),this):this._renderType},tooltip(e){return arguments.length?(e!==this._tooltip&&(this._tooltip=e,this._resetRenderer()),this):this._tooltip},loader(e){return arguments.length?(e!==this._loader&&(n.Dataflow.prototype.loader.call(this,e),this._resetRenderer()),this):this._loader},resize(){return this._autosize=1,this.touch(Y(this,"autosize"))},_resetRenderer(){this._renderer&&(this._renderer=null,this.initialize(this._el,this._elBind))},_resizeView:function(e,t,n,r,i,a){this.runAfter(s=>{var o=0;s._autosize=0,s.width()!==n&&(o=1,s.signal(V,n,G),s._resizeWidth.skip(!0)),s.height()!==r&&(o=1,s.signal(P,r,G),s._resizeHeight.skip(!0)),s._viewWidth!==e&&(s._resize=1,s._viewWidth=e),s._viewHeight!==t&&(s._resize=1,s._viewHeight=t),s._origin[0]===i[0]&&s._origin[1]===i[1]||(s._resize=1,s._origin=i),o&&s.run("enter"),a&&s.runAfter(e=>e.resize())},!1,1)},addEventListener(e,t,n){var r=t;return n&&!1===n.trap||((r=H(this,t)).raw=t),this._handler.on(e,r),this},removeEventListener(e,t){for(var n,r,i=this._handler.handlers(e),a=i.length;--a>=0;)if(r=i[a].type,n=i[a].handler,e===r&&(t===n||t===n.raw)){this._handler.off(r,n);break}return this},addResizeListener(e){var t=this._resizeListeners;return t.indexOf(e)<0&&t.push(e),this},removeResizeListener(e){var t=this._resizeListeners,n=t.indexOf(e);return n>=0&&t.splice(n,1),this},addSignalListener(e,t){return ee(this,e,Y(this,e),t)},removeSignalListener(e,t){return te(this,Y(this,e),t)},addDataListener(e,t){return ee(this,e,h(this,e).values,t)},removeDataListener(e,t){return te(this,h(this,e).values,t)},globalCursor(e){if(arguments.length){if(this._globalCursor!==!!e){const t=c(this,null);this._globalCursor=!!e,t&&c(this,t)}return this}return this._globalCursor},preventDefault(e){return arguments.length?(this._preventDefault=e,this):this._preventDefault},timer:function(e,t){this._timers.push(o.interval((function(t){e({timestamp:Date.now(),elapsed:t})}),t))},events:function(e,t,r){var i,a=this,s=new n.EventStream(r),o=function(n,r){a.runAsync(null,()=>{e===m&&function(e,t){var n=e._eventConfig.defaults,r=n.prevent,i=n.allow;return!1!==r&&!0!==i&&(!0===r||!1===i||(r?r[t]:i?!i[t]:e.preventDefault()))}(a,t)&&n.preventDefault(),s.receive(_(a,n,r))})};if("timer"===e)w(a,"timer",t)&&a.timer(o,t);else if(e===m)w(a,"view",t)&&a.addEventListener(t,o,y);else if("window"===e?w(a,"window",t)&&"undefined"!=typeof window&&(i=[window]):"undefined"!=typeof document&&w(a,"selector",t)&&(i=document.querySelectorAll(e)),i){for(var u=0,l=i.length;u<l;++u)i[u].addEventListener(t,o);a._eventListeners.push({type:t,sources:i,handler:o})}else a.warn("Can not resolve event source: "+e);return s},finalize:function(){var e,t,n,r=this._tooltip,i=this._timers,a=this._eventListeners;for(e=i.length;--e>=0;)i[e].stop();for(e=a.length;--e>=0;)for(t=(n=a[e]).sources.length;--t>=0;)n.sources[t].removeEventListener(n.type,n.handler);return r&&r.call(this,this._handler,null,null,null),this},hover:function(e,t){return t=[t||"update",(e=[e||"hover"])[0]],this.on(this.events("view","mouseover",b),z,k(e)),this.on(this.events("view","mouseout",b),z,k(t)),this},data:function(e,r){return arguments.length<2?h(this,e).values.value:g.call(this,e,n.changeset().remove(t.truthy).insert(r))},change:g,insert:function(e,t){return g.call(this,e,n.changeset().insert(t))},remove:function(e,t){return g.call(this,e,n.changeset().remove(t))},scale:function(e){var n=this._runtime.scales;return t.hasOwnProperty(n,e)||t.error("Unrecognized scale or projection: "+e),n[e].value},initialize:function(e,t){const n=this,i=n._renderType,a=n._eventConfig.bind,s=r.renderModule(i);e=n._el=e?U(n,e):null,function(e){const t=e.container();t&&(t.setAttribute("role","graphics-document"),t.setAttribute("aria-roleDescription","visualization"),l(t,e.description()))}(n),s||n.error("Unrecognized renderer type: "+i);const o=s.handler||r.CanvasHandler,u=e?s.renderer:s.headless;return n._renderer=u?j(n,n._renderer,e,u):null,n._handler=function(e,t,n,r){var i=new r(e.loader(),H(e,e.tooltip())).scene(e.scenegraph().root).initialize(n,v(e),e);return t&&t.handlers().forEach(e=>{i.on(e.type,e.handler)}),i}(n,n._handler,e,o),n._redraw=!0,e&&"none"!==a&&(t=t?n._elBind=U(n,t):e.appendChild(C("form",{class:"vega-bindings"})),n._bind.forEach(e=>{e.param.element&&"container"!==a&&(e.element=U(n,e.param.element))}),n._bind.forEach(e=>{A(n,e.element||t,e)})),n},toImageURL:async function(e,n){e!==r.RenderType.Canvas&&e!==r.RenderType.SVG&&e!==r.RenderType.PNG&&t.error("Unrecognized image type: "+e);const i=await W(this,e,n);return e===r.RenderType.SVG?function(e,t){var n=new Blob([e],{type:t});return window.URL.createObjectURL(n)}(i.svg(),"image/svg+xml"):i.canvas().toDataURL("image/png")},toCanvas:async function(e,t){return(await W(this,r.RenderType.Canvas,e,t)).canvas()},toSVG:async function(e){return(await W(this,r.RenderType.SVG,e)).svg()},getState:function(e){return this._runtime.getState(e||{data:N,signals:F,recurse:!0})},setState:function(e){return this.runAsync(null,t=>{t._trigger=!1,t._runtime.setState(e)},e=>{e._trigger=!0}),this}}),e.View=X,Object.defineProperty(e,"__esModule",{value:!0})})); | ||
| var vega=function(e,t,n,r,i,s,a){"use strict";function o(e,t){e&&(null==t?e.removeAttribute("aria-label"):e.setAttribute("aria-label",t))}const l="default";function u(e,t){const n=e.globalCursor()?"undefined"!=typeof document&&document.body:e.container();if(n)return null==t?n.style.removeProperty("cursor"):n.style.cursor=t}function c(e,n){var r=e._runtime.data;return t.hasOwnProperty(r,n)||t.error("Unrecognized data set: "+n),r[n]}function d(e,r){n.isChangeSet(r)||t.error("Second argument to changes must be a changeset.");const i=c(this,e);return i.modified=!0,this.pulse(i.input,r)}function h(e){var t=e.padding();return Math.max(0,e._viewWidth+t.left+t.right)}function g(e){var t=e.padding();return Math.max(0,e._viewHeight+t.top+t.bottom)}function p(e){var t=e.padding(),n=e._origin;return[t.left+n[0],t.top+n[1]]}function f(e,n,i){var s,a,o,l=e._renderer,u=l&&l.canvas();return u&&(o=p(e),a=n.changedTouches?n.changedTouches[0]:n,(s=r.point(a,u))[0]-=o[0],s[1]-=o[1]),n.dataflow=e,n.item=i,n.vega=function(e,n,r){const i=n?"group"===n.mark.marktype?n:n.mark.group:null;function s(e){var t,r=i;if(e)for(t=n;t;t=t.mark.group)if(t.mark.name===e){r=t;break}return r&&r.mark&&r.mark.interactive?r:{}}function a(e){if(!e)return r;t.isString(e)&&(e=s(e));const n=r.slice();for(;e;)n[0]-=e.x||0,n[1]-=e.y||0,e=e.mark&&e.mark.group;return n}return{view:t.constant(e),item:t.constant(n||{}),group:s,xy:a,x:function(e){return a(e)[0]},y:function(e){return a(e)[1]}}}(e,i,s),n}const _="view",v={trap:!1};function m(e,n,r){const i=e._eventConfig&&e._eventConfig[n];return!(!1===i||t.isObject(i)&&!i[r])||(e.warn("Blocked ".concat(n," ").concat(r," event listener.")),!1)}function w(e){return e.item}function y(e){return e.item.mark.source}function b(e){return function(t,n){return n.vega.view().changeset().encode(n.item,e)}}function z(e,t,n){const r=document.createElement(e);for(const e in t)r.setAttribute(e,t[e]);return null!=n&&(r.textContent=n),r}var k=Math.sqrt(50),C=Math.sqrt(10),x=Math.sqrt(2);const L="vega-bind",A="vega-bind-name";function S(e,n,r){if(!n)return;const i=r.param;let s=r.state;return s||(s=r.state={elements:null,active:!1,set:null,update:t=>{t!==e.signal(i.signal)&&e.runAsync(null,()=>{s.source=!0,e.signal(i.signal,t)})}},i.debounce&&(s.update=t.debounce(i.debounce,s.update))),function(e,t,n,r){const i=z("div",{class:L}),s="radio"===n.input?i:i.appendChild(z("label"));s.appendChild(z("span",{class:A},n.name||n.signal)),t.appendChild(i);let a=E;switch(n.input){case"checkbox":a=D;break;case"select":a=T;break;case"radio":a=M;break;case"range":a=R}a(e,s,n,r)}(s,n,i,e.signal(i.signal)),s.active||(e.on(e._signals[i.signal],null,()=>{s.source?s.source=!1:s.set(e.signal(i.signal))}),s.active=!0),s}function E(e,t,n,r){const i=z("input");for(const e in n)"signal"!==e&&"element"!==e&&i.setAttribute("input"===e?"type":e,n[e]);i.setAttribute("name",n.signal),i.value=r,t.appendChild(i),i.addEventListener("input",()=>e.update(i.value)),e.elements=[i],e.set=e=>i.value=e}function D(e,t,n,r){const i={type:"checkbox",name:n.signal};r&&(i.checked=!0);const s=z("input",i);t.appendChild(s),s.addEventListener("change",()=>e.update(s.checked)),e.elements=[s],e.set=e=>s.checked=!!e||null}function T(e,t,n,r){const i=z("select",{name:n.signal}),s=n.labels||[];n.options.forEach((e,t)=>{const n={value:e};O(e,r)&&(n.selected=!0),i.appendChild(z("option",n,(s[t]||e)+""))}),t.appendChild(i),i.addEventListener("change",()=>{e.update(n.options[i.selectedIndex])}),e.elements=[i],e.set=e=>{for(let t=0,r=n.options.length;t<r;++t)if(O(n.options[t],e))return void(i.selectedIndex=t)}}function M(e,t,n,r){const i=z("span",{class:"vega-bind-radio"}),s=n.labels||[];t.appendChild(i),e.elements=n.options.map((t,a)=>{const o={type:"radio",name:n.signal,value:t};O(t,r)&&(o.checked=!0);const l=z("input",o);l.addEventListener("change",()=>e.update(t));const u=z("label",{},(s[a]||t)+"");return u.prepend(l),i.appendChild(u),l}),e.set=t=>{const n=e.elements,r=n.length;for(let e=0;e<r;++e)O(n[e].value,t)&&(n[e].checked=!0)}}function R(e,t,n,r){r=void 0!==r?r:(+n.max+ +n.min)/2;const i=null!=n.max?n.max:Math.max(100,+r)||100,s=n.min||Math.min(0,i,+r)||0,a=n.step||(o=s,l=i,u=100,c=Math.abs(l-o)/Math.max(0,u),d=Math.pow(10,Math.floor(Math.log(c)/Math.LN10)),(h=c/d)>=k?d*=10:h>=C?d*=5:h>=x&&(d*=2),l<o?-d:d);var o,l,u,c,d,h;const g=z("input",{type:"range",name:n.signal,min:s,max:i,step:a});g.value=r;const p=z("span",{},+r);t.appendChild(g),t.appendChild(p);const f=()=>{p.textContent=g.value,e.update(+g.value)};g.addEventListener("input",f),g.addEventListener("change",f),e.elements=[g],e.set=e=>{g.value=e,p.textContent=e}}function O(e,t){return e===t||e+""==t+""}function j(e,t,n,r,i,s){return(t=t||new r(e.loader())).initialize(n,h(e),g(e),p(e),i,s).background(e.background())}function H(e,t){return t?function(){try{t.apply(this,arguments)}catch(t){e.error(t)}}:null}function U(e,t){if("string"==typeof t){if("undefined"==typeof document)return e.error("DOM document instance not found."),null;if(!(t=document.querySelector(t)))return e.error("Signal bind element not found: "+t),null}if(t)try{t.innerHTML=""}catch(n){t=null,e.error(n)}return t}const W=e=>+e||0;function q(e){return t.isObject(e)?{top:W(e.top),bottom:W(e.bottom),left:W(e.left),right:W(e.right)}:(e=>({top:e,bottom:e,left:e,right:e}))(W(e))}async function V(e,n,i,s){const a=r.renderModule(n),o=a&&a.headless;return o||t.error("Unrecognized renderer type: "+n),await e.runAsync(),j(e,null,null,o,i,s).renderAsync(e._scenegraph.root)}var B="width",G="height",I="padding",P={skip:!0};function F(e,t){var n=e.autosize(),r=e.padding();return t-(n&&n.contains===I?r.left+r.right:0)}function N(e,t){var n=e.autosize(),r=e.padding();return t-(n&&n.contains===I?r.top+r.bottom:0)}function J(e,n){return n.modified&&t.isArray(n.input.value)&&e.indexOf("_:vega:_")}function K(e,t){return!("parent"===e||t instanceof n.transforms.proxy)}var Q,X,Y=0,Z=0,$=0,ee=0,te=0,ne=0,re="object"==typeof performance&&performance.now?performance:Date,ie="object"==typeof window&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(e){setTimeout(e,17)};function se(){return te||(ie(ae),te=re.now()+ne)}function ae(){te=0}function oe(){this._call=this._time=this._next=null}function le(){te=(ee=re.now())+ne,Y=Z=0;try{!function(){se(),++Y;for(var e,t=Q;t;)(e=te-t._time)>=0&&t._call.call(null,e),t=t._next;--Y}()}finally{Y=0,function(){var e,t,n=Q,r=1/0;for(;n;)n._call?(r>n._time&&(r=n._time),e=n,n=n._next):(t=n._next,n._next=null,n=e?e._next=t:Q=t);X=e,ce(r)}(),te=0}}function ue(){var e=re.now(),t=e-ee;t>1e3&&(ne-=t,ee=e)}function ce(e){Y||(Z&&(Z=clearTimeout(Z)),e-te>24?(e<1/0&&(Z=setTimeout(le,e-re.now()-ne)),$&&($=clearInterval($))):($||(ee=re.now(),$=setInterval(ue,1e3)),Y=1,ie(le)))}function de(e,n,r,i){const s=e.element();s&&s.setAttribute("title",function(e){return null==e?"":t.isArray(e)?he(e):t.isObject(e)&&!t.isDate(e)?(n=e,Object.keys(n).map(e=>{const r=n[e];return e+": "+(t.isArray(r)?he(r):ge(r))}).join("\n")):e+"";var n}(i))}function he(e){return"["+e.map(ge).join(", ")+"]"}function ge(e){return t.isArray(e)?"[…]":t.isObject(e)&&!t.isDate(e)?"{…}":e}function pe(e,o){const c=this;if(o=o||{},n.Dataflow.call(c),o.loader&&c.loader(o.loader),o.logger&&c.logger(o.logger),null!=o.logLevel&&c.logLevel(o.logLevel),o.locale||e.locale){const n=t.extend({},e.locale,o.locale);c.locale(a.locale(n.number,n.time))}c._el=null,c._elBind=null,c._renderType=o.renderer||r.RenderType.Canvas,c._scenegraph=new r.Scenegraph;const d=c._scenegraph.root;c._renderer=null,c._tooltip=o.tooltip||de,c._redraw=!0,c._handler=(new r.CanvasHandler).scene(d),c._globalCursor=!1,c._preventDefault=!1,c._timers=[],c._eventListeners=[],c._resizeListeners=[],c._eventConfig=function(e){const n=t.extend({defaults:{}},e),r=(e,n)=>{n.forEach(n=>{t.isArray(e[n])&&(e[n]=t.toSet(e[n]))})};return r(n.defaults,["prevent","allow"]),r(n,["view","window","selector"]),n}(e.eventConfig),c.globalCursor(c._eventConfig.globalCursor);const h=function(e,t,r){return s.context(e,n.transforms,i.functionContext,r).parse(t)}(c,e,o.expr);c._runtime=h,c._signals=h.signals,c._bind=(e.bindings||[]).map(e=>({state:null,param:t.extend({},e)})),h.root&&h.root.set(d),d.source=h.data.root.input,c.pulse(h.data.root.input,c.changeset().insert(d.items)),c._width=c.width(),c._height=c.height(),c._viewWidth=F(c,c._width),c._viewHeight=N(c,c._height),c._origin=[0,0],c._resize=0,c._autosize=1,function(e){var t=e._signals,n=t.width,r=t.height,i=t.padding;function s(){e._autosize=e._resize=1}e._resizeWidth=e.add(null,t=>{e._width=t.size,e._viewWidth=F(e,t.size),s()},{size:n}),e._resizeHeight=e.add(null,t=>{e._height=t.size,e._viewHeight=N(e,t.size),s()},{size:r});const a=e.add(null,s,{pad:i});e._resizeWidth.rank=n.rank+1,e._resizeHeight.rank=r.rank+1,a.rank=i.rank+1}(c),function(e){e.add(null,t=>(e._background=t.bg,e._resize=1,t.bg),{bg:e._signals.background})}(c),function(e){const n=e._signals.cursor||(e._signals.cursor=e.add({user:l,item:null}));e.on(e.events("view","mousemove"),n,(e,r)=>{const i=n.value,s=i?t.isString(i)?i:i.user:l,a=r.item&&r.item.cursor||null;return i&&s===i.user&&a==i.item?i:{user:s,item:a}}),e.add(null,(function(n){let r=n.cursor,i=this.value;return t.isString(r)||(i=r.item,r=r.user),u(e,r&&r!==l?r:i||r),i}),{cursor:n})}(c),c.description(e.description),o.hover&&c.hover(),o.container&&c.initialize(o.container,o.bind)}function fe(e,n){return t.hasOwnProperty(e._signals,n)?e._signals[n]:t.error("Unrecognized signal name: "+t.stringValue(n))}function _e(e,t){const n=(e._targets||[]).filter(e=>e._update&&e._update.handler===t);return n.length?n[0]:null}function ve(e,t,n,r){let i=_e(n,r);return i||(i=H(e,()=>r(t,n.value)),i.handler=r,e.on(n,null,i)),e}function me(e,t,n){const r=_e(t,n);return r&&t._targets.remove(r),e}return oe.prototype=function(e,t,n){var r=new oe;return r.restart(e,t,n),r}.prototype={constructor:oe,restart:function(e,t,n){if("function"!=typeof e)throw new TypeError("callback is not a function");n=(null==n?se():+n)+(null==t?0:+t),this._next||X===this||(X?X._next=this:Q=this,X=this),this._call=e,this._time=n,ce()},stop:function(){this._call&&(this._call=null,this._time=1/0,ce())}},t.inherits(pe,n.Dataflow,{async evaluate(e,t,r){if(await n.Dataflow.prototype.evaluate.call(this,e,t),this._redraw||this._resize)try{this._renderer&&(this._resize&&(this._resize=0,s=p(i=this),a=h(i),o=g(i),i._renderer.background(i.background()),i._renderer.resize(a,o,s),i._handler.origin(s),i._resizeListeners.forEach(e=>{try{e(a,o)}catch(e){i.error(e)}})),await this._renderer.renderAsync(this._scenegraph.root)),this._redraw=!1}catch(e){this.error(e)}var i,s,a,o;return r&&n.asyncCallback(this,r),this},dirty(e){this._redraw=!0,this._renderer&&this._renderer.dirty(e)},description(e){if(arguments.length){const t=null!=e?e+"":null;return t!==this._desc&&o(this._el,this._desc=t),this}return this._desc},container(){return this._el},scenegraph(){return this._scenegraph},origin(){return this._origin.slice()},signal(e,t,n){const r=fe(this,e);return 1===arguments.length?r.value:this.update(r,t,n)},width(e){return arguments.length?this.signal("width",e):this.signal("width")},height(e){return arguments.length?this.signal("height",e):this.signal("height")},padding(e){return arguments.length?this.signal("padding",q(e)):q(this.signal("padding"))},autosize(e){return arguments.length?this.signal("autosize",e):this.signal("autosize")},background(e){return arguments.length?this.signal("background",e):this.signal("background")},renderer(e){return arguments.length?(r.renderModule(e)||t.error("Unrecognized renderer type: "+e),e!==this._renderType&&(this._renderType=e,this._resetRenderer()),this):this._renderType},tooltip(e){return arguments.length?(e!==this._tooltip&&(this._tooltip=e,this._resetRenderer()),this):this._tooltip},loader(e){return arguments.length?(e!==this._loader&&(n.Dataflow.prototype.loader.call(this,e),this._resetRenderer()),this):this._loader},resize(){return this._autosize=1,this.touch(fe(this,"autosize"))},_resetRenderer(){this._renderer&&(this._renderer=null,this.initialize(this._el,this._elBind))},_resizeView:function(e,t,n,r,i,s){this.runAfter(a=>{let o=0;a._autosize=0,a.width()!==n&&(o=1,a.signal(B,n,P),a._resizeWidth.skip(!0)),a.height()!==r&&(o=1,a.signal(G,r,P),a._resizeHeight.skip(!0)),a._viewWidth!==e&&(a._resize=1,a._viewWidth=e),a._viewHeight!==t&&(a._resize=1,a._viewHeight=t),a._origin[0]===i[0]&&a._origin[1]===i[1]||(a._resize=1,a._origin=i),o&&a.run("enter"),s&&a.runAfter(e=>e.resize())},!1,1)},addEventListener(e,t,n){let r=t;return n&&!1===n.trap||(r=H(this,t),r.raw=t),this._handler.on(e,r),this},removeEventListener(e,t){for(var n,r,i=this._handler.handlers(e),s=i.length;--s>=0;)if(r=i[s].type,n=i[s].handler,e===r&&(t===n||t===n.raw)){this._handler.off(r,n);break}return this},addResizeListener(e){const t=this._resizeListeners;return t.indexOf(e)<0&&t.push(e),this},removeResizeListener(e){var t=this._resizeListeners,n=t.indexOf(e);return n>=0&&t.splice(n,1),this},addSignalListener(e,t){return ve(this,e,fe(this,e),t)},removeSignalListener(e,t){return me(this,fe(this,e),t)},addDataListener(e,t){return ve(this,e,c(this,e).values,t)},removeDataListener(e,t){return me(this,c(this,e).values,t)},globalCursor(e){if(arguments.length){if(this._globalCursor!==!!e){const t=u(this,null);this._globalCursor=!!e,t&&u(this,t)}return this}return this._globalCursor},preventDefault(e){return arguments.length?(this._preventDefault=e,this):this._preventDefault},timer:function(e,t){this._timers.push(function(e,t,n){var r=new oe,i=t;return null==t?(r.restart(e,t,n),r):(r._restart=r.restart,r.restart=function(e,t,n){t=+t,n=null==n?se():+n,r._restart((function s(a){a+=i,r._restart(s,i+=t,n),e(a)}),t,n)},r.restart(e,t,n),r)}((function(t){e({timestamp:Date.now(),elapsed:t})}),t))},events:function(e,t,r){var i,s=this,a=new n.EventStream(r),o=function(n,r){s.runAsync(null,()=>{e===_&&function(e,t){var n=e._eventConfig.defaults,r=n.prevent,i=n.allow;return!1!==r&&!0!==i&&(!0===r||!1===i||(r?r[t]:i?!i[t]:e.preventDefault()))}(s,t)&&n.preventDefault(),a.receive(f(s,n,r))})};if("timer"===e)m(s,"timer",t)&&s.timer(o,t);else if(e===_)m(s,"view",t)&&s.addEventListener(t,o,v);else if("window"===e?m(s,"window",t)&&"undefined"!=typeof window&&(i=[window]):"undefined"!=typeof document&&m(s,"selector",t)&&(i=document.querySelectorAll(e)),i){for(var l=0,u=i.length;l<u;++l)i[l].addEventListener(t,o);s._eventListeners.push({type:t,sources:i,handler:o})}else s.warn("Can not resolve event source: "+e);return a},finalize:function(){var e,t,n,r=this._tooltip,i=this._timers,s=this._eventListeners;for(e=i.length;--e>=0;)i[e].stop();for(e=s.length;--e>=0;)for(t=(n=s[e]).sources.length;--t>=0;)n.sources[t].removeEventListener(n.type,n.handler);return r&&r.call(this,this._handler,null,null,null),this},hover:function(e,t){return t=[t||"update",(e=[e||"hover"])[0]],this.on(this.events("view","mouseover",w),y,b(e)),this.on(this.events("view","mouseout",w),y,b(t)),this},data:function(e,r){return arguments.length<2?c(this,e).values.value:d.call(this,e,n.changeset().remove(t.truthy).insert(r))},change:d,insert:function(e,t){return d.call(this,e,n.changeset().insert(t))},remove:function(e,t){return d.call(this,e,n.changeset().remove(t))},scale:function(e){var n=this._runtime.scales;return t.hasOwnProperty(n,e)||t.error("Unrecognized scale or projection: "+e),n[e].value},initialize:function(e,t){const n=this,i=n._renderType,s=n._eventConfig.bind,a=r.renderModule(i);e=n._el=e?U(n,e):null,function(e){const t=e.container();t&&(t.setAttribute("role","graphics-document"),t.setAttribute("aria-roleDescription","visualization"),o(t,e.description()))}(n),a||n.error("Unrecognized renderer type: "+i);const l=a.handler||r.CanvasHandler,u=e?a.renderer:a.headless;return n._renderer=u?j(n,n._renderer,e,u):null,n._handler=function(e,t,n,r){const i=new r(e.loader(),H(e,e.tooltip())).scene(e.scenegraph().root).initialize(n,p(e),e);return t&&t.handlers().forEach(e=>{i.on(e.type,e.handler)}),i}(n,n._handler,e,l),n._redraw=!0,e&&"none"!==s&&(t=t?n._elBind=U(n,t):e.appendChild(z("form",{class:"vega-bindings"})),n._bind.forEach(e=>{e.param.element&&"container"!==s&&(e.element=U(n,e.param.element))}),n._bind.forEach(e=>{S(n,e.element||t,e)})),n},toImageURL:async function(e,n){e!==r.RenderType.Canvas&&e!==r.RenderType.SVG&&e!==r.RenderType.PNG&&t.error("Unrecognized image type: "+e);const i=await V(this,e,n);return e===r.RenderType.SVG?function(e,t){const n=new Blob([e],{type:t});return window.URL.createObjectURL(n)}(i.svg(),"image/svg+xml"):i.canvas().toDataURL("image/png")},toCanvas:async function(e,t){return(await V(this,r.RenderType.Canvas,e,t)).canvas()},toSVG:async function(e){return(await V(this,r.RenderType.SVG,e)).svg()},getState:function(e){return this._runtime.getState(e||{data:J,signals:K,recurse:!0})},setState:function(e){return this.runAsync(null,t=>{t._trigger=!1,t._runtime.setState(e)},e=>{e._trigger=!0}),this}}),e.View=pe,e}({},vega,vega,vega,vega,vega,vega); | ||
| //# sourceMappingURL=vega-view.min.js.map |
+15
-17
| { | ||
| "name": "vega-view", | ||
| "version": "5.8.2", | ||
| "version": "5.9.0", | ||
| "description": "View component and transforms for Vega visualizations.", | ||
@@ -13,25 +13,23 @@ "keywords": [ | ||
| "main": "build/vega-view.js", | ||
| "module": "index", | ||
| "module": "build/vega-view.module.js", | ||
| "unpkg": "build/vega-view.min.js", | ||
| "repository": "vega/vega", | ||
| "scripts": { | ||
| "rollup": "rollup -g d3-array:d3,d3-timer:d3,vega-dataflow:vega,vega-format:vega,vega-functions:vega,vega-runtime:vega,vega-scenegraph:vega,vega-util:vega -f umd -n vega -o build/vega-view.js -- index.js", | ||
| "prebuild": "rimraf build && mkdir build", | ||
| "build": "yarn rollup", | ||
| "postbuild": "terser build/vega-view.js -c -m -o build/vega-view.min.js", | ||
| "pretest": "yarn prebuild && yarn rollup", | ||
| "prebuild": "rimraf build", | ||
| "build": "rollup -c", | ||
| "pretest": "yarn build --config-test", | ||
| "test": "tape 'test/**/*-test.js'", | ||
| "prepublishOnly": "yarn test && yarn build", | ||
| "postpublish": "git push && git push --tags" | ||
| "prepublishOnly": "yarn test && yarn build" | ||
| }, | ||
| "dependencies": { | ||
| "d3-array": "^2.7.0", | ||
| "d3-array": "^2.7.1", | ||
| "d3-timer": "^2.0.0", | ||
| "vega-dataflow": "^5.7.2", | ||
| "vega-format": "^1.0.3", | ||
| "vega-functions": "^5.7.3", | ||
| "vega-runtime": "^6.1.2", | ||
| "vega-scenegraph": "^4.9.1", | ||
| "vega-util": "^1.15.1" | ||
| "vega-dataflow": "^5.7.3", | ||
| "vega-format": "^1.0.4", | ||
| "vega-functions": "^5.8.0", | ||
| "vega-runtime": "^6.1.3", | ||
| "vega-scenegraph": "^4.9.2", | ||
| "vega-util": "^1.15.2" | ||
| }, | ||
| "gitHead": "8d6793f4ca7eaaf2d22186764e9ce2dae687cf52" | ||
| "gitHead": "4affcbedb9d14815dbb6d3b250ed231b54fc95c0" | ||
| } |
+1
-1
@@ -22,3 +22,3 @@ import {changeset, isChangeSet} from 'vega-dataflow'; | ||
| } | ||
| var dataset = dataref(this, name); | ||
| const dataset = dataref(this, name); | ||
| dataset.modified = true; | ||
@@ -25,0 +25,0 @@ return this.pulse(dataset.input, changes); |
+2
-2
| export default function(tag, attr, text) { | ||
| var el = document.createElement(tag); | ||
| for (var key in attr) el.setAttribute(key, attr[key]); | ||
| const el = document.createElement(tag); | ||
| for (const key in attr) el.setAttribute(key, attr[key]); | ||
| if (text != null) el.textContent = text; | ||
| return el; | ||
| } |
@@ -51,3 +51,3 @@ import {offset} from './render-size'; | ||
| function extension(view, item, point) { | ||
| var itemGroup = item | ||
| const itemGroup = item | ||
| ? item.mark.marktype === 'group' ? item : item.mark.group | ||
@@ -68,3 +68,3 @@ : null; | ||
| var p = point.slice(); | ||
| const p = point.slice(); | ||
| while (item) { | ||
@@ -71,0 +71,0 @@ p[0] -= item.x || 0; |
@@ -6,3 +6,3 @@ import {offset} from './render-size'; | ||
| // instantiate scenegraph handler | ||
| var handler = new constructor(view.loader(), trap(view, view.tooltip())) | ||
| const handler = new constructor(view.loader(), trap(view, view.tooltip())) | ||
| .scene(view.scenegraph().root) | ||
@@ -9,0 +9,0 @@ .initialize(el, offset(view), view); |
@@ -26,4 +26,4 @@ import renderHeadless from './render-headless'; | ||
| function toBlobURL(data, mime) { | ||
| var blob = new Blob([data], {type: mime}); | ||
| const blob = new Blob([data], {type: mime}); | ||
| return window.URL.createObjectURL(blob); | ||
| } |
+2
-2
@@ -49,3 +49,3 @@ var Width = 'width', | ||
| // respond to padding signal | ||
| var resizePadding = view.add(null, resetSize, {pad: p}); | ||
| const resizePadding = view.add(null, resetSize, {pad: p}); | ||
@@ -60,3 +60,3 @@ // set rank to run immediately after source signal | ||
| this.runAfter(view => { | ||
| var rerun = 0; | ||
| let rerun = 0; | ||
@@ -63,0 +63,0 @@ // reset autosize flag |
+2
-2
| import {isArray, isDate, isObject} from 'vega-util'; | ||
| export default function(handler, event, item, value) { | ||
| var el = handler.element(); | ||
| const el = handler.element(); | ||
| if (el) el.setAttribute('title', formatTooltip(value)); | ||
@@ -17,3 +17,3 @@ } | ||
| return Object.keys(obj).map(key => { | ||
| var v = obj[key]; | ||
| const v = obj[key]; | ||
| return key + ': ' + (isArray(v) ? formatArray(v) : formatValue(v)); | ||
@@ -20,0 +20,0 @@ }).join('\n'); |
+5
-5
@@ -131,3 +131,3 @@ import {ariaLabel} from './aria'; | ||
| function addOperatorListener(view, name, op, handler) { | ||
| var h = findOperatorHandler(op, handler); | ||
| let h = findOperatorHandler(op, handler); | ||
| if (!h) { | ||
@@ -142,3 +142,3 @@ h = trap(view, () => handler(name, op.value)); | ||
| function removeOperatorListener(view, op, handler) { | ||
| var h = findOperatorHandler(op, handler); | ||
| const h = findOperatorHandler(op, handler); | ||
| if (h) op._targets.remove(h); | ||
@@ -206,3 +206,3 @@ return view; | ||
| signal(name, value, options) { | ||
| var op = lookupSignal(this, name); | ||
| const op = lookupSignal(this, name); | ||
| return arguments.length === 1 | ||
@@ -283,3 +283,3 @@ ? op.value | ||
| addEventListener(type, handler, options) { | ||
| var callback = handler; | ||
| let callback = handler; | ||
| if (!(options && options.trap === false)) { | ||
@@ -311,3 +311,3 @@ // wrap callback in error handler | ||
| addResizeListener(handler) { | ||
| var l = this._resizeListeners; | ||
| const l = this._resizeListeners; | ||
| if (l.indexOf(handler) < 0) { | ||
@@ -314,0 +314,0 @@ // add handler if it isn't already registered |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
207381
110.62%36
9.09%3544
47.97%7
75%Updated
Updated
Updated
Updated
Updated
Updated
Updated