const { I } = inject(); module.exports = { _rootSelector: ".htx-timeseries", _channelSelector: ".htx-timeseries-channel .overlay", _overviewSelector: ".htx-timeseries-overview .overlay", _westHandleSelector: ".htx-timeseries-overview .handle--w", _eastHandleSelector: ".htx-timeseries-overview .handle--e", _stickSelector: '[text-anchor="start"]', get _channelStageSelector() { return `${this._rootSelector} .htx-timeseries-channel .new_brush`; }, get _channelStickSelector() { return `${this._rootSelector} .htx-timeseries-channel [text-anchor="start"]`; }, _stageBBox: { x: 0, y: 0, width: 0, height: 0 }, WEST: "west", EAST: "east", async lookForStage() { I.scrollPageToTop(); const bbox = await I.grabElementBoundingRect(this._channelStageSelector); this._stageBBox = bbox; }, /** * Retrieves timestamp value from a text element of timeseries' stick (cursor). * **should be used inside async with `await`** operator. * * ```js * let timestamp = await I.grabStickTime(); * ``` * @returns timestamp value * * {{ react }} */ async grabStickTime() { // xPath cannot find `text` tag so we exchange it with `*` const rawValue = await I.grabTextFrom(locate(this._channelStickSelector).find("*").at(2)); const numericPart = rawValue.match(/-?\d+(?:\.\d+)?/); const parsedValue = numericPart ? Number(numericPart[0]) : Number(rawValue); // Cursor labels are formatted with one decimal place (e.g. "0.8 Hz") even when the // underlying data is integer-based. Round to the nearest integer so equality checks // on sequential timestamps keep passing regardless of the display format. return Number.isFinite(parsedValue) ? Math.round(parsedValue) : parsedValue; }, /** * Select range on overview to zoom in * **should be used inside async with `await`** operator. * @param {number} from - relative position of start between 0 and 1 * @param {number} to - relative position of finish between 0 and 1 * @returns {Promise} * * @example * await AtTimeSeries.selectOverviewRange(.25, .75); */ async selectOverviewRange(from, to) { I.scrollPageToTop(); const overviewBBox = await I.grabElementBoundingRect(this._overviewSelector); I.moveMouse(overviewBBox.x + overviewBBox.width * from, overviewBBox.y + overviewBBox.height / 2); I.pressMouseDown(); I.moveMouse(overviewBBox.x + overviewBBox.width * to, overviewBBox.y + overviewBBox.height / 2, 3); I.pressMouseUp(); }, /** * Move range on overview to another position * @param {number} where - position between 0 and 1 * @returns {Promise} */ async clickOverview(where) { I.scrollPageToTop(); const overviewBBox = await I.grabElementBoundingRect(this._overviewSelector); I.clickAt(overviewBBox.x + overviewBBox.width * where, overviewBBox.y + overviewBBox.height / 2); }, /** * Move overview handle by mouse drag * **should be used inside async with `await`** operator. * @param {number} where - position between 0 and 1 * @param {"west"|"east"} [which="west"] - handler name * @returns {Promise} * * @example * await AtTimeSeries.moveHandle(.5, AtTimeSeries.WEST); */ async moveHandle(where, which = this.WEST) { I.scrollPageToTop(); const handlerBBox = await I.grabElementBoundingRect(this[`_${which}HandleSelector`]); const overviewBBox = await I.grabElementBoundingRect(this._overviewSelector); I.moveMouse(handlerBBox.x + handlerBBox.width / 2, handlerBBox.y + handlerBBox.height / 2); I.pressMouseDown(); I.moveMouse(overviewBBox.x + overviewBBox.width * where, overviewBBox.y + overviewBBox.height / 2, 3); I.pressMouseUp(); }, /** * Zoom by mouse wheel over the channel * **should be used inside async with `await`** operator. * @param {number} deltaY * @param {Object} [atPoint] - Point where will be called wheel action * @param {number} [atPoint.x=0.5] - relative X coordinate * @param {number} [atPoint.y=0.5] - relative Y coordinate * @returns {Promise} * * @example * // zoom in * await AtTimeSeries.zoomByMouse(-100, { x: .01 }); * // zoom out * await AtTimeSeries.zoomByMouse(100); */ async zoomByMouse(deltaY, atPoint) { const { x = 0.5, y = 0.5 } = atPoint; I.scrollPageToTop(); const channelBBox = await I.grabElementBoundingRect(this._channelSelector); I.moveMouse(channelBBox.x + channelBBox.width * x, channelBBox.y + channelBBox.height * y); I.pressKeyDown("CommandOrControl"); I.mouseWheel({ deltaY }); I.pressKeyUp("CommandOrControl"); }, /** * Move mouse over the channel * **should be used inside async with `await`** operator. * @param {Object} [atPoint] - Point where will be called wheel action * @param {number} [atPoint.x=0.5] - relative X coordinate * @param {number} [atPoint.y=0.5] - relative Y coordinate * @returns {Promise} * * @example * await AtTimeSeries.moveMouseOverChannel({ x: .01 }); */ async moveMouseOverChannel(atPoint) { const { x = 0.5, y = 0.5 } = atPoint; I.scrollPageToTop(); const channelBBox = await I.grabElementBoundingRect(this._channelSelector); I.moveMouse(channelBBox.x + channelBBox.width * x, channelBBox.y + channelBBox.height * y); }, /** * Mousedown - mousemove - mouseup drawing a region on the first Channel. Works in conjunction with lookForStage. * @example * await AtTimeSeries.lookForStage(); * AtTimeseries.drawByDrag(50, 200); * @param x * @param shiftX */ drawByDrag(x, shiftX) { I.scrollPageToTop(); I.moveMouse(this._stageBBox.x + x, this._stageBBox.y + this._stageBBox.height / 2); I.pressMouseDown(); I.moveMouse(this._stageBBox.x + x + shiftX, this._stageBBox.y + this._stageBBox.height / 2, 3); I.pressMouseUp(); }, };