@mvarble/viewport.js
Version:
Canvas rendering in Cycle.js
860 lines (694 loc) • 33 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _Viewport__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Viewport", function() { return _Viewport__WEBPACK_IMPORTED_MODULE_0__["Viewport"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ViewportDriver", function() { return _Viewport__WEBPACK_IMPORTED_MODULE_0__["ViewportDriver"]; });
/* harmony import */ var _clicks__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "isOver", function() { return _clicks__WEBPACK_IMPORTED_MODULE_1__["isOver"]; });
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getOver", function() { return _clicks__WEBPACK_IMPORTED_MODULE_1__["getOver"]; });
/* harmony default export */ __webpack_exports__["default"] = ({
Viewport: _Viewport__WEBPACK_IMPORTED_MODULE_0__["Viewport"],
ViewportDriver: _Viewport__WEBPACK_IMPORTED_MODULE_0__["ViewportDriver"],
isOver: _clicks__WEBPACK_IMPORTED_MODULE_1__["isOver"],
getOver: _clicks__WEBPACK_IMPORTED_MODULE_1__["getOver"]
});
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Viewport", function() { return Viewport; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ViewportDriver", function() { return ViewportDriver; });
/* harmony import */ var xstream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
/* harmony import */ var xstream__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(xstream__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var xstream_extra_sampleCombine__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3);
/* harmony import */ var xstream_extra_sampleCombine__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(xstream_extra_sampleCombine__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var xstream_extra_dropRepeats__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
/* harmony import */ var xstream_extra_dropRepeats__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(xstream_extra_dropRepeats__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var xstream_extra_fromEvent__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
/* harmony import */ var xstream_extra_fromEvent__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(xstream_extra_fromEvent__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _cycle_run_lib_adapt__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6);
/* harmony import */ var _cycle_run_lib_adapt__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_cycle_run_lib_adapt__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _clicks__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7);
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/**
* Viewport.js
*
* This module exports the Cycle.js driver/component pair which allow for
* declarative rendering on the canvas.
*/
// module dependencies: npm packages
// module dependencies: project modules
// exports
// these are boolean functions that help us determine the state of a snabbdom
// vdom
function isRenderable(vdom) {
if (typeof document === 'undefined') return false;
return vdom && vdom.elm && document.body.contains(vdom.elm);
}
function alreadyHasHook(vdom) {
return vdom.data && vdom.data.hook && vdom.data.hook.insert;
}
function withRenderHook(vdom, renderFunc, stateObject) {
if (alreadyHasHook(vdom)) {
if (!vdom.data.viewport) {
var insert = function insert(vnode) {
vdom.data.hook.insert(vnode);
renderFunc(vnode.elm, stateObject);
};
var newVdom = _objectSpread({}, vdom, {
data: _objectSpread({}, vdom.data, {
hook: _objectSpread({}, vdom.data.hook, {
insert: insert
}),
viewport: {
oldInsert: vdom.data.hook.insert
}
})
});
return newVdom;
} else {
var _insert = function _insert(vnode) {
if (vnode.data.viewport.oldInsert) {
vnode.data.viewport.oldInsert(vnode);
}
renderFunc(vnode.elm, stateObject);
};
var _newVdom = _objectSpread({}, vdom, {
data: _objectSpread({}, vdom.data, {
hook: _objectSpread({}, vdom.data.hook, {
insert: _insert
})
})
});
return _newVdom;
}
} else {
var _newVdom2 = _objectSpread({}, vdom);
if (!_newVdom2.data) {
_newVdom2.data = {};
}
if (!_newVdom2.data.hook) {
_newVdom2.data.hook = {};
}
_newVdom2.data.viewport = {
oldInsert: undefined
};
vdom.data.hook.insert = function (vnode) {
return renderFunc(vnode.elm, stateObject);
};
return _newVdom2;
}
}
/**
* Viewport:
*
* This is a simple component that appends a hook to the relevant snabbdom and
* prepares a stream for the ViewportDriver.
*/
function Viewport(_ref) {
var DOM = _ref.DOM,
state = _ref.state,
canvas = _ref.canvas,
render = _ref.render,
parseDeep = _ref.parseDeep;
var all$$ = xstream__WEBPACK_IMPORTED_MODULE_0___default.a.combine(state, canvas, render, parseDeep || xstream__WEBPACK_IMPORTED_MODULE_0___default.a.of(true)).map(function (_ref2) {
var _ref3 = _slicedToArray(_ref2, 4),
stateObject = _ref3[0],
vdom = _ref3[1],
renderFunc = _ref3[2],
isDeep = _ref3[3];
// if there is no vdom, we cannot do anything
if (!vdom) return xstream__WEBPACK_IMPORTED_MODULE_0___default.a.empty(); // if there is a ready vdom, we simply pile everything together
if (isRenderable(vdom)) {
return {
vdom: xstream__WEBPACK_IMPORTED_MODULE_0___default.a.of(vdom),
viewport: xstream__WEBPACK_IMPORTED_MODULE_0___default.a.of({
render: renderFunc,
state: stateObject,
elm: vdom.elm,
isDeep: isDeep
})
};
} // if the vdom is not ready, we need to add insert render hooks
var newVdom = withRenderHook(vdom, renderFunc, stateObject); // in the chance that the vdom has been patched and the object is already
// in there. In such a case, take the most recent result of the element
return {
vdom: xstream__WEBPACK_IMPORTED_MODULE_0___default.a.of(newVdom),
viewport: DOM.select(newVdom.sel).element().take(1).map(function (elm) {
return {
render: renderFunc,
state: stateObject,
elm: elm,
isDeep: isDeep
};
})
};
}); // we return the adapted DOM and viewport streams
return {
DOM: all$$.map(function (obj) {
return obj.vdom;
}).flatten(),
viewport: all$$.map(function (obj) {
return obj.viewport;
}).flatten()
};
}
/**
* ViewportDriver:
*
* This is a Cycle.js driver which allows us to have a queryable collection of
* streams corresponding to parsed events on a canvas DOM element. These parsed
* events will correspond to click events that occured over a given frame.
* This is done by leveraging the streams of a `MainDOMSource` object from the
* Cycle.js factory `DOMDriver`.
*/
function ViewportDriver(viewport$) {
// create the render callback
var next = function next(_ref4) {
var render = _ref4.render,
elm = _ref4.elm,
state = _ref4.state;
if (typeof document !== 'undefined' && document.body.contains(elm)) {
render(elm, state);
}
}; // add render callback as listener
viewport$.addListener({
next: next
}); // return the FrameSource
var state$ = viewport$.map(function (viewport) {
return {
state: viewport.state,
_scope: viewport._scope,
isDeep: viewport.isDeep
};
});
return new UnmountedFrameSource(state$);
}
/**
* UnmountedFrameSource
*
* An instance of this class is returned by the ViewportDriver.
*/
var unmountedAncestryMessage = 'Do not isolate an `UnmountedFrameSource`' + 'instance to a specific branch. Instead, mount this source first!';
var UnmountedFrameSource = /*#__PURE__*/function () {
function UnmountedFrameSource(state$, scope) {
_classCallCheck(this, UnmountedFrameSource);
this._state$ = state$;
this._scope = scope || [];
this.isolateSource = function (source, scope) {
var isolation = parseIsolationScope(scope);
if (isolation.type3) return source;
if (isolation.type1) {
return new UnmountedFrameSource(source._state$, appendScope(source._scope, isolation.type1));
}
throw new Error(unmountedAncestryMessage);
};
this.isolateSink = isolateSink;
}
_createClass(UnmountedFrameSource, [{
key: "mount",
value: function mount(domSource) {
return new FrameSourceMaster(domSource, this._state$, this._scope);
}
}, {
key: "select",
value: function select() {
throw new Error('You need to mount a `DOMSource` before filtering streams');
}
}, {
key: "events",
value: function events() {
throw new Error('You need to mount a `DOMSource` before requesting streams');
}
}]);
return UnmountedFrameSource;
}();
/**
* FrameSourceMaster
*
* Each `FrameSourceMaster` is in the context of some `DOMSource` and a stream
* of `frame.js` frames. It creates a wrapper of `DOMSource.events` and
* some additional methods which allow us to quickly parse the frame.
*/
var FrameSourceMaster = /*#__PURE__*/function () {
// our constructor
function FrameSourceMaster(domSource, state$, scope) {
_classCallCheck(this, FrameSourceMaster);
this._domSource = domSource;
this._state$ = state$;
this._scope = scope;
this._parsedStreams = {};
this.isolateSource = function (source, scope) {
var isolation = parseIsolationScope(scope);
if (isolation.type3) return source;
if (isolation.type1) {
return new FrameSourceMaster(source._domSource, source._state$, appendScope(source._scope, isolation.type1));
}
return new FrameSource(source, function () {
return true;
}, [isolation.type2]);
};
this.isolateSink = isolateSink;
}
/**
* this is a wrapper of the `DOMSource.events` method, with the added
* information of which frame `getOver(event, frame, isOver)` returned
*/
_createClass(FrameSourceMaster, [{
key: "events",
value: function events(type) {
var _this = this;
if (!this._parsedStreams[type]) {
// filter the state immediately
var state$ = this._state$.filter(function (_ref5) {
var _scope = _ref5._scope;
return withinScope(_this._scope, _scope);
}); // leverage the DOMSource events
this._parsedStreams[type] = this._domSource.events(type).compose(xstream_extra_sampleCombine__WEBPACK_IMPORTED_MODULE_1___default()(state$)).map(function (_ref6) {
var _ref7 = _slicedToArray(_ref6, 2),
event = _ref7[0],
_ref7$ = _ref7[1],
state = _ref7$.state,
isDeep = _ref7$.isDeep;
var _getOver = Object(_clicks__WEBPACK_IMPORTED_MODULE_5__["getOver"])(event, state, isDeep),
frame = _getOver.frame,
treeKeys = _getOver.treeKeys;
event.frame = frame;
event.treeKeys = treeKeys;
return event;
});
}
return Object(_cycle_run_lib_adapt__WEBPACK_IMPORTED_MODULE_4__["adapt"])(this._parsedStreams[type]);
}
/**
* this will return a `FrameSource` with the selector in place
*/
}, {
key: "select",
value: function select(selector) {
if (typeof selector !== 'function') {
throw new TypeError('`FrameSource.select` needs a function obj => bool');
return;
}
return new FrameSource(this, selector, []);
}
}]);
return FrameSourceMaster;
}();
/**
* FrameSource
*
* This class serves as a filtered `FrameSourceMaster`. The stream returned
* by `FrameSource.parentDims` is identical, while that of `FrameSource.events`
* will return only those events for which `event.frame` satisfies the
* `FrameSource.selector`, which is a function `frame => bool`.
*/
var FrameSource = /*#__PURE__*/function () {
function FrameSource(master, selector, treeKeys) {
_classCallCheck(this, FrameSource);
this._master = master;
this._selector = selector;
this._treeKeys = treeKeys;
this.isolateSource = function (source, scope) {
var isolation = parseIsolationScope(scope);
if (isolation.type3) return source;
if (isolation.type1) {
var _master = new FrameSourceMaster(source._master._domSource, source._master._state, appendScope(source._master._scope, isolation.type1));
return new FrameSource(_master, source._selector, source._treeKeys);
}
return new FrameSource(source._master, source._selector, appendScope(source._treeKeys, isolation.type2));
};
this.isolateSink = isolateSink;
}
_createClass(FrameSource, [{
key: "select",
value: function select(selector) {
var _this2 = this;
if (typeof selector !== 'function') {
throw new TypeError('`FrameSource.select` needs a function obj => bool');
return;
}
return new FrameSource(this._master, function (frame) {
return _this2._selector(frame) && selector(frame);
}, this._treeKeys);
}
}, {
key: "events",
value: function events(type) {
var _this3 = this;
return this._master.events(type).filter(function (event) {
return event && _this3._selector(event.frame) && withinScope(_this3._treeKeys, event.treeKeys);
});
}
}]);
return FrameSource;
}();
/**
* isolation:
*
* For isolating ViewportDriver sources/sinks, we have two setups: (1) isolating
* different viewports and (2) isolating frames within a single viewport.
*
* For (1), the source isolation is handled by having a simple string.
*
* isolate(SomethingWithViewport, 'someScope)
*
* This string is passed to the `_scope` attribute of the `FrameSource` instance
* and the isolation is calculated normally.
*
* For (2), the source isolation is handled by the `FrameSource` object having
* a single property `key`.
*
* isolate(SomeFrameComponent, { frameSource: { key: 'hisKey' } })
* isolate(SomeFrameComponent, { frameSource: '{"key": "hisKey"}' })
*
* The intention of such isolation is that the `FrameSource` instance will store
* an ancestry of all keys `[k1, k2, ..., kn]` added in its `_treeKeys`
* property. From there, the ancestry keys from `getOver` on each event will
* have to contain such isolation keys.
*
* Note that this isolation in (2) will only work if every frame in the
* unist tree has a `key` property.
*
* Lastly, no isolation of sinks happens in (2), as frames are not responsible
* for returning their own viewports.
*/
function isNully(object) {
var type = _typeof(object);
return !object && type !== 'boolean' && type !== 'number';
}
var errorString = "ViewportDriver isolation requires:\n (1) a string for viewport scope,\n (2) an object with attribute 'key' for filtering clicks along the tree,\n (3) a 'null' or 'undefined' for neither.\n"; // this function will return an object corresponding to the data of (1) vs (2)
function parseIsolationScope(scope) {
if (isNully(scope)) return {
type3: true
};
if (scope && scope.key) return {
type2: scope.key
};
try {
// if JSON.parse works and the object has a key attribute, we assume (2)
var object = JSON.parse(scope);
if (typeof object.key === 'undefined') {
throw new Error(errorString);
}
return {
type2: object.key
};
} catch (error) {
// otherwise, we are in (1), so simply pass the string
if (error.name !== 'SyntaxError') {
throw error;
}
return {
type1: scope
};
}
} // these functions will simply control string concatenation.
function prependScope(oldScope, scope) {
return [scope].concat(_toConsumableArray(oldScope || []));
}
function appendScope(oldScope, scope) {
return [].concat(_toConsumableArray(oldScope || []), [scope]);
} // we do not perform sink isolation in (2), as they should not be creating
// their own viewports.
function isolateSink(sink, scope) {
var isolation = parseIsolationScope(scope);
if (isolation.type1) {
return sink.map(function (viewport) {
return _objectSpread({}, viewport, {
_scope: prependScope(viewport._scope, isolation.type1)
});
});
} else {
return sink;
}
} // This will see if the sourceScope array is a subset of the streamScope array.
// Such an event corresponds to the stream with the associated streamScope
// "belonging" to the component with the associated sourceScope.
function withinScope(sourceScope, streamScope) {
if (!sourceScope || !sourceScope.length) return true;
if (!streamScope || !streamScope.length) return false;
return streamScope.slice(0, sourceScope.length).every(function (scope, i) {
return scope === sourceScope[i];
});
}
/***/ }),
/* 2 */
/***/ (function(module, exports) {
module.exports = require("xstream");
/***/ }),
/* 3 */
/***/ (function(module, exports) {
module.exports = require("xstream/extra/sampleCombine");
/***/ }),
/* 4 */
/***/ (function(module, exports) {
module.exports = require("xstream/extra/dropRepeats");
/***/ }),
/* 5 */
/***/ (function(module, exports) {
module.exports = require("xstream/extra/fromEvent");
/***/ }),
/* 6 */
/***/ (function(module, exports) {
module.exports = require("@cycle/run/lib/adapt");
/***/ }),
/* 7 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
;
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isOver", function() { return isOver; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getOver", function() { return getOver; });
/* harmony import */ var xstream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
/* harmony import */ var xstream__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(xstream__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var xstream_extra_fromEvent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
/* harmony import */ var xstream_extra_fromEvent__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(xstream_extra_fromEvent__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var unist_util_visit_parents__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8);
/* harmony import */ var unist_util_visit_parents__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(unist_util_visit_parents__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _mvarble_frames_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
/* harmony import */ var _mvarble_frames_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_mvarble_frames_js__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _mvarble_viewport_utilities__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(10);
/* harmony import */ var _mvarble_viewport_utilities__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_mvarble_viewport_utilities__WEBPACK_IMPORTED_MODULE_4__);
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); }
function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); }
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
/**
* clicks.js
*
* This module exports utilities that are useful for parsing click events
* of a canvas.
*/
// module dependencies: npm packages
// our exports
/**
* isOver
*
* This is a function that determines by `frame.data.clickDisk(s)` or
* `frame.data.clickBox(es)` if a mouse event occured over a `frame`.
* We assume that the click coordinates are those of the world frame.
*/
function isOver(event, frame) {
if (!clickable(frame)) return false; // get the mouse position
var p = Object(_mvarble_frames_js__WEBPACK_IMPORTED_MODULE_3__["locFrameTrans"])(Object(_mvarble_viewport_utilities__WEBPACK_IMPORTED_MODULE_4__["relativeMousePosition"])(event), _mvarble_frames_js__WEBPACK_IMPORTED_MODULE_3__["identityFrame"], frame); // check if in clickBox(es) or clickDisk(s)
var data = frame.data;
if (data.clickBoxes && data.clickBoxes.some(function (b) {
return inBox(p, b);
})) return true;
if (data.clickDisks && data.clickDisks.some(function (d) {
return inDisk(p, d);
})) return true;
if (data.clickDisk && inDisk(p, data.clickDisk)) return true;
return data.clickBox ? inBox(p, data.clickBox) : false;
} // helper functions
function inBox(_ref, _ref2) {
var _ref3 = _slicedToArray(_ref, 2),
x = _ref3[0],
y = _ref3[1];
var _ref4 = _slicedToArray(_ref2, 4),
mX = _ref4[0],
mY = _ref4[1],
MX = _ref4[2],
MY = _ref4[3];
return x >= mX && x <= MX && y >= mY && y <= MY;
}
function inDisk(_ref5, _ref6) {
var _ref7 = _slicedToArray(_ref5, 2),
x = _ref7[0],
y = _ref7[1];
var _ref8 = _slicedToArray(_ref6, 3),
h = _ref8[0],
k = _ref8[1],
r = _ref8[2];
return Math.pow(x - h, 2) + Math.pow(y - k, 2) <= Math.pow(r, 2);
}
function clickable(frame) {
// only frames with coordinate systems and clickBoxes can receive clicks
return frame.data && frame.worldMatrix && (frame.data.clickBox || frame.data.clickBoxes || frame.data.clickDisk || frame.data.clickDisks);
}
/**
* getOver
*
* This is a function that searches down the depth of a tree if any frame in the
* state has been clicked on. It uses `unist-util-visit` to run through the
* tree, and it proceeds down any branch of which the frame has a successful
* `isOver` return. It returns the node.
*/
function getOver(event, tree, deep) {
var frame = null;
var treeKeys = [];
unist_util_visit_parents__WEBPACK_IMPORTED_MODULE_2___default()(tree, function (node, ancestry) {
if (isOver(event, node)) {
frame = node;
treeKeys = ancestry.map(function (frame) {
return frame.key;
}).filter(function (key) {
return key;
});
return false;
}
});
if (deep && frame && frame.children && frame.children.length) {
var overData = getOver(event, _objectSpread({}, frame, {
data: {}
}), deep);
return overData.frame ? {
frame: overData.frame,
treeKeys: [].concat(_toConsumableArray(treeKeys), _toConsumableArray(overData.treeKeys))
} : {
frame: frame,
treeKeys: frame.key ? [].concat(_toConsumableArray(treeKeys), [frame.key]) : treeKeys
};
} else {
return {
frame: frame,
treeKeys: frame && frame.key ? [].concat(_toConsumableArray(treeKeys), [frame.key]) : treeKeys
};
}
}
/***/ }),
/* 8 */
/***/ (function(module, exports) {
module.exports = require("unist-util-visit-parents");
/***/ }),
/* 9 */
/***/ (function(module, exports) {
module.exports = require("@mvarble/frames.js");
/***/ }),
/* 10 */
/***/ (function(module, exports) {
module.exports = require("@mvarble/viewport-utilities");
/***/ })
/******/ ]);