diff --git a/packages/oncoprintjs/index.d.ts b/packages/oncoprintjs/index.d.ts deleted file mode 100644 index 3cb6a9f26e0..00000000000 --- a/packages/oncoprintjs/index.d.ts +++ /dev/null @@ -1,239 +0,0 @@ -declare module "oncoprintjs" -{ - // global - export type SortConfig = { - type: "alphabetical" - } | { - type: "order"; - order: string[]; - } | { - type: "cluster"; - track_group_index: number; - clusterValueFn: (datum:any)=>number; - } | {type?:""}; - - // track properties - export type TrackId = number; - export type TrackGroup = TrackId[]; - export type TrackGroupIndex = number; - export type TrackSortDirection = 0|1|-1; - export type TrackSortComparator = (d1:D, d2:D)=>number;//returns (0|1|2|-1|-2); for comparison-based sort, where 2 and -2 mean force to end or beginning (resp) no matter what direction sorted in - export type TrackSortVector = (d:D)=>(number|string)[]; // maps data to vector used for bucket sort - types of elements in each position must be same, i.e. Kth element must always be a number, or always be a string - export type TrackTooltipFn = (cell_data:D[])=>HTMLElement|string|any; - export type TrackSortSpecification = TrackSortComparator | TrackSortVector | { - mandatory:TrackSortComparator; // specifies the mandatory order for the track - preferred:TrackSortComparator; // specifies the preferred order for the track (can be overridden by mandatory order of higher track) - isVector?:false; - } | { - mandatory: TrackSortVector; // specifies the mandatory order for the track - preferred: TrackSortVector; // specifies the preferred order for the track (can be overridden by mandatory order of higher track) - isVector: true; - compareEquals?:TrackSortComparator; // specifies a comparator to be applied to sort among equal sort vectors in the *preferred* order (optional). eg sort by sample id if all else equal - }; - - export type RuleSetParams = ICategoricalRuleSetParams | - IGradientRuleSetParams | - IBarRuleSetParams | - IStackedBarRuleSetParams | - IGradientAndCategoricalRuleSetParams | - IGeneticAlterationRuleSetParams; - - interface IGeneralRuleSetParams { - legend_label?: string; - legend_base_color?: string; - exclude_from_legend?: boolean; - na_z?:number; // z index of na shapes (defaults to 1) - na_legend_label?:string; // legend label associated to NA (defaults to 'No data') - } - - // all colors are hex, rgb, or rgba - export interface ICategoricalRuleSetParams extends IGeneralRuleSetParams { - type: "categorical" - category_key: string; // key into data which gives category - category_to_color?: {[category:string]:string}; - } - - export interface IGradientRuleSetParams extends IGeneralRuleSetParams { - type: "gradient" - // either `colormap_name` or `colors` needs to be present - colors?: [number, number, number, number][]; // [r,g,b,a][] - colormap_name?: string; // name of a colormap found in src/js/heatmapcolors.js - value_stop_points: number[]; - null_color?: string; - null_legend_label?:string; - - log_scale?:boolean; - value_key: string; - value_range: [number, number]; - } - - // TODO: it would be more elegant to create multiple inheritance (if possible) since - // IGradientAndCategoricalRuleSetParams is a IGradientRuleSetParams and - // ICategoricalRuleSetParams with a different `type` field. - export interface IGradientAndCategoricalRuleSetParams extends IGeneralRuleSetParams { - type: "gradient+categorical"; - // either `colormap_name` or `colors` needs to be present - colors?: [number, number, number, number][]; // [r,g,b,a][] - colormap_name?: string; // name of a colormap found in src/js/heatmapcolors.js - value_stop_points: number[]; - null_color?: string; - - log_scale?:boolean; - value_key: string; - value_range: [number, number]; - - category_key: string; // key into data which gives category - category_to_color?: {[category:string]:string}; - } - - export interface IBarRuleSetParams extends IGeneralRuleSetParams { - type: "bar" - fill?: string; - negative_fill?: string; - - log_scale?:boolean; - value_key: string; - value_range: [number, number]; - } - - export interface IStackedBarRuleSetParams extends IGeneralRuleSetParams { - type: "stacked_bar" - value_key: string; - categories: string[]; - fills?: string[]; - } - - export interface IGeneticAlterationRuleSetParams extends IGeneralRuleSetParams { - type: "gene" - rule_params: GeneticAlterationRuleParams; - } - - export type GeneticAlterationRuleParams = { - [datumKey:string]:{ - [commaSeparatedDatumValues:string]: { - shapes: ShapeSpec[]; - legend_label: string; - exclude_from_legend?:boolean; - } - } - }; - - export type ShapeSpec = any; // TODO - - export type CustomTrackOption = {label?:string, separator?: boolean, onClick?:(id:TrackId)=>void, weight?:string, disabled?:boolean}; - - export type TrackSpec = { - target_group?:TrackGroupIndex; - track_group_header?:string; - cell_height?: number; - track_padding?: number; - has_column_spacing?: boolean; - data_id_key?: keyof D; - tooltipFn?: TrackTooltipFn; - movable?:boolean; - removable?:boolean; - removeCallback?:(track_id:TrackId)=>void; - label?: string; - sublabel?: string; - html_label?: string; - label_color?: string; - label_circle_color?: string; - label_font_weight?: string; - link_url?: string; - description?: string; - track_info?:string; - sortCmpFn?:TrackSortSpecification; - sort_direction_changeable?:boolean; - onSortDirectionChange?:(track_id:TrackId, dir:number)=>void; - init_sort_direction?:TrackSortDirection; - data?:D[]; - rule_set_params?: RuleSetParams; - expansion_of?: TrackId; - expandCallback?: (id: TrackId) => void; - expandButtonTextGetter?: (is_expanded: boolean) => string; - important_ids?:string[]; - custom_track_options?:CustomTrackOption[]; - }; - - export type InitParams = { - init_cell_width?:number; - init_cell_padding?:number; - cell_padding_off_cell_width_threshold?:number; - }; - - export default class OncoprintJS { - webgl_unavailable: boolean; - setMinimapVisible:(visible:boolean)=>void; - scrollTo:(left:number)=>void; - onHorzZoom:(callback:(newHorzZoom:number)=>void)=>void; - onMinimapClose:(callback:()=>void)=>void; - moveTrack:(target_track:TrackId, new_previous_track:TrackId)=>void; - setTrackGroupOrder:(index:TrackGroupIndex, track_order:TrackGroup)=>void; - keepSorted:(keep_sorted:boolean)=>void; - addTracks:(params_list:TrackSpec[])=>TrackId[]; - removeTrack:(track_id:TrackId)=>void; - removeTracks:(track_ids:TrackId[])=>void; - getTracks:()=>TrackId[]; - removeAllTracks: () => void; - removeExpansionTracksFor: (parent_track: TrackId) => void; - disableTrackExpansion: (track_id: TrackId) => void; - enableTrackExpansion: (track_id: TrackId) => void; - removeAllExpansionTracksInGroup: (index: TrackGroupIndex) => void; - setHorzZoomToFit: (ids: string[]) => void; - updateHorzZoomToFitIds:(ids:string[])=>void; - getMinHorzZoom:()=>number; - getHorzZoom:()=>number; - setHorzZoom:(z:number, still_keep_horz_zoomed_to_fit?:boolean)=>number; - getVertZoom:()=>number; - setVertZoom:(z:number)=>number; - setScroll:(scroll_left:number, scroll_top:number)=>void; - setZoom:(zoom_x:number, zoom_y:number)=>void; - setHorzScroll:(s:number)=>number; - setVertScroll:(s:number)=>number; - setViewport:(col:number, scroll_y_proportion:number, num_cols:number, zoom_y:number)=>void; - getTrackData:(track_id:TrackId)=>D[]; - getTrackDataIdKey:(track_id:TrackId)=>string; - setTrackData:(track_id:TrackId, data:D[], data_id_key:string)=>void; - setTrackImportantIds:(track_id:TrackId, ids?:string[])=>void; - setTrackGroupSortPriority:(priority:TrackGroupIndex[])=>void; - setTrackGroupLegendOrder:(group_order:TrackGroupIndex[])=>void; - setTrackSortDirection:(track_id:TrackId, dir:TrackSortDirection)=>TrackSortDirection; - setTrackSortComparator:(track_id:TrackId, sortCmpFn:TrackSortSpecification)=>void; - getTrackSortDirection:(track_id:TrackId)=>TrackSortDirection; - setTrackInfo:(track_id:TrackId, msg:string)=>void; - setTrackInfoTooltip:(track_id:TrackId, $tooltip_elt:any)=>void;//$tooltip_elt is JQuery HTML element - setTrackTooltipFn:(track_id:TrackId, tooltipFn:TrackTooltipFn)=>void; - sort:()=>void; - shareRuleSet:(source_track_id:TrackId, target_track_id:TrackId)=>void; - setRuleSet:(track_id:TrackId, rule_set_params:RuleSetParams)=>void; - setSortConfig:(params:SortConfig)=>void; - setIdOrder:(ids:string[])=>void; - suppressRendering:()=>void; - releaseRendering:(onComplete?:()=>void)=>void; - triggerPendingResizeAndOrganize:(onComplete?:()=>void)=>void; - hideIds:(to_hide:string[], show_others?:boolean)=>void; - hideTrackLegends:(track_ids:TrackId[])=>void; - showTrackLegends:(track_ids:TrackId[])=>void; - setCellPaddingOn:(cell_padding_on:boolean)=>void; - toSVG:(with_background:boolean)=>SVGElement; - toCanvas:(callback:(canvas:HTMLCanvasElement, truncated:boolean)=>void, resolution:number)=>HTMLImageElement; - toDataUrl:(callback:(dataURL:string)=>void)=>void; - highlightTrack:(track_id:TrackId|null)=>void; - getIdOrder:(all?:boolean)=>string[]; - setIdClipboardContents:(array:string[])=>void; - getIdClipboardContents:()=>string[]; - onClipboardChange:(callback:(array:string[])=>void)=>void; - setTrackCustomOptions:(track_id:TrackId, custom_options?:CustomTrackOption[])=>void; - setShowTrackSublabels:(show:boolean)=>void; - clearMouseOverEffects:()=>void; - setTrackMovable:(track_id:TrackId, movable:boolean)=>void; - setWidth:(width:number)=>void; - setColumnLabels:(labels:{[uid:string]:string})=>void; - setHighlightedIds:(uids:string[])=>void; - onCellMouseOver:(callback:(uid:string|null, track_id:TrackId)=>void)=>void; // null indicates no mouseover - onCellClick:(callback:(uid:string|null, track_id:TrackId)=>void)=>void; // null indicates click in empty area - - constructor(ctr_selector:string, width:number, params?:InitParams); - destroy:()=>void; - } -} diff --git a/packages/oncoprintjs/jest.config.js b/packages/oncoprintjs/jest.config.js new file mode 100644 index 00000000000..cd80892d82b --- /dev/null +++ b/packages/oncoprintjs/jest.config.js @@ -0,0 +1,12 @@ +module.exports = { + roots: ["./src/test/"], + moduleDirectories: [ + ".", "src", "src/test", "node_modules" + ], + transform: { + "^.+\\.ts$": "ts-jest", + } , + moduleNameMapper: { + "\\.(css|jpg|png|svg)$": "mocks/empty-module.js" + } +}; diff --git a/packages/oncoprintjs/karma.conf.js b/packages/oncoprintjs/karma.conf.js deleted file mode 100644 index 3827c3f5048..00000000000 --- a/packages/oncoprintjs/karma.conf.js +++ /dev/null @@ -1,74 +0,0 @@ -// Karma configuration -// Generated on Mon Sep 18 2017 14:28:54 GMT-0400 (EDT) - -var argv = require('yargs').argv; - -module.exports = function(config) { - config.set({ - - // base path that will be used to resolve all patterns (eg. files, exclude) - basePath: '', - - - // frameworks to use - // available frameworks: https://npmjs.org/browse/keyword/karma-adapter - frameworks: ['jasmine', 'commonjs'], - - - // list of files / patterns to load in the browser - files: [ - 'dist/oncoprint.bundle.js', - 'src/test/*.js' - ], - - - // list of files to exclude - exclude: [ - ], - - - // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor - preprocessors: { - 'src/test/*.js':['commonjs'], - 'dist/*.js': ['commonjs'] - }, - - - // test results reporter to use - // possible values: 'dots', 'progress' - // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['progress'], - - - // web server port - port: 9876, - - - // enable / disable colors in the output (reporters and logs) - colors: true, - - - // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_INFO, - - - // enable / disable watching file and executing tests whenever any file changes - autoWatch: true, - - - // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['PhantomJS'], - - - // Continuous Integration mode - // if true, Karma captures browsers, runs the tests and exits - singleRun: !argv.watch, - - // Concurrency level - // how many browser should be started simultaneous - concurrency: Infinity - }) -} diff --git a/packages/oncoprintjs/package-lock.json b/packages/oncoprintjs/package-lock.json deleted file mode 100644 index 655872d69d0..00000000000 --- a/packages/oncoprintjs/package-lock.json +++ /dev/null @@ -1,3887 +0,0 @@ -{ - "name": "oncoprintjs", - "version": "0.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "accepts": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", - "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", - "requires": { - "mime-types": "~2.1.16", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", - "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=", - "dev": true - }, - "acorn-dynamic-import": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz", - "integrity": "sha1-x1K9IQvvZ5UBtsbLf8hPj0cVjMQ=", - "dev": true, - "requires": { - "acorn": "^4.0.3" - } - }, - "ajv": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.0.tgz", - "integrity": "sha1-6yhAdG6dxIvV4GOjbj/UAMXqtak=", - "dev": true, - "requires": { - "co": "^4.6.0", - "fast-deep-equal": "^1.0.0", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.3.0" - } - }, - "ajv-errors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz", - "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=", - "dev": true - }, - "ajv-keywords": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", - "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "^3.0.2", - "longest": "^1.0.1", - "repeat-string": "^1.5.2" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha1-VT3Lj5HjyImEXf26NMd3IbkLnXo=", - "dev": true, - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", - "dev": true - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "asn1.js": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.2.tgz", - "integrity": "sha1-gRfvT37YfNj4kES1v/l6wkOhbJo=", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha1-YaKau2/MAm/qd+VtHG7FOnlZUfQ=", - "dev": true, - "requires": { - "lodash": "^4.14.0" - }, - "dependencies": { - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true - } - } - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha1-qRlH2h9KUW6jjltOwOw3c2deCIY=", - "dev": true - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha1-pfwpi4G54Nyi5FiCR4S2XFK6WI4=", - "dev": true - }, - "binary-extensions": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.11.0.tgz", - "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", - "dev": true - }, - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha1-LN4J617jQfSEdGuwMJsyU7GxRC8=", - "dev": true - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "~1.1.1", - "http-errors": "~1.6.2", - "iconv-lite": "0.4.19", - "on-finished": "~2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "~1.6.15" - } - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserify-aes": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.1.1.tgz", - "integrity": "sha1-OLerVe24Bv8tzaGn8WIHc6R3xJ8=", - "dev": true, - "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "browserify-cipher": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz", - "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=", - "dev": true, - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz", - "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1" - } - }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", - "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", - "dev": true, - "requires": { - "bn.js": "^4.1.1", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.2", - "elliptic": "^6.0.0", - "inherits": "^2.0.1", - "parse-asn1": "^5.0.0" - } - }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha1-KGlFnZqjviRf6P4sofRuLn9U1z8=", - "dev": true, - "requires": { - "pako": "~1.0.5" - } - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true - }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", - "dev": true - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "^0.1.3", - "lazy-cache": "^1.0.3" - } - }, - "chai": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", - "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", - "dev": true, - "requires": { - "assertion-error": "^1.0.1", - "check-error": "^1.0.1", - "deep-eql": "^3.0.0", - "get-func-name": "^2.0.0", - "pathval": "^1.0.0", - "type-detect": "^4.0.0" - } - }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", - "dev": true - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha1-h2Dk7MJy9MNjUy+SbYdKriwTl94=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "^0.1.1", - "right-align": "^0.1.1", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - } - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "^0.1.4" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "create-ecdh": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz", - "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - } - }, - "create-hash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", - "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz", - "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "lru-cache": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz", - "integrity": "sha1-Yi4y6CSItJJ5EUpPns9F581rulU=", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - } - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha1-OWz58xN/A+S45TLFj2mCVOAPgOw=", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "^0.10.9" - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "des.js": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz", - "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz", - "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - } - }, - "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "dev": true, - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "encodeurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz", - "integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA=" - }, - "enhanced-resolve": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", - "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.4.0", - "object-assign": "^4.0.1", - "tapable": "^0.2.7" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } - } - }, - "errno": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", - "dev": true, - "requires": { - "prr": "~0.0.0" - } - }, - "error-ex": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz", - "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es5-ext": { - "version": "0.10.37", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.37.tgz", - "integrity": "sha1-DudB0Ui4AGm6J9AgOTdWryV978M=", - "dev": true, - "requires": { - "es6-iterator": "~2.0.1", - "es6-symbol": "~3.1.1" - } - }, - "es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "~0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "^0.10.14", - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "^0.1.3", - "es6-weak-map": "^2.0.1", - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "dev": true, - "requires": { - "estraverse": "^4.1.0", - "object-assign": "^4.0.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1", - "es5-ext": "~0.10.14" - } - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha1-f8vbGY3HGVlDLv4ThCaE4FJaywI=", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "^2.1.0" - } - }, - "express": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz", - "integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=", - "requires": { - "accepts": "~1.3.4", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "~1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~1.1.1", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.1.0", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.2", - "qs": "6.5.1", - "range-parser": "~1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.1", - "serve-static": "1.13.1", - "setprototypeof": "1.1.0", - "statuses": "~1.3.1", - "type-is": "~1.6.15", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true, - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^1.1.3", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "finalhandler": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", - "integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.2", - "statuses": "~1.3.1", - "unpipe": "~1.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz", - "integrity": "sha1-EfgjGPX+e7LNIpZaEI6TBiCCFtg=", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.3.0", - "node-pre-gyp": "^0.6.39" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "optional": true, - "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "aproba": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.1.tgz", - "integrity": "sha1-ldNgDwdxCqDpKYxyatXs8urLq6s=", - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", - "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "block-stream": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", - "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "dev": true, - "requires": { - "inherits": "~2.0.0" - } - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.x.x" - } - }, - "brace-expansion": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", - "dev": true, - "requires": { - "balanced-match": "^0.4.1", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz", - "integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E=", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.x.x" - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", - "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.2.tgz", - "integrity": "sha1-ca1dIEvxempsqPRQxhRUBm70YeE=", - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fstream": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", - "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", - "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", - "dev": true, - "optional": true, - "requires": { - "fstream": "^1.0.0", - "inherits": "2", - "minimatch": "^3.0.0" - } - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "optional": true, - "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "ini": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", - "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "~0.1.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "optional": true, - "requires": { - "jsonify": "~0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", - "dev": true, - "requires": { - "mime-db": "~1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.39", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz", - "integrity": "sha512-OsJV74qxnvz/AMGgcfZoDaeDXKD3oY3QVIbBmwszTFkRisTSXbMQyn4UWzUMOtA5SVhrBZOTp0wcoSBgfMfMmQ==", - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "hawk": "3.1.3", - "mkdirp": "^0.5.1", - "nopt": "^4.0.1", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "request": "2.81.0", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^2.2.1", - "tar-pack": "^3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", - "integrity": "sha512-ocolIkZYZt8UveuiDS0yAkkIjid1o7lPG8cYm05yNYzBn8ykQtaiPMEGp8fY9tKdDgm8okpdKzkvu1y9hUYugA==", - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", - "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", - "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", - "dev": true, - "optional": true, - "requires": { - "deep-extend": "~0.4.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.9.tgz", - "integrity": "sha1-z3jsb0ptHrQ9JkiMrJfwQudLf8g=", - "dev": true, - "requires": { - "buffer-shims": "~1.0.0", - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~1.0.0", - "util-deprecate": "~1.0.1" - } - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" - } - }, - "rimraf": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz", - "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=", - "dev": true, - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", - "dev": true - }, - "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.x.x" - } - }, - "sshpk": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", - "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", - "dev": true, - "optional": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jodid25519": "^1.0.0", - "jsbn": "~0.1.0", - "tweetnacl": "~0.14.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", - "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", - "dev": true, - "requires": { - "block-stream": "*", - "fstream": "^1.0.2", - "inherits": "2" - } - }, - "tar-pack": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", - "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.2.0", - "fstream": "^1.0.10", - "fstream-ignore": "^1.0.5", - "once": "^1.3.3", - "readable-stream": "^2.1.4", - "rimraf": "^2.5.1", - "tar": "^2.2.1", - "uid-number": "^0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", - "dev": true, - "optional": true, - "requires": { - "punycode": "^1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", - "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", - "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } - }, - "get-caller-file": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", - "integrity": "sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "gl-matrix": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-2.3.2.tgz", - "integrity": "sha1-qsgIx0r31dsF/gTLYMoaD8sXTXQ=" - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "hash-base": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", - "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=", - "dev": true, - "requires": { - "inherits": "^2.0.1" - } - }, - "hash.js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", - "integrity": "sha1-NA3tvmKQGHFRweodd3o0SJNd+EY=", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.0" - } - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "hosted-git-info": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz", - "integrity": "sha1-bWDjSzq7yDEwYsO3mO+NkBoHrzw=", - "dev": true - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": ">= 1.3.1 < 2" - }, - "dependencies": { - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - } - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha1-90aPYBNfXl2tM5nAqBvpoWA6CCs=" - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha1-ftGxQQxqDg94z5XTuEQMY/eLhhQ=", - "dev": true - }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", - "dev": true - }, - "ipaddr.js": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz", - "integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A=" - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", - "dev": true - }, - "is-builtin-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", - "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", - "dev": true, - "requires": { - "builtin-modules": "^1.0.0" - } - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "jquery": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.0.0.tgz", - "integrity": "sha1-laKpVBKRqfgZ4Bb4W6JHEW0D5Ks=" - }, - "json-loader": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", - "integrity": "sha1-3KFKcCNf+C8KyaOr62DTN6NlGF0=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jstat": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/jstat/-/jstat-1.7.1.tgz", - "integrity": "sha1-sfRXfvhvqafWBhQa/++5W85jLug=" - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "dev": true, - "requires": { - "invert-kv": "^1.0.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "loader-runner": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.3.0.tgz", - "integrity": "sha1-9IKuqC1UPgeSFwDVpG7yb9rGuKI=", - "dev": true - }, - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "^3.1.3", - "emojis-list": "^2.0.0", - "json5": "^0.5.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "md5.js": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", - "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - }, - "dependencies": { - "hash-base": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", - "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - } - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - } - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha1-8IA1HIZbDcViqEYpZtqlNUPHik0=", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - } - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha1-Eh+evEnjdm8xGnbh+hyAA8SwOqY=" - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "~1.30.0" - } - }, - "mimic-fn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.1.0.tgz", - "integrity": "sha1-5md4PZLonb00KBi1IwudYqZyrRg=", - "dev": true - }, - "minimalistic-assert": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz", - "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "mocha": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.0.5.tgz", - "integrity": "sha512-3MM3UjZ5p8EJrYpG7s+29HAI9G7sTzKEe4+w37Dg0QP7qL4XGsV+Q2xet2cE37AqdgN1OtYQB6Vl98YiPV3PgA==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - } - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=", - "dev": true, - "optional": true - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "node-libs-browser": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.1.0.tgz", - "integrity": "sha1-X5QmPUBPbkR2fXJpAf/wVHjWAN8=", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^1.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.0", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.10.3", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "timers-browserify": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz", - "integrity": "sha1-lspT9LeUpefA4b18yIo3Ipj6AeY=", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - } - } - }, - "normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha1-EvlaMH1YNSB1oEkHuErIvpisAS8=", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "is-builtin-module": "^1.0.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha1-QrwpAKa1uL0XN2yOiCtlr8zyS/I=", - "dev": true, - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", - "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "pako": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", - "integrity": "sha1-AQEhG6pwxLykoPY/Igbpe3368lg=", - "dev": true - }, - "parse-asn1": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz", - "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "pbkdf2": { - "version": "3.0.14", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.14.tgz", - "integrity": "sha1-o14TxkeZsGzhUyD0WcIw5o5zut4=", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "proxy-addr": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz", - "integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=", - "requires": { - "forwarded": "~0.1.2", - "ipaddr.js": "1.5.2" - } - }, - "prr": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "public-encrypt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz", - "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha1-NJzfbu+J7EXBLX1es/wMhwNDptg=" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha1-x6vpzIuHwLqodrGf3oP9RkeX44w=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "randombytes": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz", - "integrity": "sha1-3ACaJGuNCaF3tLegrne8Vw9LG3k=", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.3.tgz", - "integrity": "sha1-uWt99YfwHdkXJsQY8wVTsUGOPWI=", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha1-No8lEtefnUb9/HE0mueHi7weuVw=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.0.3", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "minimatch": "^3.0.2", - "readable-stream": "^2.0.2", - "set-immediate-shim": "^1.0.1" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - } - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha1-db3FiioUls7EihKDW8VMjVYjNt0=", - "dev": true, - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", - "dev": true - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "^0.1.1" - } - }, - "ripemd160": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", - "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=", - "dev": true, - "requires": { - "hash-base": "^2.0.0", - "inherits": "^2.0.1" - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=" - }, - "schema-utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.3.0.tgz", - "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", - "dev": true, - "requires": { - "ajv": "^5.0.0" - } - }, - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - }, - "send": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz", - "integrity": "sha1-pw4coh0TgsEdDZ9iMd6ygQgNerM=", - "requires": { - "debug": "2.6.9", - "depd": "~1.1.1", - "destroy": "~1.0.4", - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "~1.6.2", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "~2.3.0", - "range-parser": "~1.2.0", - "statuses": "~1.3.1" - } - }, - "serve-static": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz", - "integrity": "sha1-TFfVNASnYdjy58HooYpH2/J4pxk=", - "requires": { - "encodeurl": "~1.0.1", - "escape-html": "~1.0.3", - "parseurl": "~1.3.2", - "send": "0.16.1" - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=" - }, - "sha.js": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.9.tgz", - "integrity": "sha1-mPZIgEdLdPSji42p08Dy0QRjPn0=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "source-list-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", - "integrity": "sha1-qqR0A/eyRakvvJfqCPJQ1gh+0IU=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "spdx-correct": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz", - "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=", - "dev": true, - "requires": { - "spdx-license-ids": "^1.0.2" - } - }, - "spdx-expression-parse": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz", - "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=", - "dev": true - }, - "spdx-license-ids": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz", - "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=", - "dev": true - }, - "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - } - }, - "stream-http": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", - "integrity": "sha1-QKBQ7I3DtTsz2ZCUFcAsC/Gr+60=", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.2.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "tapable": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", - "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", - "dev": true - }, - "tayden-clusterfck": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/tayden-clusterfck/-/tayden-clusterfck-0.7.0.tgz", - "integrity": "sha1-JeuxKRqW6LwgT/94XzIM2cOj5i4=" - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-is": { - "version": "1.6.15", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", - "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.15" - } - }, - "uglify-js": { - "version": "2.8.29", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", - "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", - "dev": true, - "requires": { - "source-map": "~0.5.1", - "uglify-to-browserify": "~1.0.0", - "yargs": "~3.10.0" - }, - "dependencies": { - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "^1.0.2", - "cliui": "^2.1.0", - "decamelize": "^1.0.0", - "window-size": "0.1.0" - } - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true, - "optional": true - }, - "uglifyjs-webpack-plugin": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz", - "integrity": "sha1-uVH0q7a9YX5m9j64kUmOORdj4wk=", - "dev": true, - "requires": { - "source-map": "^0.5.6", - "uglify-js": "^2.8.29", - "webpack-sources": "^1.0.1" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } - } - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-loader": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.1.tgz", - "integrity": "sha512-vugEeXjyYFBCUOpX+ZuaunbK3QXMKaQ3zUnRfIpRBlGkY7QizCnzyyn2ASfcxsvyU3ef+CJppVywnl3Kgf13Gg==", - "dev": true, - "requires": { - "loader-utils": "^1.1.0", - "mime": "^2.0.3", - "schema-utils": "^1.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.5.3.tgz", - "integrity": "sha512-LqZ9wY+fx3UMiiPd741yB2pj3hhil+hQc8taf4o2QGRFpWgZ2V5C8HA165DY9sS3fJwsk7uT7ZlFEyC3Ig3lLg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.2.0.tgz", - "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=", - "dev": true - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "mime": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", - "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", - "dev": true - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - } - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "validate-npm-package-license": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz", - "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=", - "dev": true, - "requires": { - "spdx-correct": "~1.0.0", - "spdx-expression-parse": "~1.0.0" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "watchpack": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.4.0.tgz", - "integrity": "sha1-ShRyvLuVK9Cpu0A2gB+VTfs5+qw=", - "dev": true, - "requires": { - "async": "^2.1.2", - "chokidar": "^1.7.0", - "graceful-fs": "^4.1.2" - }, - "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - } - } - }, - "webpack": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-3.8.1.tgz", - "integrity": "sha1-sWloqBEAq+YWCLAVPJFZ74uyvYM=", - "dev": true, - "requires": { - "acorn": "^5.0.0", - "acorn-dynamic-import": "^2.0.0", - "ajv": "^5.1.5", - "ajv-keywords": "^2.0.0", - "async": "^2.1.2", - "enhanced-resolve": "^3.4.0", - "escope": "^3.6.0", - "interpret": "^1.0.0", - "json-loader": "^0.5.4", - "json5": "^0.5.1", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "mkdirp": "~0.5.0", - "node-libs-browser": "^2.0.0", - "source-map": "^0.5.3", - "supports-color": "^4.2.1", - "tapable": "^0.2.7", - "uglifyjs-webpack-plugin": "^0.4.6", - "watchpack": "^1.4.0", - "webpack-sources": "^1.0.1", - "yargs": "^8.0.2" - }, - "dependencies": { - "acorn": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", - "integrity": "sha1-MXrHghgmwixwLWYYmrg1lnXxNdc=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "supports-color": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", - "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - }, - "yargs": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", - "integrity": "sha1-YpmpBVsc78lp/355wdkY3Osiw2A=", - "dev": true, - "requires": { - "camelcase": "^4.1.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "read-pkg-up": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^7.0.0" - } - } - } - }, - "webpack-shell-plugin": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/webpack-shell-plugin/-/webpack-shell-plugin-0.5.0.tgz", - "integrity": "sha1-Kbih2A3erg3bEOcpZn9yhlPCx0I=", - "dev": true - }, - "webpack-sources": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.1.0.tgz", - "integrity": "sha1-oQHrrlnWUHNU1x2AE5UKOot6WlQ=", - "dev": true, - "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", - "dev": true - } - } - }, - "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha1-/wS9/AEO5UfXgL7DjhrBwnd9JTo=", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "worker-loader": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-1.1.0.tgz", - "integrity": "sha1-jPIYaaB63YTWb4IdlI0jweuY6Ak=", - "dev": true, - "requires": { - "loader-utils": "^1.0.0", - "schema-utils": "^0.3.0" - } - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "dev": true, - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - }, - "yargs-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-7.0.0.tgz", - "integrity": "sha1-jQrELxbqVd69MyyvTEA4s+P139k=", - "dev": true, - "requires": { - "camelcase": "^4.1.0" - }, - "dependencies": { - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - } - } - } - } -} diff --git a/packages/oncoprintjs/package.json b/packages/oncoprintjs/package.json index 887300e6a9d..c29471815e9 100644 --- a/packages/oncoprintjs/package.json +++ b/packages/oncoprintjs/package.json @@ -2,11 +2,11 @@ "name": "oncoprintjs", "version": "0.0.1", "description": "A data visualization for cancer genomic data.", - "types": "./index.d.ts", + "types": "./dist/js/oncoprint.d.ts", "main": "./dist/oncoprint.bundle.js", "scripts": { - "build": "webpack", - "test": "mocha src/test", + "build": "rm -rf ./dist/* && webpack", + "test": "jest", "test:watch": "npm run test -- --watch" }, "repository": { @@ -43,15 +43,23 @@ }, "homepage": "https://github.com/cBioPortal/oncoprintjs", "devDependencies": { + "@types/chai": "^4.2.3", + "@types/jquery": "^3.3.31", + "@types/lodash": "^4.14.139", + "@types/mocha": "^5.2.7", "chai": "^4.1.2", + "jest": "^24.9.0", "mocha": "^5.0.5", + "ts-jest": "^24.1.0", + "ts-loader": "5.4.5", + "typescript": "^3.6.3", "url-loader": "^1.1.1", - "webpack": "^3.0.0", + "webpack": "^4.0.0", + "webpack-cli": "^3.3.9", "webpack-shell-plugin": "^0.5.0", "worker-loader": "^1.1.0" }, "dependencies": { - "express": "^4.15.3", "gl-matrix": "2.3.2", "jquery": "^3.0.0", "jstat": "^1.7.1", diff --git a/packages/oncoprintjs/src/js/CachedProperty.js b/packages/oncoprintjs/src/js/CachedProperty.js deleted file mode 100644 index 2b95e9e5182..00000000000 --- a/packages/oncoprintjs/src/js/CachedProperty.js +++ /dev/null @@ -1,26 +0,0 @@ -var CachedProperty = (function() { - function CachedProperty(init_val, updateFn) { - this.value = init_val; - this.updateFn = updateFn; - this.bound_properties = []; - } - CachedProperty.prototype.update = function() { - this.value = this.updateFn.apply(null, arguments); - for (var i=0; i { + private bound_properties:CachedProperty[] = []; + + constructor(private value:T, private updateFn:(...args:any[])=>T) { + } + + public update(...args:any[]) { + this.value = this.updateFn.apply(null, arguments); + for (let i=0; i) { + this.bound_properties.push(cached_property); + } +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/binarysearch.js b/packages/oncoprintjs/src/js/binarysearch.ts similarity index 86% rename from packages/oncoprintjs/src/js/binarysearch.js rename to packages/oncoprintjs/src/js/binarysearch.ts index 019a4943d5c..93c1b969752 100644 --- a/packages/oncoprintjs/src/js/binarysearch.js +++ b/packages/oncoprintjs/src/js/binarysearch.ts @@ -1,4 +1,4 @@ -module.exports = function(array, target_key, keyFn, return_closest_lower_if_not_found) { +export default function binarysearch(array:T[], target_key:number, keyFn:(t:T)=>number, return_closest_lower_if_not_found?:boolean) { if (!array.length) { return -1; // return -1 for an empty array } diff --git a/packages/oncoprintjs/src/js/bucketsort.js b/packages/oncoprintjs/src/js/bucketsort.ts similarity index 83% rename from packages/oncoprintjs/src/js/bucketsort.js rename to packages/oncoprintjs/src/js/bucketsort.ts index d9110a8d0d6..c3000db1406 100644 --- a/packages/oncoprintjs/src/js/bucketsort.js +++ b/packages/oncoprintjs/src/js/bucketsort.ts @@ -1,19 +1,32 @@ -var string_type = typeof ""; +import {extendArray, sgndiff} from "./utils"; -function bucketSort(array, getVector, compareEquals) { +const string_type = typeof ""; + +export type SortingVector = (number|string)[]; + +type BucketRange = { + lower_index_incl:number; + upper_index_excl:number; +} + +type CompareEquals = (a:T, b:T)=>number; + +type GetVector = (t:T)=>SortingVector; + +export function bucketSort(array:T[], getVector?:(t:T)=>SortingVector, compareEquals?:CompareEquals) { // array: an array of data // getVector: a function that takes an element of array and returns an int vector. defaults to identity // compareEquals: an optional standard sort comparator - if specified it is run on the // results of the final buckets before returning - getVector = getVector || function(d) { return d; }; + getVector = getVector || function(d:SortingVector) { return d; } as any; var current_sorted_array = array; var current_bucket_ranges = [{lower_index_incl: 0, upper_index_excl: array.length}]; - var new_sorted_array, new_bucket_ranges, bucket_range, sorted_result; + var new_sorted_array:T[], new_bucket_ranges:BucketRange[], bucket_range, sorted_result; // find max length vector, to use as template for vector component types, and whose length will be the sort depth - var max_length_vector = []; + var max_length_vector:SortingVector = []; var proposed_vector; for (var i=0; i(array:T[], getString?:(t:T)=>string) { // array: an array of data // getString: a function that takes an element of `array` and returns a string. defaults to identity // returns strings sorted in "natural order" (i.e. numbers sorted correctly - P2 comes before P10) - getString = getString || function(d) { return d; }; + getString = getString || function(d:string) { return d; } as any; // compute string vectors we'll sort with var data = array.map(function(d) { return { @@ -76,7 +89,7 @@ function stringSort(array, getString) { return sorted.map(function(datum) { return datum.d; }); } -function stringToVector(string) { +export function stringToVector(string:string) { var vector = []; var len = string.length; var numberStartIncl = -1; @@ -110,7 +123,7 @@ function stringToVector(string) { return vector; } -function compareFull(d1, d2, getVector, compareEquals) { +export function compareFull(d1:T, d2:T, getVector:GetVector, compareEquals?:CompareEquals) { // utility function - comparator that describes sort order given by bucketSort var ret = compare(getVector(d1), getVector(d2)); if (ret === 0 && compareEquals) { @@ -119,15 +132,15 @@ function compareFull(d1, d2, getVector, compareEquals) { return ret; } -function compareVectorElements(elt1, elt2) { +function compareVectorElements(elt1:number|string, elt2:number|string) { if (typeof elt1 === string_type) { - return compare(stringToVector(elt1), stringToVector(elt2)); + return compare(stringToVector(elt1 as string), stringToVector(elt2 as string)); } else { - return signOfDifference(elt1, elt2); + return sgndiff(elt1 as number, elt2 as number); } } -function compare(vector1, vector2) { +export function compare(vector1:SortingVector, vector2:SortingVector) { // utility function - comparator that describes vector sort order given by bucketSort var ret = 0; @@ -157,17 +170,14 @@ function compare(vector1, vector2) { return ret; } -function signOfDifference(k1, k2) { - return k1 < k2 ? -1 : (k1 > k2 ? 1 : 0); -} - -function extendArray(target, source) { - for (var i=0; i( + array:T[], + getVector:GetVector, + sort_range_lower_index_incl:number, + sort_range_upper_index_excl:number, + vector_index:number, + isStringElt:boolean +) { // returns { sorted_array: d[], bucket_ranges:{lower_index_incl, upper_index_excl}[]}} }, // where sorted_array only contains elements from the specified range of // array[sort_range_lower_index_incl:sort_range_upper_index_excl] @@ -182,7 +192,7 @@ function bucketSortHelper(array, getVector, sort_range_lower_index_incl, sort_ra // bucket sort the specified range // gather elements into buckets - var buckets = {}; + var buckets:{[vectorElt:string]:T[]} = {}; var vector, key; var sortFirst = []; for (var i=sort_range_lower_index_incl; i { return _hcluster(casesAndEntitites, "CASES"); } @@ -49,11 +51,6 @@ var hclusterColumns = function(casesAndEntitites) { * @return a deferred which gets resolved with the clustering result * when the clustering is done. */ -var hclusterTracks = function(casesAndEntitites) { +export function hclusterTracks(casesAndEntitites:CasesAndEntities):Promise { return _hcluster(casesAndEntitites, "ENTITIES"); -} - -module.exports = { - hclusterColumns: hclusterColumns, - hclusterTracks: hclusterTracks } \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/extractrgba.js b/packages/oncoprintjs/src/js/extractrgba.js deleted file mode 100644 index cf4c739af2b..00000000000 --- a/packages/oncoprintjs/src/js/extractrgba.js +++ /dev/null @@ -1,18 +0,0 @@ -module.exports = function (str) { - var ret = [0, 0, 0, 1]; - if (str[0] === "#") { - // hex, convert to rgba - var r = parseInt(str[1] + str[2], 16); - var g = parseInt(str[3] + str[4], 16); - var b = parseInt(str[5] + str[6], 16); - str = 'rgba('+r+','+g+','+b+',1)'; - } - var match = str.match(/^[\s]*rgba\([\s]*([0-9.]+)[\s]*,[\s]*([0-9.]+)[\s]*,[\s]*([0-9.]+)[\s]*,[\s]*([0-9.]+)[\s]*\)[\s]*$/); - if (match && match.length === 5) { - ret = [parseFloat(match[1]) / 255, - parseFloat(match[2]) / 255, - parseFloat(match[3]) / 255, - parseFloat(match[4])]; - } - return ret; -}; diff --git a/packages/oncoprintjs/src/js/extractrgba.ts b/packages/oncoprintjs/src/js/extractrgba.ts new file mode 100644 index 00000000000..1a2768ffc69 --- /dev/null +++ b/packages/oncoprintjs/src/js/extractrgba.ts @@ -0,0 +1,18 @@ +export default function extractrgba(str:string) { + let ret = [0, 0, 0, 1]; + if (str[0] === "#") { + // hex, convert to rgba + const r = parseInt(str[1] + str[2], 16); + const g = parseInt(str[3] + str[4], 16); + const b = parseInt(str[5] + str[6], 16); + str = 'rgba('+r+','+g+','+b+',1)'; + } + const match = str.match(/^[\s]*rgba\([\s]*([0-9.]+)[\s]*,[\s]*([0-9.]+)[\s]*,[\s]*([0-9.]+)[\s]*,[\s]*([0-9.]+)[\s]*\)[\s]*$/); + if (match && match.length === 5) { + ret = [parseFloat(match[1]) / 255, + parseFloat(match[2]) / 255, + parseFloat(match[3]) / 255, + parseFloat(match[4])]; + } + return ret; +}; diff --git a/packages/oncoprintjs/src/js/haselementsininterval.js b/packages/oncoprintjs/src/js/haselementsininterval.ts similarity index 90% rename from packages/oncoprintjs/src/js/haselementsininterval.js rename to packages/oncoprintjs/src/js/haselementsininterval.ts index d9c802afcb0..082b2aec772 100644 --- a/packages/oncoprintjs/src/js/haselementsininterval.js +++ b/packages/oncoprintjs/src/js/haselementsininterval.ts @@ -31,16 +31,16 @@ */ -module.exports = function(sorted_list, valueFn, lower_inc_val, upper_exc_val) { +export default function haselementsininterval(sorted_list:T[], valueFn:(t:T)=>number, lower_inc_val:number, upper_exc_val:number):boolean { // in: sorted_list, a list sorted in increasing order of valueFn // valueFn, a function that takes an element of sorted_list and returns a number // lower_inc and upper_ex: define a half-open interval [lower_inc, upper_exc) // out: boolean, true iff there are any elements whose image under valueFn is in [lower_inc, upper_exc) - var test_lower_inc = 0; - var test_upper_exc = sorted_list.length; - var middle, middle_val; - var ret = false; + let test_lower_inc = 0; + let test_upper_exc = sorted_list.length; + let middle, middle_val; + let ret = false; while (true) { if (test_lower_inc >= test_upper_exc) { break; diff --git a/packages/oncoprintjs/src/js/heatmapcolors.js b/packages/oncoprintjs/src/js/heatmapcolors.ts similarity index 99% rename from packages/oncoprintjs/src/js/heatmapcolors.js rename to packages/oncoprintjs/src/js/heatmapcolors.ts index 8cf43098d82..56106e5576c 100644 --- a/packages/oncoprintjs/src/js/heatmapcolors.js +++ b/packages/oncoprintjs/src/js/heatmapcolors.ts @@ -1,7 +1,9 @@ // Colours for the oncoprint heatmap feature // viridis comes from matplotlib's default colour set, created by Stéfan van der Walt and Nathaniel Smith -module.exports = { +import {RGBAColor} from "./oncoprintruleset"; + +export default { viridis: [ [68.086020,1.242870,84.000825,1.000000], [68.470050,2.449275,85.533885,1.000000], @@ -518,4 +520,4 @@ module.exports = { [250.475535,253.497795,160.909335,1.000000], [252.032310,254.582820,164.455620,1.000000], ] -} +} as {[themeName:string]:RGBAColor[]}; diff --git a/packages/oncoprintjs/src/js/main.js b/packages/oncoprintjs/src/js/main.js index 680d1d90a95..6170dd4bb4e 100644 --- a/packages/oncoprintjs/src/js/main.js +++ b/packages/oncoprintjs/src/js/main.js @@ -1,4 +1,4 @@ -var OncoprintJS = require('./oncoprint.js'); +import OncoprintJS from './oncoprint'; if (typeof window !== "undefined") { window.Oncoprint = OncoprintJS; diff --git a/packages/oncoprintjs/src/js/makesvgelement.js b/packages/oncoprintjs/src/js/makesvgelement.js deleted file mode 100644 index d66e10a7626..00000000000 --- a/packages/oncoprintjs/src/js/makesvgelement.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = function (tag, attrs) { - var el = document.createElementNS('http://www.w3.org/2000/svg', tag); - for (var k in attrs) { - if (attrs.hasOwnProperty(k)) { - el.setAttribute(k, attrs[k]); - } - } - return el; -}; \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/makesvgelement.ts b/packages/oncoprintjs/src/js/makesvgelement.ts new file mode 100644 index 00000000000..5241e57a286 --- /dev/null +++ b/packages/oncoprintjs/src/js/makesvgelement.ts @@ -0,0 +1,9 @@ +export default function makesvgelement(tag:string, attrs:any) { + const el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (const k in attrs) { + if (attrs.hasOwnProperty(k)) { + el.setAttribute(k, attrs[k]); + } + } + return el; +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprint.js b/packages/oncoprintjs/src/js/oncoprint.ts similarity index 57% rename from packages/oncoprintjs/src/js/oncoprint.js rename to packages/oncoprintjs/src/js/oncoprint.ts index 1b7dc3d1c34..6e8e230c979 100644 --- a/packages/oncoprintjs/src/js/oncoprint.js +++ b/packages/oncoprintjs/src/js/oncoprint.ts @@ -1,36 +1,104 @@ -require('./polyfill.js'); - -var OncoprintModel = require('./oncoprintmodel.js'); -var OncoprintWebGLCellView = require('./oncoprintwebglcellview.js'); -var OncoprintLabelView = require('./oncoprintlabelview.js'); -var OncoprintRuleSet = require('./oncoprintruleset.js'); -var OncoprintTrackOptionsView = require('./oncoprinttrackoptionsview.js'); -var OncoprintLegendView = require('./oncoprintlegendrenderer.js');//TODO: rename -var OncoprintToolTip = require('./oncoprinttooltip.js'); -var OncoprintTrackInfoView = require('./oncoprinttrackinfoview.js'); -var OncoprintMinimapView = require('./oncoprintminimapview.js'); - -var svgfactory = require('./svgfactory.js'); +import './polyfill'; + +import OncoprintModel, { + ColumnId, + ColumnProp, + CustomTrackOption, + Datum, + LibraryTrackSpec, + SortConfig, + TrackGroup, + TrackGroupIndex, + TrackId, + TrackSortDirection, + TrackSortSpecification, + TrackTooltipFn, + UserTrackSpec +} from "./oncoprintmodel"; +import OncoprintWebGLCellView from './oncoprintwebglcellview'; +import OncoprintLabelView from './oncoprintlabelview'; +import OncoprintRuleSet, {RuleSetParams} from './oncoprintruleset'; +import OncoprintTrackOptionsView from './oncoprinttrackoptionsview'; +import OncoprintLegendView from './oncoprintlegendrenderer'; +import OncoprintToolTip from './oncoprinttooltip'; +import OncoprintTrackInfoView from './oncoprinttrackinfoview'; +import OncoprintMinimapView, {MinimapViewportSpec} from './oncoprintminimapview'; + +import svgfactory from './svgfactory'; + +import $ from "jquery"; +import {clamp} from "./utils"; + +export * from "./oncoprintruleset"; +export * from "./oncoprintmodel"; + +export type InitParams = { + init_cell_width?:number; + init_cell_padding?:number; + cell_padding_off_cell_width_threshold?:number; + init_horz_zoom?: number; + init_vert_zoom?: number; + init_track_group_padding?: number; + init_cell_padding_on?: boolean; +}; -var $ = require('jquery'); +export type HorzZoomCallback = (zoom:number)=>void; +export type MinimapCloseCallback = ()=>void; +export type CellMouseOverCallback = (uid:ColumnId, track_id:TrackId)=>void; +export type CellClickCallback = (uid:ColumnId, track_id:TrackId)=>void; +export type ClipboardChangeCallback = (ids:ColumnId[])=>void; -var clamp = function(val, lower, upper) { - return Math.min(Math.max(val, lower), upper); -}; +const nextTrackId = (function () { + let ctr = 0; + return function () { + ctr += 1; + return ctr; + } +})(); -var Oncoprint = (function () { +export default class Oncoprint { // this is the controller - var nextTrackId = (function () { - var ctr = 0; - return function () { - ctr += 1; - return ctr; - } - })(); - function Oncoprint(ctr_selector, width, params) { + + public destroyed:boolean; + public webgl_unavailable:boolean; + private $ctr:JQuery; + private $oncoprint_ctr:JQuery; + private $cell_div:JQuery; + private $legend_div:JQuery; + private $track_options_div:JQuery; + private $track_info_div:JQuery; + private $dummy_scroll_div:JQuery; + private $minimap_div:JQuery; + private $cell_canvas:JQuery; + private $cell_overlay_canvas:JQuery; + + public model:OncoprintModel; + public cell_view:OncoprintWebGLCellView; + public minimap_view:OncoprintMinimapView; + public track_options_view:OncoprintTrackOptionsView; + public track_info_view:OncoprintTrackInfoView; + public label_view: OncoprintLabelView; + public legend_view: OncoprintLegendView; + + private keep_sorted:boolean; + private keep_horz_zoomed_to_fit:boolean; + private keep_horz_zoomed_to_fit_ids:ColumnId[]; + private pending_resize_and_organize:boolean; + + private horz_zoom_callbacks:HorzZoomCallback[]; + private minimap_close_callbacks:MinimapCloseCallback[]; + private cell_mouse_over_callbacks:CellMouseOverCallback[]; + private cell_click_callbacks:CellClickCallback[]; + private id_clipboard:ColumnId[]; + private clipboard_change_callbacks:ClipboardChangeCallback[]; + + private target_dummy_scroll_left:number; + private target_dummy_scroll_top:number; + + constructor(private ctr_selector:string, private width:number, params?:InitParams) { params = params || {}; - var self = this; + const self = this; this.destroyed = false; this.webgl_unavailable = (document.createElement('canvas').getContext('experimental-webgl') === null); @@ -39,39 +107,37 @@ var Oncoprint = (function () { return; } - this.ctr_selector = ctr_selector; - - var $ctr = $('').css({'position':'relative', 'display':'inline-block'}).appendTo(ctr_selector); - var $oncoprint_ctr = $('
') + const $ctr = $('').css({'position':'relative', 'display':'inline-block'}).appendTo(ctr_selector); + const $oncoprint_ctr = $('
') .css({'position':'absolute', 'display':'inline-block'}) .appendTo($ctr); - var $tooltip_ctr = $('').css({'position':'fixed', 'top':0, 'left':0, "z-index":99999}).appendTo(ctr_selector); - var $legend_ctr = $('
').css({'position':'absolute', 'display':'inline-block', 'top':0, 'left':0, 'min-height':1}) + const $tooltip_ctr = $('').css({'position':'fixed', 'top':0, 'left':0, "z-index":99999}).appendTo(ctr_selector); + const $legend_ctr = $('
').css({'position':'absolute', 'display':'inline-block', 'top':0, 'left':0, 'min-height':1}) .appendTo($ctr); - var $label_canvas = $('') + const $label_canvas = $('') .css({'display':'inline-block', 'position':'absolute', 'left':'0px', 'top':'0px'}) .addClass("noselect") - .attr({'width':'150', 'height':'250'}); + .attr({'width':'150', 'height':'250'}) as JQuery; - var $track_options_div = $('
') + const $track_options_div = $('
') .css({'position':'absolute', 'left':'150px', 'top':'0px'}) .addClass("noselect") .attr({'width':'50', 'height':'250'}); - var $legend_div = $('
') + const $legend_div = $('
') .css({'position':'absolute', 'top':'250px', 'min-height':1}) .addClass("noselect oncoprint-legend-div"); - var $cell_div = $('
') + const $cell_div = $('
') .css({'width':width, 'display':'inline-block', 'position':'absolute', @@ -79,12 +145,12 @@ var Oncoprint = (function () { 'top':'0px'}) .addClass("noselect"); - var $cell_canvas = $('') + const $cell_canvas = $('') .attr({'width':'0px', 'height':'0px'}) .css({'position':'absolute', 'top':'0px', 'left':'0px'}) - .addClass("noselect"); + .addClass("noselect") as JQuery; - var $dummy_scroll_div = $('
') + const $dummy_scroll_div = $('
') .css({'position':'absolute', 'overflow-x':'scroll', 'overflow-y':'scroll', @@ -93,39 +159,39 @@ var Oncoprint = (function () { 'height':'1px', }).addClass('oncoprintjs__scroll_div'); - var $dummy_scroll_div_contents = $('
').appendTo($dummy_scroll_div); + const $dummy_scroll_div_contents = $('
').appendTo($dummy_scroll_div); - var $cell_overlay_canvas = $('') + const $cell_overlay_canvas = $('') .attr({'width':'0px', 'height':'0px'}) .css({'position':'absolute', 'top':'0px', 'left':'0px'}) .addClass("noselect") - .addClass('oncoprintjs__cell_overlay_div'); + .addClass('oncoprintjs__cell_overlay_div') as JQuery; - var $column_label_canvas = $('') + const $column_label_canvas = $('') .attr({'width':'0px', 'height':'0px'}) .css({'position':'absolute', 'top':'0px', 'left':'0px'}) .addClass("noselect") - .addClass('oncoprintjs__column_label_canvas'); + .addClass('oncoprintjs__column_label_canvas') as JQuery; - var $track_info_div = $('
') + const $track_info_div = $('
') .css({'position':'absolute'}); - var $minimap_div = $('
').css({'position':'absolute', 'outline':'solid 1px black', 'display': 'none'}).addClass("noselect"); + const $minimap_div = $('
').css({'position':'absolute', 'outline':'solid 1px black', 'display': 'none'}).addClass("noselect"); - var $minimap_canvas = $('') + const $minimap_canvas = $('') .attr('width', 300) .attr('height', 300) .css({'position':'absolute', 'top':'0px', 'left':'0px', 'z-index':0}) - .addClass("noselect"); - var $minimap_overlay_canvas = $('') + .addClass("noselect") as JQuery; + const $minimap_overlay_canvas = $('') .attr('width', 300) .attr('height', 300) .css({'position': 'absolute', 'top':'0px', 'left':'0px', 'z-index':1}) - .addClass("noselect"); + .addClass("noselect") as JQuery; $label_canvas.appendTo($oncoprint_ctr); $cell_div.appendTo($oncoprint_ctr); @@ -163,83 +229,78 @@ var Oncoprint = (function () { this.model = new OncoprintModel(params); this.cell_view = new OncoprintWebGLCellView($cell_div, $cell_canvas, $cell_overlay_canvas, $column_label_canvas, $dummy_scroll_div_contents, this.model, new OncoprintToolTip($tooltip_ctr), function(left, right) { - var enclosed_ids = self.model.getIdsInLeftInterval(left, right); + const enclosed_ids = self.model.getIdsInLeftInterval(left, right); self.setHorzZoom(self.model.getHorzZoomToFit(self.cell_view.visible_area_width, enclosed_ids)); self.$dummy_scroll_div.scrollLeft(self.model.getZoomedColumnLeft(enclosed_ids[0])); self.id_clipboard = enclosed_ids; }, function(uid, track_id) { - doCellMouseOver(self, uid, track_id); + self.doCellMouseOver(uid, track_id); }, function(uid, track_id) { - doCellClick(self, uid, track_id); + self.doCellClick(uid, track_id); } ); this.minimap_view = new OncoprintMinimapView($minimap_div, $minimap_canvas, $minimap_overlay_canvas, this.model, this.cell_view, 150, 150, function(x,y) { self.setScroll(x,y); }, - function(vp) { + function(vp:MinimapViewportSpec) { self.setViewport(vp.col,vp.scroll_y_proportion,vp.num_cols, vp.zoom_y); }, - function(val) { - var prev_viewport = self.cell_view.getViewportOncoprintSpace(self.model); - var prev_center_onc_space = (prev_viewport.left + prev_viewport.right) / 2; - var center_column = Math.floor(prev_center_onc_space / (self.model.getCellWidth(true) + self.model.getCellPadding(true))); + function(val:number) { + const prev_viewport = self.cell_view.getViewportOncoprintSpace(self.model); + const prev_center_onc_space = (prev_viewport.left + prev_viewport.right) / 2; + const center_column = Math.floor(prev_center_onc_space / (self.model.getCellWidth(true) + self.model.getCellPadding(true))); self.setHorzZoom(val); - var viewport = self.cell_view.getViewportOncoprintSpace(self.model); - var center_column_x_zoomed = center_column * (self.model.getCellWidth() + self.model.getCellPadding()); - var half_viewport_width_zoomed = self.model.getHorzZoom() * (viewport.right - viewport.left) / 2; + const viewport = self.cell_view.getViewportOncoprintSpace(self.model); + const center_column_x_zoomed = center_column * (self.model.getCellWidth() + self.model.getCellPadding()); + const half_viewport_width_zoomed = self.model.getHorzZoom() * (viewport.right - viewport.left) / 2; self.setHorzScroll(center_column_x_zoomed - half_viewport_width_zoomed); }, - function(val) { - var prev_viewport = self.cell_view.getViewportOncoprintSpace(self.model); - var center_onc_space = (prev_viewport.top + prev_viewport.bottom) / 2; + function(val:number) { + const prev_viewport = self.cell_view.getViewportOncoprintSpace(self.model); + const center_onc_space = (prev_viewport.top + prev_viewport.bottom) / 2; self.setVertZoom(val); - var viewport = self.cell_view.getViewportOncoprintSpace(self.model); - var half_viewport_height_zoomed = self.model.getVertZoom() * (viewport.bottom - viewport.top) / 2; + const viewport = self.cell_view.getViewportOncoprintSpace(self.model); + const half_viewport_height_zoomed = self.model.getVertZoom() * (viewport.bottom - viewport.top) / 2; self.setVertScroll(center_onc_space * self.model.getVertZoom() - half_viewport_height_zoomed); }, function() { - updateHorzZoomToFit(self); - var left = self.model.getZoomedColumnLeft(); + self.updateHorzZoomToFit(); + const left = self.model.getZoomedColumnLeft(); self.setHorzScroll(Math.min.apply(null, self.keep_horz_zoomed_to_fit_ids.map(function(id) { return left[id]; }))); }, function() { self.setMinimapVisible(false); }); - /*this.minimap_view = {}; - var methods = ['moveTrack','addTracks','removeTrack','setHorzZoom','setVertZoom','setScroll','setZoom','setHorzScroll','setVertScroll','setTrackData','sort','shareRuleSet','setRuleSet','setIdOrder','suppressRendering','releaseRendering','hideIds']; - for (var i=0; i 0) { - var new_previous_track = null; + let new_previous_track = null; if (index >= 2) { new_previous_track = tracks[index-2]; } self.moveTrack(track_id, new_previous_track); } }, - function (track_id) { + function (track_id:TrackId) { // move down - var tracks = self.model.getContainingTrackGroup(track_id); - var index = tracks.indexOf(track_id); + const tracks = self.model.getContainingTrackGroup(track_id); + const index = tracks.indexOf(track_id); if (index < tracks.length - 1) { self.moveTrack(track_id, tracks[index+1]); } }, - function (track_id) { self.removeTrack(track_id); }, + function (track_id:TrackId) { self.removeTrack(track_id); }, function (track_id, dir) { self.setTrackSortDirection(track_id, dir); }, - function (track_id) { self.removeExpansionTracksFor(track_id); }); + function (track_id:TrackId) { self.removeExpansionTracksFor(track_id); }); this.track_info_view = new OncoprintTrackInfoView($track_info_div, new OncoprintToolTip($tooltip_ctr)); //this.track_info_view = new OncoprintTrackInfoView($track_info_div); @@ -258,16 +319,16 @@ var Oncoprint = (function () { // We need to handle scrolling this way because for some reason huge // canvas elements have terrible resolution. - var cell_view = this.cell_view; - var model = this.model; + const cell_view = this.cell_view; + const model = this.model; this.target_dummy_scroll_left = 0; this.target_dummy_scroll_top = 0; (function setUpOncoprintScroll(oncoprint) { $dummy_scroll_div.scroll(function (e) { - var dummy_scroll_left = $dummy_scroll_div.scrollLeft(); - var dummy_scroll_top = $dummy_scroll_div.scrollTop(); + const dummy_scroll_left = $dummy_scroll_div.scrollLeft(); + const dummy_scroll_top = $dummy_scroll_div.scrollTop(); if (dummy_scroll_left !== self.target_dummy_scroll_left || dummy_scroll_top !== self.target_dummy_scroll_top) { // In setDummyScrollDivScroll, where we intend to set the scroll programmatically without // triggering the handler, we set target_dummy_scroll_left and target_dummy_scroll_top, @@ -276,20 +337,20 @@ var Oncoprint = (function () { // Set oncoprint scroll to match self.target_dummy_scroll_left = dummy_scroll_left; self.target_dummy_scroll_top = dummy_scroll_top; - var maximum_dummy_scroll_div_scroll = maxDummyScrollDivScroll(oncoprint); - var maximum_div_scroll_left = maximum_dummy_scroll_div_scroll.left; - var maximum_div_scroll_top = maximum_dummy_scroll_div_scroll.top; - var scroll_left_prop = maximum_div_scroll_left > 0 ? dummy_scroll_left / maximum_div_scroll_left : 0; - var scroll_top_prop = maximum_div_scroll_top > 0 ? dummy_scroll_top / maximum_div_scroll_top : 0; + const maximum_dummy_scroll_div_scroll = oncoprint.maxDummyScrollDivScroll(); + const maximum_div_scroll_left = maximum_dummy_scroll_div_scroll.left; + const maximum_div_scroll_top = maximum_dummy_scroll_div_scroll.top; + let scroll_left_prop = maximum_div_scroll_left > 0 ? dummy_scroll_left / maximum_div_scroll_left : 0; + let scroll_top_prop = maximum_div_scroll_top > 0 ? dummy_scroll_top / maximum_div_scroll_top : 0; scroll_left_prop = clamp(scroll_left_prop, 0, 1); scroll_top_prop = clamp(scroll_top_prop, 0, 1); - var maximum_scroll_left = maxOncoprintScrollLeft(self); - var maximum_scroll_top = maxOncoprintScrollTop(self); - var scroll_left = Math.round(maximum_scroll_left * scroll_left_prop); - var scroll_top = Math.round(maximum_scroll_top * scroll_top_prop); + const maximum_scroll_left = oncoprint.maxOncoprintScrollLeft(); + const maximum_scroll_top = oncoprint.maxOncoprintScrollTop(); + const scroll_left = Math.round(maximum_scroll_left * scroll_left_prop); + const scroll_top = Math.round(maximum_scroll_top * scroll_top_prop); self.keep_horz_zoomed_to_fit = false; - doSetScroll(self, scroll_left, scroll_top); + oncoprint.doSetScroll(scroll_left, scroll_top); } }); })(self); @@ -303,85 +364,86 @@ var Oncoprint = (function () { this.clipboard_change_callbacks = []; this.pending_resize_and_organize = false; - - this.width = width; } - var _SetLegendTop = function (oncoprint) { - if (oncoprint.model.rendering_suppressed_depth > 0) { + private _SetLegendTop() { + if (this.model.rendering_suppressed_depth > 0) { return; } - oncoprint.$legend_div.css({'top': oncoprint.cell_view.getVisibleAreaHeight(oncoprint.model) + 30}); - }; - var setLegendTopAfterTimeout = function (oncoprint) { - if (oncoprint.model.rendering_suppressed_depth > 0) { + this.$legend_div.css({'top': this.cell_view.getVisibleAreaHeight(this.model) + 30}); + } + private setLegendTopAfterTimeout() { + if (this.model.rendering_suppressed_depth > 0) { return; } + const self = this; setTimeout(function () { - setHeight(oncoprint); - _SetLegendTop(oncoprint); + self.setHeight(); + self._SetLegendTop(); }, 0); - }; + } - var setHeight = function(oncoprint) { - oncoprint.$ctr.css({'min-height': oncoprint.cell_view.getVisibleAreaHeight(oncoprint.model) + Math.max(oncoprint.$legend_div.outerHeight(), (oncoprint.$minimap_div.is(":visible") ? oncoprint.$minimap_div.outerHeight() : 0)) + 30}); - }; + private setHeight() { + this.$ctr.css({'min-height': this.cell_view.getVisibleAreaHeight(this.model) + Math.max(this.$legend_div.outerHeight(), (this.$minimap_div.is(":visible") ? this.$minimap_div.outerHeight() : 0)) + 30}); + } - var resizeAndOrganize = function (oncoprint, onComplete) { - if (oncoprint.model.rendering_suppressed_depth > 0) { + private resizeAndOrganize(onComplete?:()=>void) { + if (this.model.rendering_suppressed_depth > 0) { return; } - var ctr_width = $(oncoprint.ctr_selector).width(); + const ctr_width = $(this.ctr_selector).width(); if (ctr_width === 0) { // dont make any adjustments while oncoprint is offscreen, DOM size calculations would be messed up - oncoprint.pending_resize_and_organize = true; + this.pending_resize_and_organize = true; return; } - oncoprint.$track_options_div.css({'left': oncoprint.label_view.getWidth()}); - oncoprint.$track_info_div.css({'left': oncoprint.label_view.getWidth() + oncoprint.track_options_view.getWidth()}); - var cell_div_left = oncoprint.label_view.getWidth() + oncoprint.track_options_view.getWidth() + oncoprint.track_info_view.getWidth(); - oncoprint.$cell_div.css('left', cell_div_left); - oncoprint.cell_view.setWidth(oncoprint.width - cell_div_left - 20, oncoprint.model); + this.$track_options_div.css({'left': this.label_view.getWidth()}); + this.$track_info_div.css({'left': this.label_view.getWidth() + this.track_options_view.getWidth()}); + const cell_div_left = this.label_view.getWidth() + this.track_options_view.getWidth() + this.track_info_view.getWidth(); + this.$cell_div.css('left', cell_div_left); + this.cell_view.setWidth(this.width - cell_div_left - 20, this.model); - _SetLegendTop(oncoprint); - oncoprint.legend_view.setWidth(oncoprint.width - oncoprint.$minimap_div.outerWidth() - 20, oncoprint.model); + this._SetLegendTop(); + this.legend_view.setWidth(this.width - this.$minimap_div.outerWidth() - 20, this.model); - setHeight(oncoprint); - oncoprint.$ctr.css({'min-width': oncoprint.width}); + this.setHeight(); + this.$ctr.css({'min-width': this.width}); + const self = this; setTimeout(function () { - if (oncoprint.keep_horz_zoomed_to_fit) { - updateHorzZoomToFit(oncoprint); + if (self.keep_horz_zoomed_to_fit) { + self.updateHorzZoomToFit(); } onComplete && onComplete(); }, 0); - }; + } - var resizeAndOrganizeAfterTimeout = function (oncoprint, onComplete) { - if (oncoprint.model.rendering_suppressed_depth > 0) { + private resizeAndOrganizeAfterTimeout(onComplete?:()=>void) { + if (this.model.rendering_suppressed_depth > 0) { return; } + const self = this; setTimeout(function () { - resizeAndOrganize(oncoprint, onComplete); + self.resizeAndOrganize(onComplete); }, 0); - }; + } - var maxOncoprintScrollLeft = function(oncoprint) { - return Math.max(0, oncoprint.cell_view.getTotalWidth(oncoprint.model) - oncoprint.cell_view.getVisibleAreaWidth()); - }; + private maxOncoprintScrollLeft() { + return Math.max(0, this.cell_view.getTotalWidth(this.model) - this.cell_view.getVisibleAreaWidth()); + } - var maxOncoprintScrollTop = function(oncoprint) { - return Math.max(0, oncoprint.cell_view.getTotalHeight(oncoprint.model)-oncoprint.cell_view.getVisibleAreaHeight(oncoprint.model)); - }; + private maxOncoprintScrollTop() { + return Math.max(0, this.cell_view.getTotalHeight(this.model)-this.cell_view.getVisibleAreaHeight(this.model)); + } - var maxDummyScrollDivScroll = function(oncoprint) { - var dummy_scroll_div_client_size = oncoprint.cell_view.getDummyScrollDivClientSize(); - var maximum_div_scroll_left = Math.max(0, (oncoprint.$dummy_scroll_div[0].scrollWidth - dummy_scroll_div_client_size.width)); - var maximum_div_scroll_top = Math.max(0, (oncoprint.$dummy_scroll_div[0].scrollHeight - dummy_scroll_div_client_size.height)); + private maxDummyScrollDivScroll() { + const dummy_scroll_div_client_size = this.cell_view.getDummyScrollDivClientSize(); + const maximum_div_scroll_left = Math.max(0, (this.$dummy_scroll_div[0].scrollWidth - dummy_scroll_div_client_size.width)); + const maximum_div_scroll_top = Math.max(0, (this.$dummy_scroll_div[0].scrollHeight - dummy_scroll_div_client_size.height)); return {'left': maximum_div_scroll_left, 'top': maximum_div_scroll_top}; - }; + } - Oncoprint.prototype.setMinimapVisible = function (visible) { + public setMinimapVisible(visible:boolean) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -389,30 +451,30 @@ var Oncoprint = (function () { this.$minimap_div.css({'display': 'block', 'top': this.cell_view.getVisibleAreaHeight(this.model) + 30, 'left': $(this.ctr_selector).width() - this.$minimap_div.outerWidth() - 10}); } else { this.$minimap_div.css('display', 'none'); - executeMinimapCloseCallbacks(this); + this.executeMinimapCloseCallbacks(); } - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.scrollTo = function(left) { + public scrollTo(left:number) { if(this.webgl_unavailable || this.destroyed) { return; } this.$dummy_scroll_div.scrollLeft(left); } - Oncoprint.prototype.onHorzZoom = function(callback) { + public onHorzZoom(callback:HorzZoomCallback) { if(this.webgl_unavailable || this.destroyed) { return; } this.horz_zoom_callbacks.push(callback); } - Oncoprint.prototype.onMinimapClose = function(callback) { + public onMinimapClose(callback:MinimapCloseCallback) { if(this.webgl_unavailable || this.destroyed) { return; } this.minimap_close_callbacks.push(callback); } - Oncoprint.prototype.moveTrack = function(target_track, new_previous_track) { + public moveTrack(target_track:TrackId, new_previous_track:TrackId) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -427,9 +489,9 @@ var Oncoprint = (function () { this.sort(); } - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.setTrackGroupOrder = function(index, track_order, dont_sort) { + public setTrackGroupOrder(index:TrackGroupIndex, track_order:TrackGroup, dont_sort?:boolean) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -443,19 +505,19 @@ var Oncoprint = (function () { this.sort(); } - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.setTrackGroupLegendOrder = function(group_order) { + public setTrackGroupLegendOrder(group_order:TrackGroup) { if(this.webgl_unavailable || this.destroyed) { return; } this.model.setTrackGroupLegendOrder(group_order); this.legend_view.setTrackGroupLegendOrder(this.model); - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.keepSorted = function(keep_sorted) { + public keepSorted(keep_sorted:boolean) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -465,20 +527,20 @@ var Oncoprint = (function () { } } - Oncoprint.prototype.addTracks = function (params_list) { + public addTracks(params_list:UserTrackSpec[]) { if(this.webgl_unavailable || this.destroyed) { return; } // Update model - var track_ids = []; - params_list = params_list.map(function (o) { + const track_ids:TrackId[] = []; + const library_params_list = (params_list as LibraryTrackSpec[]).map(function (o) { o.track_id = nextTrackId(); o.rule_set = OncoprintRuleSet(o.rule_set_params); track_ids.push(o.track_id); return o; }); - this.model.addTracks(params_list); + this.model.addTracks(library_params_list); // Update views this.cell_view.addTracks(this.model, track_ids); this.label_view.addTracks(this.model, track_ids); @@ -490,11 +552,11 @@ var Oncoprint = (function () { if (this.keep_sorted && this.model.isSortAffected(track_ids, "track")) { this.sort(); } - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); return track_ids; } - Oncoprint.prototype.removeTrack = function (track_id) { + public removeTrack(track_id:TrackId) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -511,54 +573,54 @@ var Oncoprint = (function () { if (this.keep_sorted && this.model.isSortAffected(track_id, "track")) { this.sort(); } - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.removeTracks = function(track_ids) { + public removeTracks(track_ids:TrackId[]) { if(this.webgl_unavailable || this.destroyed) { return; } this.keepSorted(false); this.suppressRendering(); - for (var i=0; i 0 ? scroll_left/maximum_scroll_left : 0; - var onc_scroll_top_prop = maximum_scroll_top > 0 ? scroll_top/maximum_scroll_top : 0; + this.cell_view.setScroll(this.model); + this.label_view.setScroll(this.model); + this.track_info_view.setScroll(this.model); + this.track_options_view.setScroll(this.model); + this.minimap_view.setScroll(this.model, this.cell_view); + } + + private setDummyScrollDivScroll() { + const scroll_left = this.model.getHorzScroll(); + const scroll_top = this.model.getVertScroll(); + + const maximum_scroll_left = this.maxOncoprintScrollLeft(); + const maximum_scroll_top = this.maxOncoprintScrollTop(); + let onc_scroll_left_prop = maximum_scroll_left > 0 ? scroll_left/maximum_scroll_left : 0; + let onc_scroll_top_prop = maximum_scroll_top > 0 ? scroll_top/maximum_scroll_top : 0; onc_scroll_left_prop = clamp(onc_scroll_left_prop, 0, 1); onc_scroll_top_prop = clamp(onc_scroll_top_prop, 0, 1); - var maximum_dummy_scroll_div_scroll = maxDummyScrollDivScroll(oncoprint); - var maximum_div_scroll_left = maximum_dummy_scroll_div_scroll.left; - var maximum_div_scroll_top = maximum_dummy_scroll_div_scroll.top; + const maximum_dummy_scroll_div_scroll = this.maxDummyScrollDivScroll(); + const maximum_div_scroll_left = maximum_dummy_scroll_div_scroll.left; + const maximum_div_scroll_top = maximum_dummy_scroll_div_scroll.top; - oncoprint.target_dummy_scroll_left = Math.round(onc_scroll_left_prop * maximum_div_scroll_left); - oncoprint.target_dummy_scroll_top = Math.round(onc_scroll_top_prop * maximum_div_scroll_top); - oncoprint.$dummy_scroll_div.scrollLeft(oncoprint.target_dummy_scroll_left); - oncoprint.$dummy_scroll_div.scrollTop(oncoprint.target_dummy_scroll_top); - }; + this.target_dummy_scroll_left = Math.round(onc_scroll_left_prop * maximum_div_scroll_left); + this.target_dummy_scroll_top = Math.round(onc_scroll_top_prop * maximum_div_scroll_top); + this.$dummy_scroll_div.scrollLeft(this.target_dummy_scroll_left); + this.$dummy_scroll_div.scrollTop(this.target_dummy_scroll_top); + } - Oncoprint.prototype.setScroll = function(scroll_left, scroll_top) { + public setScroll(scroll_left:number, scroll_top:number) { if(this.webgl_unavailable || this.destroyed) { return; } - doSetScroll(this, scroll_left, scroll_top); - setDummyScrollDivScroll(this); + this.doSetScroll(scroll_left, scroll_top); + this.setDummyScrollDivScroll(); } - Oncoprint.prototype.setZoom = function(zoom_x, zoom_y) { + public setZoom(zoom_x:number, zoom_y:number) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -733,12 +795,12 @@ var Oncoprint = (function () { this.minimap_view.setZoom(this.model, this.cell_view); } - Oncoprint.prototype.setHorzScroll = function(s) { + public setHorzScroll(s:number) { if(this.webgl_unavailable || this.destroyed) { return; } // Update model - this.model.setHorzScroll(Math.min(s, maxOncoprintScrollLeft(this))); + this.model.setHorzScroll(Math.min(s, this.maxOncoprintScrollLeft())); // Update views this.cell_view.setHorzScroll(this.model); this.label_view.setHorzScroll(this.model); @@ -746,16 +808,16 @@ var Oncoprint = (function () { this.track_options_view.setHorzScroll(this.model); this.minimap_view.setHorzScroll(this.model, this.cell_view); // Update dummy scroll div - setDummyScrollDivScroll(this); + this.setDummyScrollDivScroll(); return this.model.getHorzScroll(); } - Oncoprint.prototype.setVertScroll = function(s) { + public setVertScroll(s:number) { if(this.webgl_unavailable || this.destroyed) { return; } // Update model - this.model.setVertScroll(Math.min(s, maxOncoprintScrollTop(this))); + this.model.setVertScroll(Math.min(s, this.maxOncoprintScrollTop())); // Update views this.cell_view.setVertScroll(this.model); this.label_view.setVertScroll(this.model); @@ -763,33 +825,33 @@ var Oncoprint = (function () { this.track_options_view.setVertScroll(this.model); this.minimap_view.setVertScroll(this.model, this.cell_view); // Update dummy scroll div - setDummyScrollDivScroll(this); + this.setDummyScrollDivScroll(); return this.model.getVertScroll(); } - Oncoprint.prototype.setViewport = function(col, scroll_y_proportion, num_cols, zoom_y) { + public setViewport(col:number, scroll_y_proportion:number, num_cols:number, zoom_y:number) { if(this.webgl_unavailable || this.destroyed) { return; } // Zoom - var zoom_x = this.model.getHorzZoomToFitNumCols(this.cell_view.getVisibleAreaWidth(), num_cols); + const zoom_x = this.model.getHorzZoomToFitNumCols(this.cell_view.getVisibleAreaWidth(), num_cols); this.setZoom(zoom_x, zoom_y); // Scroll - var scroll_left = Math.min(col * (this.model.getCellWidth() + this.model.getCellPadding()), maxOncoprintScrollLeft(this)); - var scroll_top = Math.min(scroll_y_proportion*this.model.getOncoprintHeight(), maxOncoprintScrollTop(this)); + const scroll_left = Math.min(col * (this.model.getCellWidth() + this.model.getCellPadding()), this.maxOncoprintScrollLeft()); + const scroll_top = Math.min(scroll_y_proportion*this.model.getOncoprintHeight(), this.maxOncoprintScrollTop()); this.setScroll(scroll_left, scroll_top); - executeHorzZoomCallbacks(this); + this.executeHorzZoomCallbacks(); } - Oncoprint.prototype.getTrackData = function (track_id) { + public getTrackData(track_id:TrackId) { if(this.webgl_unavailable || this.destroyed) { return; } return this.model.getTrackData(track_id); } - Oncoprint.prototype.getTrackDataIdKey = function(track_id) { + public getTrackDataIdKey(track_id:TrackId) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -804,7 +866,7 @@ var Oncoprint = (function () { * @param {string} data_id_key - name of the property of the * data objects to use as the (column) key */ - Oncoprint.prototype.setTrackData = function (track_id, data, data_id_key) { + public setTrackData(track_id:TrackId, data:Datum[], data_id_key:string&keyof Datum) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -816,20 +878,20 @@ var Oncoprint = (function () { if (this.keep_sorted && this.model.isSortAffected(track_id, "track")) { this.sort(); } - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.setTrackImportantIds = function(track_id, ids) { + public setTrackImportantIds(track_id:TrackId, ids:ColumnId[]|undefined) { if(this.webgl_unavailable || this.destroyed) { return; } this.model.setTrackImportantIds(track_id, ids); this.cell_view.setTrackImportantIds(this.model, track_id); this.legend_view.setTrackImportantIds(this.model); - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.setTrackGroupSortPriority = function(priority) { + public setTrackGroupSortPriority(priority:TrackGroupIndex[]) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -839,10 +901,10 @@ var Oncoprint = (function () { if (this.keep_sorted) { this.sort(); } - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.setTrackSortDirection = function(track_id, dir) { + public setTrackSortDirection(track_id:TrackId, dir:TrackSortDirection) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -856,7 +918,7 @@ var Oncoprint = (function () { return this.model.getTrackSortDirection(track_id); } - Oncoprint.prototype.setTrackSortComparator = function(track_id, sortCmpFn) { + public setTrackSortComparator(track_id:TrackId, sortCmpFn:TrackSortSpecification) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -866,14 +928,14 @@ var Oncoprint = (function () { } } - Oncoprint.prototype.getTrackSortDirection = function(track_id) { + public getTrackSortDirection(track_id:TrackId) { if(this.webgl_unavailable || this.destroyed) { return; } return this.model.getTrackSortDirection(track_id); } - Oncoprint.prototype.setTrackInfo = function(track_id, msg) { + public setTrackInfo(track_id:TrackId, msg:string) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -881,26 +943,26 @@ var Oncoprint = (function () { this.track_info_view.setTrackInfo(this.model); } - Oncoprint.prototype.setTrackTooltipFn = function(track_id, tooltipFn) { + public setTrackTooltipFn(track_id:TrackId, tooltipFn:TrackTooltipFn) { if(this.webgl_unavailable || this.destroyed) { return; } this.model.setTrackTooltipFn(track_id, tooltipFn); } - Oncoprint.prototype.setShowTrackSublabels = function(show) { + public setShowTrackSublabels(show:boolean) { this.model.setShowTrackSublabels(show); this.label_view.setShowTrackSublabels(this.model); - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.sort = function() { + public sort() { if(this.webgl_unavailable || this.destroyed) { return; } - var self = this; - this.model.sort().then(function(x) {; + const self = this; + this.model.sort().then(function(x) { self.label_view.sort(self.model); self.cell_view.sort(self.model); self.minimap_view.sort(self.model, self.cell_view); @@ -915,7 +977,7 @@ var Oncoprint = (function () { }); } - Oncoprint.prototype.shareRuleSet = function(source_track_id, target_track_id) { + public shareRuleSet(source_track_id:TrackId, target_track_id:TrackId) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -925,7 +987,7 @@ var Oncoprint = (function () { this.minimap_view.shareRuleSet(this.model, this.cell_view); } - Oncoprint.prototype.setRuleSet = function(track_id, rule_set_params) { + public setRuleSet(track_id:TrackId, rule_set_params:RuleSetParams) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -933,10 +995,10 @@ var Oncoprint = (function () { this.cell_view.setRuleSet(this.model, track_id); this.legend_view.setRuleSet(this.model); this.minimap_view.setRuleSet(this.model, this.cell_view); - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.setSortConfig = function(params) { + public setSortConfig(params:SortConfig) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -948,7 +1010,7 @@ var Oncoprint = (function () { this.sort(); } } - Oncoprint.prototype.setIdOrder = function(ids) { + public setIdOrder(ids:ColumnId[]) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -963,7 +1025,7 @@ var Oncoprint = (function () { } } - Oncoprint.prototype.disableInteraction = function() { + public disableInteraction() { if(this.webgl_unavailable || this.destroyed) { return; } @@ -973,7 +1035,7 @@ var Oncoprint = (function () { //this.track_info_view.disableInteraction(); //this.legend_view.disableInteraction(); } - Oncoprint.prototype.enableInteraction = function() { + public enableInteraction() { if(this.webgl_unavailable || this.destroyed) { return; } @@ -983,7 +1045,7 @@ var Oncoprint = (function () { //this.track_info_view.enableInteraction(); //this.legend_view.enableInteraction(); } - Oncoprint.prototype.suppressRendering = function() { + public suppressRendering() { if(this.webgl_unavailable || this.destroyed) { return; } @@ -996,7 +1058,7 @@ var Oncoprint = (function () { this.minimap_view.suppressRendering(); } - Oncoprint.prototype.releaseRendering = function(onComplete) { + public releaseRendering(onComplete?:()=>void) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -1009,21 +1071,21 @@ var Oncoprint = (function () { this.track_info_view.releaseRendering(this.model); this.legend_view.releaseRendering(this.model); this.minimap_view.releaseRendering(this.model, this.cell_view); - resizeAndOrganizeAfterTimeout(this, onComplete); + this.resizeAndOrganizeAfterTimeout(onComplete); } } - Oncoprint.prototype.triggerPendingResizeAndOrganize = function(onComplete) { + public triggerPendingResizeAndOrganize(onComplete?:()=>void) { if(this.webgl_unavailable || this.destroyed) { return; } if (this.pending_resize_and_organize) { this.pending_resize_and_organize = false; - resizeAndOrganizeAfterTimeout(this, onComplete); + this.resizeAndOrganizeAfterTimeout(onComplete); } } - Oncoprint.prototype.hideIds = function(to_hide, show_others) { + public hideIds(to_hide:ColumnId[], show_others?:boolean) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -1032,27 +1094,27 @@ var Oncoprint = (function () { this.minimap_view.hideIds(this.model, this.cell_view); } - Oncoprint.prototype.hideTrackLegends = function(track_ids) { + public hideTrackLegends(track_ids:TrackId[]) { if(this.webgl_unavailable || this.destroyed) { return; } track_ids = [].concat(track_ids); this.model.hideTrackLegends(track_ids); this.legend_view.hideTrackLegends(this.model); - setLegendTopAfterTimeout(this); + this.setLegendTopAfterTimeout(); } - Oncoprint.prototype.showTrackLegends = function(track_ids) { + public showTrackLegends(track_ids:TrackId[]) { if(this.webgl_unavailable || this.destroyed) { return; } track_ids = [].concat(track_ids); this.model.showTrackLegends(track_ids); this.legend_view.showTrackLegends(this.model); - setLegendTopAfterTimeout(this); + this.setLegendTopAfterTimeout(); } - Oncoprint.prototype.setCellPaddingOn = function(cell_padding_on) { + public setCellPaddingOn(cell_padding_on:boolean) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -1060,108 +1122,108 @@ var Oncoprint = (function () { this.cell_view.setCellPaddingOn(this.model); } - Oncoprint.prototype.setTrackCustomOptions = function(track_id, options) { + public setTrackCustomOptions(track_id:TrackId, options:CustomTrackOption[]|undefined) { this.model.setTrackCustomOptions(track_id, options); this.track_options_view.setTrackCustomOptions(this.model); } - Oncoprint.prototype.setTrackInfoTooltip = function(track_id, $tooltip_elt) { + public setTrackInfoTooltip(track_id:TrackId, $tooltip_elt:JQuery|undefined) { this.model.setTrackInfoTooltip(track_id, $tooltip_elt); } - Oncoprint.prototype.setTrackMovable = function(track_id, movable) { + public setTrackMovable(track_id:TrackId, movable:boolean) { this.model.setTrackMovable(track_id, movable); this.track_options_view.setTrackMovable(this.model); this.label_view.setTrackMovable(this.model); } - Oncoprint.prototype.setWidth = function(width) { + public setWidth(width:number) { this.width = width; - resizeAndOrganize(this); + this.resizeAndOrganize(); } - Oncoprint.prototype.setColumnLabels = function(labels) { + public setColumnLabels(labels:ColumnProp) { this.model.setColumnLabels(labels); this.cell_view.setColumnLabels(this.model); - resizeAndOrganizeAfterTimeout(this); + this.resizeAndOrganizeAfterTimeout(); } - Oncoprint.prototype.onCellMouseOver = function(callback) { + public onCellMouseOver(callback:CellMouseOverCallback) { this.cell_mouse_over_callbacks.push(callback); } - Oncoprint.prototype.onCellClick = function(callback) { + public onCellClick(callback:CellClickCallback) { this.cell_click_callbacks.push(callback); } - Oncoprint.prototype.toSVG = function(with_background) { + public toSVG(with_background?:boolean) { if(this.webgl_unavailable || this.destroyed) { return; } // Returns svg DOM element - var root = svgfactory.svg(10, 10); + const root = svgfactory.svg(10, 10); this.$ctr.append(root); - var everything_group = svgfactory.group(0,0); + const everything_group = svgfactory.group(0,0); root.appendChild(everything_group); - var bgrect = svgfactory.bgrect(10,10,'#ffffff'); + const bgrect = svgfactory.bgrect(10,10,'#ffffff'); if (with_background) { everything_group.appendChild(bgrect); } - var label_view_group = this.label_view.toSVGGroup(this.model, true, 0, 0); + const label_view_group = this.label_view.toSVGGroup(this.model, true, 0, 0); everything_group.appendChild(label_view_group); - var track_info_group_x = label_view_group.getBBox().width + 30; - var track_info_group = this.track_info_view.toSVGGroup(this.model, track_info_group_x, 0); + const track_info_group_x = label_view_group.getBBox().width + 30; + const track_info_group = this.track_info_view.toSVGGroup(this.model, track_info_group_x, 0); everything_group.appendChild(track_info_group); - var cell_view_group_x = track_info_group_x + track_info_group.getBBox().width + 10; - var cell_view_group = this.cell_view.toSVGGroup(this.model, cell_view_group_x, 0); + const cell_view_group_x = track_info_group_x + track_info_group.getBBox().width + 10; + const cell_view_group = this.cell_view.toSVGGroup(this.model, cell_view_group_x, 0); everything_group.appendChild(cell_view_group); everything_group.appendChild(this.legend_view.toSVGGroup(this.model, 0, cell_view_group.getBBox().y + cell_view_group.getBBox().height+20)); - var everything_box = everything_group.getBBox(); - var everything_width = everything_box.x + everything_box.width; - var everything_height = everything_box.y + everything_box.height; - root.setAttribute('width', everything_width); - root.setAttribute('height', everything_height); + const everything_box = everything_group.getBBox(); + const everything_width = everything_box.x + everything_box.width; + const everything_height = everything_box.y + everything_box.height; + root.setAttribute('width', everything_width as any); + root.setAttribute('height', everything_height as any); if (with_background) { - bgrect.setAttribute('width', everything_width); - bgrect.setAttribute('height', everything_height); + bgrect.setAttribute('width', everything_width as any); + bgrect.setAttribute('height', everything_height as any); } root.parentNode.removeChild(root); return root; } - Oncoprint.prototype.toCanvas = function(callback, resolution) { + public toCanvas(callback:(canvas:HTMLCanvasElement, truncated:boolean)=>void, resolution?:number) { if(this.webgl_unavailable || this.destroyed) { return; } // Returns data url, requires IE >= 11 - var MAX_CANVAS_SIDE = 8192; - var svg = this.toSVG(true); + const MAX_CANVAS_SIDE = 8192; + const svg = this.toSVG(true); svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); - var width = parseInt(svg.getAttribute('width'), 10); - var height = parseInt(svg.getAttribute('height'), 10); - var canvas = document.createElement('canvas'); + const width = parseInt(svg.getAttribute('width'), 10); + const height = parseInt(svg.getAttribute('height'), 10); + const canvas = document.createElement('canvas'); resolution = resolution || 1; - var truncated = width*resolution > MAX_CANVAS_SIDE || height*resolution > MAX_CANVAS_SIDE; - canvas.setAttribute('width', Math.min(MAX_CANVAS_SIDE, width*resolution)); - canvas.setAttribute('height', Math.min(MAX_CANVAS_SIDE, height*resolution)); + const truncated = width*resolution > MAX_CANVAS_SIDE || height*resolution > MAX_CANVAS_SIDE; + canvas.setAttribute('width', Math.min(MAX_CANVAS_SIDE, width*resolution).toString()); + canvas.setAttribute('height', Math.min(MAX_CANVAS_SIDE, height*resolution).toString()); - var container = document.createElement("div"); + const container = document.createElement("div"); container.appendChild(svg); - var svg_data_str = container.innerHTML; - var svg_data_uri = "data:image/svg+xml;base64,"+window.btoa(svg_data_str); + const svg_data_str = container.innerHTML; + const svg_data_uri = "data:image/svg+xml;base64,"+window.btoa(svg_data_str); - var ctx = canvas.getContext('2d'); + const ctx = canvas.getContext('2d'); ctx.setTransform(resolution,0,0,resolution,0,0); - var img = new Image(); + const img = new Image(); img.onload = function() { ctx.drawImage(img, 0, 0); @@ -1175,7 +1237,7 @@ var Oncoprint = (function () { return img; } - Oncoprint.prototype.toDataUrl = function(callback) { + public toDataUrl(callback:(dataURL:string)=>void) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -1184,14 +1246,14 @@ var Oncoprint = (function () { }); } - Oncoprint.prototype.highlightTrack = function(track_id) { + public highlightTrack(track_id:TrackId) { if(this.webgl_unavailable || this.destroyed) { return; } this.label_view.highlightTrack(track_id, this.model); } - Oncoprint.prototype.setHighlightedIds = function(ids) { + public setHighlightedIds(ids:ColumnId[]) { if(this.webgl_unavailable || this.destroyed) { return; } @@ -1199,36 +1261,36 @@ var Oncoprint = (function () { this.cell_view.setHighlightedIds(this.model); } - Oncoprint.prototype.getIdOrder = function(all) { + public getIdOrder(all?:boolean) { if(this.webgl_unavailable || this.destroyed) { return; } return this.model.getIdOrder(all); } - Oncoprint.prototype.setIdClipboardContents = function(array) { + public setIdClipboardContents(array:ColumnId[]) { if(this.webgl_unavailable || this.destroyed) { return; } this.id_clipboard = array.slice(); - for (var i=0; i 1) && - !model.isTrackInClusteredGroup(track_id) && - model.getTrackMovable(track_id) - ) { - startDragging(view, track_id, evt.offsetY); - } - }); - - view.$canvas.on("mousemove", function(evt) { - if (view.dragged_label_track_id !== null) { - var track_group = model.getContainingTrackGroup(view.dragged_label_track_id); - var bottommost_track = model.getLastExpansion(track_group[track_group.length - 1]); - var max_drag_y = view.track_tops[bottommost_track] + model.getTrackHeight(bottommost_track) - view.scroll_y; - var min_drag_y = view.track_tops[track_group[0]] - 5 - view.scroll_y; - view.drag_mouse_y = Math.min(evt.pageY - view.$canvas.offset().top, max_drag_y); - view.drag_mouse_y = Math.max(view.drag_mouse_y, min_drag_y); - renderAllLabels(view); - } else { - var hovered_track = isMouseOnLabel(view, evt.pageY - view.$canvas.offset().top); - if (hovered_track !== null) { - var $tooltip_div = $('
'); - var offset = view.$canvas[0].getBoundingClientRect(); - if (isNecessaryToShortenLabel(view, view.labels[hovered_track]) - || view.track_link_urls[hovered_track]) { - $tooltip_div.append(formatTooltipHeader( - view.labels[hovered_track], - view.html_labels[hovered_track], - view.track_link_urls[hovered_track])); - } - var track_description = view.track_descriptions[hovered_track]; - if (track_description.length > 0) { - $tooltip_div.append($('
').text(track_description)); - } - // dragging info - if (model.getTrackMovable(hovered_track)) { - if (model.isTrackInClusteredGroup(hovered_track)) { - view.$canvas.css('cursor', 'not-allowed'); - $tooltip_div.append("dragging disabled for clustered tracks"); - } else if (model.getContainingTrackGroup(hovered_track).length > 1) { - view.$canvas.css('cursor', 'move'); - $tooltip_div.append("hold to drag"); - } - } - if ($tooltip_div.contents().length > 0) { - view.tooltip.fadeIn(200, renderedLabelWidth(view, view.labels[hovered_track]) + offset.left, view.cell_tops[hovered_track] + offset.top - view.scroll_y, $tooltip_div); - } - } else { - view.$canvas.css('cursor', 'auto'); - view.tooltip.hide(); - } - } - }); - - view.$canvas.on("mouseup mouseleave", function(evt) { - if (view.dragged_label_track_id !== null) { - var track_group = model.getContainingTrackGroup(view.dragged_label_track_id); - var previous_track_id = getLabelAboveMouseSpace(view, track_group, evt.offsetY, view.dragged_label_track_id); - stopDragging(view, previous_track_id); - } - view.tooltip.hideIfNotAlreadyGoingTo(150); - }); - })(this); - - } - var renderedLabelWidth = function(view, label) { - return view.ctx.measureText(shortenLabelIfNecessary(view, label)).width/view.supersampling_ratio; - }; - var updateFromModel = function(view, model) { - if (view.rendering_suppressed) { - return; - } - view.show_sublabels = model.getShowTrackSublabels(); - view.scroll_y = model.getVertScroll(); - view.track_tops = model.getZoomedTrackTops(); - view.cell_tops = model.getCellTops(); - view.cell_tops_view_space = {}; - view.cell_heights = {}; - view.tracks = model.getTracks(); - view.track_descriptions = {}; - - view.ctx.font = 'bold '+view.getFontSize()+'px Arial'; - view.minimum_track_height = Number.POSITIVE_INFINITY; - view.maximum_label_width = 0; - for (var i=0; i getMaximumLabelLength(view); - }; - var shortenLabelIfNecessary = function(view, label) { - if (isNecessaryToShortenLabel(view, label)) { - return label.substring(0, getMaximumLabelLength(view)-3) + '...'; - } else { - return label; - } - }; - var formatTooltipHeader = function (label, html_label, link_url) { - var header_contents; - if (link_url) { - header_contents = ( - $('') - .attr('href', link_url)); - } else { - header_contents = $(''); - } - header_contents.append(html_label || document.createTextNode(label)); - return $('').append(header_contents); - }; - var renderAllLabels = function(view) { - if (view.rendering_suppressed) { - return; - } - view.ctx.clearRect(0,0,view.$canvas[0].width,view.$canvas[0].height); - - if (view.highlighted_track !== null) { - if (view.cell_tops_view_space.hasOwnProperty(view.highlighted_track)) { - view.ctx.fillStyle = 'rgba(255,255,0,0.4)'; - view.ctx.fillRect(0, view.cell_tops_view_space[view.highlighted_track], view.getWidth()*view.supersampling_ratio, view.cell_heights_view_space[view.highlighted_track]); - } - } - var font_size = view.getFontSize(); - var tracks = view.tracks; - var sublabelX = {}; - for (var i=0; i y) { - break; - } else { - candidate_track = track_ids[i]; - } - } - return candidate_track; - } - } - var getLabelBelowMouseSpace = function(view, track_ids, y, track_to_exclude) { - if (y > view.cell_tops[track_ids[track_ids.length-1]] - view.scroll_y) { - return null; - } else { - var candidate_track = null; - for (var i=track_ids.length-1; i>=0; i--) { - if (track_to_exclude !== null && track_to_exclude === track_ids[i]) { - continue; - } - if (view.cell_tops[track_ids[i]] - view.scroll_y < y) { - break; - } else { - candidate_track = track_ids[i]; - } - } - return candidate_track; - } - } - - var startDragging = function(view, track_id, mouse_y) { - view.dragged_label_track_id = track_id; - view.drag_mouse_y = mouse_y; - renderAllLabels(view); - } - var stopDragging = function(view, new_previous_track_id) { - view.drag_callback(view.dragged_label_track_id, new_previous_track_id); - view.dragged_label_track_id = null; - renderAllLabels(view); - } - - var getMaximumLabelLength = function(view) { - return 18; - }; - - OncoprintLabelView.prototype.getWidth = function() { - //return this.maximum_label_width + 20; - return Math.max(this.maximum_label_width/this.supersampling_ratio + 10, 70); - } - OncoprintLabelView.prototype.getFontSize = function(no_supersampling_adjustment) { - return (no_supersampling_adjustment ? 1 : this.supersampling_ratio) * Math.max(Math.min(this.base_font_size, this.minimum_track_height), 7); - } - OncoprintLabelView.prototype.setDragCallback = function(callback) { - this.drag_callback = callback; - } - OncoprintLabelView.prototype.removeTrack = function (model, track_id) { - updateFromModel(this, model); - resizeAndClear(this, model); - renderAllLabels(this, model); - } - OncoprintLabelView.prototype.moveTrack = function (model) { - updateFromModel(this, model); - resizeAndClear(this, model); - renderAllLabels(this, model); - } - OncoprintLabelView.prototype.setTrackGroupOrder = function (model) { - updateFromModel(this, model); - resizeAndClear(this, model); - renderAllLabels(this, model); - } - OncoprintLabelView.prototype.addTracks = function (model, track_ids) { - for (var i=0; i = {}; + private cell_tops:TrackProp = {}; + private cell_tops_view_space:TrackProp = {}; + private cell_tops_this_space:TrackProp = {}; + private cell_heights:TrackProp = {}; + private cell_heights_view_space:TrackProp = {}; + private cell_heights_this_space:TrackProp = {}; + private label_middles_view_space:TrackProp = {}; + private label_middles_this_space:TrackProp = {}; + private label_left_padding:TrackProp = {}; + private labels:TrackProp = {}; + private sublabels:TrackProp = {}; + private label_colors:TrackProp = {}; + private label_circle_colors:TrackProp = {}; + private label_font_weight:TrackProp = {}; + private html_labels:TrackProp = {}; + private track_link_urls:TrackProp = {}; + private track_descriptions:TrackProp = {}; + private minimum_track_height = Number.POSITIVE_INFINITY; + private maximum_label_width = Number.NEGATIVE_INFINITY; + private tracks:TrackId[] = []; + private show_sublabels:boolean; + + private rendering_suppressed = false; + private highlighted_track:TrackId|null = null; + private drag_callback:(target_track:TrackId, new_previous_track:TrackId)=>void; + private dragged_label_track_id:TrackId|null; + private drag_mouse_y:number|null; + private scroll_y:number = 0; + private ctx:CanvasRenderingContext2D; + + constructor(private $canvas:JQuery, private model:OncoprintModel, private tooltip:OncoprintToolTip) { + const view = this; + this.show_sublabels = model.getShowTrackSublabels(); + + this.setUpContext(); + + (function setUpDragging(view) { + view.drag_callback = function(target_track, new_previous_track) {}; + view.dragged_label_track_id = null; + view.drag_mouse_y = null; + + view.$canvas.on("mousedown", function(evt) { + view.tooltip.hide(); + const track_id = view.isMouseOnLabel(evt.offsetY); + if (track_id !== null && + (model.getContainingTrackGroup(track_id).length > 1) && + !model.isTrackInClusteredGroup(track_id) && + model.getTrackMovable(track_id) + ) { + view.startDragging(track_id, evt.offsetY); + } + }); + + view.$canvas.on("mousemove", function(evt) { + if (view.dragged_label_track_id !== null) { + const track_group = model.getContainingTrackGroup(view.dragged_label_track_id); + const bottommost_track = model.getLastExpansion(track_group[track_group.length - 1]); + const max_drag_y = view.track_tops[bottommost_track] + model.getTrackHeight(bottommost_track) - view.scroll_y; + const min_drag_y = view.track_tops[track_group[0]] - 5 - view.scroll_y; + view.drag_mouse_y = Math.min(evt.pageY - view.$canvas.offset().top, max_drag_y); + view.drag_mouse_y = Math.max(view.drag_mouse_y, min_drag_y); + view.renderAllLabels(); + } else { + const hovered_track = view.isMouseOnLabel(evt.pageY - view.$canvas.offset().top); + if (hovered_track !== null) { + const $tooltip_div = $('
'); + const offset = view.$canvas[0].getBoundingClientRect(); + if (view.isNecessaryToShortenLabel(view.labels[hovered_track]) + || view.track_link_urls[hovered_track]) { + $tooltip_div.append(OncoprintLabelView.formatTooltipHeader( + view.labels[hovered_track], + view.html_labels[hovered_track], + view.track_link_urls[hovered_track])); + } + const track_description = view.track_descriptions[hovered_track]; + if (track_description.length > 0) { + $tooltip_div.append($('
').text(track_description)); + } + // dragging info + if (model.getTrackMovable(hovered_track)) { + if (model.isTrackInClusteredGroup(hovered_track)) { + view.$canvas.css('cursor', 'not-allowed'); + $tooltip_div.append("dragging disabled for clustered tracks"); + } else if (model.getContainingTrackGroup(hovered_track).length > 1) { + view.$canvas.css('cursor', 'move'); + $tooltip_div.append("hold to drag"); + } + } + if ($tooltip_div.contents().length > 0) { + view.tooltip.fadeIn(200, view.renderedLabelWidth(view.labels[hovered_track]) + offset.left, view.cell_tops[hovered_track] + offset.top - view.scroll_y, $tooltip_div); + } + } else { + view.$canvas.css('cursor', 'auto'); + view.tooltip.hide(); + } + } + }); + + view.$canvas.on("mouseup mouseleave", function(evt) { + if (view.dragged_label_track_id !== null) { + const track_group = model.getContainingTrackGroup(view.dragged_label_track_id); + const previous_track_id = view.getLabelAboveMouseSpace(track_group, evt.offsetY, view.dragged_label_track_id); + view.stopDragging(previous_track_id); + } + view.tooltip.hideIfNotAlreadyGoingTo(150); + }); + })(this); + } + + private circleRadius() { + return this.minimum_track_height*0.8/2; + } + + private renderedLabelWidth(label:string) { + return this.ctx.measureText(this.shortenLabelIfNecessary(label)).width/this.supersampling_ratio; + } + + private updateFromModel(model:OncoprintModel) { + if (this.rendering_suppressed) { + return; + } + this.show_sublabels = model.getShowTrackSublabels(); + this.scroll_y = model.getVertScroll(); + this.track_tops = model.getZoomedTrackTops() as TrackProp; + this.cell_tops = model.getCellTops() as TrackProp; + this.cell_tops_this_space = {}; + this.cell_heights = {}; + this.tracks = model.getTracks(); + this.track_descriptions = {}; + + this.ctx.font = 'bold '+this.getFontSize()+'px Arial'; + this.minimum_track_height = Number.POSITIVE_INFINITY; + this.maximum_label_width = 0; + for (let i=0; i this.getMaximumLabelLength(); + } + private shortenLabelIfNecessary(label:string) { + if (this.isNecessaryToShortenLabel(label)) { + return label.substring(0, this.getMaximumLabelLength()-3) + '...'; + } else { + return label; + } + } + private static formatTooltipHeader(label:string, html_label:any, link_url:string) { + let header_contents; + if (link_url) { + header_contents = ( + $('') + .attr('href', link_url)); + } else { + header_contents = $(''); + } + header_contents.append(html_label || document.createTextNode(label)); + return $('').append(header_contents); + } + private renderAllLabels() { + if (this.rendering_suppressed) { + return; + } + this.ctx.clearRect(0,0,this.$canvas[0].width,this.$canvas[0].height); + + if (this.highlighted_track !== null) { + if (this.cell_tops_this_space.hasOwnProperty(this.highlighted_track)) { + this.ctx.fillStyle = 'rgba(255,255,0,0.4)'; + this.ctx.fillRect(0, this.cell_tops_this_space[this.highlighted_track], this.getWidth()*this.supersampling_ratio, this.cell_heights_this_space[this.highlighted_track]); + } + } + const font_size = this.getFontSize(); + const tracks = this.tracks; + const sublabelX:TrackProp = {}; + for (let i=0; i y) { + break; + } else { + candidate_track = track_ids[i]; + } + } + return candidate_track; + } + } + private getLabelBelowMouseSpace(track_ids:TrackId[], y:number, track_to_exclude:TrackId|null) { + if (y > this.cell_tops[track_ids[track_ids.length-1]] - this.scroll_y) { + return null; + } else { + let candidate_track = null; + for (let i=track_ids.length-1; i>=0; i--) { + if (track_to_exclude !== null && track_to_exclude === track_ids[i]) { + continue; + } + if (this.cell_tops[track_ids[i]] - this.scroll_y < y) { + break; + } else { + candidate_track = track_ids[i]; + } + } + return candidate_track; + } + } + + private startDragging(track_id:TrackId, mouse_y:number) { + this.dragged_label_track_id = track_id; + this.drag_mouse_y = mouse_y; + this.renderAllLabels(); + } + private stopDragging(new_previous_track_id:TrackId) { + this.drag_callback(this.dragged_label_track_id, new_previous_track_id); + this.dragged_label_track_id = null; + this.renderAllLabels(); + } + + private getMaximumLabelLength() { + return 18; + } + + public getWidth() { + //return this.maximum_label_width + 20; + return Math.max(this.maximum_label_width/this.supersampling_ratio + 10, 70); + } + public getFontSize(no_supersampling_adjustment?:boolean) { + return (no_supersampling_adjustment ? 1 : this.supersampling_ratio) * Math.max(Math.min(this.base_font_size, this.minimum_track_height), 7); + } + public setDragCallback(callback:OncoprintLabelView["drag_callback"]) { + this.drag_callback = callback; + } + public removeTrack(model:OncoprintModel, track_id:TrackId) { + this.updateFromModel(model); + this.resizeAndClear(model); + this.renderAllLabels(); + } + public moveTrack(model:OncoprintModel) { + this.updateFromModel(model); + this.resizeAndClear(model); + this.renderAllLabels(); + } + public setTrackGroupOrder(model:OncoprintModel) { + this.updateFromModel(model); + this.resizeAndClear(model); + this.renderAllLabels(); + } + public addTracks(model:OncoprintModel, track_ids:TrackId[]) { + for (let i=0; i; + const tracks = model.getTracks(); + for (let i=0; i; + private rendering_suppressed = false; + private width:number; - this.rule_set_label_config = { - weight: 'bold', - size: 12, - font: 'Arial' - }; - this.rule_label_config = { - weight: 'normal', - size: 12, - font: 'Arial' - }; + private rule_set_label_config = { + weight: 'bold', + size: 12, + font: 'Arial' + }; + private rule_label_config = { + weight: 'normal', + size: 12, + font: 'Arial' + }; + + private padding_after_rule_set_label = 10; + private padding_between_rules = 20; + private padding_between_rule_set_rows = 10; - this.padding_after_rule_set_label = 10; - this.padding_between_rules = 20; - this.padding_between_rule_set_rows = 10; + constructor (private $div:JQuery, private base_width:number, private base_height:number) { + this.$svg = $(svgfactory.svg(200,200)).appendTo(this.$div); + this.width = $div.width(); } - var renderLegend = function(view, model, target_svg, show_all) { - if (view.rendering_suppressed) { + private renderLegend(model:OncoprintModel, target_svg?:SVGElement, show_all?:boolean) { + if (this.rendering_suppressed) { return; } if (typeof target_svg === 'undefined') { - target_svg = view.$svg[0]; + target_svg = this.$svg[0]; } - if (!nodeIsVisible(target_svg)) { + if (!nodeIsVisible(target_svg as any as HTMLElement)) { return; } $(target_svg).empty(); - var defs = svgfactory.defs(); + const defs = svgfactory.defs(); target_svg.appendChild(defs); - var everything_group = svgfactory.group(0,0); + const everything_group = svgfactory.group(0,0); target_svg.appendChild(everything_group); - var rule_sets = model.getRuleSets(); - var y = 0; - var rule_start_x = 200; - for (var i=0; i 0) { - var label = svgfactory.text(rule_sets[i].legend_label, 0, 0, 12, 'Arial', 'bold'); + const label = svgfactory.text(rule_sets[i].legend_label, 0, 0, 12, 'Arial', 'bold'); rule_set_group.appendChild(label); svgfactory.wrapText(label, rule_start_x); } })(); - var x = rule_start_x + view.padding_after_rule_set_label; - var in_group_y_offset = 0; + let x = rule_start_x + this.padding_after_rule_set_label; + let in_group_y_offset = 0; - var labelSort = function(ruleA, ruleB) { - var labelA = ruleA.rule.legend_label; - var labelB = ruleB.rule.legend_label; + const labelSort = function(ruleA:RuleWithId, ruleB:RuleWithId) { + const labelA = ruleA.rule.legend_label; + const labelB = ruleB.rule.legend_label; if (labelA && labelB) { return labelA.localeCompare(labelB); } else if (!labelA && !labelB) { @@ -97,8 +99,8 @@ var OncoprintLegendView = (function() { rules.sort(function(ruleA, ruleB) { // sort, by legend_order, then alphabetically - var orderA = ruleA.rule.legend_order; - var orderB = ruleB.rule.legend_order; + const orderA = ruleA.rule.legend_order; + const orderB = ruleB.rule.legend_order; if (typeof orderA === "undefined" && typeof orderB === "undefined") { // if neither have defined order, then sort alphabetically @@ -129,161 +131,157 @@ var OncoprintLegendView = (function() { } } }); - for (var j=0; j view.width) { - x = rule_start_x + view.padding_after_rule_set_label; - in_group_y_offset = rule_set_group.getBBox().height + view.padding_between_rule_set_rows; + if (x + group.getBBox().width > this.width) { + x = rule_start_x + this.padding_after_rule_set_label; + in_group_y_offset = rule_set_group.getBBox().height + this.padding_between_rule_set_rows; group.setAttribute('transform', 'translate('+x+','+in_group_y_offset+')'); } x += group.getBBox().width; - x += view.padding_between_rules; + x += this.padding_between_rules; } y += rule_set_group.getBBox().height; - y += 3*view.padding_between_rule_set_rows; + y += 3*this.padding_between_rule_set_rows; } - var everything_box = everything_group.getBBox(); - view.$svg[0].setAttribute('width', everything_box.width); + const everything_box = everything_group.getBBox(); + this.$svg[0].setAttribute('width', everything_box.width.toString()); // add 10px to height to give room for rectangle stroke, which doesn't factor in accurately into the bounding box // so that bounding boxes are too small to show the entire stroke (see https://github.com/cBioPortal/cbioportal/issues/3994) - view.$svg[0].setAttribute('height', everything_box.height + 10); - }; + this.$svg[0].setAttribute('height', (everything_box.height + 10).toString()); + } - var ruleToSVGGroup = function(rule, view, model, target_svg, target_defs) { - var root = svgfactory.group(0,0); - var config = rule.getLegendConfig(); + private ruleToSVGGroup(rule:Rule, model:OncoprintModel, target_svg:SVGElement, target_defs:SVGDefsElement) { + const root = svgfactory.group(0,0); + const config = rule.getLegendConfig(); if (config.type === 'rule') { - var concrete_shapes = rule.apply(config.target, model.getCellWidth(true), view.base_height); + const concrete_shapes = rule.apply(config.target, model.getCellWidth(true), this.base_height); if (rule.legend_base_color) { // generate backgrounds - var baseRect = svgfactory.rect(0, 0, model.getCellWidth(true), view.base_height, rule.legend_base_color); + const baseRect = svgfactory.rect(0, 0, model.getCellWidth(true), this.base_height, rule.legend_base_color); root.appendChild(baseRect); } // generate shapes - for (var i=0; ivoid; + private layout_numbers = { + window_width:-1, + window_height:-1, + vertical_zoom_area_width:-1, + horizontal_zoom_area_height:-1, + padding:-1, + window_bar_height:-1, + canvas_left:-1, + canvas_top:-1 + }; + private current_rect:OverlayRectSpec = { top:0, left: 0, width: 0, height: 0, col: 0, num_cols: 0 }; + + private $window_bar:JQuery; + private $close_btn:JQuery; + private horizontal_zoom:OncoprintZoomSlider; + private vertical_zoom:OncoprintZoomSlider; + + private ctx:OncoprintWebGLContext|null; + private overlay_ctx:CanvasRenderingContext2D|null; + private pMatrix:any; + private mvMatrix:any; + private shader_program:OncoprintShaderProgram; + + private resize_hover:"r"|"l"|"t"|"b"|"tl"|"br"|"bl"|"tr"|false = false; + + private rendering_suppressed = false; + + constructor( + private $div:JQuery, + private $canvas:JQuery, + private $overlay_canvas:JQuery, + model:OncoprintModel, + cell_view:OncoprintWebGLCellView, + width:number, + height:number, + drag_callback:(x:number, y:number)=>void, + viewport_callback:(vp:MinimapViewportSpec)=>void, + horz_zoom_callback:(z:number)=>void, + vert_zoom_callback:(z:number)=>void, + zoom_to_fit_callback:()=>void, + close_callback:()=>void + ) { this.$div = $div; this.$canvas = $canvas; this.$overlay_canvas = $overlay_canvas; - var self = this; - var padding = 4; - var vertical_zoom_area_width = 20; - var horizontal_zoom_area_height = 20; - var window_bar_height = 20; + const self = this; + const padding = 4; + const vertical_zoom_area_width = 20; + const horizontal_zoom_area_height = 20; + const window_bar_height = 20; this.handleContextLost = (function() { // catch when context lost and refresh it // eg if cell view uses a ton of contexts, then browser clears oldest context, // then the minimap would be empty until we refresh the context and rerender - drawOncoprintAndOverlayRect(this, model, cell_view); + self.drawOncoprintAndOverlayRect(model, cell_view); }).bind(this); this.$canvas[0].addEventListener("webglcontextlost", this.handleContextLost); @@ -201,6 +93,7 @@ var OncoprintMinimapView = (function () { canvas_left: padding, canvas_top: window_bar_height + padding, }; + this.$div.css({'min-width': this.layout_numbers.window_width, 'min-height': this.layout_numbers.window_height, 'outline':'solid 1px black', 'background-color':'#ffffff'}); @@ -245,9 +138,9 @@ var OncoprintMinimapView = (function () { 'onChange': function(val) { vert_zoom_callback(val); }}); (function setUpZoomToFitButton() { - var btn_height = self.layout_numbers.horizontal_zoom_area_height - padding; - var btn_width = self.layout_numbers.vertical_zoom_area_width - padding; - var $btn = $('
').css({'position': 'absolute', + const btn_height = self.layout_numbers.horizontal_zoom_area_height - padding; + const btn_width = self.layout_numbers.vertical_zoom_area_width - padding; + const $btn = $('
').css({'position': 'absolute', 'min-height': btn_height, 'min-width': btn_width, 'outline': 'solid 1px black', @@ -268,75 +161,75 @@ var OncoprintMinimapView = (function () { zoom_to_fit_callback = zoom_to_fit_callback || function() {}; $btn.click(zoom_to_fit_callback); })(); - getWebGLContextAndSetUpMatrices(this); - setUpShaders(this); + this.getWebGLContextAndSetUpMatrices(); + this.setUpShaders(); this.overlay_ctx = $overlay_canvas[0].getContext("2d"); - this.img = new Image(); - this.current_rect = {'top': 0, 'left': 0, 'width': 0, 'height': 0, 'col':0, 'num_cols':0}; - - var self = this; - this.img.onload = function () { - self.ctx.drawImage(img, 0, 0); - }; - // Set up dragging - var resize_hit_zone = 5; - var mouseInRectDragZone = function (x, y) { + const resize_hit_zone = 5; + function mouseInRectDragZone(x:number, y:number) { return ((x >= self.current_rect.left + resize_hit_zone) && (x <= self.current_rect.left + self.current_rect.width - resize_hit_zone) && (y >= self.current_rect.top + resize_hit_zone) && (y <= self.current_rect.top + self.current_rect.height - resize_hit_zone)); - }; - var mouseInsideRectHitZone = function(x,y) { + } + + function mouseInsideRectHitZone(x:number, y:number) { return (x >= self.current_rect.left - resize_hit_zone) && (x <= self.current_rect.left + self.current_rect.width + resize_hit_zone) && (y >= self.current_rect.top - resize_hit_zone) && (y <= self.current_rect.top + self.current_rect.height + resize_hit_zone); - }; - var mouseInRightHorzResizeZone = function (x,y) { + } + + function mouseInRightHorzResizeZone(x:number, y:number) { return !mouseInTopLeftResizeZone(x,y) && !mouseInTopRightResizeZone(x,y) && !mouseInBottomLeftResizeZone(x,y) && !mouseInBottomRightResizeZone(x,y) && mouseInsideRectHitZone(x,y) && (Math.abs(x - (self.current_rect.left + self.current_rect.width)) < resize_hit_zone); - }; - var mouseInLeftHorzResizeZone = function (x,y) { + } + + function mouseInLeftHorzResizeZone(x:number, y:number) { return !mouseInTopLeftResizeZone(x,y) && !mouseInTopRightResizeZone(x,y) && !mouseInBottomLeftResizeZone(x,y) && !mouseInBottomRightResizeZone(x,y) && mouseInsideRectHitZone(x,y) && (Math.abs(x - self.current_rect.left) < resize_hit_zone); - }; - var mouseInTopVertResizeZone = function (x,y) { + } + + function mouseInTopVertResizeZone(x:number, y:number) { return !mouseInTopLeftResizeZone(x,y) && !mouseInTopRightResizeZone(x,y) && !mouseInBottomLeftResizeZone(x,y) && !mouseInBottomRightResizeZone(x,y) && mouseInsideRectHitZone(x,y) && (Math.abs(y - self.current_rect.top) < resize_hit_zone); - }; - var mouseInBottomVertResizeZone = function (x, y) { + } + + function mouseInBottomVertResizeZone(x:number, y:number) { return !mouseInTopLeftResizeZone(x, y) && !mouseInTopRightResizeZone(x, y) && !mouseInBottomLeftResizeZone(x, y) && !mouseInBottomRightResizeZone(x, y) && mouseInsideRectHitZone(x,y) && (Math.abs(y - (self.current_rect.top + self.current_rect.height)) < resize_hit_zone); - }; - var mouseInTopLeftResizeZone = function(x,y) { + } + + function mouseInTopLeftResizeZone(x:number, y:number) { return (Math.abs(y - self.current_rect.top) < resize_hit_zone) && (Math.abs(x - self.current_rect.left) < resize_hit_zone); - }; - var mouseInBottomLeftResizeZone = function(x,y) { + } + + function mouseInBottomLeftResizeZone(x:number, y:number) { return (Math.abs(y - (self.current_rect.top + self.current_rect.height)) < resize_hit_zone) && (Math.abs(x - self.current_rect.left) < resize_hit_zone); - }; - var mouseInTopRightResizeZone = function(x,y) { + } + + function mouseInTopRightResizeZone(x:number, y:number) { return (Math.abs(y - self.current_rect.top) < resize_hit_zone) && (Math.abs(x - (self.current_rect.left + self.current_rect.width)) < resize_hit_zone); - }; - var mouseInBottomRightResizeZone = function(x,y) { + } + + function mouseInBottomRightResizeZone(x:number, y:number) { return (Math.abs(y - (self.current_rect.top + self.current_rect.height)) < resize_hit_zone) && (Math.abs(x - (self.current_rect.left + self.current_rect.width)) < resize_hit_zone); - }; + } - this.resize_hover = false; - var updateRectResizeHoverLocation = function(x,y) { + function updateRectResizeHoverLocation(x?:number, y?:number) { if (typeof x === "undefined") { self.resize_hover = false; } else { @@ -360,9 +253,10 @@ var OncoprintMinimapView = (function () { self.resize_hover = false; } } - }; - var updateCSSCursor = function(x, y) { - var cursor_val; + } + + function updateCSSCursor(x?:number, y?:number) { + let cursor_val; if (typeof x === "undefined") { cursor_val = 'auto'; } else { @@ -381,44 +275,48 @@ var OncoprintMinimapView = (function () { } } $div.css('cursor', cursor_val); - }; - var getCanvasMouse = function(view, div_mouse_x, div_mouse_y) { - var canv_top = parseInt(view.$canvas[0].style.top, 10); - var canv_left = parseInt(view.$canvas[0].style.left, 10); - var canv_width = parseInt(view.$canvas[0].width, 10); - var canv_height = parseInt(view.$canvas[0].height, 10); + } - var mouse_x = div_mouse_x - canv_left; - var mouse_y = div_mouse_y - canv_top; + function getCanvasMouse(view:OncoprintMinimapView, div_mouse_x:number, div_mouse_y:number) { + const canv_top = parseInt(view.$canvas[0].style.top, 10); + const canv_left = parseInt(view.$canvas[0].style.left, 10); + const canv_width = parseInt(view.$canvas[0].width as any, 10); + const canv_height = parseInt(view.$canvas[0].height as any, 10); - var outside = mouse_x < 0 || mouse_x >= canv_width || mouse_y < 0 || mouse_y >= canv_height; + const mouse_x = div_mouse_x - canv_left; + const mouse_y = div_mouse_y - canv_top; + + const outside = mouse_x < 0 || mouse_x >= canv_width || mouse_y < 0 || mouse_y >= canv_height; return {'mouse_x': mouse_x, 'mouse_y': mouse_y, 'outside': outside}; - }; - var dragging = false; - var drag_type = false; - var drag_start_col = -1; - var drag_start_vert_scroll = -1; - var drag_start_x = -1; - var drag_start_y = -1; - var drag_start_vert_zoom = -1; - var y_ratio = -1; + } + + let dragging = false; + let drag_type:"move"|"resize_r"|"resize_l"|"resize_b"|"resize_t"|"resize_tr"|"resize_br"|"resize_tl"|"resize_bl"|false = false; + let drag_start_col = -1; + let drag_start_vert_scroll = -1; + let drag_start_x = -1; + let drag_start_y = -1; + let drag_start_vert_zoom = -1; + let y_ratio = -1; + let drag_start_rect:OverlayRectSpec; + $(document).on("mousedown", function (evt) { - var offset = self.$div.offset(); - var overlay_mouse_x = evt.pageX - offset.left; - var overlay_mouse_y = evt.pageY - offset.top; - var mouse = getCanvasMouse(self, overlay_mouse_x, overlay_mouse_y); + const offset = self.$div.offset(); + const overlay_mouse_x = evt.pageX - offset.left; + const overlay_mouse_y = evt.pageY - offset.top; + const mouse = getCanvasMouse(self, overlay_mouse_x, overlay_mouse_y); if (!mouse.outside) { - var mouse_x = mouse.mouse_x; - var mouse_y = mouse.mouse_y; + const mouse_x = mouse.mouse_x; + const mouse_y = mouse.mouse_y; dragging = false; drag_type = false; - y_ratio = model.getOncoprintHeight() / parseInt(self.$canvas[0].height, 10); + y_ratio = model.getOncoprintHeight() / parseInt(self.$canvas[0].height as any, 10); if (mouseInRectDragZone(mouse_x, mouse_y)) { drag_type = "move"; } else if (mouseInRightHorzResizeZone(mouse_x, mouse_y)) { @@ -450,30 +348,30 @@ var OncoprintMinimapView = (function () { } }); $(document).on("mousemove", function (evt) { - var offset = self.$div.offset(); - var overlay_mouse_x = evt.pageX - offset.left; - var overlay_mouse_y = evt.pageY - offset.top; - var mouse = getCanvasMouse(self, overlay_mouse_x, overlay_mouse_y); - var mouse_x = mouse.mouse_x; - var mouse_y = mouse.mouse_y; - var zoom = getZoom(self, model); - var cell_width = model.getCellWidth(true)*zoom.x; + const offset = self.$div.offset(); + const overlay_mouse_x = evt.pageX - offset.left; + const overlay_mouse_y = evt.pageY - offset.top; + const mouse = getCanvasMouse(self, overlay_mouse_x, overlay_mouse_y); + const mouse_x = mouse.mouse_x; + const mouse_y = mouse.mouse_y; + let zoom = self.getZoom(model); + let cell_width = model.getCellWidth(true)*zoom.x; if (dragging) { evt.preventDefault(); - var delta_col = Math.floor(mouse_x / cell_width) - Math.floor(drag_start_x / cell_width); - var delta_y = mouse_y - drag_start_y; + let delta_col = Math.floor(mouse_x / cell_width) - Math.floor(drag_start_x / cell_width); + let delta_y = mouse_y - drag_start_y; if (drag_type === "move") { - var delta_y_scroll = delta_y * y_ratio; + const delta_y_scroll = delta_y * y_ratio; drag_callback((drag_start_col + delta_col)*(model.getCellWidth() + model.getCellPadding()), drag_start_vert_scroll + delta_y_scroll); } else { - var render_rect; - var zoom = getZoom(self, model); - var max_num_cols = model.getIdOrder().length; - var min_num_cols = Math.ceil(cell_view.getVisibleAreaWidth() / (model.getCellWidth(true) + model.getCellPadding(true, true))); - var max_height = model.getOncoprintHeight(true) * zoom.y; - var min_height = model.getCellViewHeight() * zoom.y; - var drag_start_right_col = drag_start_rect.col + drag_start_rect.num_cols; - var drag_start_bottom = drag_start_rect.top + drag_start_rect.height; + let render_rect:Partial; + zoom = self.getZoom(model); + const max_num_cols = model.getIdOrder().length; + const min_num_cols = Math.ceil(cell_view.getVisibleAreaWidth() / (model.getCellWidth(true) + model.getCellPadding(true, true))); + const max_height = model.getOncoprintHeight(true) * zoom.y; + const min_height = model.getCellViewHeight() * zoom.y; + const drag_start_right_col = drag_start_rect.col + drag_start_rect.num_cols; + const drag_start_bottom = drag_start_rect.top + drag_start_rect.height; if (drag_type === "resize_r") { // Width must be valid delta_col = clamp(delta_col, @@ -600,14 +498,14 @@ var OncoprintMinimapView = (function () { 'top': drag_start_rect.top, 'col': drag_start_rect.col + delta_col, 'num_cols': drag_start_rect.num_cols - delta_col, - 'height': drag_start_rect.height + delta_y + 'height': drag_start_rect.height + delta_y, }; } - var cell_width = model.getCellWidth(true)*zoom.x; + cell_width = model.getCellWidth(true)*zoom.x; // Compute render left and width render_rect.left = render_rect.col * cell_width; render_rect.width = render_rect.num_cols * cell_width; - drawOverlayRect(self, null, null, render_rect); + self.drawOverlayRect(null, null, render_rect as OverlayRectSpec); } } else { if (mouse.outside) { @@ -617,16 +515,17 @@ var OncoprintMinimapView = (function () { updateCSSCursor(mouse_x, mouse_y); updateRectResizeHoverLocation(mouse_x, mouse_y); } - drawOverlayRect(self, model, cell_view); + self.drawOverlayRect(model, cell_view); } }); - var endDrag = function() { + + function endDrag() { if (dragging) { if (["resize_t", "resize_b", "resize_l", "resize_r", - "resize_tl", "resize_tr", "resize_bl", "resize_br"].indexOf(drag_type) > -1) { + "resize_tl", "resize_tr", "resize_bl", "resize_br"].indexOf(drag_type as any) > -1) { viewport_callback({ 'col': self.current_rect.col, - 'scroll_y_proportion': (self.current_rect.top / parseInt(self.$canvas[0].height, 10)), + 'scroll_y_proportion': (self.current_rect.top / parseInt(self.$canvas[0].height as any, 10)), 'num_cols': self.current_rect.num_cols, 'zoom_y': (drag_start_rect.height / self.current_rect.height) * drag_start_vert_zoom }); @@ -634,36 +533,37 @@ var OncoprintMinimapView = (function () { dragging = false; drag_type = false; } - }; + } + $(document).on("mouseup", function (evt) { - var offset = self.$div.offset(); - var overlay_mouse_x = evt.pageX - offset.left; - var overlay_mouse_y = evt.pageY - offset.top; + const offset = self.$div.offset(); + const overlay_mouse_x = evt.pageX - offset.left; + const overlay_mouse_y = evt.pageY - offset.top; endDrag(); - var mouse = getCanvasMouse(self, overlay_mouse_x, overlay_mouse_y); + const mouse = getCanvasMouse(self, overlay_mouse_x, overlay_mouse_y); if (!mouse.outside) { - var mouse_x = mouse.mouse_x; - var mouse_y = mouse.mouse_y; + let mouse_x = mouse.mouse_x; + let mouse_y = mouse.mouse_y; updateCSSCursor(mouse_x, mouse_y); updateRectResizeHoverLocation(mouse_x, mouse_y); } else { updateCSSCursor(); updateRectResizeHoverLocation(); } - drawOverlayRect(self, model, cell_view); + self.drawOverlayRect(model, cell_view); }); (function setUpWindowDrag() { - var start_mouse_x; - var start_mouse_y; - var start_left; - var start_top; - var handleDrag = function(evt) { + let start_mouse_x = 0; + let start_mouse_y = 0; + let start_left = 0; + let start_top = 0; + function handleDrag(evt:MouseMoveEvent) { evt.preventDefault(); - var delta_mouse_x = evt.pageX - start_mouse_x; - var delta_mouse_y = evt.pageY - start_mouse_y; + const delta_mouse_x = evt.pageX - start_mouse_x; + const delta_mouse_y = evt.pageY - start_mouse_y; self.setWindowPosition(start_left + delta_mouse_x, start_top + delta_mouse_y); - }; + } self.$window_bar.hover(function() { $(this).css({'cursor':'move'}); }, function() { @@ -681,45 +581,199 @@ var OncoprintMinimapView = (function () { $(document).off("mousemove", handleDrag); }); })(); + } + + + private getNewCanvas() { + const old_canvas = this.$canvas[0]; + old_canvas.removeEventListener("webglcontextlost", this.handleContextLost); + const new_canvas = old_canvas.cloneNode(); + new_canvas.addEventListener("webglcontextlost", this.handleContextLost); + const parent_node = old_canvas.parentNode; + parent_node.removeChild(old_canvas); + parent_node.insertBefore(new_canvas, this.$overlay_canvas[0]); + this.$canvas = $(new_canvas) as JQuery; + this.ctx = null; + } + + private getWebGLCanvasContext() { + try { + const canvas = this.$canvas[0]; + const ctx = this.ctx || canvas.getContext("experimental-webgl", {alpha: false, antialias: true}) as OncoprintWebGLContext; + ctx.clearColor(1.0, 1.0, 1.0, 1.0); + ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT); + ctx.viewportWidth = canvas.width; + ctx.viewportHeight = canvas.height; + ctx.viewport(0, 0, ctx.viewportWidth, ctx.viewportHeight); + ctx.enable(ctx.DEPTH_TEST); + ctx.enable(ctx.BLEND); + ctx.blendEquation(ctx.FUNC_ADD); + ctx.blendFunc(ctx.SRC_ALPHA, ctx.ONE_MINUS_SRC_ALPHA); + ctx.depthMask(false); + + return ctx; + } catch (e) { + return null; + } + } + + private ensureWebGLContext() { + for (let i=0; i<5; i++) { + if (!this.ctx || this.ctx.isContextLost()) { + // have to get a new canvas when context is lost by browser + this.getNewCanvas(); + this.ctx = this.getWebGLCanvasContext(); + this.setUpShaders(); + } else { + break; + } + } + if (!this.ctx || this.ctx.isContextLost()) { + throw new Error("Unable to get WebGL context for Oncoprint Minimap"); + } + } + + private createShaderProgram(vertex_shader:WebGLShader, fragment_shader:WebGLShader) { + const program = this.ctx.createProgram(); + this.ctx.attachShader(program, vertex_shader); + this.ctx.attachShader(program, fragment_shader); + this.ctx.linkProgram(program); + + const success = this.ctx.getProgramParameter(program, this.ctx.LINK_STATUS); + if (!success) { + const msg = this.ctx.getProgramInfoLog(program); + this.ctx.deleteProgram(program); + throw "Unable to link shader program: " + msg; + } + + return program; } - var getTrackBuffers = function (view, cell_view, track_id) { - var pos_buffer = view.ctx.createBuffer(); - var pos_array = cell_view.vertex_data[track_id].pos_array; + private createShader(source:string, type:"VERTEX_SHADER"|"FRAGMENT_SHADER") { + const shader = this.ctx.createShader(this.ctx[type]); + this.ctx.shaderSource(shader, source); + this.ctx.compileShader(shader); + + const success = this.ctx.getShaderParameter(shader, this.ctx.COMPILE_STATUS); + if (!success) { + const msg = this.ctx.getShaderInfoLog(shader); + this.ctx.deleteShader(shader); + throw "Unable to compile shader: " + msg; + } + + return shader; + } + + private getWebGLContextAndSetUpMatrices() { + this.ctx = this.getWebGLCanvasContext(); + (function initializeMatrices(self) { + const mvMatrix = gl_matrix.mat4.create(); + gl_matrix.mat4.lookAt(mvMatrix, [0, 0, 1], [0, 0, 0], [0, 1, 0]); + self.mvMatrix = mvMatrix; + + const pMatrix = gl_matrix.mat4.create(); + gl_matrix.mat4.ortho(pMatrix, 0, self.ctx.viewportWidth, self.ctx.viewportHeight, 0, -5, 1000); // y axis inverted so that y increases down like SVG + self.pMatrix = pMatrix; + })(this); + } - view.ctx.bindBuffer(view.ctx.ARRAY_BUFFER, pos_buffer); - view.ctx.bufferData(view.ctx.ARRAY_BUFFER, new Float32Array(pos_array), view.ctx.STATIC_DRAW); + private setUpShaders() { + const vertex_shader_source = ['precision highp float;', + 'attribute float aPosVertex;', + 'attribute float aColVertex;', + 'attribute float aVertexOncoprintColumn;', + 'uniform float columnWidth;', + 'uniform float zoomX;', + 'uniform float zoomY;', + 'uniform mat4 uMVMatrix;', + 'uniform mat4 uPMatrix;', + 'uniform float offsetY;', + 'uniform float positionBitPackBase;', + 'uniform float texSize;', + 'varying float texCoord;', + 'vec3 unpackVec3(float packedVec3, float base) {', + ' float pos0 = floor(packedVec3 / (base*base));', + ' float pos0Contr = pos0*base*base;', + ' float pos1 = floor((packedVec3 - pos0Contr)/base);', + ' float pos1Contr = pos1*base;', + ' float pos2 = packedVec3 - pos0Contr - pos1Contr;', + ' return vec3(pos0, pos1, pos2);', + '}', + 'void main(void) {', + ' gl_Position = vec4(unpackVec3(aPosVertex, positionBitPackBase), 1.0);', + ' gl_Position[0] += aVertexOncoprintColumn*columnWidth;', + ' gl_Position[1] += offsetY;', + ' gl_Position *= vec4(zoomX, zoomY, 1.0, 1.0);', + ' gl_Position = uPMatrix * uMVMatrix * gl_Position;', + ' texCoord = (aColVertex + 0.5) / texSize;', + '}'].join('\n'); + const fragment_shader_source = ['precision mediump float;', + 'varying float texCoord;', + 'uniform sampler2D uSampler;', + 'void main(void) {', + ' gl_FragColor = texture2D(uSampler, vec2(texCoord, 0.5));', + '}'].join('\n'); + const vertex_shader = this.createShader(vertex_shader_source, 'VERTEX_SHADER'); + const fragment_shader = this.createShader(fragment_shader_source, 'FRAGMENT_SHADER'); + + const shader_program = this.createShaderProgram(vertex_shader, fragment_shader) as OncoprintShaderProgram; + shader_program.vertexPositionAttribute = this.ctx.getAttribLocation(shader_program, 'aPosVertex'); + this.ctx.enableVertexAttribArray(shader_program.vertexPositionAttribute); + shader_program.vertexColorAttribute = this.ctx.getAttribLocation(shader_program, 'aColVertex'); + this.ctx.enableVertexAttribArray(shader_program.vertexColorAttribute); + shader_program.vertexOncoprintColumnAttribute = this.ctx.getAttribLocation(shader_program, 'aVertexOncoprintColumn'); + this.ctx.enableVertexAttribArray(shader_program.vertexOncoprintColumnAttribute); + + shader_program.samplerUniform = this.ctx.getUniformLocation(shader_program, 'uSampler'); + shader_program.pMatrixUniform = this.ctx.getUniformLocation(shader_program, 'uPMatrix'); + shader_program.mvMatrixUniform = this.ctx.getUniformLocation(shader_program, 'uMVMatrix'); + shader_program.columnWidthUniform = this.ctx.getUniformLocation(shader_program, 'columnWidth'); + shader_program.zoomXUniform = this.ctx.getUniformLocation(shader_program, 'zoomX'); + shader_program.zoomYUniform = this.ctx.getUniformLocation(shader_program, 'zoomY'); + shader_program.offsetYUniform = this.ctx.getUniformLocation(shader_program, 'offsetY'); + shader_program.positionBitPackBaseUniform = this.ctx.getUniformLocation(shader_program, 'positionBitPackBase'); + shader_program.texSizeUniform = this.ctx.getUniformLocation(shader_program, 'texSize'); + + this.shader_program = shader_program; + } + + private getTrackBuffers(cell_view:OncoprintWebGLCellView, track_id:TrackId) { + const pos_buffer = this.ctx.createBuffer() as OncoprintTrackBuffer; + const pos_array = cell_view.vertex_data[track_id].pos_array; + + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, pos_buffer); + this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(pos_array), this.ctx.STATIC_DRAW); pos_buffer.itemSize = 1; pos_buffer.numItems = pos_array.length / pos_buffer.itemSize; - var col_buffer = view.ctx.createBuffer(); - var col_array = cell_view.vertex_data[track_id].col_array; + const col_buffer = this.ctx.createBuffer() as OncoprintTrackBuffer; + const col_array = cell_view.vertex_data[track_id].col_array; - view.ctx.bindBuffer(view.ctx.ARRAY_BUFFER, col_buffer); - view.ctx.bufferData(view.ctx.ARRAY_BUFFER, new Float32Array(col_array), view.ctx.STATIC_DRAW); + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, col_buffer); + this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(col_array), this.ctx.STATIC_DRAW); col_buffer.itemSize = 1; col_buffer.numItems = col_array.length / col_buffer.itemSize; - var tex = view.ctx.createTexture(); - view.ctx.bindTexture(view.ctx.TEXTURE_2D, tex); + const tex = this.ctx.createTexture(); + this.ctx.bindTexture(this.ctx.TEXTURE_2D, tex); - var color_bank = cell_view.vertex_data[track_id].col_bank; - var width = Math.pow(2, Math.ceil(Math.log2(color_bank.length / 4))); + const color_bank = cell_view.vertex_data[track_id].col_bank; + const width = Math.pow(2, Math.ceil((Math as any).log2(color_bank.length / 4))); while (color_bank.length < 4 * width) { color_bank.push(0); } - var height = 1; - view.ctx.texImage2D(view.ctx.TEXTURE_2D, 0, view.ctx.RGBA, width, height, 0, view.ctx.RGBA, view.ctx.UNSIGNED_BYTE, new Uint8Array(color_bank)); - view.ctx.texParameteri(view.ctx.TEXTURE_2D, view.ctx.TEXTURE_MIN_FILTER, view.ctx.NEAREST); - view.ctx.texParameteri(view.ctx.TEXTURE_2D, view.ctx.TEXTURE_MAG_FILTER, view.ctx.NEAREST); + const height = 1; + this.ctx.texImage2D(this.ctx.TEXTURE_2D, 0, this.ctx.RGBA, width, height, 0, this.ctx.RGBA, this.ctx.UNSIGNED_BYTE, new Uint8Array(color_bank)); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.NEAREST); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.NEAREST); - var color_texture = {'texture': tex, 'size': width}; + const color_texture = {'texture': tex, 'size': width}; - var vertex_column_buffer = view.ctx.createBuffer(); - var vertex_column_array = cell_view.vertex_column_array[track_id]; - view.ctx.bindBuffer(view.ctx.ARRAY_BUFFER, vertex_column_buffer); - view.ctx.bufferData(view.ctx.ARRAY_BUFFER, new Float32Array(vertex_column_array), view.ctx.STATIC_DRAW); + const vertex_column_buffer = this.ctx.createBuffer() as OncoprintTrackBuffer; + const vertex_column_array = cell_view.vertex_column_array[track_id]; + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, vertex_column_buffer); + this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(vertex_column_array), this.ctx.STATIC_DRAW); vertex_column_buffer.itemSize = 1; vertex_column_buffer.numItems = vertex_column_array.length / vertex_column_buffer.itemSize; @@ -729,69 +783,70 @@ var OncoprintMinimapView = (function () { 'column': vertex_column_buffer}; }; - var drawOncoprint = function (view, model, cell_view) { - if (view.rendering_suppressed) { + private drawOncoprint(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + if (this.rendering_suppressed) { return; } - ensureWebGLContext(view); + this.ensureWebGLContext(); - var zoom = getZoom(view, model); + const zoom = this.getZoom(model); - view.ctx.clearColor(1.0, 1.0, 1.0, 1.0); - view.ctx.clear(view.ctx.COLOR_BUFFER_BIT | view.ctx.DEPTH_BUFFER_BIT); + this.ctx.clearColor(1.0, 1.0, 1.0, 1.0); + this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT); - var tracks = model.getTracks(); - for (var i = 0; i < tracks.length; i++) { - var track_id = tracks[i]; - var cell_top = model.getCellTops(track_id, true); - var buffers = getTrackBuffers(view, cell_view, track_id); + const tracks = model.getTracks(); + for (let i = 0; i < tracks.length; i++) { + const track_id = tracks[i]; + const cell_top = model.getCellTops(track_id, true); + const buffers = this.getTrackBuffers(cell_view, track_id); if (buffers.position.numItems === 0) { continue; } - view.ctx.useProgram(view.shader_program); - view.ctx.bindBuffer(view.ctx.ARRAY_BUFFER, buffers.position); - view.ctx.vertexAttribPointer(view.shader_program.vertexPositionAttribute, buffers.position.itemSize, view.ctx.FLOAT, false, 0, 0); - view.ctx.bindBuffer(view.ctx.ARRAY_BUFFER, buffers.color); - view.ctx.vertexAttribPointer(view.shader_program.vertexColorAttribute, buffers.color.itemSize, view.ctx.FLOAT, false, 0, 0); - - view.ctx.bindBuffer(view.ctx.ARRAY_BUFFER, buffers.column); - view.ctx.vertexAttribPointer(view.shader_program.vertexOncoprintColumnAttribute, buffers.column.itemSize, view.ctx.FLOAT, false, 0, 0); - - view.ctx.activeTexture(view.ctx.TEXTURE0); - view.ctx.bindTexture(view.ctx.TEXTURE_2D, buffers.color_tex.texture); - view.ctx.uniform1i(view.shader_program.samplerUniform, 0); - view.ctx.uniform1f(view.shader_program.texSizeUniform, buffers.color_tex.size); - - view.ctx.uniformMatrix4fv(view.shader_program.pMatrixUniform, false, view.pMatrix); - view.ctx.uniformMatrix4fv(view.shader_program.mvMatrixUniform, false, view.mvMatrix); - view.ctx.uniform1f(view.shader_program.columnWidthUniform, model.getCellWidth(true)); - view.ctx.uniform1f(view.shader_program.zoomXUniform, zoom.x); - view.ctx.uniform1f(view.shader_program.zoomYUniform, zoom.y); - view.ctx.uniform1f(view.shader_program.offsetYUniform, cell_top); - view.ctx.uniform1f(view.shader_program.positionBitPackBaseUniform, cell_view.position_bit_pack_base); - - view.ctx.drawArrays(view.ctx.TRIANGLES, 0, buffers.position.numItems); + this.ctx.useProgram(this.shader_program); + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, buffers.position); + this.ctx.vertexAttribPointer(this.shader_program.vertexPositionAttribute, buffers.position.itemSize, this.ctx.FLOAT, false, 0, 0); + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, buffers.color); + this.ctx.vertexAttribPointer(this.shader_program.vertexColorAttribute, buffers.color.itemSize, this.ctx.FLOAT, false, 0, 0); + + this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, buffers.column); + this.ctx.vertexAttribPointer(this.shader_program.vertexOncoprintColumnAttribute, buffers.column.itemSize, this.ctx.FLOAT, false, 0, 0); + + this.ctx.activeTexture(this.ctx.TEXTURE0); + this.ctx.bindTexture(this.ctx.TEXTURE_2D, buffers.color_tex.texture); + this.ctx.uniform1i(this.shader_program.samplerUniform, 0); + this.ctx.uniform1f(this.shader_program.texSizeUniform, buffers.color_tex.size); + + this.ctx.uniformMatrix4fv(this.shader_program.pMatrixUniform, false, this.pMatrix); + this.ctx.uniformMatrix4fv(this.shader_program.mvMatrixUniform, false, this.mvMatrix); + this.ctx.uniform1f(this.shader_program.columnWidthUniform, model.getCellWidth(true)); + this.ctx.uniform1f(this.shader_program.zoomXUniform, zoom.x); + this.ctx.uniform1f(this.shader_program.zoomYUniform, zoom.y); + this.ctx.uniform1f(this.shader_program.offsetYUniform, cell_top); + this.ctx.uniform1f(this.shader_program.positionBitPackBaseUniform, cell_view.position_bit_pack_base); + + this.ctx.drawArrays(this.ctx.TRIANGLES, 0, buffers.position.numItems); } - }; - var getZoom = function (view, model) { - var zoom_x = parseInt(view.$canvas[0].width, 10) / model.getOncoprintWidthNoColumnPadding(true); - var zoom_y = parseInt(view.$canvas[0].height, 10) / model.getOncoprintHeight(true); + } + + private getZoom(model:OncoprintModel) { + let zoom_x = parseInt(this.$canvas[0].width as any, 10) / model.getOncoprintWidthNoColumnPadding(true); + let zoom_y = parseInt(this.$canvas[0].height as any, 10) / model.getOncoprintHeight(true); zoom_x = Math.max(0, Math.min(1, zoom_x)); zoom_y = Math.max(0, Math.min(1, zoom_y)); return { x: zoom_x, y: zoom_y }; - }; + } - var drawOverlayRect = function (view, model, cell_view, opt_rect) { - if (view.rendering_suppressed) { + private drawOverlayRect(model:OncoprintModel, cell_view:OncoprintWebGLCellView, opt_rect?:OverlayRectSpec) { + if (this.rendering_suppressed) { return; } - var left, width, top, height, col, num_cols; + let left, width, top, height, col, num_cols; if (opt_rect) { left = opt_rect.left; width = opt_rect.width; @@ -800,11 +855,11 @@ var OncoprintMinimapView = (function () { col = opt_rect.col; num_cols = opt_rect.num_cols; } else { - var cell_width = model.getCellWidth(true); - var cell_padding = model.getCellPadding(true); - var viewport = cell_view.getViewportOncoprintSpace(model); + const cell_width = model.getCellWidth(true); + const cell_padding = model.getCellPadding(true); + const viewport = cell_view.getViewportOncoprintSpace(model); - var zoom = getZoom(view, model); + const zoom = this.getZoom(model); col = Math.floor(viewport.left / (cell_width + cell_padding)); num_cols = Math.min(model.getIdOrder().length - col, Math.floor(viewport.right / (cell_width + cell_padding)) - Math.floor(viewport.left / (cell_width + cell_padding))); @@ -814,10 +869,10 @@ var OncoprintMinimapView = (function () { height = (viewport.bottom - viewport.top) * zoom.y; } - var ctx = view.overlay_ctx; - var canv = view.$overlay_canvas[0]; - var canv_width = parseInt(canv.width, 10); - var canv_height = parseInt(canv.height, 10); + const ctx = this.overlay_ctx; + const canv = this.$overlay_canvas[0]; + const canv_width = parseInt(canv.width as any, 10); + const canv_height = parseInt(canv.height as any, 10); // Clear ctx.fillStyle = "rgba(0,0,0,0)"; @@ -826,14 +881,14 @@ var OncoprintMinimapView = (function () { ctx.fillStyle = "rgba(255,255,255,0.4)"; ctx.fillRect(left, top, width, height); // Draw border line by line - var unhover_color = "rgba(0,0,0,0.75)"; - var hover_color = "rgba(255,0,0,1)"; - var unhover_width = 1; - var hover_width = 2; - var top_is_hovered = view.resize_hover === "t" || view.resize_hover === "tr" || view.resize_hover === "tl"; - var right_is_hovered = view.resize_hover === "r" || view.resize_hover === "tr" || view.resize_hover === "br"; - var bottom_is_hovered = view.resize_hover === "b" || view.resize_hover === "br" || view.resize_hover === "bl"; - var left_is_hovered = view.resize_hover === "l" || view.resize_hover === "tl" || view.resize_hover === "bl"; + const unhover_color = "rgba(0,0,0,0.75)"; + const hover_color = "rgba(255,0,0,1)"; + const unhover_width = 1; + const hover_width = 2; + const top_is_hovered = this.resize_hover === "t" || this.resize_hover === "tr" || this.resize_hover === "tl"; + const right_is_hovered = this.resize_hover === "r" || this.resize_hover === "tr" || this.resize_hover === "br"; + const bottom_is_hovered = this.resize_hover === "b" || this.resize_hover === "br" || this.resize_hover === "bl"; + const left_is_hovered = this.resize_hover === "l" || this.resize_hover === "tl" || this.resize_hover === "bl"; // Draw top border ctx.beginPath(); ctx.moveTo(left, top); @@ -863,7 +918,7 @@ var OncoprintMinimapView = (function () { ctx.lineTo(left, top); ctx.stroke(); - view.current_rect = { + this.current_rect = { 'top':top, 'left':left, 'width':width, @@ -871,91 +926,89 @@ var OncoprintMinimapView = (function () { 'col': col, 'num_cols': num_cols }; - }; - var drawOncoprintAndOverlayRect = function (view, model, cell_view) { - if (view.rendering_suppressed) { + } + + private drawOncoprintAndOverlayRect(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + if (this.rendering_suppressed) { return; } - drawOncoprint(view, model, cell_view); - drawOverlayRect(view, model, cell_view); - }; + this.drawOncoprint(model, cell_view); + this.drawOverlayRect(model, cell_view); + } - OncoprintMinimapView.prototype.moveTrack = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public moveTrack(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.addTracks = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public addTracks(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.removeTrack = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public removeTrack(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.setHorzZoom = function (model, cell_view) { - drawOverlayRect(this, model, cell_view); + public setHorzZoom(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOverlayRect(model, cell_view); this.horizontal_zoom.setSliderValue(model.getHorzZoom()); } - OncoprintMinimapView.prototype.setVertZoom = function (model, cell_view) { - drawOverlayRect(this, model, cell_view); + public setVertZoom(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOverlayRect(model, cell_view); this.vertical_zoom.setSliderValue(model.getVertZoom()); } - OncoprintMinimapView.prototype.setZoom = function(model, cell_view) { - drawOverlayRect(this, model, cell_view); + public setZoom(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOverlayRect(model, cell_view); this.horizontal_zoom.setSliderValue(model.getHorzZoom()); this.vertical_zoom.setSliderValue(model.getVertZoom()); } - OncoprintMinimapView.prototype.setScroll = function (model, cell_view) { - drawOverlayRect(this, model, cell_view); + public setScroll(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.setHorzScroll = function (model, cell_view) { - drawOverlayRect(this, model, cell_view); + public setHorzScroll(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.setVertScroll = function (model, cell_view) { - drawOverlayRect(this, model, cell_view); + public setVertScroll(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.setViewport = function (model, cell_view) { - drawOverlayRect(this, model, cell_view); + public setViewport(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOverlayRect(model, cell_view); this.horizontal_zoom.setSliderValue(model.getHorzZoom()); this.vertical_zoom.setSliderValue(model.getVertZoom()); } - OncoprintMinimapView.prototype.sort = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public sort(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.setTrackData = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public setTrackData(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.shareRuleSet = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public shareRuleSet(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.setRuleSet = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public setRuleSet(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.setIdOrder = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public setIdOrder(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.suppressRendering = function () { + public suppressRendering() { this.rendering_suppressed = true; } - OncoprintMinimapView.prototype.releaseRendering = function (model, cell_view) { + public releaseRendering(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { this.rendering_suppressed = false; - drawOncoprintAndOverlayRect(this, model, cell_view); + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.hideIds = function (model, cell_view) { - drawOncoprintAndOverlayRect(this, model, cell_view); + public hideIds(model:OncoprintModel, cell_view:OncoprintWebGLCellView) { + this.drawOncoprintAndOverlayRect(model, cell_view); } - OncoprintMinimapView.prototype.setWindowPosition = function(x, y) { + public setWindowPosition(x:number, y:number) { this.$div.css({'top': y, 'left': x}); } - OncoprintMinimapView.prototype.setWidth = function (w, model, cell_view) { + public setWidth(w:number, model:OncoprintModel, cell_view:OncoprintWebGLCellView) { this.$canvas[0].width = w; this.$overlay_canvas[0].width = w; - getWebGLContextAndSetUpMatrices(this); - setUpShaders(this); + this.getWebGLContextAndSetUpMatrices(); + this.setUpShaders(); this.overlay_ctx = this.$overlay_canvas[0].getContext("2d"); - drawOncoprintAndOverlayRect(this, model, cell_view); + this.drawOncoprintAndOverlayRect(model, cell_view); } - return OncoprintMinimapView; -})(); - -module.exports = OncoprintMinimapView; +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprintmodel.js b/packages/oncoprintjs/src/js/oncoprintmodel.js deleted file mode 100644 index 43471618690..00000000000 --- a/packages/oncoprintjs/src/js/oncoprintmodel.js +++ /dev/null @@ -1,1749 +0,0 @@ -/* jshint browserify: true, asi: true */ -var binarysearch = require('./binarysearch.js'); -var hasElementsInInterval = require('./haselementsininterval.js'); -var CachedProperty = require('./CachedProperty.js'); -var clustering = require('./clustering.js'); -var $ = require('jquery'); -var BucketSort = require("./bucketsort.js"); -var doesCellIntersectPixel = require("./utils.js").doesCellIntersectPixel; -var cloneShallow = require("./utils.js").cloneShallow; -var _ = require("lodash"); - -function ifndef(x, val) { - return (typeof x === "undefined" ? val : x); -} - -var UnionOfSets = (function() { - // a set, to be passed in as argument, is an object where the values are truthy - function UnionOfSets() { - this.union_count = {}; - this.sets = {}; - } - var setOfKeys = function (obj) { - var set = {}; - for (var k in obj) { - if (typeof obj[k] !== 'undefined') { - set[k] = true; - } - } - return set; - }; - UnionOfSets.prototype.putSet = function(id, set) { - this.removeSet(id); - this.sets[id] = set; - - var union_count = this.union_count; - for (var k in set) { - if (set[k]) { - this.union_count[k] = this.union_count[k] || 0; - this.union_count[k] += 1; - } - } - } - UnionOfSets.prototype.removeSet = function(id) { - var union_count = this.union_count; - var old_set = this.sets[id] || {}; - for (var k in old_set) { - if (old_set[k]) { - union_count[k] -= 1; - if (union_count[k] === 0) { - delete union_count[k]; - } - } - } - delete this.sets[id]; - } - UnionOfSets.prototype.getUnion = function() { - return setOfKeys(this.union_count); - } - return UnionOfSets; -})(); - -var setUnion = function(list_of_sets) { - var union = {}; - for (var i=0; i rule set id - this.track_active_rules = {}; // from track id to active rule map (map with rule ids as keys) - this.track_info = {}; - this.$track_info_tooltip_elt = {}; - this.track_has_column_spacing = {}; // track id -> boolean - this.track_expansion_enabled = {}; // track id -> boolean or undefined - this.track_expand_callback = {}; // track id -> function that adds expansion tracks for its track if set - this.track_expand_button_getter = {}; // track id -> function from boolean to string if customized - this.track_expansion_tracks = {}; // track id -> array of track ids if applicable - this.track_expansion_parent = {}; // track id -> track id if applicable - this.track_custom_options = {}; // track id -> { label, onClick, weight, disabled }[] ( see index.d.ts :: CustomTrackOption ) - - // Rule Set Properties - this.rule_sets = {}; // map from rule set id to rule set - this.rule_set_active_rules = {}; // map from rule set id to map from rule id to use count - - // Cached and Recomputed Properties - this.visible_id_order = new CachedProperty([], function () { - var hidden_ids = model.hidden_ids; - return model.id_order.filter(function (id) { - return !hidden_ids[id]; - }); - }); - this.track_id_to_datum = new CachedProperty({}, function(model, track_id) { - var curr = model.track_id_to_datum.get(); - if (model.getContainingTrackGroup(track_id) !== null) { - var map = {}; - var data = model.getTrackData(track_id) || []; - var data_id_key = model.getTrackDataIdKey(track_id) || ''; - for (var i=0; i 0 && group.length > 0) { - y += model.getTrackGroupHeaderSize(); - } - for (var j = 0; j < group.length; j++) { - var track_id = group[j]; - tops[track_id] = y; - y += model.getTrackHeight(track_id, true); - } - if (group.length > 0) { - y += model.getTrackGroupPadding(true); - } - } - return tops; - }); - this.cell_tops = new CachedProperty({}, function() { - var track_tops = model.track_tops.get(); - var cell_tops = {}; - for (var k in track_tops) { - if (track_tops.hasOwnProperty(k)) { - cell_tops[k] = track_tops[k] + model.getTrackPadding(k, true); - } - } - return cell_tops; - }); - this.label_tops = new CachedProperty({}, function() { - return model.cell_tops.get(); - }); - - this.track_tops.addBoundProperty(this.cell_tops); - this.cell_tops.addBoundProperty(this.label_tops); - - this.track_tops_zoomed = new CachedProperty({}, function () { - var tops = {}; - var groups = model.getTrackGroups(); - var y = 0; - for (var i = 0; i < groups.length; i++) { - var group = groups[i]; - for (var j = 0; j < group.length; j++) { - var track_id = group[j]; - tops[track_id] = y; - y += model.getTrackHeight(track_id); - } - if (group.length > 0) { - y += model.getTrackGroupPadding(); - } - } - return tops; - }); - this.cell_tops_zoomed = new CachedProperty({}, function() { - var track_tops = model.track_tops_zoomed.get(); - var cell_tops = {}; - for (var k in track_tops) { - if (track_tops.hasOwnProperty(k)) { - cell_tops[k] = track_tops[k] + model.getTrackPadding(k); - } - } - return cell_tops; - }); - this.label_tops_zoomed = new CachedProperty({}, function() { - return model.cell_tops_zoomed.get(); - }); - - this.track_tops.addBoundProperty(this.track_tops_zoomed); - this.track_tops_zoomed.addBoundProperty(this.cell_tops_zoomed); - this.cell_tops_zoomed.addBoundProperty(this.label_tops_zoomed); - - this.column_left = new CachedProperty({}, function() { - var cell_width = model.getCellWidth(true); - var cell_padding = model.getCellPadding(true); - var left = {}; - var ids = model.getIdOrder(); - for (var i = 0; i < ids.length; i++) { - left[ids[i]] = i * (cell_width + cell_padding); - } - return left; - }); - - this.zoomed_column_left = new CachedProperty({}, function() { - var cell_width = model.getCellWidth(); - var cell_padding = model.getCellPadding(); - var left = {}; - var ids = model.getIdOrder(); - for (var i = 0; i < ids.length; i++) { - left[ids[i]] = i * (cell_width + cell_padding); - } - return left; - }); - this.column_left_no_padding = new CachedProperty({}, function() { - var cell_width = model.getCellWidth(true); - var left = {}; - var ids = model.getIdOrder(); - for (var i = 0; i < ids.length; i++) { - left[ids[i]] = i * cell_width; - } - return left; - }); - this.column_left.addBoundProperty(this.zoomed_column_left); - this.column_left.addBoundProperty(this.column_left_no_padding); - - this.precomputed_comparator = new CachedProperty({}, function(model, track_id) { - var curr_precomputed_comparator = model.precomputed_comparator.get(); - curr_precomputed_comparator[track_id] = new PrecomputedComparator(model.getTrackData(track_id), - model.getTrackSortComparator(track_id), - model.getTrackSortDirection(track_id), - model.getTrackDataIdKey(track_id)); - return curr_precomputed_comparator; - });// track_id -> PrecomputedComparator - } - - OncoprintModel.prototype.toggleCellPadding = function () { - this.cell_padding_on = !this.cell_padding_on; - this.column_left.update(); - return this.cell_padding_on; - } - - OncoprintModel.prototype.getCellPadding = function (base, dont_consider_zoom) { - return (this.cell_padding * (base ? 1 : this.horz_zoom)) * (+this.cell_padding_on) * (dont_consider_zoom ? 1 : +(!this.cell_padding_off_because_of_zoom)); - } - - OncoprintModel.prototype.getHorzZoom = function () { - return this.horz_zoom; - } - - OncoprintModel.prototype.getHorzZoomToFitNumCols = function(width, num_cols) { - var cell_width = this.getCellWidth(true); - var zoom_if_cell_padding_on = clamp(width / (num_cols*(cell_width + this.cell_padding)), - 0,1); - var zoom_if_cell_padding_off = clamp(width / (num_cols*cell_width), - 0,1); - var zoom; - if (!this.cell_padding_on) { - zoom = zoom_if_cell_padding_off; - } else { - if (cell_width * zoom_if_cell_padding_on < this.cell_padding_off_cell_width_threshold) { - if (cell_width * zoom_if_cell_padding_off >= this.cell_padding_off_cell_width_threshold) { - // Because of cell padding toggling there's no way to get exactly the desired number of columns. - // We can see this by contradiction: if we assume that cell padding is on, and try to fit exactly - // our number of columns, we end up turning cell padding off (outer if statement). If we assume that - // cell padding is off and try to fit our number of columns, we find that cell padding is on (inner if statement). - // Thus, it's impossible to show this exact number of columns - we either under or overshoot it. We - // thus should overshoot it by as little as possible, show as few columns as possible while still fitting - // this amount. It must be exactly at the threshold for switching. - // - var unrounded_zoom = this.cell_padding_off_cell_width_threshold / cell_width; - var unrounded_num_cols = width / (unrounded_zoom * cell_width); - var rounded_num_cols = Math.ceil(unrounded_num_cols); - zoom = width / (rounded_num_cols * cell_width); - } else { - zoom = zoom_if_cell_padding_off; - } - } else { - zoom = zoom_if_cell_padding_on; - } - } - return zoom; - } - OncoprintModel.prototype.getHorzZoomToFit = function(width, ids) { - ids = ids || []; - if (ids.length === 0) { - return 1; - } - var id_to_index_map = this.getVisibleIdToIndexMap(); - var indexes = ids.map(function(id) { return id_to_index_map[id]; }); - var max = Number.NEGATIVE_INFINITY; - var min = Number.POSITIVE_INFINITY; - for (var i=0; i= this.cell_padding_off_cell_width_threshold && this.cell_padding_off_because_of_zoom) { - setCellPaddingOffBecauseOfZoom(this, false); - } - return this.horz_zoom; - } - - - OncoprintModel.prototype.getVertZoom = function() { - return this.vert_zoom; - } - - OncoprintModel.prototype.setVertZoom = function (z) { - var min_zoom = this.getMinVertZoom(); - this.vert_zoom = clamp(z, min_zoom, 1); - this.track_tops.update(); - return this.vert_zoom; - } - - OncoprintModel.prototype.hideTrackLegends = function(track_ids) { - track_ids = [].concat(track_ids); - for (var i=0; i zB) { - return 1; - } else { - return 0; - } - }; - return shapes.map(function(shape_list, index) { - if (sort_by_z) { - shape_list.sort(z_comparator); - } - return { - id: data[index][id_key], - shape_list: shape_list - }; - }); - } - - OncoprintModel.prototype.getActiveRules = function(rule_set_id) { - var rule_set_active_rules = this.rule_set_active_rules[rule_set_id]; - if (rule_set_active_rules) { - return this.rule_sets[rule_set_id].getRulesWithId().filter(function(rule_with_id) { - return !!rule_set_active_rules[rule_with_id.id]; - }); - } else { - return []; - } - } - - function _setTrackImportantIds(model, track_id, ids) { - if (!ids) { - model.track_important_ids[track_id] = undefined; - } else { - model.track_important_ids[track_id] = ids.reduce(function(map, next_id) { - map[next_id] = true; - return map; - }, {}); - } - } - - OncoprintModel.prototype.setTrackImportantIds = function(track_id, ids) { - _setTrackImportantIds(this, track_id, ids); - } - OncoprintModel.prototype.getTrackImportantIds = function(track_id) { - return this.track_important_ids[track_id]; - } - - OncoprintModel.prototype.getRuleSets = function() { - // return rule sets, in track group legend order - var self = this; - var legend_order = this.getTrackGroupLegendOrder(); - var used_track_groups = {}; - var track_groups = this.getTrackGroups(); - var sorted_track_groups = []; - for (var i=0; i -1)); - } - - OncoprintModel.prototype.setIdOrder = function (ids) { - this.id_order = ids.slice(); - Object.freeze(this.id_order); - this.id_to_index.update(); - this.visible_id_order.update(); - this.column_left.update(); - } - - OncoprintModel.prototype.hideIds = function (to_hide, show_others) { - if (show_others) { - this.hidden_ids = {}; - } - for (var j = 0, len = to_hide.length; j < len; j++) { - this.hidden_ids[to_hide[j]] = true; - } - this.visible_id_order.update(); - this.column_left.update(); - } - - OncoprintModel.prototype.setHighlightedIds = function(ids) { - this.highlighted_ids = ids; - } - - OncoprintModel.prototype.getHighlightedIds = function() { - return this.highlighted_ids; - } - - OncoprintModel.prototype.setTrackGroupOrder = function(index, track_order) { - this.track_groups[index] = track_order; - - this.track_tops.update(); - } - - OncoprintModel.prototype.moveTrackGroup = function (from_index, to_index) { - var new_groups = []; - var new_headers = []; - var group_to_move = this.track_groups[from_index]; - for (var i = 0; i < this.track_groups.length; i++) { - if (i !== from_index && i !== to_index) { - new_groups.push(this.track_groups[i]); - new_headers.push(this.track_group_header[i]); - } - if (i === to_index) { - new_groups.push(group_to_move); - new_headers.push(this.track_group_header[from_index]); - } - } - this.track_groups = new_groups; - this.track_group_header = new_headers; - this.track_tops.update(); - return this.track_groups; - } - - OncoprintModel.prototype.addTracks = function (params_list) { - for (var i = 0; i < params_list.length; i++) { - var params = params_list[i]; - addTrack(this, params.track_id, params.target_group, params.track_group_header, - params.cell_height, params.track_padding, params.has_column_spacing, - params.data_id_key, params.tooltipFn, params.link_url, params.removable, params.movable, - params.removeCallback, params.label, params.sublabel, params.description, params.track_info, - params.sortCmpFn, params.sort_direction_changeable, params.init_sort_direction, params.onSortDirectionChange, - params.data, params.rule_set, params.track_label_color, params.track_label_circle_color, params.html_label, - params.track_label_font_weight, params.track_label_left_padding, - params.expansion_of, params.expandCallback, params.expandButtonTextGetter, params.important_ids, - params.custom_track_options, params.$track_info_tooltip_elt - ); - } - this.track_tops.update(); - } - - var addTrack = function (model, track_id, target_group, track_group_header, - cell_height, track_padding, has_column_spacing, - data_id_key, tooltipFn, link_url, removable, movable, - removeCallback, label, sublabel, description, track_info, - sortCmpFn, sort_direction_changeable, init_sort_direction, onSortDirectionChange, - data, rule_set, track_label_color, track_label_circle_color, html_label, - track_label_font_weight, track_label_left_padding, - expansion_of, expandCallback, expandButtonTextGetter, - important_ids, custom_track_options, $track_info_tooltip_elt - ) { - model.$track_info_tooltip_elt[track_id] = $track_info_tooltip_elt; - model.track_custom_options[track_id] = ifndef(custom_track_options, []); - model.track_label[track_id] = ifndef(label, "Label"); - model.track_sublabel[track_id] = ifndef(sublabel, ""); - model.track_label_color[track_id] = ifndef(track_label_color, "black"); - model.track_label_circle_color[track_id] = track_label_circle_color; - model.track_label_font_weight[track_id] = track_label_font_weight; - model.track_label_left_padding[track_id] = ifndef(track_label_left_padding, 0); - model.track_link_url[track_id] = ifndef(link_url, null); - model.track_description[track_id] = ifndef(description, ""); - model.cell_height[track_id] = ifndef(cell_height, 23); - model.track_padding[track_id] = ifndef(track_padding, 5); - model.track_has_column_spacing[track_id] = ifndef(has_column_spacing, true); - - model.track_tooltip_fn[track_id] = ifndef(tooltipFn, function (d) { - return d + ''; - }); - model.track_movable[track_id] = ifndef(movable, true); - model.track_removable[track_id] = ifndef(removable, false); - model.track_remove_callback[track_id] = ifndef(removeCallback, function() {}); - - if (typeof expandCallback !== 'undefined') { - model.track_expand_callback[track_id] = expandCallback; - model.track_expansion_enabled[track_id] = true; - } - if (typeof expandButtonTextGetter !== 'undefined') { - model.track_expand_button_getter[track_id] = expandButtonTextGetter; - } - - model.track_sort_cmp_fn[track_id] = ifndef(sortCmpFn, function () { - return 0; - }); - - model.track_sort_direction_changeable[track_id] = ifndef(sort_direction_changeable, false); - model.track_sort_direction_change_callback[track_id] = ifndef(onSortDirectionChange, function() {}); - model.track_data[track_id] = ifndef(data, []); - model.track_data_id_key[track_id] = ifndef(data_id_key, 'id'); - - model.track_info[track_id] = ifndef(track_info, ""); - - if (typeof html_label !== 'undefined') { - model.track_html_label[track_id] = html_label; - } - - if (typeof rule_set !== 'undefined') { - model.rule_sets[rule_set.rule_set_id] = rule_set; - model.rule_set_active_rules[rule_set.rule_set_id] = {}; - model.track_rule_set_id[track_id] = rule_set.rule_set_id; - } - model.track_active_rules[track_id] = {}; - - if (important_ids) { - _setTrackImportantIds(model, track_id, important_ids); - } - - model.track_sort_direction[track_id] = ifndef(init_sort_direction, 1); - - target_group = ifndef(target_group, 0); - while (target_group >= model.track_groups.length) { - model.track_groups.push([]); - model.track_group_header.push(""); - } - if (track_group_header) { - model.track_group_header[target_group] = track_group_header; - } - - var group_array = model.track_groups[target_group]; - var target_index = (expansion_of !== undefined - ? group_array.indexOf(model.getLastExpansion(expansion_of)) + 1 - : group_array.length - ); - group_array.splice(target_index, 0, track_id); - - if (expansion_of !== undefined) { - if (!model.track_expansion_tracks.hasOwnProperty(expansion_of)) { - model.track_expansion_tracks[expansion_of] = []; - } - if (model.track_expansion_tracks[expansion_of].indexOf(track_id) !== -1) { - throw new Error('Illegal state: duplicate expansion track ID'); - } - model.track_expansion_parent[track_id] = expansion_of; - model.track_expansion_tracks[expansion_of].push(track_id); - } - - model.track_id_to_datum.update(model, track_id); - model.track_present_ids.update(model, track_id); - model.precomputed_comparator.update(model, track_id); - - model.setIdOrder(Object.keys(model.present_ids.get())); - } - - // get a reference to the array that stores the order of tracks in - // the same group - var _getMajorTrackGroup = function (oncoprint_model, track_id, return_index) { - var group; - track_id = parseInt(track_id); - var i; - for (i = 0; i < oncoprint_model.track_groups.length; i++) { - if (oncoprint_model.track_groups[i].indexOf(track_id) > -1) { - group = oncoprint_model.track_groups[i]; - break; - } - } - if (group) { - return return_index ? i : group; - } else { - return null; - } - } - // get an array listing the track IDs that a track can move around - var _getEffectiveTrackGroup = function (oncoprint_model, track_id) { - var group, - parent_id = oncoprint_model.track_expansion_parent[track_id]; - if (parent_id === undefined) { - group = (function(major_group) { - return (major_group === null ? null - : major_group.filter(function (sibling_id) { - return oncoprint_model.track_expansion_parent[sibling_id] === undefined; - })); - })(_getMajorTrackGroup(oncoprint_model, track_id)); - } else { - group = oncoprint_model.track_expansion_tracks[parent_id]; - } - return group ? group.slice() : null; - } - - var isRuleSetUsed = function(model, rule_set_id) { - var used = false; - var tracks = model.getTracks(); - for (var i=0; i= cell_tops[nearest_track] + this.getCellHeight(nearest_track)) { - // we know y is past the top of the track (>= cell_tops[nearest_track]), so this checks if y is past the bottom of the track - return null; - } - - // At this point, we know y is inside a track - - // Finally, return all ids within 1 px of x to the right - var ids = []; - var hitzone_width = this.getCellWidth(); - if (!this.getTrackHasColumnSpacing(nearest_track)) { - hitzone_width += this.getCellPadding(); - } - for (var i=nearest_id_index; i x+1) { - break; - } - } - if (ids.length > 0) { - return {'ids': ids, 'track': nearest_track, 'top': cell_tops[nearest_track], 'left': zoomed_column_left[ids[0]]}; - } - return null; - }; - - OncoprintModel.prototype.getTrackDatum = function(track_id, id) { - var datum = this.track_id_to_datum.get()[track_id][id]; - if (typeof datum === 'undefined') { - datum = null; - } - return datum; - } - - OncoprintModel.prototype.getTrackTops = function (desired_track_id) { - if (typeof desired_track_id === 'undefined') { - return copyShallowObject(this.track_tops.get()); - } else { - return this.track_tops.get()[desired_track_id]; - } - } - - OncoprintModel.prototype.getZoomedTrackTops = function (desired_track_id) { - if (typeof desired_track_id === 'undefined') { - return copyShallowObject(this.track_tops_zoomed.get()); - } else { - return this.track_tops_zoomed.get()[desired_track_id]; - } - } - - OncoprintModel.prototype.getCellTops = function(desired_track_id, base) { - if (typeof desired_track_id === 'undefined') { - return copyShallowObject((base ? this.cell_tops : this.cell_tops_zoomed).get()); - } else { - return (base ? this.cell_tops : this.cell_tops_zoomed).get()[desired_track_id]; - } - } - OncoprintModel.prototype.getLabelTops = function(desired_track_id, base) { - if (typeof desired_track_id === 'undefined') { - return copyShallowObject((base ? this.label_tops : this.label_tops_zoomed).get()); - } else { - return (base ? this.label_tops : this.label_tops_zoomed).get()[desired_track_id]; - } - } - - OncoprintModel.prototype.getContainingTrackGroup = function (track_id) { - return _getEffectiveTrackGroup(this, track_id); - } - - OncoprintModel.prototype.getContainingTrackGroupIndex = function(track_id) { - return _getMajorTrackGroup(this, track_id, true); - } - - OncoprintModel.prototype.setTrackGroupHeader = function(track_group_id, text) { - this.track_group_header[track_group_id] = text; - this.track_tops.update(); - } - - OncoprintModel.prototype.getTrackGroupHeader = function(track_group_id) { - return this.track_group_header[track_group_id] || ""; - } - - OncoprintModel.prototype.getTrackGroupHeaderSize = function() { - return 20; - } - - OncoprintModel.prototype.getTrackGroups = function () { - // TODO: make read-only - return this.track_groups; - } - - OncoprintModel.prototype.getTracks = function () { - var ret = []; - for (var i = 0; i < this.track_groups.length; i++) { - for (var j = 0; j < this.track_groups[i].length; j++) { - ret.push(this.track_groups[i][j]); - } - } - return ret; - } - - OncoprintModel.prototype.getIdsInLeftInterval = function(left, right) { - var cell_width = this.getCellWidth(); - var cell_padding = this.getCellPadding(); - var id_order = this.getIdOrder(); - - // left_id_index and right_id_index are inclusive - var left_id_index = Math.floor(left/(cell_width + cell_padding)); - var left_remainder = left - left_id_index*(cell_width + cell_padding); - if (left_remainder > cell_width) { - left_id_index += 1; - } - var right_id_index = Math.floor(right/(cell_width + cell_padding)); - return id_order.slice(left_id_index, right_id_index+1); - } - OncoprintModel.prototype.getColumnLeft = function(id) { - if (typeof id === 'undefined') { - return this.column_left.get(); - } else { - return this.column_left.get()[id]; - } - } - - OncoprintModel.prototype.getColumnLeftNoPadding = function(id) { - if (typeof id === 'undefined') { - return this.column_left_no_padding.get(); - } else { - return this.column_left_no_padding.get()[id]; - } - } - - OncoprintModel.prototype.getZoomedColumnLeft = function(id) { - if (typeof id === 'undefined') { - return this.zoomed_column_left.get(); - } else { - return this.zoomed_column_left.get()[id]; - } - } - - - OncoprintModel.prototype.getOncoprintHeight = function(base) { - var tracks = this.getTracks(); - var last_track = tracks[tracks.length-1]; - return (base ? this.getTrackTops(last_track) : this.getZoomedTrackTops(last_track))+this.getTrackHeight(last_track, base) - + this.getBottomPadding(); - } - - OncoprintModel.prototype.getOncoprintWidth = function(base) { - return this.getIdOrder().length*(this.getCellWidth(base) + this.getCellPadding(base)); - } - - OncoprintModel.prototype.getOncoprintWidthNoColumnPadding = function(base) { - return this.getIdOrder().length*this.getCellWidth(base); - } - - OncoprintModel.prototype.getCellViewHeight = function() { - return Math.min(this.max_height, this.getOncoprintHeight()); - } - - OncoprintModel.prototype.getColumnLabels = function() { - return this.column_labels; - } - - OncoprintModel.prototype.setColumnLabels = function(labels) { - this.column_labels = labels; - } - - OncoprintModel.prototype.moveTrack = function (track_id, new_previous_track) { - - function moveContiguousValues(uniqArray, first_value, last_value, new_predecessor) { - var old_start_index = uniqArray.indexOf(first_value), - old_end_index = uniqArray.indexOf(last_value); - var values = uniqArray.slice(old_start_index, old_end_index + 1); - uniqArray.splice(old_start_index, values.length); - var new_position = (new_predecessor === null ? 0 : uniqArray.indexOf(new_predecessor)+1); - uniqArray.splice.bind(uniqArray, new_position, 0).apply(null, values); - } - - var track_group = _getMajorTrackGroup(this, track_id), - expansion_parent = this.track_expansion_parent[track_id], - flat_previous_track; - - if (track_group !== null) { - // if an expansion track moves above all other tracks it can, - // place it directly below its expansion parent - if (expansion_parent !== undefined && new_previous_track === null) { - flat_previous_track = expansion_parent; - // otherwise, place the track under (the last expansion track of) - // its sibling - } else { - flat_previous_track = this.getLastExpansion(new_previous_track); - } - moveContiguousValues(track_group, track_id, this.getLastExpansion(track_id), flat_previous_track); - } - - // keep the order of expansion siblings up-to-date as well - if (this.track_expansion_parent[track_id] !== undefined) { - moveContiguousValues(this.track_expansion_tracks[expansion_parent], track_id, track_id, new_previous_track); - } - - this.track_tops.update(); - }; - - OncoprintModel.prototype.getTrackLabel = function (track_id) { - return this.track_label[track_id]; - } - - OncoprintModel.prototype.getTrackSublabel = function(track_id) { - return this.track_sublabel[track_id]; - } - - OncoprintModel.prototype.getShowTrackSublabels = function() { - return this.show_track_sublabels; - } - - OncoprintModel.prototype.setShowTrackSublabels = function(show) { - return this.show_track_sublabels = show; - } - - OncoprintModel.prototype.getTrackLabelColor = function (track_id) { - return this.track_label_color[track_id]; - } - - OncoprintModel.prototype.getTrackLabelCircleColor = function(track_id) { - return this.track_label_circle_color[track_id]; - } - - OncoprintModel.prototype.getTrackLabelFontWeight = function(track_id) { - return this.track_label_font_weight[track_id]; - } - - OncoprintModel.prototype.getTrackLabelLeftPadding = function(track_id) { - return this.track_label_left_padding[track_id]; - } - - OncoprintModel.prototype.getOptionalHtmlTrackLabel = function (track_id) { - return this.track_html_label[track_id]; - } - - OncoprintModel.prototype.getTrackLinkUrl = function (track_id) { - return this.track_link_url[track_id]; - } - - OncoprintModel.prototype.getTrackDescription = function(track_id) { - return this.track_description[track_id]; - } - - OncoprintModel.prototype.getTrackTooltipFn = function (track_id) { - return this.track_tooltip_fn[track_id]; - } - OncoprintModel.prototype.setTrackTooltipFn = function (track_id, tooltipFn) { - this.track_tooltip_fn[track_id] = tooltipFn; - } - - OncoprintModel.prototype.getTrackDataIdKey = function (track_id) { - return this.track_data_id_key[track_id]; - } - - OncoprintModel.prototype.getTrackGroupPadding = function (base) { - return this.track_group_padding * (base ? 1 : this.vert_zoom); - } - - OncoprintModel.prototype.isTrackRemovable = function (track_id) { - return this.track_removable[track_id]; - } - - OncoprintModel.prototype.isTrackSortDirectionChangeable = function (track_id) { - return this.track_sort_direction_changeable[track_id]; - } - - OncoprintModel.prototype.isTrackExpandable = function (track_id) { - // return true if the flag is defined and true - return Boolean(this.track_expansion_enabled[track_id]); - } - - OncoprintModel.prototype.expandTrack = function (track_id) { - return this.track_expand_callback[track_id](track_id); - } - - OncoprintModel.prototype.disableTrackExpansion = function (track_id) { - this.track_expansion_enabled[track_id] = false; - } - - OncoprintModel.prototype.enableTrackExpansion = function (track_id) { - if (!this.track_expand_callback.hasOwnProperty(track_id)) { - throw new Error("Track '" + track_id +"' has no expandCallback"); - } - this.track_expansion_enabled[track_id] = true; - } - - OncoprintModel.prototype.isTrackExpanded = function (track_id) { - return this.track_expansion_tracks.hasOwnProperty(track_id) && - this.track_expansion_tracks[track_id].length > 0; - } - - OncoprintModel.prototype.getExpandButtonText = function (track_id) { - var self = this; - var getExpandButtonFunction = function (track_id) { - return (self.track_expand_button_getter[track_id] || - function (is_expanded) { - return is_expanded ? 'Expand more' : 'Expand'; - }); - }; - return getExpandButtonFunction(track_id)(this.isTrackExpanded(track_id)); - } - - /** - * Checks if one track is the expansion of another - * - * @param {number} expansion_track_id - the ID of the track to check - * @param {number} set_track_id - the ID of the track it may be an expansion of - */ - OncoprintModel.prototype.isExpansionOf = function (expansion_track_id, set_track_id) { - return this.track_expansion_tracks.hasOwnProperty(set_track_id) && - this.track_expansion_tracks[set_track_id].indexOf(expansion_track_id) !== -1; - } - - /** - * Finds the bottom-most track in a track's expansion group - * - * @param track_id - the ID of the track to start from - * @returns the ID of its last expansion, or the unchanged param if none - */ - OncoprintModel.prototype.getLastExpansion = function (track_id) { - var direct_children = this.track_expansion_tracks[track_id]; - while (direct_children && direct_children.length) { - track_id = direct_children[direct_children.length - 1]; - direct_children = this.track_expansion_tracks[track_id]; - } - return track_id; - } - - OncoprintModel.prototype.getTrackCustomOptions = function(track_id) { - return this.track_custom_options[track_id]; - } - - OncoprintModel.prototype.setTrackCustomOptions = function(track_id, options) { - this.track_custom_options[track_id] = options; - } - - OncoprintModel.prototype.setTrackInfoTooltip = function(track_id, $tooltip_elt) { - this.$track_info_tooltip_elt[track_id] = $tooltip_elt; - } - - OncoprintModel.prototype.$getTrackInfoTooltip = function(track_id) { - return this.$track_info_tooltip_elt[track_id]; - } - - OncoprintModel.prototype.getRuleSet = function (track_id) { - return this.rule_sets[this.track_rule_set_id[track_id]]; - } - - OncoprintModel.prototype.shareRuleSet = function(source_track_id, target_track_id) { - setTrackActiveRules(this, target_track_id, {}); - - var old_rule_set_id = this.track_rule_set_id[target_track_id]; - this.track_rule_set_id[target_track_id] = this.track_rule_set_id[source_track_id]; - if (!isRuleSetUsed(this, old_rule_set_id)) { - removeRuleSet(this, old_rule_set_id); - } - } - - OncoprintModel.prototype.setRuleSet = function(track_id, rule_set) { - setTrackActiveRules(this, track_id, {}); - - var curr_rule_set_id = this.track_rule_set_id[track_id]; - this.rule_sets[rule_set.rule_set_id] = rule_set; - this.rule_set_active_rules[rule_set.rule_set_id] = {}; - this.track_rule_set_id[track_id] = rule_set.rule_set_id; - - var rule_set_used = isRuleSetUsed(this, curr_rule_set_id); - if (!rule_set_used) { - removeRuleSet(this, curr_rule_set_id); - } - } - - OncoprintModel.prototype.getTrackSortComparator = function(track_id) { - return this.track_sort_cmp_fn[track_id]; - } - - OncoprintModel.prototype.setTrackSortComparator = function(track_id, sortCmpFn) { - this.track_sort_cmp_fn[track_id] = sortCmpFn; - this.precomputed_comparator.update(this, track_id); - } - - OncoprintModel.prototype.getTrackData = function (track_id) { - return this.track_data[track_id]; - } - - OncoprintModel.prototype.clusterTrackGroup = function(track_group_index, clusterValueFn) { - var sort_config_at_call = cloneShallow(this.sort_config); - // Prepare input - var self = this; - var def = new $.Deferred(); - var cluster_input = {}; - - // Use data from tracks on the same level of expansion as the first one - // in the track group as input, i.e. the outer level excluding any - // expansions - var track_group = this.getTrackGroups()[track_group_index]; - var track_ids = []; - if (track_group !== undefined) { - track_ids = _getEffectiveTrackGroup(this, track_group[0]) || []; - } - for (var i = 0; i < track_ids.length; i++) { - var track_id = track_ids[i]; - var data_id_key = this.getTrackDataIdKey(track_id); - var data = this.getTrackData(track_id); - for (var j=0; j indB) { - // switch if necessary to make process WLOG - var tmp = indA; - indA = indB; - indB = tmp; - should_negate_result = true; - } - // See if any changepoints in [indA, indB) - var res = 0; - if (hasElementsInInterval(this.mandatory_change_points, function(x) { return x; }, indA, indB)) { - res = -1; - } else if (hasElementsInInterval(this.preferred_change_points, function(x) { return x; }, indA, indB)) { - res = -0.5; - } - if (should_negate_result) { - res = res * -1; - } - return res; - } - return PrecomputedComparator; -})(); -module.exports = OncoprintModel; diff --git a/packages/oncoprintjs/src/js/oncoprintmodel.ts b/packages/oncoprintjs/src/js/oncoprintmodel.ts new file mode 100644 index 00000000000..74682f7bddd --- /dev/null +++ b/packages/oncoprintjs/src/js/oncoprintmodel.ts @@ -0,0 +1,1727 @@ +/* jshint browserify: true, asi: true */ + +import binarysearch from './binarysearch'; +import hasElementsInInterval from './haselementsininterval'; +import CachedProperty from './CachedProperty'; +import {hclusterColumns, hclusterTracks} from './clustering'; +import $ from 'jquery'; +import * as BucketSort from "./bucketsort"; +import {cloneShallow, doesCellIntersectPixel, ifndef} from "./utils"; +import _ from "lodash"; +import {RuleSet, RuleSetParams, RuleWithId} from "./oncoprintruleset"; +import {InitParams} from "./oncoprint"; +import {ComputedShapeParams} from "./oncoprintshape"; +import {CaseItem, EntityItem} from "./workers/clustering-worker"; +import PrecomputedComparator from "./precomputedcomparator"; + +export type ColumnId = string; +export type TrackId = number; +export type Datum = any; +export type RuleSetId = number; +export type TrackGroup = TrackId[]; +export type TrackGroupIndex = number; +export type TrackSortDirection = 0|1|-1; +export type TrackSortComparator = (d1:D, d2:D)=>number;//returns (0|1|2|-1|-2); for comparison-based sort, where 2 and -2 mean force to end or beginning (resp) no matter what direction sorted in +export type TrackSortVector = (d:D)=>(number|string)[]; // maps data to vector used for bucket sort - types of elements in each position must be same, i.e. Kth element must always be a number, or always be a string +export type TrackTooltipFn = (cell_data:D[])=>HTMLElement|string|any; +export type TrackSortSpecificationComparators = { + mandatory:TrackSortComparator; // specifies the mandatory order for the track + preferred:TrackSortComparator; // specifies the preferred order for the track (can be overridden by mandatory order of higher track) + isVector?:false; +}; +export type TrackSortSpecificationVectors = { + mandatory: TrackSortVector; // specifies the mandatory order for the track + preferred: TrackSortVector; // specifies the preferred order for the track (can be overridden by mandatory order of higher track) + isVector: true; + compareEquals?:TrackSortComparator; // specifies a comparator to be applied to sort among equal sort vectors in the *preferred* order (optional). eg sort by sample id if all else equal +}; +export type TrackSortSpecification = TrackSortSpecificationComparators | TrackSortSpecificationVectors; +export type ActiveRules = {[ruleId:number]:boolean}; +export type ActiveRulesCount = {[ruleId:number]:number}; +export type TrackSortDirectionChangeCallback = (track_id:TrackId, dir:number)=>void; +export type CustomTrackOption = {label?:string, separator?: boolean, onClick?:(id:TrackId)=>void, weight?:string, disabled?:boolean}; +export type UserTrackSpec = { + target_group?:TrackGroupIndex; + track_group_header?:string; + cell_height?: number; + track_padding?: number; + has_column_spacing?: boolean; + data_id_key?: string & keyof D; + tooltipFn?: TrackTooltipFn; + movable?:boolean; + removable?:boolean; + removeCallback?:(track_id:TrackId)=>void; + label?: string; + sublabel?: string; + html_label?: string; + track_label_color?: string; + track_label_circle_color?: string; + track_label_font_weight?: string; + track_label_left_padding?: number; + link_url?: string; + description?: string; + track_info?:string; + sortCmpFn:TrackSortSpecification; + sort_direction_changeable?:boolean; + onSortDirectionChange?:TrackSortDirectionChangeCallback; + init_sort_direction?:TrackSortDirection; + data?:D[]; + rule_set_params?: RuleSetParams; + expansion_of?: TrackId; + expandCallback?: (id: TrackId) => void; + expandButtonTextGetter?: (is_expanded: boolean) => string; + important_ids?:string[]; + custom_track_options?:CustomTrackOption[]; + $track_info_tooltip_elt?:JQuery; +}; +export type LibraryTrackSpec = UserTrackSpec & { rule_set:RuleSet, track_id:TrackId}; +export type TrackOverlappingCells = { + ids:ColumnId[], + track:TrackId, + top:number, + left:number +}; + +export type SortConfig = { + type: "alphabetical" +} | { + type: "order"; + order: string[]; +} | { + type: "cluster"; + track_group_index: number; + clusterValueFn: (datum:any)=>number; +} | {type?:""}; + +export type IdentifiedShapeList = { + id:ColumnId; + shape_list:ComputedShapeParams[]; +}; + +export type ClusterSortResult = { + track_group_index:TrackGroupIndex; + track_id_order:TrackId[]; +}; + +class UnionOfSets { + // a set, to be passed in as argument, is an object where the values are truthy + private union_count:{[key:string]:number} = {}; + private sets:{[setId:string]:{[key:string]:boolean}} = {}; + + private setOfKeys(obj:{[key:string]:any}) { + const set:{[key:string]:boolean} = {}; + for (const k of Object.keys(obj)) { + if (typeof obj[k] !== 'undefined') { + set[k] = true; + } + } + return set; + } + + public putSet(id:string, set:{[key:string]:boolean}) { + this.removeSet(id); + this.sets[id] = set; + + for (const k of Object.keys(set)) { + if (set[k]) { + this.union_count[k] = this.union_count[k] || 0; + this.union_count[k] += 1; + } + } + } + + public removeSet(id:string) { + const union_count = this.union_count; + const old_set = this.sets[id] || {}; + for (const k of Object.keys(old_set)) { + if (old_set[k]) { + union_count[k] -= 1; + if (union_count[k] === 0) { + delete union_count[k]; + } + } + } + delete this.sets[id]; + } + + public getUnion() { + return this.setOfKeys(this.union_count); + } +} + +function arrayUnique(arr:string[]) { + const present:{[elt:string]:boolean} = {}; + const unique = []; + for (let i=0; i(obj:{[key:string]:T}) { + const copy:{[key:string]:T} = {}; + for (const key of Object.keys(obj)) { + copy[key] = obj[key]; + } + return copy; +} + +function clamp(x:number, lower:number, upper:number) { + return Math.min(upper, Math.max(lower, x)); +} + +const MIN_ZOOM_PIXELS = 100; +const MIN_CELL_HEIGHT_PIXELS = 3; + +export type TrackProp = {[trackId:number]:T}; +export type ColumnProp = {[columnId:string]:T}; + +export default class OncoprintModel { + + // Global properties + private sort_config:SortConfig; + public rendering_suppressed_depth:number; + + // Rendering properties + private max_height:number; + private cell_width:number; + private horz_zoom:number; + private vert_zoom:number; + private horz_scroll:number; + private vert_scroll:number; + private bottom_padding:number; + private track_group_padding:number; + private cell_padding:number; + private cell_padding_on:boolean; + private cell_padding_off_cell_width_threshold:number; + private cell_padding_off_because_of_zoom:boolean; + private id_order:ColumnId[]; + private hidden_ids:ColumnProp; + private highlighted_ids:ColumnId[]; + private track_group_legend_order:TrackGroupIndex[]; + private show_track_sublabels:boolean; + private column_labels:ColumnProp; + + // Track properties + private track_important_ids:TrackProp>;// set of "important" ids - only these ids will cause a used rule to become active and thus shown in the legend + private track_label:TrackProp; + private track_label_color:TrackProp; + private track_label_circle_color:TrackProp; + private track_label_font_weight:TrackProp; + private track_label_left_padding:TrackProp; + private track_sublabel:TrackProp; + private track_html_label:TrackProp; + private track_link_url:TrackProp; + private track_description:TrackProp; + private cell_height:TrackProp; + private track_padding:TrackProp; + private track_data_id_key:TrackProp; + private track_tooltip_fn:TrackProp>; + private track_movable:TrackProp; + private track_removable:TrackProp; + private track_remove_callback:TrackProp<(track_id:TrackId)=>void>; + private track_sort_cmp_fn:TrackProp>; + private track_sort_direction_changeable:TrackProp; + private track_sort_direction:TrackProp; + private track_sort_direction_change_callback:TrackProp; + private track_data:TrackProp; + private track_rule_set_id:TrackProp; + private track_active_rules:TrackProp; + private track_info:TrackProp; + private $track_info_tooltip_elt:TrackProp; + private track_has_column_spacing:TrackProp; + private track_expansion_enabled:TrackProp; + private track_expand_callback:TrackProp<(trackId:TrackId)=>void>; + private track_expand_button_getter:TrackProp<(is_expanded:boolean)=>string>; + public track_expansion_tracks:TrackProp; + private track_expansion_parent:TrackProp; + private track_custom_options:TrackProp; + + // Rule set properties + private rule_sets:{[ruleSetId:number]:RuleSet}; + private rule_set_active_rules:{[ruleSetId:number]:ActiveRulesCount}; + + // Cached and recomputed properties + private visible_id_order:CachedProperty; + private track_id_to_datum:CachedProperty>>; + private track_present_ids:CachedProperty; + private present_ids:CachedProperty>; + private id_to_index:CachedProperty>; + private visible_id_to_index:CachedProperty>; + private track_tops:CachedProperty>; + private cell_tops:CachedProperty>; + private label_tops:CachedProperty>; + private track_tops_zoomed:CachedProperty>; + private cell_tops_zoomed:CachedProperty>; + private label_tops_zoomed:CachedProperty>; + private column_left:CachedProperty>; + private zoomed_column_left:CachedProperty>; + private column_left_no_padding:CachedProperty>; + private precomputed_comparator:CachedProperty>>; + + private track_groups:TrackGroup[]; + private track_group_sort_priority:TrackGroupIndex[]; + private track_group_header:string[]; + + constructor(params:InitParams) { + + const model = this; + + this.sort_config = {}; + this.rendering_suppressed_depth = 0; + + this.max_height = 500; + this.cell_width = ifndef(params.init_cell_width, 6); + this.horz_zoom = ifndef(params.init_horz_zoom, 1); + this.vert_zoom = ifndef(params.init_vert_zoom, 1); + this.horz_scroll = 0; + this.vert_scroll = 0; + this.bottom_padding = 0; + this.track_group_padding = ifndef(params.init_track_group_padding, 10); + this.cell_padding = ifndef(params.init_cell_padding, 3); + this.cell_padding_on = ifndef(params.init_cell_padding_on, true); + this.cell_padding_off_cell_width_threshold = ifndef(params.cell_padding_off_cell_width_threshold, 2); + this.cell_padding_off_because_of_zoom = (this.getCellWidth() < this.cell_padding_off_cell_width_threshold); + this.id_order = []; + this.hidden_ids = {}; + this.highlighted_ids = []; + this.track_group_legend_order = []; + this.show_track_sublabels = false; + this.column_labels = {}; + + // Track Properties + this.track_important_ids = {}; // a set of "important" ids - only these ids will cause a used rule to become active and thus shown in the legend + this.track_label = {}; + this.track_label_color = {}; + this.track_label_circle_color = {}; + this.track_label_font_weight = {}; + this.track_label_left_padding = {}; // TODO: consolidate track styling properties into one object (help me typescript) + this.track_sublabel = {}; + this.track_html_label = {}; + this.track_link_url = {}; + this.track_description = {}; + this.cell_height = {}; + this.track_padding = {}; + this.track_data_id_key = {}; + this.track_tooltip_fn = {}; + this.track_movable = {}; + this.track_removable = {}; + this.track_remove_callback = {}; + this.track_sort_cmp_fn = {}; + this.track_sort_direction_changeable = {}; + this.track_sort_direction = {}; // 1: ascending, -1: descending, 0: not + this.track_sort_direction_change_callback = {}; + this.track_data = {}; + this.track_rule_set_id = {}; // track id -> rule set id + this.track_active_rules = {}; // from track id to active rule map (map with rule ids as keys) + this.track_info = {}; + this.$track_info_tooltip_elt = {}; + this.track_has_column_spacing = {}; // track id -> boolean + this.track_expansion_enabled = {}; // track id -> boolean or undefined + this.track_expand_callback = {}; // track id -> function that adds expansion tracks for its track if set + this.track_expand_button_getter = {}; // track id -> function from boolean to string if customized + this.track_expansion_tracks = {}; // track id -> array of track ids if applicable + this.track_expansion_parent = {}; // track id -> track id if applicable + this.track_custom_options = {}; // track id -> { label, onClick, weight, disabled }[] ( see index.d.ts :: CustomTrackOption ) + + // Rule Set Properties + this.rule_sets = {}; // map from rule set id to rule set + this.rule_set_active_rules = {}; // map from rule set id to map from rule id to use count + + // Cached and Recomputed Properties + this.visible_id_order = new CachedProperty([], function () { + const hidden_ids = model.hidden_ids; + return model.id_order.filter(function (id) { + return !hidden_ids[id]; + }); + }); + this.track_id_to_datum = new CachedProperty({}, function(model, track_id) { + const curr = model.track_id_to_datum.get(); + if (model.getContainingTrackGroup(track_id) !== null) { + const map:ColumnProp = {}; + const data = model.getTrackData(track_id) || []; + const data_id_key = model.getTrackDataIdKey(track_id) || ''; + for (let i=0; i = {}; + const data = model.getTrackData(track_id) || []; + const data_id_key = model.getTrackDataIdKey(track_id) || ''; + for (let i = 0; i < data.length; i++) { + ids[data[i][data_id_key] as string] = true; + } + union.putSet(track_id, ids); + } else { + union.removeSet(track_id); + } + return union; + }); + this.present_ids = new CachedProperty({}, function() { + return model.track_present_ids.get().getUnion(); + }); + this.track_present_ids.addBoundProperty(this.present_ids); + + this.id_to_index = new CachedProperty({}, function() { + const id_to_index:ColumnProp = {}; + const id_order = model.getIdOrder(true); + for (let i=0; i = {}; + const id_order = model.getIdOrder(); + for (let i=0; i = {}; + const groups = model.getTrackGroups(); + let y = 0; + for (let i = 0; i < groups.length; i++) { + const group = groups[i]; + if (model.getTrackGroupHeader(i).length > 0 && group.length > 0) { + y += model.getTrackGroupHeaderSize(); + } + for (let j = 0; j < group.length; j++) { + const track_id = group[j]; + tops[track_id] = y; + y += model.getTrackHeight(track_id, true); + } + if (group.length > 0) { + y += model.getTrackGroupPadding(true); + } + } + return tops; + }); + this.cell_tops = new CachedProperty({}, function() { + const track_tops = model.track_tops.get(); + const cell_tops:TrackProp = {}; + for (const k in track_tops) { + if (track_tops.hasOwnProperty(k)) { + const key = parseInt(k, 10); + cell_tops[key] = track_tops[key] + model.getTrackPadding(key, true); + } + } + return cell_tops; + }); + this.label_tops = new CachedProperty({}, function() { + return model.cell_tops.get(); + }); + + this.track_tops.addBoundProperty(this.cell_tops); + this.cell_tops.addBoundProperty(this.label_tops); + + this.track_tops_zoomed = new CachedProperty({}, function () { + const tops:TrackProp = {}; + const groups = model.getTrackGroups(); + let y = 0; + for (let i = 0; i < groups.length; i++) { + const group = groups[i]; + for (let j = 0; j < group.length; j++) { + const track_id = group[j]; + tops[track_id] = y; + y += model.getTrackHeight(track_id); + } + if (group.length > 0) { + y += model.getTrackGroupPadding(); + } + } + return tops; + }); + this.cell_tops_zoomed = new CachedProperty({}, function() { + const track_tops = model.track_tops_zoomed.get(); + const cell_tops:TrackProp = {}; + for (const k in track_tops) { + if (track_tops.hasOwnProperty(k)) { + const key = parseInt(k, 10); + cell_tops[key] = track_tops[key] + model.getTrackPadding(key); + } + } + return cell_tops; + }); + this.label_tops_zoomed = new CachedProperty({}, function() { + return model.cell_tops_zoomed.get(); + }); + + this.track_tops.addBoundProperty(this.track_tops_zoomed); + this.track_tops_zoomed.addBoundProperty(this.cell_tops_zoomed); + this.cell_tops_zoomed.addBoundProperty(this.label_tops_zoomed); + + this.column_left = new CachedProperty({}, function() { + const cell_width = model.getCellWidth(true); + const cell_padding = model.getCellPadding(true); + const left:ColumnProp = {}; + const ids = model.getIdOrder(); + for (let i = 0; i < ids.length; i++) { + left[ids[i]] = i * (cell_width + cell_padding); + } + return left; + }); + + this.zoomed_column_left = new CachedProperty({}, function() { + const cell_width = model.getCellWidth(); + const cell_padding = model.getCellPadding(); + const left:ColumnProp = {}; + const ids = model.getIdOrder(); + for (let i = 0; i < ids.length; i++) { + left[ids[i]] = i * (cell_width + cell_padding); + } + return left; + }); + this.column_left_no_padding = new CachedProperty({}, function() { + const cell_width = model.getCellWidth(true); + const left:ColumnProp = {}; + const ids = model.getIdOrder(); + for (let i = 0; i < ids.length; i++) { + left[ids[i]] = i * cell_width; + } + return left; + }); + this.column_left.addBoundProperty(this.zoomed_column_left); + this.column_left.addBoundProperty(this.column_left_no_padding); + + this.precomputed_comparator = new CachedProperty({}, function(model:OncoprintModel, track_id:TrackId) { + const curr_precomputed_comparator = model.precomputed_comparator.get(); + curr_precomputed_comparator[track_id] = new PrecomputedComparator(model.getTrackData(track_id), + model.getTrackSortComparator(track_id), + model.getTrackSortDirection(track_id), + model.getTrackDataIdKey(track_id)); + return curr_precomputed_comparator; + });// track_id -> PrecomputedComparator + } + + public toggleCellPadding() { + this.cell_padding_on = !this.cell_padding_on; + this.column_left.update(); + return this.cell_padding_on; + } + + public getCellPadding(base?:boolean, dont_consider_zoom?:boolean) { + return (this.cell_padding * (base ? 1 : this.horz_zoom)) * (+this.cell_padding_on) * (dont_consider_zoom ? 1 : +(!this.cell_padding_off_because_of_zoom)); + } + + public getHorzZoom() { + return this.horz_zoom; + } + + public getHorzZoomToFitNumCols(width:number, num_cols:number) { + const cell_width = this.getCellWidth(true); + const zoom_if_cell_padding_on = clamp(width / (num_cols*(cell_width + this.cell_padding)), + 0,1); + const zoom_if_cell_padding_off = clamp(width / (num_cols*cell_width), + 0,1); + let zoom; + if (!this.cell_padding_on) { + zoom = zoom_if_cell_padding_off; + } else { + if (cell_width * zoom_if_cell_padding_on < this.cell_padding_off_cell_width_threshold) { + if (cell_width * zoom_if_cell_padding_off >= this.cell_padding_off_cell_width_threshold) { + // Because of cell padding toggling there's no way to get exactly the desired number of columns. + // We can see this by contradiction: if we assume that cell padding is on, and try to fit exactly + // our number of columns, we end up turning cell padding off (outer if statement). If we assume that + // cell padding is off and try to fit our number of columns, we find that cell padding is on (inner if statement). + // Thus, it's impossible to show this exact number of columns - we either under or overshoot it. We + // thus should overshoot it by as little as possible, show as few columns as possible while still fitting + // this amount. It must be exactly at the threshold for switching. + // + const unrounded_zoom = this.cell_padding_off_cell_width_threshold / cell_width; + const unrounded_num_cols = width / (unrounded_zoom * cell_width); + const rounded_num_cols = Math.ceil(unrounded_num_cols); + zoom = width / (rounded_num_cols * cell_width); + } else { + zoom = zoom_if_cell_padding_off; + } + } else { + zoom = zoom_if_cell_padding_on; + } + } + return zoom; + } + + public getHorzZoomToFit(width:number, ids:ColumnId[]) { + ids = ids || []; + if (ids.length === 0) { + return 1; + } + const id_to_index_map = this.getVisibleIdToIndexMap(); + const indexes = ids.map(function(id) { return id_to_index_map[id]; }); + let max = Number.NEGATIVE_INFINITY; + let min = Number.POSITIVE_INFINITY; + for (let i=0; i= this.cell_padding_off_cell_width_threshold && this.cell_padding_off_because_of_zoom) { + this.setCellPaddingOffBecauseOfZoom(false); + } + return this.horz_zoom; + } + + public getVertZoom() { + return this.vert_zoom; + } + + public setVertZoom(z:number) { + const min_zoom = this.getMinVertZoom(); + this.vert_zoom = clamp(z, min_zoom, 1); + this.track_tops.update(); + return this.vert_zoom; + } + + public hideTrackLegends(track_ids:TrackId[]) { + track_ids = [].concat(track_ids); + for (let i=0; iparseInt(x,10)); + for (let i=0; iparseInt(x, 0)); + for (let i=0; i zB) { + return 1; + } else { + return 0; + } + } + return shapes.map(function(shape_list:ComputedShapeParams[], index:number) { + if (sort_by_z) { + shape_list.sort(z_comparator); + } + return { + id: data[index][id_key], + shape_list: shape_list + }; + }); + } + + public getActiveRules(rule_set_id:RuleSetId) { + const rule_set_active_rules = this.rule_set_active_rules[rule_set_id]; + if (rule_set_active_rules) { + return this.rule_sets[rule_set_id].getRulesWithId().filter(function(rule_with_id:RuleWithId) { + return !!rule_set_active_rules[rule_with_id.id]; + }); + } else { + return []; + } + } + + public setTrackImportantIds(track_id:TrackId, ids?:ColumnId[]) { + if (!ids) { + this.track_important_ids[track_id] = undefined; + } else { + this.track_important_ids[track_id] = ids.reduce(function(map:ColumnProp, next_id:ColumnId) { + map[next_id] = true; + return map; + }, {}); + } + } + + public getTrackImportantIds(track_id:TrackId) { + return this.track_important_ids[track_id]; + } + + public getRuleSets() { + // return rule sets, in track group legend order + const self = this; + const legend_order = this.getTrackGroupLegendOrder(); + const used_track_groups:{[trackGroupIndex:number]:boolean} = {}; + const track_groups = this.getTrackGroups(); + const sorted_track_groups = []; + for (let i=0; ix.toString())); + return unique_rule_set_ids.map(function(rule_set_id) { + return self.rule_sets[parseInt(rule_set_id, 10)]; + }); + } + + public getTrackHasColumnSpacing(track_id:TrackId) { + return !!(this.track_has_column_spacing[track_id]); + } + + public getCellWidth(base?:boolean) { + return this.cell_width * (base ? 1 : this.horz_zoom); + } + + public getCellHeight(track_id:TrackId, base?:boolean) { + return this.cell_height[track_id] * (base ? 1 : this.vert_zoom); + } + + public getTrackInfo(track_id:TrackId) { + return this.track_info[track_id]; + } + + public setTrackInfo(track_id:TrackId, msg:string) { + this.track_info[track_id] = msg; + } + + public getTrackHeight(track_id:TrackId, base?:boolean) { + return this.getCellHeight(track_id, base) + 2*this.getTrackPadding(track_id, base); + } + + public getTrackPadding(track_id:TrackId, base?:boolean) { + return this.track_padding[track_id] * (base ? 1 : this.vert_zoom); + } + public getBottomPadding() { + return this.bottom_padding; + } + public getTrackSortDirection(track_id:TrackId) { + return this.track_sort_direction[track_id]; + } + public setTrackSortDirection(track_id:TrackId, dir:TrackSortDirection, no_callback?:boolean) { + // see above for dir options + this.track_sort_direction[track_id] = dir; + if (!no_callback) { + this.track_sort_direction_change_callback[track_id](track_id, dir); + } + this.precomputed_comparator.update(this, track_id); + } + + public setCellPaddingOn(cell_padding_on:boolean) { + this.cell_padding_on = cell_padding_on; + this.column_left.update(); + } + public getIdOrder(all?:boolean) { + if (all) { + return this.id_order; // TODO: should be read-only + } else { + return this.visible_id_order.get(); + } + } + public getIdToIndexMap() { + return this.id_to_index.get(); + } + public getVisibleIdToIndexMap() { + return this.visible_id_to_index.get(); + } + + public getHiddenIds() { + const hidden_ids = this.hidden_ids; + return this.id_order.filter(function (id) { + return !!hidden_ids[id]; + }); + } + + public isSortAffected(modified_ids:TrackId | TrackId[], group_or_track:"track"|"group") { + modified_ids = [].concat(modified_ids); + let group_indexes; + const self = this; + if (group_or_track === "track") { + group_indexes = modified_ids.map(function(id) { + return self.getContainingTrackGroupIndex(id); + }); + } else { + group_indexes = modified_ids; + } + return (this.sort_config.type !== "cluster" || + (group_indexes.indexOf(this.sort_config.track_group_index) > -1)); + } + + public setIdOrder(ids:ColumnId[]) { + this.id_order = ids.slice(); + Object.freeze(this.id_order); + this.id_to_index.update(); + this.visible_id_order.update(); + this.column_left.update(); + } + + public hideIds(to_hide:ColumnId[], show_others?:boolean) { + if (show_others) { + this.hidden_ids = {}; + } + for (let j = 0, len = to_hide.length; j < len; j++) { + this.hidden_ids[to_hide[j]] = true; + } + this.visible_id_order.update(); + this.column_left.update(); + } + + public setHighlightedIds(ids:ColumnId[]) { + this.highlighted_ids = ids; + } + + public getHighlightedIds() { + return this.highlighted_ids; + } + + public setTrackGroupOrder(index:TrackGroupIndex, track_order:TrackGroup) { + this.track_groups[index] = track_order; + + this.track_tops.update(); + } + + public moveTrackGroup(from_index:TrackGroupIndex, to_index:TrackGroupIndex) { + const new_groups = []; + const new_headers = []; + const group_to_move = this.track_groups[from_index]; + for (let i = 0; i < this.track_groups.length; i++) { + if (i !== from_index && i !== to_index) { + new_groups.push(this.track_groups[i]); + new_headers.push(this.track_group_header[i]); + } + if (i === to_index) { + new_groups.push(group_to_move); + new_headers.push(this.track_group_header[from_index]); + } + } + this.track_groups = new_groups; + this.track_group_header = new_headers; + this.track_tops.update(); + return this.track_groups; + } + + public addTracks(params_list:LibraryTrackSpec[]) { + for (let i = 0; i < params_list.length; i++) { + const params = params_list[i]; + this.addTrack(params); + } + this.track_tops.update(); + } + + private addTrack(params: LibraryTrackSpec) { + const track_id = params.track_id; + this.$track_info_tooltip_elt[track_id] = params.$track_info_tooltip_elt; + this.track_custom_options[track_id] = ifndef(params.custom_track_options, []); + this.track_label[track_id] = ifndef(params.label, "Label"); + this.track_sublabel[track_id] = ifndef(params.sublabel, ""); + this.track_label_color[track_id] = ifndef(params.track_label_color, "black"); + this.track_label_circle_color[track_id] = params.track_label_circle_color; + this.track_label_font_weight[track_id] = params.track_label_font_weight; + this.track_label_left_padding[track_id] = ifndef(params.track_label_left_padding, 0); + this.track_link_url[track_id] = ifndef(params.link_url, null); + this.track_description[track_id] = ifndef(params.description, ""); + this.cell_height[track_id] = ifndef(params.cell_height, 23); + this.track_padding[track_id] = ifndef(params.track_padding, 5); + this.track_has_column_spacing[track_id] = ifndef(params.has_column_spacing, true); + + this.track_tooltip_fn[track_id] = ifndef(params.tooltipFn, function (d) { + return d + ''; + }); + this.track_movable[track_id] = ifndef(params.movable, true); + this.track_removable[track_id] = ifndef(params.removable, false); + this.track_remove_callback[track_id] = ifndef(params.removeCallback, function() {}); + + if (typeof params.expandCallback !== 'undefined') { + this.track_expand_callback[track_id] = params.expandCallback; + this.track_expansion_enabled[track_id] = true; + } + if (typeof params.expandButtonTextGetter !== 'undefined') { + this.track_expand_button_getter[track_id] = params.expandButtonTextGetter; + } + + this.track_sort_cmp_fn[track_id] = params.sortCmpFn; + + this.track_sort_direction_changeable[track_id] = ifndef(params.sort_direction_changeable, false); + this.track_sort_direction_change_callback[track_id] = ifndef(params.onSortDirectionChange, function() {}); + this.track_data[track_id] = ifndef(params.data, []); + this.track_data_id_key[track_id] = ifndef(params.data_id_key, 'id'); + + this.track_info[track_id] = ifndef(params.track_info, ""); + + if (typeof params.html_label !== 'undefined') { + this.track_html_label[track_id] = params.html_label; + } + + if (typeof params.rule_set !== 'undefined') { + this.rule_sets[params.rule_set.rule_set_id] = params.rule_set; + this.rule_set_active_rules[params.rule_set.rule_set_id] = {}; + this.track_rule_set_id[track_id] = params.rule_set.rule_set_id; + } + this.track_active_rules[track_id] = {}; + + if (params.important_ids) { + this.setTrackImportantIds(track_id, params.important_ids); + } + + this.track_sort_direction[track_id] = ifndef(params.init_sort_direction, 1); + + params.target_group = ifndef(params.target_group, 0); + while (params.target_group >= this.track_groups.length) { + this.track_groups.push([]); + this.track_group_header.push(""); + } + if (params.track_group_header) { + this.track_group_header[params.target_group] = params.track_group_header; + } + + const group_array = this.track_groups[params.target_group]; + const target_index = (params.expansion_of !== undefined + ? group_array.indexOf(this.getLastExpansion(params.expansion_of)) + 1 + : group_array.length + ); + group_array.splice(target_index, 0, track_id); + + if (params.expansion_of !== undefined) { + if (!this.track_expansion_tracks.hasOwnProperty(params.expansion_of)) { + this.track_expansion_tracks[params.expansion_of] = []; + } + if (this.track_expansion_tracks[params.expansion_of].indexOf(track_id) !== -1) { + throw new Error('Illegal state: duplicate expansion track ID'); + } + this.track_expansion_parent[track_id] = params.expansion_of; + this.track_expansion_tracks[params.expansion_of].push(track_id); + } + + this.track_id_to_datum.update(this, track_id); + this.track_present_ids.update(this, track_id); + this.precomputed_comparator.update(this, track_id); + + this.setIdOrder(Object.keys(this.present_ids.get())); + } + + // get a reference to the array that stores the order of tracks in + // the same group + private _getMajorTrackGroup(track_id:TrackId, return_index?:boolean) { + let group; + let i; + for (i = 0; i < this.track_groups.length; i++) { + if (this.track_groups[i].indexOf(track_id) > -1) { + group = this.track_groups[i]; + break; + } + } + if (group) { + return return_index ? i : group; + } else { + return null; + } + } + // get an array listing the track IDs that a track can move around + private _getEffectiveTrackGroup(track_id:TrackId) { + const self = this; + let group, + parent_id = this.track_expansion_parent[track_id]; + if (parent_id === undefined) { + group = (function(major_group:TrackGroup) { + return (major_group === null ? null + : major_group.filter(function (sibling_id) { + return self.track_expansion_parent[sibling_id] === undefined; + })); + })(this._getMajorTrackGroup(track_id) as TrackGroup); + } else { + group = this.track_expansion_tracks[parent_id]; + } + return group ? group.slice() : null; + } + + private isRuleSetUsed(rule_set_id:RuleSetId) { + let used = false; + const tracks = this.getTracks(); + for (let i=0; i; + // this gets the nearest lower index + const nearest_id_index = binarysearch(id_order, x, function(id) { return zoomed_column_left[id];}, true); + if (nearest_id_index === -1) { + return null; + } + + // Next, see if it's in a track + const tracks = this.getTracks(); + const cell_tops = this.getCellTops() as TrackProp; + const nearest_track_index = binarysearch(tracks, y, function (track) { + return cell_tops[track]; + }, true); + if (nearest_track_index === -1) { + return null; + } + const nearest_track = tracks[nearest_track_index]; + if (y >= cell_tops[nearest_track] + this.getCellHeight(nearest_track)) { + // we know y is past the top of the track (>= cell_tops[nearest_track]), so this checks if y is past the bottom of the track + return null; + } + + // At this point, we know y is inside a track + + // Finally, return all ids within 1 px of x to the right + const ids = []; + let hitzone_width = this.getCellWidth(); + if (!this.getTrackHasColumnSpacing(nearest_track)) { + hitzone_width += this.getCellPadding(); + } + for (let i=nearest_id_index; i x+1) { + break; + } + } + if (ids.length > 0) { + return {'ids': ids, 'track': nearest_track, 'top': cell_tops[nearest_track], 'left': zoomed_column_left[ids[0]]}; + } + return null; + }; + + public getTrackDatum(track_id:TrackId, id:ColumnId) { + let datum = this.track_id_to_datum.get()[track_id][id]; + if (typeof datum === 'undefined') { + datum = null; + } + return datum; + } + + public getTrackTops():TrackProp; + public getTrackTops(desired_track_id:TrackId):number; + public getTrackTops(desired_track_id?:TrackId) { + if (typeof desired_track_id === 'undefined') { + return copyShallowObject(this.track_tops.get()); + } else { + return this.track_tops.get()[desired_track_id]; + } + } + + public getZoomedTrackTops():TrackProp; + public getZoomedTrackTops(desired_track_id:TrackId):number; + public getZoomedTrackTops(desired_track_id?:TrackId) { + if (typeof desired_track_id === 'undefined') { + return copyShallowObject(this.track_tops_zoomed.get()); + } else { + return this.track_tops_zoomed.get()[desired_track_id]; + } + } + + public getCellTops(desired_track_id?:undefined, base?:boolean):TrackProp; + public getCellTops(desired_track_id:TrackId, base?:boolean):number; + public getCellTops(desired_track_id?:TrackId, base?:boolean) { + if (typeof desired_track_id === 'undefined') { + return copyShallowObject((base ? this.cell_tops : this.cell_tops_zoomed).get()); + } else { + return (base ? this.cell_tops : this.cell_tops_zoomed).get()[desired_track_id]; + } + } + + public getLabelTops():TrackProp; + public getLabelTops(desired_track_id:TrackId):number; + public getLabelTops(desired_track_id?:TrackId, base?:boolean) { + if (typeof desired_track_id === 'undefined') { + return copyShallowObject((base ? this.label_tops : this.label_tops_zoomed).get()); + } else { + return (base ? this.label_tops : this.label_tops_zoomed).get()[desired_track_id]; + } + } + + public getContainingTrackGroup(track_id:TrackId) { + return this._getEffectiveTrackGroup(track_id); + } + + public getContainingTrackGroupIndex(track_id:TrackId) { + return this._getMajorTrackGroup(track_id, true); + } + + public setTrackGroupHeader(track_group_id:TrackGroupIndex, text:string) { + this.track_group_header[track_group_id] = text; + this.track_tops.update(); + } + + public getTrackGroupHeader(track_group_id:TrackGroupIndex) { + return this.track_group_header[track_group_id] || ""; + } + + public getTrackGroupHeaderSize() { + return 20; + } + + public getTrackGroups() { + // TODO: make read-only + return this.track_groups; + } + + public getTracks() { + const ret = []; + for (let i = 0; i < this.track_groups.length; i++) { + for (let j = 0; j < this.track_groups[i].length; j++) { + ret.push(this.track_groups[i][j]); + } + } + return ret; + } + + public getIdsInLeftInterval(left:number, right:number) { + const cell_width = this.getCellWidth(); + const cell_padding = this.getCellPadding(); + const id_order = this.getIdOrder(); + + // left_id_index and right_id_index are inclusive + let left_id_index = Math.floor(left/(cell_width + cell_padding)); + const left_remainder = left - left_id_index*(cell_width + cell_padding); + if (left_remainder > cell_width) { + left_id_index += 1; + } + const right_id_index = Math.floor(right/(cell_width + cell_padding)); + return id_order.slice(left_id_index, right_id_index+1); + } + + public getColumnLeft():ColumnProp; + public getColumnLeft(id:ColumnId):number; + public getColumnLeft(id?:ColumnId) { + if (typeof id === 'undefined') { + return this.column_left.get(); + } else { + return this.column_left.get()[id]; + } + } + + public getColumnLeftNoPadding():ColumnProp; + public getColumnLeftNoPadding(id:ColumnId):number; + public getColumnLeftNoPadding(id?:ColumnId) { + if (typeof id === 'undefined') { + return this.column_left_no_padding.get(); + } else { + return this.column_left_no_padding.get()[id]; + } + } + + public getZoomedColumnLeft():ColumnProp; + public getZoomedColumnLeft(id:ColumnId):number; + public getZoomedColumnLeft(id?:ColumnId) { + if (typeof id === 'undefined') { + return this.zoomed_column_left.get(); + } else { + return this.zoomed_column_left.get()[id]; + } + } + + + public getOncoprintHeight(base?:boolean) { + const tracks = this.getTracks(); + const last_track = tracks[tracks.length-1]; + return (base ? this.getTrackTops(last_track) as number : this.getZoomedTrackTops(last_track) as number)+this.getTrackHeight(last_track, base) + + this.getBottomPadding(); + } + + public getOncoprintWidth(base?:boolean) { + return this.getIdOrder().length*(this.getCellWidth(base) + this.getCellPadding(base)); + } + + public getOncoprintWidthNoColumnPadding(base?:boolean) { + return this.getIdOrder().length*this.getCellWidth(base); + } + + public getCellViewHeight() { + return Math.min(this.max_height, this.getOncoprintHeight()); + } + + public getColumnLabels() { + return this.column_labels; + } + + public setColumnLabels(labels:ColumnProp) { + this.column_labels = labels; + } + + public moveTrack(track_id:TrackId, new_previous_track:TrackId) { + + function moveContiguousValues(uniqArray:T[], first_value:T, last_value:T, new_predecessor:T) { + const old_start_index = uniqArray.indexOf(first_value), + old_end_index = uniqArray.indexOf(last_value); + const values = uniqArray.slice(old_start_index, old_end_index + 1); + uniqArray.splice(old_start_index, values.length); + const new_position = (new_predecessor === null ? 0 : uniqArray.indexOf(new_predecessor)+1); + uniqArray.splice.bind(uniqArray, new_position, 0).apply(null, values); + } + + const track_group = this._getMajorTrackGroup(track_id) as TrackGroup, + expansion_parent = this.track_expansion_parent[track_id]; + + let flat_previous_track; + + if (track_group !== null) { + // if an expansion track moves above all other tracks it can, + // place it directly below its expansion parent + if (expansion_parent !== undefined && new_previous_track === null) { + flat_previous_track = expansion_parent; + // otherwise, place the track under (the last expansion track of) + // its sibling + } else { + flat_previous_track = this.getLastExpansion(new_previous_track); + } + moveContiguousValues(track_group, track_id, this.getLastExpansion(track_id), flat_previous_track); + } + + // keep the order of expansion siblings up-to-date as well + if (this.track_expansion_parent[track_id] !== undefined) { + moveContiguousValues(this.track_expansion_tracks[expansion_parent], track_id, track_id, new_previous_track); + } + + this.track_tops.update(); + }; + + public getTrackLabel(track_id:TrackId) { + return this.track_label[track_id]; + } + + public getTrackSublabel(track_id:TrackId) { + return this.track_sublabel[track_id]; + } + + public getShowTrackSublabels() { + return this.show_track_sublabels; + } + + public setShowTrackSublabels(show:boolean) { + return this.show_track_sublabels = show; + } + + public getTrackLabelColor(track_id:TrackId) { + return this.track_label_color[track_id]; + } + + public getTrackLabelCircleColor(track_id:TrackId) { + return this.track_label_circle_color[track_id]; + } + + public getTrackLabelFontWeight(track_id:TrackId) { + return this.track_label_font_weight[track_id]; + } + + public getTrackLabelLeftPadding(track_id:TrackId) { + return this.track_label_left_padding[track_id]; + } + + public getOptionalHtmlTrackLabel(track_id:TrackId) { + return this.track_html_label[track_id]; + } + + public getTrackLinkUrl(track_id:TrackId) { + return this.track_link_url[track_id]; + } + + public getTrackDescription(track_id:TrackId) { + return this.track_description[track_id]; + } + + public getTrackTooltipFn(track_id:TrackId) { + return this.track_tooltip_fn[track_id]; + } + public setTrackTooltipFn(track_id:TrackId, tooltipFn:TrackTooltipFn) { + this.track_tooltip_fn[track_id] = tooltipFn; + } + + public getTrackDataIdKey(track_id:TrackId) { + return this.track_data_id_key[track_id]; + } + + public getTrackGroupPadding(base?:boolean) { + return this.track_group_padding * (base ? 1 : this.vert_zoom); + } + + public isTrackRemovable(track_id:TrackId) { + return this.track_removable[track_id]; + } + + public isTrackSortDirectionChangeable(track_id:TrackId) { + return this.track_sort_direction_changeable[track_id]; + } + + public isTrackExpandable(track_id:TrackId) { + // return true if the flag is defined and true + return Boolean(this.track_expansion_enabled[track_id]); + } + + public expandTrack(track_id:TrackId) { + return this.track_expand_callback[track_id](track_id); + } + + public disableTrackExpansion(track_id:TrackId) { + this.track_expansion_enabled[track_id] = false; + } + + public enableTrackExpansion(track_id:TrackId) { + if (!this.track_expand_callback.hasOwnProperty(track_id)) { + throw new Error("Track '" + track_id +"' has no expandCallback"); + } + this.track_expansion_enabled[track_id] = true; + } + + public isTrackExpanded(track_id:TrackId) { + return this.track_expansion_tracks.hasOwnProperty(track_id) && + this.track_expansion_tracks[track_id].length > 0; + } + + public getExpandButtonText(track_id:TrackId) { + const self = this; + const getExpandButtonFunction = function (track_id:TrackId) { + return (self.track_expand_button_getter[track_id] || + function (is_expanded) { + return is_expanded ? 'Expand more' : 'Expand'; + }); + }; + return getExpandButtonFunction(track_id)(this.isTrackExpanded(track_id)); + } + + /** + * Checks if one track is the expansion of another + * + * @param {number} expansion_track_id - the ID of the track to check + * @param {number} set_track_id - the ID of the track it may be an expansion of + */ + public isExpansionOf(expansion_track_id:TrackId, set_track_id:TrackId) { + return this.track_expansion_tracks.hasOwnProperty(set_track_id) && + this.track_expansion_tracks[set_track_id].indexOf(expansion_track_id) !== -1; + } + + /** + * Finds the bottom-most track in a track's expansion group + * + * @param track_id - the ID of the track to start from + * @returns the ID of its last expansion, or the unchanged param if none + */ + public getLastExpansion(track_id:TrackId) { + let direct_children = this.track_expansion_tracks[track_id]; + while (direct_children && direct_children.length) { + track_id = direct_children[direct_children.length - 1]; + direct_children = this.track_expansion_tracks[track_id]; + } + return track_id; + } + + public getTrackCustomOptions(track_id:TrackId) { + return this.track_custom_options[track_id]; + } + + public setTrackCustomOptions(track_id:TrackId, options:CustomTrackOption[]|undefined) { + this.track_custom_options[track_id] = options; + } + + public setTrackInfoTooltip(track_id:TrackId, $tooltip_elt:JQuery|undefined) { + this.$track_info_tooltip_elt[track_id] = $tooltip_elt; + } + + public $getTrackInfoTooltip(track_id:TrackId) { + return this.$track_info_tooltip_elt[track_id]; + } + + public getRuleSet(track_id:TrackId) { + return this.rule_sets[this.track_rule_set_id[track_id]]; + } + + public shareRuleSet(source_track_id:TrackId, target_track_id:TrackId) { + this.setTrackActiveRules(target_track_id, {}); + + const old_rule_set_id = this.track_rule_set_id[target_track_id]; + this.track_rule_set_id[target_track_id] = this.track_rule_set_id[source_track_id]; + if (!this.isRuleSetUsed(old_rule_set_id)) { + this.removeRuleSet(old_rule_set_id); + } + } + + public setRuleSet(track_id:TrackId, rule_set:RuleSet) { + this.setTrackActiveRules(track_id, {}); + + const curr_rule_set_id = this.track_rule_set_id[track_id]; + this.rule_sets[rule_set.rule_set_id] = rule_set; + this.rule_set_active_rules[rule_set.rule_set_id] = {}; + this.track_rule_set_id[track_id] = rule_set.rule_set_id; + + const rule_set_used = this.isRuleSetUsed(curr_rule_set_id); + if (!rule_set_used) { + this.removeRuleSet(curr_rule_set_id); + } + } + + public getTrackSortComparator(track_id:TrackId) { + return this.track_sort_cmp_fn[track_id]; + } + + public setTrackSortComparator(track_id:TrackId, sortCmpFn:TrackSortSpecification) { + this.track_sort_cmp_fn[track_id] = sortCmpFn; + this.precomputed_comparator.update(this, track_id); + } + + public getTrackData(track_id:TrackId) { + return this.track_data[track_id]; + } + + public clusterTrackGroup(track_group_index:TrackGroupIndex, clusterValueFn:(d:Datum)=>number):Promise { + const sort_config_at_call = cloneShallow(this.sort_config); + // Prepare input + const self = this; + //@ts-ignore + const def = new $.Deferred(); + const cluster_input:ColumnProp> = {}; + + // Use data from tracks on the same level of expansion as the first one + // in the track group as input, i.e. the outer level excluding any + // expansions + const track_group = this.getTrackGroups()[track_group_index]; + let track_ids:TrackId[] = []; + if (track_group !== undefined) { + track_ids = this._getEffectiveTrackGroup(track_group[0]) || []; + } + for (let i = 0; i < track_ids.length; i++) { + const track_id = track_ids[i]; + const data_id_key = this.getTrackDataIdKey(track_id); + const data = this.getTrackData(track_id); + for (let j=0; j { + //@ts-ignore + const def = new $.Deferred(); + this.sort_config = this.sort_config || {}; + if (this.sort_config.type === "alphabetical") { + this.sortAlphabetical(); + def.resolve(); + } else if (this.sort_config.type === "order") { + this.setIdOrder(this.sort_config.order); + def.resolve(); + } else if (this.sort_config.type === "cluster") { + this.clusterTrackGroup(this.sort_config.track_group_index, + this.sort_config.clusterValueFn).then(function(x) { + def.resolve(x); + }); + } else { + this.sortByTracks(); + def.resolve(); + } + return def.promise(); + } + + public setSortConfig(params:SortConfig) { + this.sort_config = params; + } + + public getTrackMovable(track_id:TrackId) { + return this.track_movable[track_id]; + } + + public setTrackMovable(track_id:TrackId, movable:boolean) { + this.track_movable[track_id] = movable; + } + + public isTrackInClusteredGroup(track_id:TrackId) { + return this.sort_config.type === "cluster" && + (this.sort_config.track_group_index === this.getContainingTrackGroupIndex(track_id)); + } +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprintruleset.js b/packages/oncoprintjs/src/js/oncoprintruleset.ts similarity index 54% rename from packages/oncoprintjs/src/js/oncoprintruleset.js rename to packages/oncoprintjs/src/js/oncoprintruleset.ts index cbd453fb96f..07cc3394fe8 100644 --- a/packages/oncoprintjs/src/js/oncoprintruleset.js +++ b/packages/oncoprintjs/src/js/oncoprintruleset.ts @@ -19,35 +19,163 @@ * line: x1, y1, x2, y2, stroke, stroke-width */ -var Shape = require('./oncoprintshape.js'); -var extractRGBA = require('./extractrgba.js'); -var heatmapColors = require('./heatmapcolors.js'); -var binarysearch = require('./binarysearch.js'); -var cloneShallow = require('./utils.js').cloneShallow; - -function ifndef(x, val) { - return (typeof x === "undefined" ? val : x); +import {ComputedShapeParams, Ellipse, Line, Rectangle, Shape, ShapeParams, Triangle} from "./oncoprintshape"; +import heatmapColors from "./heatmapcolors"; +import binarysearch from "./binarysearch"; +import {Omit, cloneShallow, ifndef, objectValues, shallowExtend} from "./utils"; +import {ActiveRules, ColumnProp, Datum, RuleSetId} from "./oncoprintmodel"; + +export type RuleSetParams = ILinearInterpRuleSetParams | ICategoricalRuleSetParams | + IGradientRuleSetParams | + IBarRuleSetParams | + IStackedBarRuleSetParams | + IGradientAndCategoricalRuleSetParams | + IGeneticAlterationRuleSetParams; + +interface IGeneralRuleSetParams { + type?:RuleSetType; + legend_label?: string; + legend_base_color?: string; + exclude_from_legend?: boolean; + na_z?:number; // z index of na shapes (defaults to 1) + na_legend_label?:string; // legend label associated to NA (defaults to 'No data') } +interface ILinearInterpRuleSetParams extends IGeneralRuleSetParams { + log_scale?:boolean; + value_key:string; + value_range:[number, number]; +} + +// all colors are hex, rgb, or rgba +export interface ICategoricalRuleSetParams extends IGeneralRuleSetParams { + type: RuleSetType.CATEGORICAL; + category_key: string; // key into data which gives category + category_to_color?: {[category:string]:string}; +} + +export interface IGradientRuleSetParams extends ILinearInterpRuleSetParams { + type: RuleSetType.GRADIENT; + // either `colormap_name` or `colors` needs to be present + colors?: RGBAColor[]; // [r,g,b,a][] + colormap_name?: string; // name of a colormap found in src/js/heatmapcolors.js + value_stop_points: number[]; + null_color?: string; + null_legend_label?:string; +} + +// TODO: it would be more elegant to create multiple inheritance (if possible) since +// IGradientAndCategoricalRuleSetParams is a IGradientRuleSetParams and +// ICategoricalRuleSetParams with a different `type` field. +export interface IGradientAndCategoricalRuleSetParams extends IGeneralRuleSetParams { + type: RuleSetType.GRADIENT_AND_CATEGORICAL; + // either `colormap_name` or `colors` needs to be present + colors?: [number, number, number, number][]; // [r,g,b,a][] + colormap_name?: string; // name of a colormap found in src/js/heatmapcolors.js + value_stop_points: number[]; + null_color?: string; + + log_scale?:boolean; + value_key: string; + value_range: [number, number]; + + category_key: string; // key into data which gives category + category_to_color?: {[category:string]:string}; +} + +export interface IBarRuleSetParams extends ILinearInterpRuleSetParams { + type: RuleSetType.BAR; + fill?: string; + negative_fill?: string; +} + +export interface IStackedBarRuleSetParams extends IGeneralRuleSetParams { + type: RuleSetType.STACKED_BAR; + value_key: string; + categories: string[]; + fills?: string[]; +} + +export interface IGeneticAlterationRuleSetParams extends IGeneralRuleSetParams { + type: RuleSetType.GENE; + rule_params: GeneticAlterationRuleParams; +} + +export type GeneticAlterationRuleParams = { + [datumKey:string]:{ + [commaSeparatedDatumValues:string]: { + shapes: ShapeParams[]; + legend_label: string; + exclude_from_legend?:boolean; + } + } +}; + + +export type RGBAColor = [number,number,number,number]; //[0,255] x [0,255] x [0,255] x [0,1] + +type RuleParams = { + shapes:ShapeParams[]; + legend_label?:string; + exclude_from_legend?:boolean; + legend_config?:RuleLegendConfig; + legend_order?:number; + legend_base_color?:string; +}; + +type RuleLegendConfig = + { type: "rule", target:any } | + { + type: "number", + range:[number, number], + range_type:LinearInterpRangeType, + positive_color:string, + negative_color:string, + interpFn:(val:number)=>number + } | // range: [lower, upper] + { + type: "gradient", + range:[number,number], + colorFn:(val:number)=>string + }; + + +export enum RuleSetType { + CATEGORICAL = "categorical", + GRADIENT = "gradient", + GRADIENT_AND_CATEGORICAL = "gradient+categorical", + BAR = "bar", + STACKED_BAR = "stacked_bar", + GENE = "gene" +} + +export type RuleId = number; + +export type RuleWithId = { + id:RuleId; + rule:Rule; +}; + function makeIdCounter() { - var id = 0; + let id = 0; return function () { id += 1; return id; }; } -function intRange(length) { - var ret = []; - for (var i=0; i) { /* params: * - legend_label * - exclude_from_legend */ - this.rule_set_id = getRuleSetId(); + this.rule_set_id = RuleSet.getRuleSetId(); this.legend_label = params.legend_label; this.legend_base_color = params.legend_base_color; this.exclude_from_legend = params.exclude_from_legend; @@ -201,32 +318,32 @@ var RuleSet = (function () { } - RuleSet.prototype.getLegendLabel = function () { + public getLegendLabel() { return this.legend_label; } - RuleSet.prototype.getRuleSetId = function () { + public getRuleSetId() { return this.rule_set_id; } - RuleSet.prototype.addRules = function (list_of_params) { - var self = this; + public addRules(list_of_params:RuleParams[]) { + const self = this; return list_of_params.map(function (params) { - return self.addRule(params); + return self._addRule(params); }); } - RuleSet.prototype.addRule = function (params, rule_id) { + public _addRule(params:RuleParams, rule_id?:RuleId) { if (typeof rule_id === "undefined") { - rule_id = getRuleId(); + rule_id = RuleSet.getRuleId(); } this.rules_with_id.push({id: rule_id, rule: new Rule(params)}); return rule_id; } - RuleSet.prototype.removeRule = function (rule_id) { + public removeRule(rule_id:RuleId) { var index = -1; - for (var i = 0; i < this.rules_with_id.length; i++) { + for (let i = 0; i < this.rules_with_id.length; i++) { if (this.rules_with_id[i].id === rule_id) { index = i; break; @@ -238,9 +355,9 @@ var RuleSet = (function () { delete this.active_rule_ids[rule_id]; } - RuleSet.prototype.getRuleWithId = function (rule_id) { - var ret = null; - for (var i = 0; i < this.rules_with_id.length; i++) { + public getRuleWithId(rule_id:RuleId) { + let ret = null; + for (let i = 0; i < this.rules_with_id.length; i++) { if (this.rules_with_id[i].id === rule_id) { ret = this.rules_with_id[i]; break; @@ -249,27 +366,36 @@ var RuleSet = (function () { return ret; } - RuleSet.prototype.isExcludedFromLegend = function () { + public isExcludedFromLegend() { return this.exclude_from_legend; } - RuleSet.prototype.getRecentlyUsedRules = function () { - var self = this; + public getRule(rule_id:RuleId):Rule { + return this.getRuleWithId(rule_id).rule; + } + + public getRecentlyUsedRules() { + const self = this; return Object.keys(this.active_rule_ids).map( function (rule_id) { - return self.getRule(rule_id); + return self.getRule(parseInt(rule_id, 10)); }); } - RuleSet.prototype.applyRulesToDatum = function (rules_with_id, datum, cell_width, cell_height) { - var shapes = []; - var rules_len = rules_with_id.length; - for (var j = 0; j < rules_len; j++) { + public applyRulesToDatum(rules_with_id:RuleWithId[], datum:Datum, cell_width:number, cell_height:number) { + let shapes:ComputedShapeParams[] = []; + const rules_len = rules_with_id.length; + for (let j = 0; j < rules_len; j++) { shapes = shapes.concat(rules_with_id[j].rule.apply(datum, cell_width, cell_height)); } return shapes; } - RuleSet.prototype.apply = function (data, cell_width, cell_height, out_active_rules, data_id_key, important_ids) { + + public getRulesWithId(datum?:Datum):RuleWithId[] { + throw "Not implemented on base class"; + } + + public apply(data:Datum[], cell_width:number, cell_height:number, out_active_rules:ActiveRules|undefined, data_id_key:string&keyof Datum, important_ids?:ColumnProp) { // Returns a list of lists of concrete shapes, in the same order as data // optional parameter important_ids determines which ids count towards active rules (optional parameter data_id_key // is used for this too) @@ -279,7 +405,7 @@ var RuleSet = (function () { var should_mark_active = !important_ids || !!important_ids[datum[data_id_key]]; var rules = this.getRulesWithId(datum); if (typeof out_active_rules !== 'undefined' && should_mark_active) { - for (var j = 0; j < rules.length; j++) { + for (let j = 0; j < rules.length; j++) { out_active_rules[rules[j].id] = true; } } @@ -287,29 +413,22 @@ var RuleSet = (function () { } return ret; } +} - return RuleSet; -})(); - -var LookupRuleSet = (function () { - function LookupRuleSet(params) { - RuleSet.call(this, params); - this.lookup_map_by_key_and_value = {}; - this.lookup_map_by_key = {}; - this.universal_rules = []; - - this.rule_id_to_conditions = {}; - } - LookupRuleSet.prototype = Object.create(RuleSet.prototype); +class LookupRuleSet extends RuleSet { + private lookup_map_by_key_and_value:{[key:string]:{[value:string]:RuleWithId}} = {}; + private lookup_map_by_key:{[key:string]:RuleWithId} = {}; + private universal_rules:RuleWithId[] = []; + private rule_id_to_conditions:{[ruleId:number]:{ key:string, value:string }[] } = {}; - LookupRuleSet.prototype.getRulesWithId = function (datum) { + public getRulesWithId(datum?:Datum) { if (typeof datum === 'undefined') { return this.rules_with_id; } - var ret = []; + let ret:RuleWithId[] = []; ret = ret.concat(this.universal_rules); for (var key in datum) { - if (typeof datum[key] !== 'undefined') { + if (typeof datum[key] !== 'undefined' && datum.hasOwnProperty(key)) { var key_rule = this.lookup_map_by_key[key]; if (typeof key_rule !== 'undefined') { ret.push(key_rule); @@ -323,35 +442,35 @@ var LookupRuleSet = (function () { return ret; } - var indexRuleForLookup = function (rule_set, condition_key, condition_value, rule_with_id) { + private indexRuleForLookup(condition_key:string, condition_value:string, rule_with_id:RuleWithId) { if (condition_key === null) { - rule_set.universal_rules.push(rule_with_id); + this.universal_rules.push(rule_with_id); } else { if (condition_value === null) { - rule_set.lookup_map_by_key[condition_key] = rule_with_id; + this.lookup_map_by_key[condition_key] = rule_with_id; } else { - rule_set.lookup_map_by_key_and_value[condition_key] = rule_set.lookup_map_by_key_and_value[condition_key] || {}; - rule_set.lookup_map_by_key_and_value[condition_key][condition_value] = rule_with_id; + this.lookup_map_by_key_and_value[condition_key] = this.lookup_map_by_key_and_value[condition_key] || {}; + this.lookup_map_by_key_and_value[condition_key][condition_value] = rule_with_id; } } - rule_set.rule_id_to_conditions[rule_with_id.id] = rule_set.rule_id_to_conditions[rule_with_id.id] || []; - rule_set.rule_id_to_conditions[rule_with_id.id].push({key: condition_key, value: condition_value}); + this.rule_id_to_conditions[rule_with_id.id] = this.rule_id_to_conditions[rule_with_id.id] || []; + this.rule_id_to_conditions[rule_with_id.id].push({key: condition_key, value: condition_value}); }; - LookupRuleSet.prototype.addRule = function (condition_key, condition_value, params) { - var rule_id = RuleSet.prototype.addRule.call(this, params); + public addRule(condition_key:string, condition_value:any, params:RuleParams) { + const rule_id = this._addRule(params); - indexRuleForLookup(this, condition_key, condition_value, this.getRuleWithId(rule_id)); + this.indexRuleForLookup(condition_key, condition_value, this.getRuleWithId(rule_id)); return rule_id; } - LookupRuleSet.prototype.linkExistingRule = function (condition_key, condition_value, existing_rule_id) { - indexRuleForLookup(this, condition_key, condition_value, this.getRuleWithId(existing_rule_id)); + public linkExistingRule(condition_key:string, condition_value:string, existing_rule_id:RuleId) { + this.indexRuleForLookup(condition_key, condition_value, this.getRuleWithId(existing_rule_id)); } - LookupRuleSet.prototype.removeRule = function (rule_id) { - RuleSet.prototype.removeRule.call(this, rule_id); + public removeRule(rule_id:RuleId) { + super.removeRule(rule_id); while (this.rule_id_to_conditions[rule_id].length > 0) { var condition = this.rule_id_to_conditions[rule_id].pop(); @@ -376,13 +495,14 @@ var LookupRuleSet = (function () { } delete this.rule_id_to_conditions[rule_id]; } - return LookupRuleSet; -})(); +} -var ConditionRuleSet = (function () { - function ConditionRuleSet(params, omitNArule) { - RuleSet.call(this, params); - this.rule_id_to_condition = {}; +type ConditionRuleSetCondition = (d:Datum)=>boolean; +class ConditionRuleSet extends RuleSet { + private rule_id_to_condition:{[ruleId:number]:ConditionRuleSetCondition} = {}; + + constructor(params:RuleSetParams, omitNArule?:boolean) { + super(params); if (!omitNArule) { this.addRule(function (d) { @@ -397,14 +517,13 @@ var ConditionRuleSet = (function () { }); } } - ConditionRuleSet.prototype = Object.create(RuleSet.prototype); - ConditionRuleSet.prototype.getRulesWithId = function (datum) { + public getRulesWithId(datum?:Datum) { if (typeof datum === 'undefined') { return this.rules_with_id; } - var ret = []; - for (var i = 0; i < this.rules_with_id.length; i++) { + const ret = []; + for (let i = 0; i < this.rules_with_id.length; i++) { if (this.rule_id_to_condition[this.rules_with_id[i].id](datum)) { ret.push(this.rules_with_id[i]); } @@ -412,28 +531,24 @@ var ConditionRuleSet = (function () { return ret; } - ConditionRuleSet.prototype.addRule = function (condition, params, rule_id) { - rule_id = RuleSet.prototype.addRule.call(this, params, rule_id); + public addRule(condition:ConditionRuleSetCondition, params:RuleParams, rule_id?:RuleId) { + rule_id = this._addRule(params, rule_id); this.rule_id_to_condition[rule_id] = condition; return rule_id; } - ConditionRuleSet.prototype.removeRule = function (rule_id) { - RuleSet.prototype.removeRule.call(this, rule_id); + public removeRule(rule_id:RuleId) { + super.removeRule(rule_id); delete this.rule_id_to_condition[rule_id]; } +} - return ConditionRuleSet; -})(); - -var CategoricalRuleSet = (function () { - function CategoricalRuleSet(params, omitNArule) { - /* params - * - category_key - * - categoryToColor - */ - LookupRuleSet.call(this, params); - +class CategoricalRuleSet extends LookupRuleSet { + public readonly category_key:string; + private readonly category_to_color:{[category:string]:string}; + private readonly getUnusedColor:(color?:string)=>string; + constructor(params:Omit, omitNArule?:boolean) { + super(params); if (!omitNArule) { this.addRule(NA_STRING, true, { shapes: makeNAShapes(params.na_z || 1000), @@ -447,20 +562,17 @@ var CategoricalRuleSet = (function () { this.category_key = params.category_key; this.category_to_color = cloneShallow(ifndef(params.category_to_color, {})); this.getUnusedColor = makeUniqueColorGetter(objectValues(this.category_to_color).map(colorToHex)); - for (var category in this.category_to_color) { - if (this.category_to_color.hasOwnProperty(category)) { - var color = this.category_to_color[category]; - addCategoryRule(this, category, color); - this.getUnusedColor(color); - } + for (const category of Object.keys(this.category_to_color)) { + const color = this.category_to_color[category]; + this.addCategoryRule(category, color); + this.getUnusedColor(color); } } - CategoricalRuleSet.prototype = Object.create(LookupRuleSet.prototype); - var addCategoryRule = function (ruleset, category, color) { - var legend_rule_target = {}; - legend_rule_target[ruleset.category_key] = category; - var rule_params = { + private addCategoryRule(category:string, color:string) { + const legend_rule_target:any = {}; + legend_rule_target[this.category_key] = category; + const rule_params:RuleParams = { shapes: [{ type: 'rectangle', fill: color, @@ -469,59 +581,60 @@ var CategoricalRuleSet = (function () { exclude_from_legend: false, legend_config: {'type': 'rule', 'target': legend_rule_target} }; - ruleset.addRule(ruleset.category_key, category, rule_params); - }; + this.addRule(this.category_key, category, rule_params); + } - CategoricalRuleSet.prototype.apply = function (data, cell_width, cell_height, out_active_rules, data_id_key, important_ids) { + public apply(data:Datum, cell_width:number, cell_height:number, out_active_rules:ActiveRules|undefined, data_id_key:string&keyof Datum, important_ids?:ColumnProp) { // First ensure there is a color for all categories - for (var i = 0, data_len = data.length; i < data_len; i++) { + for (let i = 0, data_len = data.length; i < data_len; i++) { if (data[i][NA_STRING]) { continue; } - var category = data[i][this.category_key]; + const category = data[i][this.category_key]; if (!this.category_to_color.hasOwnProperty(category)) { - var color = this.getUnusedColor(); + const color = this.getUnusedColor(); this.category_to_color[category] = color; - addCategoryRule(this, category, color); + this.addCategoryRule(category, color); } } // Then propagate the call up - return LookupRuleSet.prototype.apply.call(this, data, cell_width, cell_height, out_active_rules, data_id_key, important_ids); - }; + return super.apply(data, cell_width, cell_height, out_active_rules, data_id_key, important_ids); + } +} - return CategoricalRuleSet; -})(); +export enum LinearInterpRangeType { + ALL = 'ALL', // all values positive, negative and zero + NON_NEGATIVE = 'NON_NEGATIVE', // value range all positive values inclusive zero (0) + NON_POSITIVE = 'NON_POSITIVE' // value range all negative values inclusive zero (0) +} -var LinearInterpRuleSet = (function () { - function LinearInterpRuleSet(params) { - /* params - * - log_scale - * - value_key - * - value_range - */ - ConditionRuleSet.call(this, params); +class LinearInterpRuleSet extends ConditionRuleSet { + + protected value_key:string; + protected value_range:[number, number]; + protected log_scale?:boolean; + protected type:string; + protected makeInterpFn:()=>((valToConvert:number)=>number); + protected inferred_value_range:[number, number]; + + constructor(params:ILinearInterpRuleSetParams) { + super(params); this.value_key = params.value_key; this.value_range = params.value_range; this.log_scale = params.log_scale; // boolean this.type = params.type; - this.rangeTypes = { - 'ALL': 'ALL', // all values positive, negative and zero - 'NON_NEGATIVE': 'NON_NEGATIVE', // value range all positive values inclusive zero (0) - 'NON_POSITIVE': 'NON_POSITIVE' // value range all negative values inclusive zero (0) - }; this.makeInterpFn = function () { - var range = this.getEffectiveValueRange(); - var rangeType = this.getValueRangeType(); - var plotType = this.type; - var rangeTypes = this.rangeTypes; + const range = this.getEffectiveValueRange(); + const rangeType = this.getValueRangeType(); + const plotType = this.type; if (this.log_scale) { var shift_to_make_pos = Math.abs(range[0]) + 1; var log_range = Math.log(range[1] + shift_to_make_pos) - Math.log(range[0] + shift_to_make_pos); var log_range_lower = Math.log(range[0] + shift_to_make_pos); return function (val) { - val = parseFloat(val); + val = parseFloat(val as any); return (Math.log(val + shift_to_make_pos) - log_range_lower) / log_range; }; } else { @@ -530,13 +643,13 @@ var LinearInterpRuleSet = (function () { range_lower = range[0], range_higher = range[1]; if (plotType === 'bar') { - if (rangeType === rangeTypes.NON_POSITIVE) { + if (rangeType === LinearInterpRangeType.NON_POSITIVE) { // when data only contains non positive values return (val - range_higher) / range_spread; - } else if (rangeType === rangeTypes.NON_NEGATIVE) { + } else if (rangeType === LinearInterpRangeType.NON_NEGATIVE) { // when data only contains non negative values return (val - range_lower) / range_spread; - } else if (rangeType === rangeTypes.ALL) { + } else if (rangeType === LinearInterpRangeType.ALL) { range_spread = Math.abs(range[0]) > range[1] ? Math.abs(range[0]) : range[1]; return val / range_spread; } @@ -547,10 +660,9 @@ var LinearInterpRuleSet = (function () { } }; } - LinearInterpRuleSet.prototype = Object.create(ConditionRuleSet.prototype); - LinearInterpRuleSet.prototype.getEffectiveValueRange = function () { - var ret = (this.value_range && this.value_range.slice()) || [undefined, undefined]; + protected getEffectiveValueRange():[number,number] { + const ret = (this.value_range && this.value_range.slice()) || [undefined, undefined]; if (typeof ret[0] === "undefined") { ret[0] = this.inferred_value_range[0]; } @@ -562,25 +674,25 @@ var LinearInterpRuleSet = (function () { ret[0] -= ret[0] / 2; ret[1] += ret[1] / 2; } - return ret; - }; - LinearInterpRuleSet.prototype.getValueRangeType = function () { + return ret as [number,number]; + } + protected getValueRangeType() { var range = this.getEffectiveValueRange(); if (range[0] < 0 && range[1] <=0) { - return this.rangeTypes.NON_POSITIVE; + return LinearInterpRangeType.NON_POSITIVE; } else if (range[0] >= 0 && range[1] > 0) { - return this.rangeTypes.NON_NEGATIVE; + return LinearInterpRangeType.NON_NEGATIVE; } else { - return this.rangeTypes.ALL; + return LinearInterpRangeType.ALL; } - }; + } - LinearInterpRuleSet.prototype.apply = function (data, cell_width, cell_height, out_active_rules, data_id_key, important_ids) { + public apply(data:Datum, cell_width:number, cell_height:number, out_active_rules:ActiveRules|undefined, data_id_key:string&keyof Datum, important_ids?:ColumnProp) { // First find value range - var value_min = Number.POSITIVE_INFINITY; - var value_max = Number.NEGATIVE_INFINITY; + let value_min = Number.POSITIVE_INFINITY; + let value_max = Number.NEGATIVE_INFINITY; for (var i = 0, datalen = data.length; i < datalen; i++) { - var d = data[i]; + const d = data[i]; if (isNaN(d[this.value_key])) { continue; } @@ -597,27 +709,22 @@ var LinearInterpRuleSet = (function () { this.updateLinearRules(); // Then propagate the call up - return ConditionRuleSet.prototype.apply.call(this, data, cell_width, cell_height, out_active_rules, data_id_key, important_ids); - }; + return super.apply(data, cell_width, cell_height, out_active_rules, data_id_key, important_ids); + } - LinearInterpRuleSet.prototype.updateLinearRules = function () { + protected updateLinearRules() { throw "Not implemented in abstract class"; - }; - - return LinearInterpRuleSet; -})(); + } +} -var GradientRuleSet = (function () { - function GradientRuleSet(params) { - /* params - * - colors || colormap_name - * - value_stop_points - * - null_color - * - null_legend_label - */ - LinearInterpRuleSet.call(this, params); +class GradientRuleSet extends LinearInterpRuleSet { + private colors:RGBAColor[] = []; + private value_stop_points: number[]; + private null_color?:string; + private gradient_rule:RuleId; - this.colors = []; + constructor(params:Omit) { + super(params); if (params.colors) { this.colors = params.colors || []; } else if (params.colormap_name) { @@ -644,30 +751,26 @@ var GradientRuleSet = (function () { legend_config: {'type':'rule', 'target':{ [value_key]:null } } }); } - GradientRuleSet.prototype = Object.create(LinearInterpRuleSet.prototype); - // interpScaleColors, - // were adapted from politiken-journalism's scale-color-perceptual repo on Github - var linInterpColors = function(t, begin_color, end_color) { + static linInterpColors(t:number, begin_color:RGBAColor, end_color:RGBAColor) { // 0 <= t <= 1 - // begin_color and end_color are 4-element arrays in ([0,255])x([0,255])x([0,255])x([0,1]) return [ Math.round(begin_color[0]*(1-t) + end_color[0]*t), Math.round(begin_color[1]*(1-t) + end_color[1]*t), Math.round(begin_color[2]*(1-t) + end_color[2]*t), begin_color[3]*(1-t) + end_color[3]*t ]; - }; + } - GradientRuleSet.prototype.makeColorFn = function(colors, interpFn) { - var value_stop_points = this.value_stop_points; - var stop_points; + private makeColorFn(colors:RGBAColor[], interpFn:(valToConvert:number)=>number) { + const value_stop_points = this.value_stop_points; + let stop_points:number[]; if (value_stop_points) { stop_points = value_stop_points.map(interpFn); } else { stop_points = intRange(colors.length).map(function(x) { return x/(colors.length -1); }); } - return function(t) { + return function(t:number) { // 0 <= t <= 1 var begin_interval_index = binarysearch(stop_points, t, function(x) { return x; }, true); if (begin_interval_index === -1) { @@ -681,22 +784,22 @@ var GradientRuleSet = (function () { var interval_t = (t - stop_points[begin_interval_index]) / spread; var begin_color = colors[begin_interval_index]; var end_color = colors[end_interval_index]; - return "rgba(" + linInterpColors(interval_t, begin_color, end_color).join(",") + ")"; + return "rgba(" + GradientRuleSet.linInterpColors(interval_t, begin_color, end_color).join(",") + ")"; } }; } - GradientRuleSet.prototype.updateLinearRules = function () { - var rule_id; + protected updateLinearRules() { + let rule_id; if (typeof this.gradient_rule !== "undefined") { rule_id = this.gradient_rule; this.removeRule(this.gradient_rule); } - var interpFn = this.makeInterpFn(); - var colorFn = this.makeColorFn(this.colors, interpFn); - var value_key = this.value_key; - var null_color = this.null_color; + const interpFn = this.makeInterpFn(); + const colorFn = this.makeColorFn(this.colors, interpFn); + const value_key = this.value_key; + const null_color = this.null_color; this.gradient_rule = this.addRule(function (d) { return d[NA_STRING] !== true && d[value_key] !== null; @@ -709,34 +812,35 @@ var GradientRuleSet = (function () { } }], exclude_from_legend: false, - legend_config: {'type': 'gradient', 'range': this.getEffectiveValueRange(), 'colorFn':colorFn} + legend_config: {'type': "gradient" as "gradient", 'range': this.getEffectiveValueRange(), 'colorFn':colorFn} }, rule_id); - }; + } +} - return GradientRuleSet; -})(); +class BarRuleSet extends LinearInterpRuleSet { + private fill:string; + private negative_fill:string; + private bar_rule?:RuleId; -var BarRuleSet = (function () { - function BarRuleSet(params) { - LinearInterpRuleSet.call(this, params); + constructor(params:IBarRuleSetParams) { + super(params); this.fill = params.fill || 'rgba(0,128,0,1)'; // green this.negative_fill = params.negative_fill || 'rgba(255,0,0,1)'; //red } - BarRuleSet.prototype = Object.create(LinearInterpRuleSet.prototype); - BarRuleSet.prototype.updateLinearRules = function () { - var rule_id; + protected updateLinearRules() { + let rule_id; if (typeof this.bar_rule !== "undefined") { rule_id = this.bar_rule; this.removeRule(this.bar_rule); } - var interpFn = this.makeInterpFn(); - var value_key = this.value_key; - var positive_color = this.fill; - var negative_color = this.negative_fill; - var yPosFn = this.getYPosPercentagesFn(); - var cellHeightFn = this.getCellHeightPercentagesFn(); + const interpFn = this.makeInterpFn(); + const value_key = this.value_key; + const positive_color = this.fill; + const negative_color = this.negative_fill; + const yPosFn = this.getYPosPercentagesFn(); + const cellHeightFn = this.getCellHeightPercentagesFn(); this.bar_rule = this.addRule(function (d) { return d[NA_STRING] !== true; }, @@ -756,74 +860,69 @@ var BarRuleSet = (function () { }], exclude_from_legend: false, legend_config: { - 'type': 'number', + 'type': 'number' as 'number', 'range': this.getEffectiveValueRange(), 'range_type': this.getValueRangeType(), 'positive_color': positive_color, 'negative_color': negative_color, - 'interpFn': interpFn} + 'interpFn': interpFn + } }, rule_id); - }; - BarRuleSet.prototype.getYPosPercentagesFn = function () { - var ret; + } + + public getYPosPercentagesFn() { + let ret; switch (this.getValueRangeType()) { - case this.rangeTypes.NON_POSITIVE: - ret = (function(t) { return "0%"; }); + case LinearInterpRangeType.NON_POSITIVE: + ret = (function(t:number) { return "0%"; }); break; - case this.rangeTypes.NON_NEGATIVE: - ret = (function(t) { return (1 - t) * 100 + "%"; }); + case LinearInterpRangeType.NON_NEGATIVE: + ret = (function(t:number) { return (1 - t) * 100 + "%"; }); break; - case this.rangeTypes.ALL: - ret = (function(t) { return Math.min(1-t, 1)*50 + "%"; }); + case LinearInterpRangeType.ALL: + ret = (function(t:number) { return Math.min(1-t, 1)*50 + "%"; }); break; } return ret; - }; + } - BarRuleSet.prototype.getCellHeightPercentagesFn = function () { - var ret; + public getCellHeightPercentagesFn() { + let ret; switch (this.getValueRangeType()) { - case this.rangeTypes.NON_POSITIVE: - ret = (function(t) { return -t * 100 + "%"; }); + case LinearInterpRangeType.NON_POSITIVE: + ret = (function(t:number) { return -t * 100 + "%"; }); break; - case this.rangeTypes.NON_NEGATIVE: - ret = (function(t) { return t * 100 + "%"; }); + case LinearInterpRangeType.NON_NEGATIVE: + ret = (function(t:number) { return t * 100 + "%"; }); break; - case this.rangeTypes.ALL: - ret = (function(t) { return Math.abs(t) * 50 + "%"; }); + case LinearInterpRangeType.ALL: + ret = (function(t:number) { return Math.abs(t) * 50 + "%"; }); break; } return ret; - }; - - return BarRuleSet; -})(); + } +} -var StackedBarRuleSet = (function() { - function StackedBarRuleSet(params) { - /* params - * - categories - * - value_key - * - fills - */ - ConditionRuleSet.call(this, params); - var value_key = params.value_key; - var fills = params.fills || []; - var categories = params.categories || []; - var getUnusedColor = makeUniqueColorGetter(fills); +class StackedBarRuleSet extends ConditionRuleSet { + constructor(params:IStackedBarRuleSetParams) { + super(params); + const value_key = params.value_key; + const fills = params.fills || []; + const categories = params.categories || []; + const getUnusedColor = makeUniqueColorGetter(fills); // Initialize with default values while (fills.length < categories.length) { fills.push(getUnusedColor()); } - var self = this; - for (var i=0; i < categories.length; i++) { + const self = this; + for (let i=0; i < categories.length; i++) { (function(I) { - var legend_target = {}; + const legend_target:any = {}; legend_target[value_key] = {}; - for (var j=0; j) { - var shapes = []; + const shapes = []; // check the type of datum (categorical or continuous) and delegate // fetching of shapes to the appropriate RuleSet class - for (var i = 0; i < data.length; i++) { - var datum = data[i]; + for (let i = 0; i < data.length; i++) { + const datum = data[i]; if ( this.isCategorical(datum) ) { shapes.push( this.categoricalRuleSet.apply([datum], cell_width, cell_height, out_active_rules, data_id_key, important_ids)[0] ); } else { @@ -987,35 +1082,43 @@ var GradientCategoricalRuleSet = (function() { } // RuleSet API - GradientCategoricalRuleSet.prototype.getRulesWithId = function(datum) { - var categoricalRules = this.categoricalRuleSet.getRulesWithId(datum); - var gradientRules = this.gradientRuleSet.getRulesWithId(datum); - var rules = categoricalRules.concat(gradientRules); + public getRulesWithId(datum?:Datum) { + const categoricalRules = this.categoricalRuleSet.getRulesWithId(datum); + const gradientRules = this.gradientRuleSet.getRulesWithId(datum); + const rules = categoricalRules.concat(gradientRules); return rules; } // helper function - GradientCategoricalRuleSet.prototype.isCategorical = function(datum) { + public isCategorical(datum:Datum) { // A categorical value is recognized by presence of a category attribute. // Note: a categorical datum still requires a continuous value (used for clustering). return datum[this.categoricalRuleSet.category_key] !== undefined; } +} - return GradientCategoricalRuleSet; -})(); - -module.exports = function (params) { - if (params.type === 'categorical') { - return new CategoricalRuleSet(params); - } else if (params.type === 'gradient') { - return new GradientRuleSet(params); - } else if (params.type === 'gradient+categorical') { - return new GradientCategoricalRuleSet(params); - } else if (params.type === 'bar') { - return new BarRuleSet(params); - } else if (params.type === 'stacked_bar') { - return new StackedBarRuleSet(params); - } else if (params.type === 'gene') { - return new GeneticAlterationRuleSet(params); +export default function (params:RuleSetParams) { + let ret:RuleSet; + switch (params.type) { + case RuleSetType.CATEGORICAL: + ret = new CategoricalRuleSet(params as ICategoricalRuleSetParams); + break; + case RuleSetType.GRADIENT: + ret = new GradientRuleSet(params as IGradientRuleSetParams); + break; + case RuleSetType.GRADIENT_AND_CATEGORICAL: + ret = new GradientCategoricalRuleSet(params as IGradientAndCategoricalRuleSetParams); + break; + case RuleSetType.BAR: + ret = new BarRuleSet(params as IBarRuleSetParams); + break; + case RuleSetType.STACKED_BAR: + ret = new StackedBarRuleSet(params as IStackedBarRuleSetParams); + break; + case RuleSetType.GENE: + default: + ret = new GeneticAlterationRuleSet(params as IGeneticAlterationRuleSetParams); + break; } + return ret; }; diff --git a/packages/oncoprintjs/src/js/oncoprintshape.js b/packages/oncoprintjs/src/js/oncoprintshape.js deleted file mode 100644 index 7da22c5c5ac..00000000000 --- a/packages/oncoprintjs/src/js/oncoprintshape.js +++ /dev/null @@ -1,156 +0,0 @@ -var Shape = (function() { - var default_parameter_values = { - 'width': '100%', - 'height': '100%', - 'x': '0%', - 'y': '0%', - 'z': 0, - 'x1': '0%', - 'x2': '0%', - 'x3': '0%', - 'y1': '0%', - 'y2': '0%', - 'y3': '0%', - 'stroke': 'rgba(0,0,0,0)', - 'fill': 'rgba(23,23,23,1)', - 'stroke-width': '0', - 'stroke-opacity': '0' - }; - var parameter_name_to_dimension_index = { - 'stroke-width':0, - 'width': 0, - 'x':0, - 'x1':0, - 'x2':0, - 'x3':0, - 'height':1, - 'y':1, - 'y1':1, - 'y2':1, - 'y3':1 - }; - var hash_parameter_order = Object.keys(default_parameter_values).concat("type"); - - function Shape(params) { - this.params = params; - this.params_with_type = {}; - this.completeWithDefaults(); - this.markParameterTypes(); - } - - var getCachedShape = (function() { - var cache = {}; // shape cache to save memory - - return function(computed_params) { - var hash = Shape.hashComputedShape(computed_params); - cache[hash] = cache[hash] || Object.freeze(computed_params); - return cache[hash]; - }; - })(); - - Shape.prototype.completeWithDefaults = function() { - var required_parameters = this.getRequiredParameters(); - for (var i=0; i(string|number); +export type ShapeParams = {[x in StringOnlyParameter]?:string|ParamFunction} & {[x in StringOrNumberParameter]?:string|number|ParamFunction}; +type ShapeParamsWithType = {[x in Parameter]?:({ type:"function", value:ParamFunction} | {type:"value", value:string|number})}; +export type ComputedShapeParams = {[x in StringOnlyParameter]?:string} & {[x in StringOrNumberParameter]?:string|number}; + +export class Shape { + + private static cache:{[hash:string]:ComputedShapeParams} = {}; // shape cache to reuse objects and thus save memory + private params_with_type:ShapeParamsWithType = {}; + + constructor(private params:ShapeParams) { + this.completeWithDefaults(); + this.markParameterTypes(); + } + + public static hashComputedShape(computed_params:ComputedShapeParams, z_index?:number|string) { + return hash_parameter_order.reduce(function (hash:string, param_name:Parameter) { + return hash + "," + computed_params[param_name]; + }, "") + "," + z_index; + } + + private static getCachedShape(computed_params:ComputedShapeParams) { + const hash = Shape.hashComputedShape(computed_params); + Shape.cache[hash] = Shape.cache[hash] || Object.freeze(computed_params); + return Shape.cache[hash]; + } + + public getRequiredParameters():Parameter[] { + throw "Not defined for base class"; + } + + public completeWithDefaults() { + const required_parameters = this.getRequiredParameters(); + for (let i=0; i = {}; + const param_names = Object.keys(this.params_with_type) as Parameter[]; + const dimensions:[number, number] = [base_width, base_height]; + for (let i=0; i -1) { return { 'rgb': str, 'opacity': 1 }; } - var rgba_arr = extractRGBA(str); + const rgba_arr = extractRGBA(str); return { 'rgb': 'rgb('+rgba_arr[0]*255+','+rgba_arr[1]*255+','+rgba_arr[2]*255+')', 'opacity': rgba_arr[3] }; -}; +} -var rectangleToSVG = function (params, offset_x, offset_y) { +function rectangleToSVG(params:{ + width:any, + height:any, + x:any, + y:any, + stroke:string, + "stroke-width":number, + fill:string +}, offset_x:number, offset_y:number) { var stroke_color = extractColor(params.stroke); var fill_color = extractColor(params.fill); return makeSVGElement('rect', { width: params.width, height: params.height, - x: parseFloat(params.x) + offset_x, - y: parseFloat(params.y) + offset_y, + x: parseFloat(params.x as any) + offset_x, + y: parseFloat(params.y as any) + offset_y, stroke: stroke_color.rgb, 'stroke-opacity': stroke_color.opacity, 'stroke-width': params['stroke-width'], fill: fill_color.rgb, 'fill-opacity': fill_color.opacity }); -}; +} -var triangleToSVG = function (params, offset_x, offset_y) { +function triangleToSVG(params:{ + x1:any, + y1:any, + x2:any, + y2:any, + x3:any, + y3:any, + stroke:string, + "stroke-width":number, + fill:string +}, offset_x:number, offset_y:number) { var stroke_color = extractColor(params.stroke); var fill_color = extractColor(params.fill); return makeSVGElement('polygon', { @@ -53,9 +63,17 @@ var triangleToSVG = function (params, offset_x, offset_y) { fill: fill_color.rgb, 'fill-opacity': fill_color.opacity }); -}; +} -var ellipseToSVG = function (params, offset_x, offset_y) { +function ellipseToSVG(params:{ + width:any, + height:any, + x:any, + y:any, + stroke:string, + "stroke-width":number, + fill:string +}, offset_x:number, offset_y:number) { var stroke_color = extractColor(params.stroke); var fill_color = extractColor(params.fill); return makeSVGElement('ellipse', { @@ -69,9 +87,16 @@ var ellipseToSVG = function (params, offset_x, offset_y) { fill: fill_color.rgb, 'fill-opacity': fill_color.opacity }); -}; +} -var lineToSVG = function (params, offset_x, offset_y) { +function lineToSVG(params:{ + x1:any, + y1:any, + x2:any, + y2:any, + stroke:string, + "stroke-width":number +}, offset_x:number, offset_y:number) { var stroke_color = extractColor(params.stroke); return makeSVGElement('line', { x1: parseFloat(params.x1) + offset_x, @@ -82,17 +107,17 @@ var lineToSVG = function (params, offset_x, offset_y) { 'stroke-opacity': stroke_color.opacity, 'stroke-width': params['stroke-width'], }); -}; +} -module.exports = function(oncoprint_shape_computed_params, offset_x, offset_y) { +export default function(oncoprint_shape_computed_params:ComputedShapeParams, offset_x:number, offset_y:number) { var type = oncoprint_shape_computed_params.type; if (type === 'rectangle') { - return rectangleToSVG(oncoprint_shape_computed_params, offset_x, offset_y); + return rectangleToSVG(oncoprint_shape_computed_params as any, offset_x, offset_y); } else if (type === 'triangle') { - return triangleToSVG(oncoprint_shape_computed_params, offset_x, offset_y); + return triangleToSVG(oncoprint_shape_computed_params as any, offset_x, offset_y); } else if (type === 'ellipse') { - return ellipseToSVG(oncoprint_shape_computed_params, offset_x, offset_y); + return ellipseToSVG(oncoprint_shape_computed_params as any, offset_x, offset_y); } else if (type === 'line') { - return lineToSVG(oncoprint_shape_computed_params, offset_x, offset_y); + return lineToSVG(oncoprint_shape_computed_params as any, offset_x, offset_y); } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprintshapetovertexes.js b/packages/oncoprintjs/src/js/oncoprintshapetovertexes.ts similarity index 62% rename from packages/oncoprintjs/src/js/oncoprintshapetovertexes.js rename to packages/oncoprintjs/src/js/oncoprintshapetovertexes.ts index c50bc73f7ca..2da6d607b0f 100644 --- a/packages/oncoprintjs/src/js/oncoprintshapetovertexes.js +++ b/packages/oncoprintjs/src/js/oncoprintshapetovertexes.ts @@ -1,15 +1,17 @@ -var halfsqrt2 = Math.sqrt(2) / 2; +import {ComputedShapeParams} from "./oncoprintshape"; -var extractRGBA = function (str) { - var ret = [0, 0, 0, 1]; +const halfsqrt2 = Math.sqrt(2) / 2; + +function extractRGBA(str:string):[number,number,number,number] { + let ret:[number,number,number,number] = [0, 0, 0, 1]; if (str[0] === "#") { // hex, convert to rgba - var r = parseInt(str[1] + str[2], 16); - var g = parseInt(str[3] + str[4], 16); - var b = parseInt(str[5] + str[6], 16); + const r = parseInt(str[1] + str[2], 16); + const g = parseInt(str[3] + str[4], 16); + const b = parseInt(str[5] + str[6], 16); str = 'rgba('+r+','+g+','+b+',1)'; } - var match = str.match(/^[\s]*rgba\([\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9.]+)[\s]*\)[\s]*$/); + const match = str.match(/^[\s]*rgba\([\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9]+)[\s]*,[\s]*([0-9.]+)[\s]*\)[\s]*$/); if (match && match.length === 5) { ret = [parseFloat(match[1]) / 255, parseFloat(match[2]) / 255, @@ -17,13 +19,15 @@ var extractRGBA = function (str) { parseFloat(match[4])]; } return ret; -}; +} + +type AddVertexCallback = (vertex:[number,number,number], color:[number,number,number,number])=>void; -var rectangleToVertexes = function(params, z_index, addVertex) { - var x = parseFloat(params.x), y = parseFloat(params.y), height = parseFloat(params.height), width = parseFloat(params.width); +function rectangleToVertexes(params:ComputedShapeParams, z_index:number, addVertex:AddVertexCallback) { + const x = parseFloat(params.x as any), y = parseFloat(params.y as any), height = parseFloat(params.height as any), width = parseFloat(params.width as any); // Fill - var fill_rgba = extractRGBA(params.fill); + const fill_rgba = extractRGBA(params.fill); addVertex([x,y,z_index], fill_rgba); addVertex([x+width, y, z_index], fill_rgba); addVertex([x+width, y+height, z_index], fill_rgba); @@ -33,10 +37,10 @@ var rectangleToVertexes = function(params, z_index, addVertex) { addVertex([x,y+height,z_index],fill_rgba); // Stroke - var stroke_width = parseFloat(params['stroke-width']); + const stroke_width = parseFloat(params['stroke-width'] as any); if (stroke_width > 0) { // left side - var stroke_rgba = extractRGBA(params.stroke); + const stroke_rgba = extractRGBA(params.stroke); addVertex([x, y, z_index], stroke_rgba); addVertex([x + stroke_width, y, z_index], stroke_rgba); addVertex([x + stroke_width, y + height, z_index], stroke_rgba); @@ -72,19 +76,21 @@ var rectangleToVertexes = function(params, z_index, addVertex) { addVertex([x + width, y + height - stroke_width, z_index], stroke_rgba); addVertex([x, y + height - stroke_width, z_index], stroke_rgba); } -}; -var triangleToVertexes = function(params, z_index, addVertex) { - var fill_rgba = extractRGBA(params.fill); - addVertex([parseFloat(params.x1), parseFloat(params.y1), z_index], fill_rgba); - addVertex([parseFloat(params.x2), parseFloat(params.y2), z_index], fill_rgba); - addVertex([parseFloat(params.x3), parseFloat(params.y3), z_index], fill_rgba); -}; -var ellipseToVertexes = function(params, z_index, addVertex) { - var center = {x: parseFloat(params.x) + parseFloat(params.width) / 2, y: parseFloat(params.y) + parseFloat(params.height) / 2}; - var horzrad = parseFloat(params.width) / 2; - var vertrad = parseFloat(params.height) / 2; - - var fill_rgba = extractRGBA(params.fill); +} + +function triangleToVertexes(params:ComputedShapeParams, z_index:number, addVertex:AddVertexCallback) { + const fill_rgba = extractRGBA(params.fill); + addVertex([parseFloat(params.x1 as any), parseFloat(params.y1 as any), z_index], fill_rgba); + addVertex([parseFloat(params.x2 as any), parseFloat(params.y2 as any), z_index], fill_rgba); + addVertex([parseFloat(params.x3 as any), parseFloat(params.y3 as any), z_index], fill_rgba); +} + +function ellipseToVertexes(params:ComputedShapeParams, z_index:number, addVertex:AddVertexCallback) { + const center = {x: parseFloat(params.x as any) + parseFloat(params.width as any) / 2, y: parseFloat(params.y as any) + parseFloat(params.height as any) / 2}; + const horzrad = parseFloat(params.width as any) / 2; + const vertrad = parseFloat(params.height as any) / 2; + + const fill_rgba = extractRGBA(params.fill); addVertex([center.x, center.y, z_index], fill_rgba); addVertex([center.x + horzrad, center.y, z_index], fill_rgba); addVertex([center.x + halfsqrt2 * horzrad, center.y + halfsqrt2 * vertrad, z_index], fill_rgba); @@ -116,19 +122,20 @@ var ellipseToVertexes = function(params, z_index, addVertex) { addVertex([center.x, center.y, z_index], fill_rgba); addVertex([center.x + halfsqrt2 * horzrad, center.y - halfsqrt2 * vertrad, z_index], fill_rgba); addVertex([center.x + horzrad, center.y, z_index], fill_rgba); -}; -var lineToVertexes = function(params, z_index, addVertex) { +} + +function lineToVertexes(params:ComputedShapeParams, z_index:number, addVertex:AddVertexCallback) { // For simplicity of dealing with webGL we'll implement lines as thin triangle pairs - var x1 = parseFloat(params.x1); - var x2 = parseFloat(params.x2); - var y1 = parseFloat(params.y1); - var y2 = parseFloat(params.y2); + let x1 = parseFloat(params.x1 as any); + let x2 = parseFloat(params.x2 as any); + let y1 = parseFloat(params.y1 as any); + let y2 = parseFloat(params.y2 as any); if (x1 !== x2) { // WLOG make x1,y1 the one on the left if (Math.min(x1, x2) === x2) { - var tmpx1 = x1; - var tmpy1 = y1; + const tmpx1 = x1; + const tmpy1 = y1; x1 = x2; y1 = y2; x2 = tmpx1; @@ -136,19 +143,19 @@ var lineToVertexes = function(params, z_index, addVertex) { } } - var perpendicular_vector = [y2 - y1, x1 - x2]; - var perpendicular_vector_length = Math.sqrt(perpendicular_vector[0] * perpendicular_vector[0] + perpendicular_vector[1] * perpendicular_vector[1]); - var unit_perp_vector = [perpendicular_vector[0] / perpendicular_vector_length, perpendicular_vector[1] / perpendicular_vector_length]; + const perpendicular_vector = [y2 - y1, x1 - x2]; + const perpendicular_vector_length = Math.sqrt(perpendicular_vector[0] * perpendicular_vector[0] + perpendicular_vector[1] * perpendicular_vector[1]); + const unit_perp_vector = [perpendicular_vector[0] / perpendicular_vector_length, perpendicular_vector[1] / perpendicular_vector_length]; - var half_stroke_width = parseFloat(params['stroke-width']) / 2; - var direction1 = [unit_perp_vector[0] * half_stroke_width, unit_perp_vector[1] * half_stroke_width]; - var direction2 = [direction1[0] * -1, direction1[1] * -1]; - var A = [x1 + direction1[0], y1 + direction1[1]]; - var B = [x1 + direction2[0], y1 + direction2[1]]; - var C = [x2 + direction1[0], y2 + direction1[1]]; - var D = [x2 + direction2[0], y2 + direction2[1]]; + const half_stroke_width = parseFloat(params['stroke-width'] as any) / 2; + const direction1 = [unit_perp_vector[0] * half_stroke_width, unit_perp_vector[1] * half_stroke_width]; + const direction2 = [direction1[0] * -1, direction1[1] * -1]; + const A = [x1 + direction1[0], y1 + direction1[1]]; + const B = [x1 + direction2[0], y1 + direction2[1]]; + const C = [x2 + direction1[0], y2 + direction1[1]]; + const D = [x2 + direction2[0], y2 + direction2[1]]; - var stroke_rgba = extractRGBA(params.stroke); + const stroke_rgba = extractRGBA(params.stroke); addVertex([A[0], A[1], z_index], stroke_rgba); addVertex([B[0], B[1], z_index], stroke_rgba); addVertex([C[0], C[1], z_index], stroke_rgba); @@ -156,13 +163,14 @@ var lineToVertexes = function(params, z_index, addVertex) { addVertex([C[0], C[1], z_index], stroke_rgba); addVertex([D[0], D[1], z_index], stroke_rgba); addVertex([B[0], B[1], z_index], stroke_rgba); -}; -module.exports = function(oncoprint_shape_computed_params, z_index, addVertex) { +} + +export default function(oncoprint_shape_computed_params:ComputedShapeParams, z_index:number, addVertex:AddVertexCallback) { // target_position_array is an array with 3-d float vertexes // target_color_array is an array with rgba values in [0,1] // We pass them in to save on concatenation costs - var type = oncoprint_shape_computed_params.type; + const type = oncoprint_shape_computed_params.type; if (type === "rectangle") { return rectangleToVertexes(oncoprint_shape_computed_params, z_index, addVertex); } else if (type === "triangle") { diff --git a/packages/oncoprintjs/src/js/oncoprinttooltip.js b/packages/oncoprintjs/src/js/oncoprinttooltip.js deleted file mode 100644 index 864568d648c..00000000000 --- a/packages/oncoprintjs/src/js/oncoprinttooltip.js +++ /dev/null @@ -1,117 +0,0 @@ -var $ = require('jquery'); - -var TOOLTIP_CLASS = "oncoprintjs__tooltip"; - -var OncoprintToolTip = (function() { - function OncoprintToolTip($container, params) { - params = params || {}; - this.$container = $container; - this.$div = $('
').addClass(TOOLTIP_CLASS).appendTo($container).css({'background-color':'rgba(255,255,255,1)', 'position':'absolute', 'display':'none', 'border':'1px solid black', 'max-width':300, 'min-width':150}); - if (params.noselect) { - this.$div.addClass("noselect"); - } - this.hide_timeout_id = undefined; - this.show_timeout_id = undefined; - this.center = false; - - this.shown = false; - - var self = this; - this.$div.on("mousemove", function(evt) { - evt.stopPropagation(); - cancelScheduledHide(self); - }); - this.$div.on("mouseleave", function(evt) { - evt.stopPropagation(); - self.hide(); - }); - } - OncoprintToolTip.prototype.show = function(wait, viewport_x, viewport_y, $contents, fade) { - cancelScheduledHide(this); - - if (typeof wait !== 'undefined' && !this.shown) { - var self = this; - cancelScheduledShow(this); - this.show_timeout_id = setTimeout(function() { - doShow(self, viewport_x, viewport_y, $contents, fade); - }, wait); - } else { - doShow(this, viewport_x, viewport_y, $contents, fade); - } - } - var doShow = function(tt, viewport_x, viewport_y, $contents, fade) { - cancelScheduledShow(tt); - tt.show_timeout_id = undefined; - tt.$div.empty(); - tt.$div.css({'top':0, 'left':0, 'z-index':9999}); // put up top left so that it doesnt cause any page expansion, before the position calculation which depends on page size - tt.$div.append($contents); - if (!fade) { - tt.$div.show(); - } else { - tt.$div.stop().fadeIn('fast'); - } - // adjust tooltip position based on size of contents - var x = viewport_x - (tt.center ? tt.$div.width()/2 : 0); - var y = viewport_y - tt.$div.height(); - // clamp to visible area - var min_padding = 20; - y = Math.max(y, min_padding); // make sure not too high - y = Math.min(y, $(window).height() - tt.$div.height()); // make sure not too low - x = Math.max(x, min_padding); // make sure not too left - x = Math.min(x, $(window).width() - tt.$div.width() - min_padding); // make sure not too right - - tt.$div.css({'top':y, 'left':x, 'z-index':9999}); - tt.shown = true; - }; - var doHide = function(tt, fade) { - cancelScheduledHide(tt); - tt.hide_timeout_id = undefined; - if (!fade) { - tt.$div.hide(); - } else { - tt.$div.fadeOut(); - } - tt.shown = false; - }; - var cancelScheduledShow = function(tt) { - clearTimeout(tt.show_timeout_id); - tt.show_timeout_id = undefined; - }; - var cancelScheduledHide = function(tt) { - clearTimeout(tt.hide_timeout_id); - tt.hide_timeout_id = undefined; - }; - OncoprintToolTip.prototype.showIfNotAlreadyGoingTo = function(wait, viewport_x, viewport_y, $contents) { - if (typeof this.show_timeout_id === 'undefined') { - this.show(wait, viewport_x, viewport_y, $contents); - } - } - OncoprintToolTip.prototype.hideIfNotAlreadyGoingTo = function(wait) { - if (typeof this.hide_timeout_id === 'undefined') { - this.hide(wait); - } - }; - OncoprintToolTip.prototype.hide = function(wait) { - cancelScheduledShow(this); - - if (!this.shown) { - return; - } - - if (typeof wait !== 'undefined') { - var self = this; - cancelScheduledHide(this); - this.hide_timeout_id = setTimeout(function() { - doHide(self); - }, wait); - } else { - doHide(this); - } - } - OncoprintToolTip.prototype.fadeIn = function(wait, viewport_x, viewport_y, $contents) { - this.show(wait, viewport_x, viewport_y, $contents, true); - } - return OncoprintToolTip; -})(); - -module.exports = OncoprintToolTip; diff --git a/packages/oncoprintjs/src/js/oncoprinttooltip.ts b/packages/oncoprintjs/src/js/oncoprinttooltip.ts new file mode 100644 index 00000000000..dbf64a75e76 --- /dev/null +++ b/packages/oncoprintjs/src/js/oncoprinttooltip.ts @@ -0,0 +1,122 @@ +import $ from "jquery"; + +const TOOLTIP_CLASS = "oncoprintjs__tooltip"; + +export type OncoprintTooltipParams = { noselect?:boolean }; + +export default class OncoprintToolTip { + private $div:JQuery; + private hide_timeout_id:number|undefined; + private show_timeout_id:number|undefined; + public center:boolean; + private shown:boolean; + + constructor(private $container:JQuery, params?:OncoprintTooltipParams) { + params = params || {}; + + this.$div = $('
').addClass(TOOLTIP_CLASS).appendTo($container).css({'background-color':'rgba(255,255,255,1)', 'position':'absolute', 'display':'none', 'border':'1px solid black', 'max-width':300, 'min-width':150}); + if (params.noselect) { + this.$div.addClass("noselect"); + } + this.hide_timeout_id = undefined; + this.show_timeout_id = undefined; + this.center = false; + + this.shown = false; + + const self = this; + this.$div.on("mousemove", function(evt) { + evt.stopPropagation(); + self.cancelScheduledHide(); + }); + this.$div.on("mouseleave", function(evt) { + evt.stopPropagation(); + self.hide(); + }); + } + public show(wait:number|undefined, viewport_x:number, viewport_y:number, $contents:JQuery, fade?:boolean) { + this.cancelScheduledHide(); + + if (typeof wait !== 'undefined' && !this.shown) { + const self = this; + this.cancelScheduledShow(); + this.show_timeout_id = setTimeout(function() { + self.doShow(viewport_x, viewport_y, $contents, fade); + }, wait); + } else { + this.doShow(viewport_x, viewport_y, $contents, fade); + } + } + private doShow(viewport_x:number, viewport_y:number, $contents:JQuery, fade?:boolean) { + this.cancelScheduledShow(); + this.show_timeout_id = undefined; + this.$div.empty(); + this.$div.css({'top':0, 'left':0, 'z-index':9999}); // put up top left so that it doesnt cause any page expansion, before the position calculation which depends on page size + this.$div.append($contents); + if (!fade) { + this.$div.show(); + } else { + this.$div.stop().fadeIn('fast'); + } + // adjust tooltip position based on size of contents + let x = viewport_x - (this.center ? this.$div.width()/2 : 0); + let y = viewport_y - this.$div.height(); + // clamp to visible area + const min_padding = 20; + y = Math.max(y, min_padding); // make sure not too high + y = Math.min(y, $(window).height() - this.$div.height()); // make sure not too low + x = Math.max(x, min_padding); // make sure not too left + x = Math.min(x, $(window).width() - this.$div.width() - min_padding); // make sure not too right + + this.$div.css({'top':y, 'left':x, 'z-index':9999}); + this.shown = true; + } + private doHide(fade?:boolean) { + this.cancelScheduledHide(); + this.hide_timeout_id = undefined; + if (!fade) { + this.$div.hide(); + } else { + this.$div.fadeOut(); + } + this.shown = false; + }; + private cancelScheduledShow() { + clearTimeout(this.show_timeout_id); + this.show_timeout_id = undefined; + } + private cancelScheduledHide() { + clearTimeout(this.hide_timeout_id); + this.hide_timeout_id = undefined; + } + public showIfNotAlreadyGoingTo(wait:number|undefined, viewport_x:number, viewport_y:number, $contents:JQuery) { + if (typeof this.show_timeout_id === 'undefined') { + this.show(wait, viewport_x, viewport_y, $contents); + } + } + public hideIfNotAlreadyGoingTo(wait?:number) { + if (typeof this.hide_timeout_id === 'undefined') { + this.hide(wait); + } + } + public hide(wait?:number) { + this.cancelScheduledShow(); + + if (!this.shown) { + return; + } + + if (typeof wait !== 'undefined') { + const self = this; + this.cancelScheduledHide(); + this.hide_timeout_id = setTimeout(function() { + self.doHide(); + }, wait); + } else { + this.doHide(); + } + } + public fadeIn(wait:number|undefined, viewport_x:number, viewport_y:number, $contents:JQuery) { + this.show(wait, viewport_x, viewport_y, $contents, true); + } +} \ No newline at end of file diff --git a/packages/oncoprintjs/src/js/oncoprinttrackinfoview.js b/packages/oncoprintjs/src/js/oncoprinttrackinfoview.js deleted file mode 100644 index 68dac88bd15..00000000000 --- a/packages/oncoprintjs/src/js/oncoprinttrackinfoview.js +++ /dev/null @@ -1,172 +0,0 @@ -var svgfactory = require('./svgfactory.js'); -var $ = require('jquery'); - -var OncoprintTrackInfoView = (function () { - function OncoprintTrackInfoView($div, tooltip) { - this.$div = $div; - this.tooltip = tooltip; - this.tooltip.center = false; - this.$ctr = $('
').css({'position': 'absolute', 'overflow-y':'hidden', 'overflow-x':'hidden'}).appendTo(this.$div); - this.$text_ctr = $('
').css({'position':'absolute'}).appendTo(this.$ctr); - this.base_font_size = 12; - this.font_family = 'Arial'; - this.font_weight = 'bold'; - this.width = 0; - - this.$label_elts = []; - - this.rendering_suppressed = false; - } - - var destroyLabelElts = function(view) { - for (var i=0; i').css({'position': 'absolute', - 'font-family': view.font_family, - 'font-weight': view.font_weight, - 'font-size': font_size}) - .addClass('noselect'); - var text = model.getTrackInfo(tracks[i]); - if (!text) { - return; - } - $new_label.text(text); - $new_label.appendTo(view.$text_ctr); - view.$label_elts.push($new_label); - setTimeout(function() { - $new_label.on("mousemove", function() { - var $tooltip_elt = model.$getTrackInfoTooltip(tracks[i]); - if ($tooltip_elt) { - var offset = $new_label[0].getBoundingClientRect(); - view.tooltip.fadeIn(200, offset.left, offset.top, $tooltip_elt); - } - }).on("mouseleave", function() { - view.tooltip.hideIfNotAlreadyGoingTo(150); - }); - }, 0); // delay to give time for render before adding events - var top = label_tops[tracks[i]] + (model.getCellHeight(tracks[i]) - $new_label.outerHeight()) / 2; - $new_label.css({'top': top + 'px'}); - view.width = Math.max(32, view.width, $new_label[0].clientWidth); - })(); - } - if (view.width > 0) { - view.width += 10; - } - }; - var scroll = function (view, scroll_y) { - if (view.rendering_suppressed) { - return; - } - view.$text_ctr.css({'top': -scroll_y}); - }; - - var resize = function (view, model) { - if (view.rendering_suppressed) { - return; - } - view.$div.css({'width': view.getWidth(), 'height': model.getCellViewHeight()}); - view.$ctr.css({'width': view.getWidth(), 'height': model.getCellViewHeight()}); - }; - - OncoprintTrackInfoView.prototype.getFontSize = function () { - return Math.max(Math.min(this.base_font_size, this.minimum_track_height), 7); - } - OncoprintTrackInfoView.prototype.getWidth = function () { - return this.width; - } - OncoprintTrackInfoView.prototype.addTracks = function (model) { - renderAllInfo(this, model); - resize(this, model); - } - OncoprintTrackInfoView.prototype.moveTrack = function (model) { - renderAllInfo(this, model); - resize(this, model); - } - OncoprintTrackInfoView.prototype.setTrackGroupOrder = function(model) { - renderAllInfo(this, model); - resize(this, model); - } - OncoprintTrackInfoView.prototype.removeTrack = function (model) { - renderAllInfo(this, model); - resize(this, model); - } - OncoprintTrackInfoView.prototype.setTrackInfo = function (model) { - renderAllInfo(this, model); - resize(this, model); - } - OncoprintTrackInfoView.prototype.setScroll = function(model) { - this.setVertScroll(model); - } - OncoprintTrackInfoView.prototype.setHorzScroll = function (model) { - } - OncoprintTrackInfoView.prototype.setVertScroll = function (model) { - scroll(this, model.getVertScroll()); - } - OncoprintTrackInfoView.prototype.setZoom = function(model) { - this.setVertZoom(model); - } - - OncoprintTrackInfoView.prototype.setViewport = function(model) { - renderAllInfo(this, model); - resize(this, model); - scroll(this, model.getVertScroll()); - } - OncoprintTrackInfoView.prototype.setVertZoom = function (model) { - renderAllInfo(this, model); - resize(this, model); - } - OncoprintTrackInfoView.prototype.suppressRendering = function () { - this.rendering_suppressed = true; - } - OncoprintTrackInfoView.prototype.releaseRendering = function (model) { - this.rendering_suppressed = false; - renderAllInfo(this, model); - resize(this, model); - scroll(this, model.getVertScroll()); - } - OncoprintTrackInfoView.prototype.destroy = function() { - destroyLabelElts(this); - } - OncoprintTrackInfoView.prototype.toSVGGroup = function (model, offset_x, offset_y) { - var root = svgfactory.group((offset_x || 0), (offset_y || 0)); - var cell_tops = model.getCellTops(); - var tracks = model.getTracks(); - for (var i = 0; i < tracks.length; i++) { - var track_id = tracks[i]; - var y = cell_tops[track_id] + model.getCellHeight(track_id) / 2; - var info = model.getTrackInfo(track_id); - var text_elt = svgfactory.text(info, 0, y, this.font_size, this.font_family, this.font_weight, "bottom"); - text_elt.setAttribute("dy", "0.35em"); - root.appendChild(text_elt); - } - return root; - } - return OncoprintTrackInfoView; -})(); - -module.exports = OncoprintTrackInfoView; diff --git a/packages/oncoprintjs/src/js/oncoprinttrackinfoview.ts b/packages/oncoprintjs/src/js/oncoprinttrackinfoview.ts new file mode 100644 index 00000000000..725cf4505f3 --- /dev/null +++ b/packages/oncoprintjs/src/js/oncoprinttrackinfoview.ts @@ -0,0 +1,178 @@ +import svgfactory from "./svgfactory"; +import $ from "jquery"; +import OncoprintToolTip from "./oncoprinttooltip"; +import OncoprintModel from "./oncoprintmodel"; + +export default class OncoprintTrackInfoView { + + private $ctr:JQuery; + private $text_ctr:JQuery; + private base_font_size = 12; + private font_family = 'Arial'; + private font_weight = 'bold'; + private width = 0; + private $label_elts:JQuery[] = []; + private rendering_suppressed = false; + private minimum_track_height:number; + + constructor(private $div:JQuery, private tooltip:OncoprintToolTip) { + this.tooltip.center = false; + + this.$ctr = $('
').css({'position': 'absolute', 'overflow-y':'hidden', 'overflow-x':'hidden'}).appendTo(this.$div); + this.$text_ctr = $('
').css({'position':'absolute'}).appendTo(this.$ctr); + } + + private destroyLabelElts() { + for (let i=0; i').css({'position': 'absolute', + 'font-family': self.font_family, + 'font-weight': self.font_weight, + 'font-size': font_size}) + .addClass('noselect'); + const text = model.getTrackInfo(tracks[i]); + if (!text) { + return; + } + $new_label.text(text); + $new_label.appendTo(self.$text_ctr); + self.$label_elts.push($new_label); + setTimeout(function() { + $new_label.on("mousemove", function() { + const $tooltip_elt = model.$getTrackInfoTooltip(tracks[i]); + if ($tooltip_elt) { + const offset = $new_label[0].getBoundingClientRect(); + self.tooltip.fadeIn(200, offset.left, offset.top, $tooltip_elt); + } + }).on("mouseleave", function() { + self.tooltip.hideIfNotAlreadyGoingTo(150); + }); + }, 0); // delay to give time for render before adding events + const top = label_tops[tracks[i]] + (model.getCellHeight(tracks[i]) - $new_label.outerHeight()) / 2; + $new_label.css({'top': top + 'px'}); + self.width = Math.max(32, self.width, $new_label[0].clientWidth); + })(); + } + if (this.width > 0) { + this.width += 10; + } + }; + private scroll(scroll_y:number) { + if (this.rendering_suppressed) { + return; + } + this.$text_ctr.css({'top': -scroll_y}); + }; + + private resize(model:OncoprintModel) { + if (this.rendering_suppressed) { + return; + } + this.$div.css({'width': this.getWidth(), 'height': model.getCellViewHeight()}); + this.$ctr.css({'width': this.getWidth(), 'height': model.getCellViewHeight()}); + }; + + public getFontSize() { + return Math.max(Math.min(this.base_font_size, this.minimum_track_height), 7); + } + public getWidth() { + return this.width; + } + public addTracks(model:OncoprintModel) { + this.renderAllInfo(model); + this.resize(model); + } + public moveTrack(model:OncoprintModel) { + this.renderAllInfo(model); + this.resize(model); + } + public setTrackGroupOrder(model:OncoprintModel) { + this.renderAllInfo(model); + this.resize(model); + } + public removeTrack(model:OncoprintModel) { + this.renderAllInfo(model); + this.resize(model); + } + public setTrackInfo(model:OncoprintModel) { + this.renderAllInfo(model); + this.resize(model); + } + public setScroll(model:OncoprintModel) { + this.setVertScroll(model); + } + public setHorzScroll(model:OncoprintModel) { + } + public setVertScroll(model:OncoprintModel) { + this.scroll(model.getVertScroll()); + } + public setZoom(model:OncoprintModel) { + this.setVertZoom(model); + } + + public setViewport(model:OncoprintModel) { + this.renderAllInfo(model); + this.resize(model); + this.scroll(model.getVertScroll()); + } + public setVertZoom(model:OncoprintModel) { + this.renderAllInfo(model); + this.resize(model); + } + public suppressRendering() { + this.rendering_suppressed = true; + } + public releaseRendering(model:OncoprintModel) { + this.rendering_suppressed = false; + this.renderAllInfo(model); + this.resize(model); + this.scroll(model.getVertScroll()); + } + public destroy() { + this.destroyLabelElts(); + } + public toSVGGroup(model:OncoprintModel, offset_x:number, offset_y:number) { + const root = svgfactory.group((offset_x || 0), (offset_y || 0)); + const cell_tops = model.getCellTops(); + const tracks = model.getTracks(); + + const font_size = this.getFontSize(); + + for (let i = 0; i < tracks.length; i++) { + const track_id = tracks[i]; + const y = cell_tops[track_id] + model.getCellHeight(track_id) / 2; + const info = model.getTrackInfo(track_id); + const text_elt = svgfactory.text(info, 0, y, font_size, this.font_family, this.font_weight, "bottom"); + text_elt.setAttribute("dy", "0.35em"); + root.appendChild(text_elt); + } + return root; + } +} diff --git a/packages/oncoprintjs/src/js/oncoprinttrackoptionsview.js b/packages/oncoprintjs/src/js/oncoprinttrackoptionsview.js deleted file mode 100644 index f92d978520c..00000000000 --- a/packages/oncoprintjs/src/js/oncoprinttrackoptionsview.js +++ /dev/null @@ -1,383 +0,0 @@ -var $ = require('jquery'); -var menuDotsIcon = require('../img/menudots.svg'); - -var TOGGLE_BTN_CLASS = "oncoprintjs__track_options__toggle_btn_img"; -var TOGGLE_BTN_OPEN_CLASS = "oncoprintjs__track_options__open"; -var DROPDOWN_CLASS = "oncoprintjs__track_options__dropdown"; -var SEPARATOR_CLASS = "oncoprintjs__track_options__separator"; -var NTH_CLASS_PREFIX = "nth-"; - -var OncoprintTrackOptionsView = (function () { - function OncoprintTrackOptionsView($div, moveUpCallback, moveDownCallback, removeCallback, sortChangeCallback, unexpandCallback) { - var position = $div.css('position'); - if (position !== 'absolute' && position !== 'relative') { - console.log("WARNING: div passed to OncoprintTrackOptionsView must be absolute or relative positioned - layout problems will occur"); - } - - this.moveUpCallback = moveUpCallback; - this.moveDownCallback = moveDownCallback; - this.removeCallback = removeCallback; // function(track_id) { ... } - this.sortChangeCallback = sortChangeCallback; // function(track_id, dir) { ... } - this.unexpandCallback = unexpandCallback; // function(track_id) - - this.$div = $div; - this.$ctr = $('
').css({'position': 'absolute', 'overflow-y':'hidden', 'overflow-x':'hidden'}).appendTo(this.$div); - this.$buttons_ctr = $('
').css({'position':'absolute'}).appendTo(this.$ctr); - this.$dropdown_ctr = $('
').css({'position': 'absolute'}).appendTo(this.$div); - - this.img_size; - - this.rendering_suppressed = false; - - this.track_options_$elts = {}; - - this.menu_shown = {}; - - var self = this; - this.clickHandler = function() { - $(self).trigger('oncoprint-track-options-view.click-out'); - }; - $(document).on("click", this.clickHandler); - - this.interaction_disabled = false; - } - - var renderAllOptions = function(view, model) { - if (view.rendering_suppressed) { - return; - } - $(view).off('oncoprint-track-options-view.click-out'); - $(view).on('oncoprint-track-options-view.click-out', function() { - for (var track_id in view.track_options_$elts) { - if (view.track_options_$elts.hasOwnProperty(track_id)) { - hideTrackMenu(view, track_id); - } - } - }); - - view.$buttons_ctr.empty(); - view.$dropdown_ctr.empty(); - scroll(view, model.getVertScroll()); - - var tracks = model.getTracks(); - var minimum_track_height = Number.POSITIVE_INFINITY; - for (var i = 0; i < tracks.length; i++) { - minimum_track_height = Math.min(minimum_track_height, model.getTrackHeight(tracks[i])); - } - view.img_size = Math.floor(minimum_track_height * 0.75); - - for (var i = 0; i < tracks.length; i++) { - renderTrackOptions(view, model, tracks[i], i); - } - }; - - var scroll = function (view, scroll_y) { - if (view.rendering_suppressed) { - return; - } - view.$buttons_ctr.css({'top': -scroll_y}); - view.$dropdown_ctr.css({'top': -scroll_y}); - }; - - var resize = function (view, model) { - if (view.rendering_suppressed) { - return; - } - view.$div.css({'width': view.getWidth(), 'height': model.getCellViewHeight()}); - view.$ctr.css({'width': view.getWidth(), 'height': model.getCellViewHeight()}); - }; - - var hideTrackMenu = function (view, track_id) { - view.menu_shown[track_id] = false; - var $elts = view.track_options_$elts[track_id]; - $elts.$dropdown.css({'z-index': 1}); - $elts.$dropdown.css({'border': '1px solid rgba(125,125,125,0)'}); - $elts.$img.css({'border': '1px solid rgba(125,125,125,0)'}); - $elts.$dropdown.fadeOut(100); - }; - - var showTrackMenu = function (view, track_id) { - view.menu_shown[track_id] = true; - var $elts = view.track_options_$elts[track_id]; - $elts.$dropdown.css({'z-index': 10}); - $elts.$dropdown.css({'border': '1px solid rgba(125,125,125,1)'}); - $elts.$img.css({'border': '1px solid rgba(125,125,125,1)'}); - $elts.$dropdown.fadeIn(100); - }; - - var hideMenusExcept = function (view, track_id) { - track_id = track_id.toString(); - for (var other_track_id in view.track_options_$elts) { - if (view.track_options_$elts.hasOwnProperty(other_track_id)) { - if (other_track_id === track_id) { - continue; - } - hideTrackMenu(view, other_track_id); - } - } - }; - - var $makeDropdownOption = function (text, weight, disabled, callback) { - var li = $('
  • ').text(text).css({'font-weight': weight, 'font-size': 12, 'border-bottom': '1px solid rgba(0,0,0,0.3)'}) - if (!disabled) { - if (callback) { - li.addClass("clickable"); - li.css({'cursor': 'pointer'}); - li.click(callback) - .hover(function () { - $(this).css({'background-color': 'rgb(200,200,200)'}); - }, function () { - $(this).css({'background-color': 'rgba(255,255,255,0)'}); - }); - } else { - li.click(function(evt) { evt.stopPropagation(); }); - } - } else { - li.addClass("disabled"); - li.css({'color': 'rgb(200, 200, 200)', 'cursor': 'default'}); - } - return li; - }; - var $makeDropdownSeparator = function () { - return $('
  • ').css({'border-top': '1px solid black'}).addClass(SEPARATOR_CLASS); - }; - - var renderSortArrow = function($sortarrow, model, track_id) { - var sortarrow_char = ''; - if (model.isTrackSortDirectionChangeable(track_id)){ - sortarrow_char = { - '1': '', - '-1': '', - '0': ''}[model.getTrackSortDirection(track_id)]; - } - $sortarrow.html(sortarrow_char); - } - - var renderTrackOptions = function (view, model, track_id, index) { - var $div, $img, $sortarrow, $dropdown; - var top = model.getZoomedTrackTops(track_id); - $div = $('
    ').appendTo(view.$buttons_ctr).css({'position': 'absolute', 'left': '0px', 'top': top + 'px', 'white-space': 'nowrap'}); - $img = $('').appendTo($div) - .attr({ - 'src': menuDotsIcon, - 'width': view.img_size, - 'height': view.img_size - }) - .css({ - 'float': 'left', - 'cursor': 'pointer', - 'border': '1px solid rgba(125,125,125,0)' - }).addClass(TOGGLE_BTN_CLASS).addClass(NTH_CLASS_PREFIX+(index+1)); - $sortarrow = $('').appendTo($div).css({'position': 'absolute', 'top': Math.floor(view.img_size / 4) + 'px'}); - $dropdown = $('
      ').appendTo(view.$dropdown_ctr) - .css({ - 'position':'absolute', - 'width': 120, - 'display': 'none', - 'list-style-type': 'none', - 'padding-left': '6', - 'padding-right': '6', - 'float': 'right', - 'background-color': 'rgb(255,255,255)', - 'left':'0px', 'top': top + view.img_size + 'px' - }).addClass(DROPDOWN_CLASS).addClass(NTH_CLASS_PREFIX+(index+1)); - view.track_options_$elts[track_id] = {'$div': $div, '$img': $img, '$dropdown': $dropdown}; - - renderSortArrow($sortarrow, model, track_id); - - $img.hover(function (evt) { - if (!view.menu_shown[track_id]) { - $(this).css({'border': '1px solid rgba(125,125,125,0.3)'}); - } - }, function (evt) { - if (!view.menu_shown[track_id]) { - $(this).css({'border': '1px solid rgba(125,125,125,0)'}); - } - }); - $img.click(function (evt) { - evt.stopPropagation(); - if ($dropdown.is(":visible")) { - $img.addClass(TOGGLE_BTN_OPEN_CLASS); - hideTrackMenu(view, track_id); - } else { - $img.removeClass(TOGGLE_BTN_OPEN_CLASS); - showTrackMenu(view, track_id); - } - hideMenusExcept(view, track_id); - }); - - var movingAndSortingDisabled = model.getTrackMovable(track_id) && model.isTrackInClusteredGroup(track_id); - - if (model.getTrackMovable(track_id)) { - $dropdown.append($makeDropdownOption('Move up', 'normal', movingAndSortingDisabled, function (evt) { - evt.stopPropagation(); - view.moveUpCallback(track_id); - })); - $dropdown.append($makeDropdownOption('Move down', 'normal', movingAndSortingDisabled, function (evt) { - evt.stopPropagation(); - view.moveDownCallback(track_id); - })); - } - if (model.isTrackRemovable(track_id)) { - $dropdown.append($makeDropdownOption('Remove track', 'normal', false, function (evt) { - evt.stopPropagation(); - view.removeCallback(track_id); - })); - } - if (model.isTrackSortDirectionChangeable(track_id)) { - $dropdown.append($makeDropdownSeparator()); - var $sort_inc_li; - var $sort_dec_li; - var $dont_sort_li; - $sort_inc_li = $makeDropdownOption('Sort a-Z', (model.getTrackSortDirection(track_id) === 1 ? 'bold' : 'normal'), movingAndSortingDisabled, function (evt) { - evt.stopPropagation(); - $sort_inc_li.css('font-weight', 'bold'); - $sort_dec_li.css('font-weight', 'normal'); - $dont_sort_li.css('font-weight', 'normal'); - view.sortChangeCallback(track_id, 1); - renderSortArrow($sortarrow, model, track_id); - }); - $sort_dec_li = $makeDropdownOption('Sort Z-a', (model.getTrackSortDirection(track_id) === -1 ? 'bold' : 'normal'), movingAndSortingDisabled, function (evt) { - evt.stopPropagation(); - $sort_inc_li.css('font-weight', 'normal'); - $sort_dec_li.css('font-weight', 'bold'); - $dont_sort_li.css('font-weight', 'normal'); - view.sortChangeCallback(track_id, -1); - renderSortArrow($sortarrow, model, track_id); - }); - $dont_sort_li = $makeDropdownOption('Don\'t sort track', (model.getTrackSortDirection(track_id) === 0 ? 'bold' : 'normal'), movingAndSortingDisabled, function (evt) { - evt.stopPropagation(); - $sort_inc_li.css('font-weight', 'normal'); - $sort_dec_li.css('font-weight', 'normal'); - $dont_sort_li.css('font-weight', 'bold'); - view.sortChangeCallback(track_id, 0); - renderSortArrow($sortarrow, model, track_id); - }); - $dropdown.append($sort_inc_li); - $dropdown.append($sort_dec_li); - $dropdown.append($dont_sort_li); - } - if (model.isTrackExpandable(track_id)) { - $dropdown.append($makeDropdownOption( - model.getExpandButtonText(track_id), - 'normal', - false, - function (evt) { - evt.stopPropagation(); - // close the menu to discourage clicking again, as it - // may take a moment to finish expanding - renderAllOptions(view, model); - model.expandTrack(track_id); - })); - } - if (model.isTrackExpanded(track_id)) { - $dropdown.append($makeDropdownOption( - 'Remove expansion', - 'normal', - false, - function (evt) { - evt.stopPropagation(); - view.unexpandCallback(track_id); - })); - } - // Add custom options - var custom_options = model.getTrackCustomOptions(track_id); - if (custom_options && custom_options.length > 0) { - for (var i=0; ivoid; +export default class OncoprintTrackOptionsView { + + private $ctr:JQuery; + private $buttons_ctr:JQuery; + private $dropdown_ctr:JQuery; + + private img_size:number; + private rendering_suppressed = false; + private track_options_$elts:TrackProp<{ $div:JQuery, $img:JQuery, $dropdown:JQuery}> = {}; + private menu_shown:TrackProp = {}; + private clickHandler:()=>void; + private interaction_disabled = false; + + constructor( + private $div:JQuery, + private moveUpCallback:TrackCallback, + private moveDownCallback:TrackCallback, + private removeCallback:TrackCallback, + private sortChangeCallback:(trackId:TrackId, sortDirection:TrackSortDirection)=>void, + private unexpandCallback:TrackCallback + ) { + const position = $div.css('position'); + if (position !== 'absolute' && position !== 'relative') { + console.log("WARNING: div passed to OncoprintTrackOptionsView must be absolute or relative positioned - layout problems will occur"); + } + + this.$ctr = $('
      ').css({'position': 'absolute', 'overflow-y':'hidden', 'overflow-x':'hidden'}).appendTo(this.$div); + this.$buttons_ctr = $('
      ').css({'position':'absolute'}).appendTo(this.$ctr); + this.$dropdown_ctr = $('
      ').css({'position': 'absolute'}).appendTo(this.$div); + + const self = this; + this.clickHandler = function() { + $(self).trigger('oncoprint-track-options-view.click-out'); + }; + $(document).on("click", this.clickHandler); + } + + private renderAllOptions(model:OncoprintModel) { + if (this.rendering_suppressed) { + return; + } + const self = this; + $(this).off('oncoprint-track-options-view.click-out'); + $(this).on('oncoprint-track-options-view.click-out', function() { + for (var track_id in self.track_options_$elts) { + if (self.track_options_$elts.hasOwnProperty(track_id)) { + self.hideTrackMenu(parseInt(track_id, 10)); + } + } + }); + + this.$buttons_ctr.empty(); + this.$dropdown_ctr.empty(); + this.scroll(model.getVertScroll()); + + var tracks = model.getTracks(); + var minimum_track_height = Number.POSITIVE_INFINITY; + for (let i = 0; i < tracks.length; i++) { + minimum_track_height = Math.min(minimum_track_height, model.getTrackHeight(tracks[i])); + } + this.img_size = Math.floor(minimum_track_height * 0.75); + + for (let i = 0; i < tracks.length; i++) { + this.renderTrackOptions(model, tracks[i], i); + } + } + + private scroll(scroll_y:number) { + if (this.rendering_suppressed) { + return; + } + this.$buttons_ctr.css({'top': -scroll_y}); + this.$dropdown_ctr.css({'top': -scroll_y}); + } + + private resize(model:OncoprintModel) { + if (this.rendering_suppressed) { + return; + } + this.$div.css({'width': this.getWidth(), 'height': model.getCellViewHeight()}); + this.$ctr.css({'width': this.getWidth(), 'height': model.getCellViewHeight()}); + } + + private hideTrackMenu(track_id:TrackId) { + this.menu_shown[track_id] = false; + const $elts = this.track_options_$elts[track_id]; + $elts.$dropdown.css({'z-index': 1}); + $elts.$dropdown.css({'border': '1px solid rgba(125,125,125,0)'}); + $elts.$img.css({'border': '1px solid rgba(125,125,125,0)'}); + $elts.$dropdown.fadeOut(100); + } + + private showTrackMenu(track_id:TrackId) { + this.menu_shown[track_id] = true; + const $elts = this.track_options_$elts[track_id]; + $elts.$dropdown.css({'z-index': 10}); + $elts.$dropdown.css({'border': '1px solid rgba(125,125,125,1)'}); + $elts.$img.css({'border': '1px solid rgba(125,125,125,1)'}); + $elts.$dropdown.fadeIn(100); + } + + private hideMenusExcept(track_id:TrackId) { + for (const _other_track_id in this.track_options_$elts) { + if (this.track_options_$elts.hasOwnProperty(_other_track_id)) { + const other_track_id = parseInt(_other_track_id, 10); + if (other_track_id === track_id) { + continue; + } + this.hideTrackMenu(other_track_id); + } + } + } + + private static $makeDropdownOption(text:string, weight:string, disabled:boolean, callback:(evt:ClickEvent)=>void) { + const li = $('
    • ').text(text).css({'font-weight': weight, 'font-size': 12, 'border-bottom': '1px solid rgba(0,0,0,0.3)'}); + if (!disabled) { + if (callback) { + li.addClass("clickable"); + li.css({'cursor': 'pointer'}); + li.click(callback) + .hover(function () { + $(this).css({'background-color': 'rgb(200,200,200)'}); + }, function () { + $(this).css({'background-color': 'rgba(255,255,255,0)'}); + }); + } else { + li.click(function(evt) { evt.stopPropagation(); }); + } + } else { + li.addClass("disabled"); + li.css({'color': 'rgb(200, 200, 200)', 'cursor': 'default'}); + } + return li; + } + + private static $makeDropdownSeparator() { + return $('
    • ').css({'border-top': '1px solid black'}).addClass(SEPARATOR_CLASS); + } + + private static renderSortArrow($sortarrow:JQuery, model:OncoprintModel, track_id:TrackId) { + let sortarrow_char = ''; + if (model.isTrackSortDirectionChangeable(track_id)){ + sortarrow_char = { + '1': '', + '-1': '', + '0': ''}[model.getTrackSortDirection(track_id)]; + } + $sortarrow.html(sortarrow_char); + } + + private renderTrackOptions(model:OncoprintModel, track_id:TrackId, index:number) { + let $div:JQuery, $img:JQuery, $sortarrow:JQuery, $dropdown:JQuery; + const top = model.getZoomedTrackTops(track_id); + $div = $('
      ').appendTo(this.$buttons_ctr).css({'position': 'absolute', 'left': '0px', 'top': top + 'px', 'white-space': 'nowrap'}); + $img = $('').appendTo($div) + .attr({ + 'src': menuDotsIcon, + 'width': this.img_size, + 'height': this.img_size + }) + .css({ + 'float': 'left', + 'cursor': 'pointer', + 'border': '1px solid rgba(125,125,125,0)' + }).addClass(TOGGLE_BTN_CLASS).addClass(NTH_CLASS_PREFIX+(index+1)); + $sortarrow = $('').appendTo($div).css({'position': 'absolute', 'top': Math.floor(this.img_size / 4) + 'px'}); + $dropdown = $('
        ').appendTo(this.$dropdown_ctr) + .css({ + 'position':'absolute', + 'width': 120, + 'display': 'none', + 'list-style-type': 'none', + 'padding-left': '6', + 'padding-right': '6', + 'float': 'right', + 'background-color': 'rgb(255,255,255)', + 'left':'0px', 'top': top + this.img_size + 'px' + }).addClass(DROPDOWN_CLASS).addClass(NTH_CLASS_PREFIX+(index+1)); + this.track_options_$elts[track_id] = {'$div': $div, '$img': $img, '$dropdown': $dropdown}; + + OncoprintTrackOptionsView.renderSortArrow($sortarrow, model, track_id); + + const self = this; + $img.hover(function (evt) { + if (!self.menu_shown[track_id]) { + $(this).css({'border': '1px solid rgba(125,125,125,0.3)'}); + } + }, function (evt) { + if (!self.menu_shown[track_id]) { + $(this).css({'border': '1px solid rgba(125,125,125,0)'}); + } + }); + $img.click(function (evt) { + evt.stopPropagation(); + if ($dropdown.is(":visible")) { + $img.addClass(TOGGLE_BTN_OPEN_CLASS); + self.hideTrackMenu(track_id); + } else { + $img.removeClass(TOGGLE_BTN_OPEN_CLASS); + self.showTrackMenu(track_id); + } + self.hideMenusExcept(track_id); + }); + + const movingAndSortingDisabled = model.getTrackMovable(track_id) && model.isTrackInClusteredGroup(track_id); + + if (model.getTrackMovable(track_id)) { + $dropdown.append(OncoprintTrackOptionsView.$makeDropdownOption('Move up', 'normal', movingAndSortingDisabled, function (evt) { + evt.stopPropagation(); + self.moveUpCallback(track_id); + })); + $dropdown.append(OncoprintTrackOptionsView.$makeDropdownOption('Move down', 'normal', movingAndSortingDisabled, function (evt) { + evt.stopPropagation(); + self.moveDownCallback(track_id); + })); + } + if (model.isTrackRemovable(track_id)) { + $dropdown.append(OncoprintTrackOptionsView.$makeDropdownOption('Remove track', 'normal', false, function (evt) { + evt.stopPropagation(); + self.removeCallback(track_id); + })); + } + if (model.isTrackSortDirectionChangeable(track_id)) { + $dropdown.append(OncoprintTrackOptionsView.$makeDropdownSeparator()); + let $sort_inc_li:JQuery; + let $sort_dec_li:JQuery; + let $dont_sort_li:JQuery; + $sort_inc_li = OncoprintTrackOptionsView.$makeDropdownOption('Sort a-Z', (model.getTrackSortDirection(track_id) === 1 ? 'bold' : 'normal'), movingAndSortingDisabled, function (evt) { + evt.stopPropagation(); + $sort_inc_li.css('font-weight', 'bold'); + $sort_dec_li.css('font-weight', 'normal'); + $dont_sort_li.css('font-weight', 'normal'); + self.sortChangeCallback(track_id, 1); + OncoprintTrackOptionsView.renderSortArrow($sortarrow, model, track_id); + }); + $sort_dec_li = OncoprintTrackOptionsView.$makeDropdownOption('Sort Z-a', (model.getTrackSortDirection(track_id) === -1 ? 'bold' : 'normal'), movingAndSortingDisabled, function (evt) { + evt.stopPropagation(); + $sort_inc_li.css('font-weight', 'normal'); + $sort_dec_li.css('font-weight', 'bold'); + $dont_sort_li.css('font-weight', 'normal'); + self.sortChangeCallback(track_id, -1); + OncoprintTrackOptionsView.renderSortArrow($sortarrow, model, track_id); + }); + $dont_sort_li = OncoprintTrackOptionsView.$makeDropdownOption('Don\'t sort track', (model.getTrackSortDirection(track_id) === 0 ? 'bold' : 'normal'), movingAndSortingDisabled, function (evt) { + evt.stopPropagation(); + $sort_inc_li.css('font-weight', 'normal'); + $sort_dec_li.css('font-weight', 'normal'); + $dont_sort_li.css('font-weight', 'bold'); + self.sortChangeCallback(track_id, 0); + OncoprintTrackOptionsView.renderSortArrow($sortarrow, model, track_id); + }); + $dropdown.append($sort_inc_li); + $dropdown.append($sort_dec_li); + $dropdown.append($dont_sort_li); + } + if (model.isTrackExpandable(track_id)) { + $dropdown.append(OncoprintTrackOptionsView.$makeDropdownOption( + model.getExpandButtonText(track_id), + 'normal', + false, + function (evt) { + evt.stopPropagation(); + // close the menu to discourage clicking again, as it + // may take a moment to finish expanding + self.renderAllOptions(model); + model.expandTrack(track_id); + })); + } + if (model.isTrackExpanded(track_id)) { + $dropdown.append(OncoprintTrackOptionsView.$makeDropdownOption( + 'Remove expansion', + 'normal', + false, + function (evt) { + evt.stopPropagation(); + self.unexpandCallback(track_id); + })); + } + // Add custom options + const custom_options = model.getTrackCustomOptions(track_id); + if (custom_options && custom_options.length > 0) { + for (var i=0; i b) { - return 1; - } else { - return 0; - } -}; - -var arrayFindIndex = function(arr, callback, start_index) { - start_index = start_index || 0; - for (var i=start_index; i