Skip to content

Commit

Permalink
#196 add basic time-slice functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Mrowetz committed Jun 24, 2017
1 parent a07766f commit 4092002
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 2 deletions.
3 changes: 3 additions & 0 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ <h1>WPT HAR</h1>
showAlignmentHelpers: false,
showMimeTypeIcon: false,
showIndicatorIcons: false,
timeSlices: [0, 750, 1300],
timeSliceOnEnter: (slice, evt) => {console.log("timeSliceOnEnter", slice, evt)},
timeSliceOnLeave: (slice, evt) => {console.log("timeSliceOnLeave", slice, evt)}
}, "fileinput2", outputHolder2El, "test-data/wpt_github.com.151226_X7_b43d35e592fab70e0ba012fe11a41020.har");
})(window.perfCascade)
</script>
Expand Down
15 changes: 15 additions & 0 deletions src/ts/helpers/feature-detection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** feature detection if browser supports `passive` event-listener */
let passiveSupported = false;

// feature detection if passive event listener are supported
try {
const options = Object.defineProperty({}, "passive", {
get() { passiveSupported = true; },
});
window.addEventListener("test", null, options); // test add enpyt evt listener
} catch (err) { /** ignore */}

/**
* If true browser supports `passive` event listerners
*/
export const supportsPassiveEventListener: boolean = passiveSupported;
17 changes: 16 additions & 1 deletion src/ts/helpers/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,28 @@ export function validateOptions(options: ChartRenderOption): ChartRenderOption {
const ensureBoolean = (name: keyof ChartRenderOption) => {
options[name] = !!options[name];
};
const ensureArray = (name: keyof ChartRenderOption) => {
if (!Array.isArray(options[name])) {
throw TypeError(`option "${name}" needs to be an array`);
}
};
const ensureFunctionOrUndefined = (name: keyof ChartRenderOption) => {
if (typeof options[name] !== "function"
&& options[name] !== undefined
&& options[name] !== null) {
throw TypeError(`option "${name}" needs to be a function (or undefined)`);
}
};

validateInt("leftColumnWith");
validateInt("rowHeight");
validateInt("selectedPage");
ensureBoolean("showAlignmentHelpers");
ensureBoolean("showIndicatorIcons");
ensureBoolean("showMimeTypeIcon");

ensureArray("timeSlices");
ensureFunctionOrUndefined("onParsed");
ensureFunctionOrUndefined("timeSliceOnEnter");
ensureFunctionOrUndefined("timeSliceOnLeave");
return options;
}
2 changes: 2 additions & 0 deletions src/ts/typing/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ export interface Context {
diagramHeight: number;
/** Chart config/customization options */
options: ChartRenderOption;
/** start time of the current time-slice (if available) */
activeTimeslice?: number;
}
1 change: 0 additions & 1 deletion src/ts/typing/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export interface ChartRenderOption {
legendHolder: HTMLElement;
/** Callback called when the HAR doc has been parsed into PerfCascases */
onParsed: (data: WaterfallDocs) => void;

/** Segments of consecutive slices of time in milliseconds */
timeSlices: number[];
/** callback called when entering a new timeSlice */
Expand Down
74 changes: 74 additions & 0 deletions src/ts/waterfall/sub-components/svg-time-slices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { supportsPassiveEventListener } from "../../helpers/feature-detection";
import { Context } from "../../typing/context";

/**
* Calculates current position in Milliseconds
* @param offsetX offset on the X-axis relative to `fullWidth` in px
* @param leftColumnWidthPerc width of the the left (description) column
* @param fullWidth Width of the full chart (including the left column)
* @param fullDuration Duration in milliseconds
*/
const toPosInMs = (offsetX: number, leftColumnWidthPerc: number, fullWidth: number, fullDuration: number) => {
const leftWidthPx = fullWidth / 100 * leftColumnWidthPerc;
if (offsetX <= leftWidthPx) {
return null; // not over waterfall content
}
const rigthWidthPx = fullWidth - leftWidthPx;
const posRightPx = offsetX - leftWidthPx;
const posRightPerc = posRightPx / rigthWidthPx;
/** Curent position in Milliseconds */
return fullDuration * posRightPerc;
};

/**
* Generates a slice-change check `mousemove` callback
* @param context context object
*/
const sliceChangeCheck = (context: Context) => {
const fullDuration = context.unit * 100;
const options = context.options;
const leftColumnWidthPerc = context.options.leftColumnWith;
const slices = options.timeSlices;
const lastSlideIndex = options.timeSlices.length - 1;

const onMouseMoveSliceCheck = (evt: MouseEvent) => {
const tar = evt.currentTarget as SVGSVGElement;
const currMs = toPosInMs(evt.offsetX, leftColumnWidthPerc, tar.clientWidth, fullDuration);
if (currMs === null) {
if (context.activeTimeslice !== null) {
options.timeSliceOnLeave(context.activeTimeslice, evt);
context.activeTimeslice = null;
}
return;
}
for (let i = 0; i <= lastSlideIndex; i++) {
if ( currMs >= slices[i] && (i === lastSlideIndex || currMs < slices[i + 1])) {
if (context.activeTimeslice !== slices[i] ) {
if (context.activeTimeslice !== null) {
options.timeSliceOnLeave(context.activeTimeslice, evt);
}
context.activeTimeslice = slices[i];
options.timeSliceOnEnter(slices[i], evt);
}
}
}
};
return onMouseMoveSliceCheck;
};

/**
* Sets up the event listener and detection for the time-slice hit-detection
* @param holder
* @param context
*/
export const setupTimeSlices = (holder: SVGElement, context: Context) => {
const pass: any = supportsPassiveEventListener ? {
passive: true,
} : false; // ts does not have typing for passive yet - use any

holder.addEventListener("mousemove", sliceChangeCheck(context), pass);
holder.addEventListener("mouseleave", (evt: MouseEvent) => {
context.options.timeSliceOnLeave(context.activeTimeslice, evt);
context.activeTimeslice = null;
}, pass);
};
5 changes: 5 additions & 0 deletions src/ts/waterfall/svg-chart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { makeTooltip } from "./row/svg-tooltip";
import * as alignmentHelper from "./sub-components/svg-alignment-helper";
import * as generalComponents from "./sub-components/svg-general-components";
import * as marks from "./sub-components/svg-marks";
import { setupTimeSlices } from "./sub-components/svg-time-slices";

/**
* Get a string that's as wide, or wider than any number from 0-n.
Expand Down Expand Up @@ -56,6 +57,7 @@ function createContext(data: WaterfallData, options: ChartRenderOption,
const unit = data.durationMs / 100;
const diagramHeight = (entriesToShow.length + 1) * options.rowHeight;
const context = {
activeTimeslice: null,
diagramHeight,
options,
overlayManager: undefined,
Expand Down Expand Up @@ -191,6 +193,9 @@ export function createWaterfallSvg(data: WaterfallData, options: ChartRenderOpti
timeLineHolder.appendChild(rowHolder);
timeLineHolder.appendChild(overlayHolder);
timeLineHolder.appendChild(makeTooltip(options));
if (options.timeSlices.length > 0 && (options.timeSliceOnEnter || options.timeSliceOnLeave)) {
setupTimeSlices(timeLineHolder, context);
}

return timeLineHolder;
}

0 comments on commit 4092002

Please sign in to comment.