3 |
6 |
7 | Cancel
8 | Okay
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/utils/get-url-query-params.js:
--------------------------------------------------------------------------------
1 | const getUrlQueryParams = (url) => {
2 | if (!url) { return {}; }
3 |
4 | return (/^[?#]/.test(url) ? url.slice(1) : url)
5 | .split('&')
6 | .reduce((params, param) => {
7 | const [key, value] = param.split('=');
8 | params[key] = value ? decodeURIComponent(value.replace(/\+/g, ' ')) : '';
9 | return params;
10 | }, {});
11 | };
12 |
13 | export default getUrlQueryParams;
14 |
--------------------------------------------------------------------------------
/src/utils/download-as-json.js:
--------------------------------------------------------------------------------
1 | const downloadAsJson = function (obj) {
2 | const json = encodeURIComponent(JSON.stringify(obj, null, 2));
3 | const data = `text/json;charset=utf-8,${json}`;
4 | const a = document.createElement('a');
5 | a.href = `data:${data}`;
6 | a.download = 'hipiler-piles.json';
7 | a.innerHTML = '';
8 | document.body.appendChild(a);
9 | a.click();
10 | a.remove();
11 | };
12 |
13 | export default downloadAsJson;
14 |
--------------------------------------------------------------------------------
/aurelia_project/tasks/test.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test",
3 | "description": "Runs all unit tests and reports the results.",
4 | "flags": [
5 | {
6 | "name": "env",
7 | "description": "Sets the build environment.",
8 | "type": "string"
9 | },
10 | {
11 | "name": "watch",
12 | "description": "Watches test files for changes and re-runs the tests automatically.",
13 | "type": "boolean"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/src/utils/download-as-csv.js:
--------------------------------------------------------------------------------
1 | import * as Papa from 'papaparse';
2 |
3 | const downloadAsCsv = function (arr) {
4 | const csv = Papa.unparse(arr);
5 | const data = `text/csv;charset=utf-8,${csv}`;
6 | const a = document.createElement('a');
7 | a.href = `data:${data}`;
8 | a.download = 'hipiler-piles.csv';
9 | a.innerHTML = '';
10 | document.body.appendChild(a);
11 | a.click();
12 | a.remove();
13 | };
14 |
15 | export default downloadAsCsv;
16 |
--------------------------------------------------------------------------------
/src/services/chrom-info.js:
--------------------------------------------------------------------------------
1 | let isReady;
2 |
3 | const ready = new Promise((resolve) => {
4 | isReady = resolve;
5 | });
6 |
7 | let chromInfo;
8 |
9 | export default class ChromInfo {
10 | get ready () {
11 | return ready;
12 | }
13 |
14 | get () {
15 | return chromInfo;
16 | }
17 |
18 | set (newChromInfo) {
19 | if (!chromInfo && newChromInfo) {
20 | chromInfo = newChromInfo;
21 | isReady(chromInfo);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/redux-logger.js:
--------------------------------------------------------------------------------
1 | const logger = store => next => (action) => {
2 | console.group(action.type); // eslint-disable-line no-console
3 | console.info('%c dispatching', 'color: #7fbcff', action); // eslint-disable-line no-console
4 | let result = next(action);
5 | console.log('%c next state', 'color: #8eff80', store.getState()); // eslint-disable-line no-console
6 | console.groupEnd(action.type); // eslint-disable-line no-console
7 | return result;
8 | };
9 |
10 | export default logger;
11 |
--------------------------------------------------------------------------------
/aurelia_project/tasks/run.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "run",
3 | "description": "Builds the application and serves up the assets via a local web server, watching files for changes as you work.",
4 | "flags": [
5 | {
6 | "name": "env",
7 | "description": "Sets the build environment.",
8 | "type": "string"
9 | },
10 | {
11 | "name": "watch",
12 | "description": "Watches source files for changes and refreshes the app automatically.",
13 | "type": "boolean"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/src/assets/styles/columns.scss:
--------------------------------------------------------------------------------
1 | .column-1 {
2 | width: 100%;
3 | }
4 |
5 | .column-1-2 {
6 | width: 50%;
7 | }
8 |
9 | .column-1-3 {
10 | width: 33.33%;
11 | }
12 |
13 | .column-2-3 {
14 | width: 66.66%;
15 | }
16 |
17 | .column-1-4 {
18 | width: 25%;
19 | }
20 |
21 | .column-3-4 {
22 | width: 75%;
23 | }
24 |
25 | .column-1-5 {
26 | width: 20%;
27 | }
28 |
29 | .column-2-5 {
30 | width: 40%;
31 | }
32 |
33 | .column-3-5 {
34 | width: 60%;
35 | }
36 |
37 | .column-4-5 {
38 | width: 80%;
39 | }
40 |
--------------------------------------------------------------------------------
/src/assets/styles/colors.scss:
--------------------------------------------------------------------------------
1 | $black: #000;
2 | $gray: #999;
3 | $white: #fff;
4 |
5 | $gray-light: #bfbfbf;
6 | $gray-lighter: #d9d9d9;
7 | $gray-lightest: #e5e5e5;
8 |
9 | $gray-dark: #666;
10 | $gray-darker: #444;
11 | $gray-darkest: #222;
12 |
13 | $green: #40bf00;
14 | $orange: #ff5500;
15 | $red: #f60029;
16 | $yellow: #ffcc00;
17 | $blue: #4e40ff;
18 |
19 | $primary: $orange;
20 | $secondary: $blue;
21 |
22 | $active: $green;
23 | $ready: $yellow;
24 |
25 | $error-text: darken($red, 25%);
26 | $error-bg: lighten($red, 25%);
27 |
--------------------------------------------------------------------------------
/src/assets/styles/flexbox.scss:
--------------------------------------------------------------------------------
1 | .flex-c {
2 | display: flex;
3 | }
4 |
5 | .flex-d-c {
6 | flex-direction: column;
7 | }
8 |
9 | .flex-jc-c {
10 | justify-content: center;
11 | }
12 |
13 | .flex-jc-r {
14 | justify-content: flex-end;
15 | }
16 |
17 | .flex-jc-sb {
18 | justify-content: space-between;
19 | }
20 |
21 | .flex-a-c {
22 | align-items: center;
23 | }
24 |
25 | .flex-a-s {
26 | align-items: stretch;
27 | }
28 |
29 | .flex-w {
30 | flex-wrap: wrap;
31 | }
32 |
33 | .flex-g-1 {
34 | flex-grow: 1;
35 | }
36 |
--------------------------------------------------------------------------------
/CITATION.bib:
--------------------------------------------------------------------------------
1 | @article{lekschas2018hipiler,
2 | author = {Fritz Lekschas and Benjamin Bach and Peter Kerpedjiev and Nils Gehlenborg and Hanspeter Pfister},
3 | title = {HiPiler: Visual Exploration Of Large Genome Interaction Matrices With Interactive Small Multiples},
4 | journal = {IEEE Transactions on Visualization and Computer Graphics},
5 | series = {InfoVis ’17},
6 | year = {2018},
7 | month = {1},
8 | day = {1},
9 | volume = {24},
10 | number = {1},
11 | pages = {522-531},
12 | doi = {10.1109/TVCG.2017.2745978},
13 | }
14 |
--------------------------------------------------------------------------------
/src/views/explore-defaults.js:
--------------------------------------------------------------------------------
1 | export const COLUMN_NAMES = [
2 | 'matrix',
3 | 'details'
4 | ];
5 |
6 | export const COLUMNS = {
7 | matrixWidth: 40,
8 | matrixWidthUnit: '%',
9 | detailsWidth: 15,
10 | detailsWidthUnit: 'rem'
11 | };
12 |
13 | export const CSS = {
14 | details: {
15 | flexBasis: `${COLUMNS.detailsWidth}${COLUMNS.detailsWidthUnit}`
16 | },
17 | matrix: {
18 | flexBasis: `${COLUMNS.matrixWidth}${COLUMNS.matrixWidthUnit}`
19 | }
20 | };
21 |
22 | export default {
23 | COLUMN_NAMES,
24 | COLUMNS
25 | };
26 |
--------------------------------------------------------------------------------
/scripts/cp-prod.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Move CNAME one dir up
4 | mv ../$1/CNAME ../__CNAME__
5 | mv ../$1/sitemap.xml ../__sitemap.xml__
6 |
7 | rm -r ../$1/*
8 | cp -r ghp/* ../$1/
9 |
10 | # Move CNAME back
11 | mv ../__CNAME__ ../$1/CNAME
12 | mv ../__sitemap.xml__ ../$1/sitemap.xml
13 |
14 | cd ../$1/
15 |
16 | # Add symlinks to index.html for HTML5 History
17 | ln -s index.html about.html
18 | ln -s index.html configurator.html
19 | ln -s index.html docs.html
20 | ln -s index.html explore.html
21 | ln -s index.html 404.html
22 |
23 | touch .nojekyll
24 |
--------------------------------------------------------------------------------
/src/utils/arrays-equal.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Check if two arrays are shallowly equal in terms of their values.
3 | *
4 | * @description
5 | * This methid assumes that arrays of primitives are compared.
6 | *
7 | * @param {array} a - First array.
8 | * @param {array} b - Second array,
9 | * @return {boolean} If `true` arrays are equal.
10 | */
11 | export default function arraysEqual (a, b) {
12 | if (!a || !b) { return false; }
13 |
14 | if (a.length !== b.length) { return false; }
15 |
16 | return a.every((element, index) => element === b[index]);
17 | }
18 |
--------------------------------------------------------------------------------
/src/views/docs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/assets/styles/mixins/rotate.scss:
--------------------------------------------------------------------------------
1 | @mixin rotate($degree, $speed, $easing) {
2 | -webkit-animation: spin $speed $easing 1;
3 | -moz-animation: spin $speed $easing 1;
4 | animation: spin $speed $easing 1;
5 | -webkit-transform-origin: 50% 50%;
6 | -moz-transform-origin: 50% 50%;
7 | transform-origin: 50% 50%;
8 |
9 | @-moz-keyframes spin { 100% { -moz-transform: rotate($degree); } }
10 | @-webkit-keyframes spin { 100% { -webkit-transform: rotate($degree); } }
11 | @keyframes spin { 100% { -webkit-transform: rotate($degree); transform:rotate($degree); } }
12 | }
13 |
--------------------------------------------------------------------------------
/src/utils/ping.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Simple ping method.
3 | *
4 | * @param {string} host - Address to be pinged.
5 | * @return {object} Promise resolving to `true` if address is available.
6 | */
7 | export default function ping (host) {
8 | return new Promise((resolve, reject) => {
9 | const img = new Image();
10 |
11 | img.onload = () => { resolve(); };
12 | img.onerror = () => { resolve(); };
13 | img.src = `${host}?cachebreaker=${Date.now()}`;
14 |
15 | setTimeout(() => {
16 | reject(Error('Server not available'));
17 | }, 1500);
18 | });
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/anchor-link.js:
--------------------------------------------------------------------------------
1 | import { Container } from 'aurelia-dependency-injection';
2 | import { Router } from 'aurelia-router';
3 |
4 | const container = Container.instance;
5 | const router = container.get(Router);
6 |
7 | const anchorLink = (path, anchor) => (router.history._hasPushState
8 | ? `${router.generate(path)}#${anchor}`
9 | : `${router.generate(path)}/${anchor}`
10 | );
11 |
12 | export default anchorLink;
13 |
14 | export const getAnchor = (path, anchor) => (router.history._hasPushState
15 | ? anchor
16 | : `${router.generate(path).slice(1)}/${anchor}`
17 | );
18 |
--------------------------------------------------------------------------------
/aurelia_project/tasks/process-markup.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp';
2 | import htmlmin from 'gulp-htmlmin';
3 | import changedInPlace from 'gulp-changed-in-place';
4 | import project from '../aurelia.json';
5 | import {build} from 'aurelia-cli';
6 |
7 | export default function processMarkup() {
8 | return gulp.src(project.markupProcessor.source)
9 | .pipe(changedInPlace({firstPass:true}))
10 | .pipe(htmlmin({
11 | removeComments: true,
12 | collapseWhitespace: true,
13 | minifyCSS: true,
14 | minifyJS: true
15 | }))
16 | .pipe(build.bundle());
17 | }
--------------------------------------------------------------------------------
/src/components/higlass/higlass-defaults.js:
--------------------------------------------------------------------------------
1 | export const CONFIG = {};
2 |
3 | export const FRAGMENTS_HIGHLIGHT = true;
4 |
5 | export const FRAGMENTS_SELECTION = false;
6 |
7 | export const FRAGMENTS_SELECTION_FADE_OUT = false;
8 |
9 | export const FGM_LOCATION_HIGHLIGHT_SIZE = 6;
10 |
11 | export const GRAYSCALE = true;
12 |
13 | export const GRAYSCALE_COLORS = [
14 | '#fff',
15 | '#bbb',
16 | '#777',
17 | '#222'
18 | ];
19 |
20 | export const INTERACTIONS = false;
21 |
22 | export const SELECTION_VIEW = [];
23 |
24 | export const SELECTION_DOMAIN_DISPATCH_DEBOUNCE = 100;
25 |
--------------------------------------------------------------------------------
/src/utils/has-parent.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Test whether a DOM element is the parent of another DOM element.
3 | *
4 | * @param {object} el - Potential child element.
5 | * @param {object} target - Target parent element which is tested to have `el`
6 | * as a child.
7 | * @return {Boolean} If `true` `el` has `target` as a parent.
8 | */
9 | export default function hasParent (el, target) {
10 | let _el = el;
11 |
12 | while (_el !== target && _el.tagName !== 'HTML') {
13 | _el = _el.parentNode;
14 | }
15 |
16 | if (_el === target) {
17 | return true;
18 | }
19 |
20 | return false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/utils/read-json-file.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Read a loaded JSON file.
3 | *
4 | * @param {object} file - File object to be read.
5 | * @return {object} JS object.
6 | */
7 | export default function (file) {
8 | return new Promise((resolve, reject) => {
9 | const reader = new FileReader();
10 |
11 | reader.addEventListener('load', (event) => {
12 | let json;
13 |
14 | try {
15 | json = JSON.parse(event.target.result);
16 | } catch (e) {
17 | reject(e);
18 | }
19 |
20 | resolve(json);
21 | });
22 |
23 | reader.readAsText(file);
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/aurelia_project/tasks/build.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp';
2 | import transpile from './transpile';
3 | import processMarkup from './process-markup';
4 | import processCSS from './process-css';
5 | import {build} from 'aurelia-cli';
6 | import project from '../aurelia.json';
7 |
8 | export default gulp.series(
9 | readProjectConfiguration,
10 | gulp.parallel(
11 | transpile,
12 | processMarkup,
13 | processCSS
14 | ),
15 | writeBundles
16 | );
17 |
18 | function readProjectConfiguration() {
19 | return build.src(project);
20 | }
21 |
22 | function writeBundles() {
23 | return build.dest();
24 | }
25 |
--------------------------------------------------------------------------------
/src/views/about.js:
--------------------------------------------------------------------------------
1 | // Utils etc.
2 | import anchorLink, { getAnchor } from 'utils/anchor-link';
3 | import scrollToAnchor from 'utils/scroll-to-anchor';
4 |
5 |
6 | export class About {
7 | constructor () {
8 | this.anchorLink = anchorLink;
9 | this.getAnchor = getAnchor;
10 | }
11 |
12 | /* ----------------------- Aurelia-specific methods ----------------------- */
13 |
14 | activate (urlParams) {
15 | if (urlParams.anchor) {
16 | this.anchor = `/about/${urlParams.anchor}`;
17 | }
18 | }
19 |
20 | attached () {
21 | if (this.anchor) {
22 | scrollToAnchor(this.anchor);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/assets/styles/higlass.scss:
--------------------------------------------------------------------------------
1 | higlass {
2 | display: block;
3 | margin: 2rem 0.5rem 0 0;
4 |
5 | .genome-position-search {
6 | .input-group {
7 | display: flex;
8 |
9 | div:first-child {
10 | flex-grow: 1;
11 | }
12 | }
13 |
14 | .search-bar:focus {
15 | outline: 0;
16 | }
17 |
18 | .btn {
19 | width: 30px;
20 | height: 30px;
21 |
22 | &::before {
23 | content: 'Go'
24 | }
25 |
26 | &.btn-sm {
27 | padding: 5px;
28 | }
29 | }
30 | }
31 | }
32 |
33 | .matrix-full-screen-view higlass {
34 | margin-top: 0;
35 | padding-top: 0;
36 | }
37 |
--------------------------------------------------------------------------------
/src/utils/worker-clusterfck.js:
--------------------------------------------------------------------------------
1 | /* eslint no-var:0, prefer-arrow-callback:0, object-shorthand:0, no-undef:0 */
2 |
3 | self.onmessage = function (event) {
4 | var error;
5 | var clusters = [];
6 | var kmeans = new clusterfck.Kmeans(event.data.centroids || []);
7 |
8 | try {
9 | clusters = kmeans.cluster(event.data.data, event.data.numClusters || 3);
10 | } catch (e) {
11 | error = e.message;
12 | }
13 |
14 | clusters = clusters.map(function (cluster) {
15 | return cluster.map(function (entry) {
16 | return entry.id;
17 | });
18 | });
19 |
20 | self.postMessage({
21 | clusters: clusters,
22 | error: error
23 | });
24 | };
25 |
--------------------------------------------------------------------------------
/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | http://hipiler.higlass.io/
5 | 2018-06-11
6 | monthly
7 | 0.5
8 |
9 |
10 | http://hipiler.higlass.io/about
11 | 2018-06-11
12 | monthly
13 | 0.5
14 |
15 |
16 | http://hipiler.higlass.io/research
17 | 2018-06-11
18 | monthly
19 | 0.5
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/views/explore-reducers.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import { UPDATE_WIDTH } from 'views/explore-actions';
4 | import { COLUMNS } from 'views/explore-defaults';
5 |
6 | import fragments from 'components/fragments/fragments-reducers';
7 | import higlass from 'components/higlass/higlass-reducers';
8 |
9 | export function columns (state = { ...COLUMNS }, action) {
10 | switch (action.type) {
11 | case UPDATE_WIDTH:
12 | return {
13 | ...state,
14 | [`${action.payload.column}Width`]: action.payload.width
15 | };
16 | default:
17 | return state;
18 | }
19 | }
20 |
21 | export default combineReducers({
22 | columns,
23 | fragments,
24 | higlass
25 | });
26 |
--------------------------------------------------------------------------------
/src/views/not-found.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
Oh no! This shouldn't have happened.
10 |
11 |
12 | The page you are trying to access doesn't exist. May we kindly ask you to check the URL for typos or to send us an angry message complaining about the bug?
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/utils/halt-resume.js:
--------------------------------------------------------------------------------
1 | // Aurelia
2 | import { LogManager } from 'aurelia-framework';
3 |
4 | const logger = LogManager.getLogger('higlass');
5 |
6 | /**
7 | * Simple stack holder that fires all halted functions once it's resumed.
8 | */
9 | export default function HaltResume () {
10 | const stack = [];
11 |
12 | const halt = function (f, params) {
13 | stack.push([f, params]);
14 | };
15 |
16 | const resume = function () {
17 | let entry = stack.pop();
18 | while (entry) {
19 | try {
20 | entry[0](...entry[1]);
21 | } catch (e) {
22 | logger.error(`Could not resume function: ${e}`);
23 | }
24 | entry = stack.pop();
25 | }
26 | };
27 |
28 | return { halt, resume };
29 | }
30 |
--------------------------------------------------------------------------------
/src/app-reducer.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 |
3 | import { RESET_STATE } from 'app-actions';
4 | import explore from 'views/explore-reducers';
5 |
6 | /**
7 | * The global / app reducer
8 | *
9 | * Add all all the high level reducers here. The state hierarchy is as follows
10 | * ```
11 | * {
12 | *
: {
13 | * ,
14 | * ...
15 | * },
16 | * global: {
17 | *
18 | * }
19 | * }
20 | * ````
21 | */
22 | const appReducer = combineReducers({
23 | // Views
24 | explore
25 | // Components
26 | });
27 |
28 | const rootReducer = (state, action) => {
29 | if (action.type === RESET_STATE) {
30 | state = undefined;
31 | }
32 |
33 | return appReducer(state, action);
34 | };
35 |
36 | export default rootReducer;
37 |
--------------------------------------------------------------------------------
/src/components/svg-icon/svg-icon.js:
--------------------------------------------------------------------------------
1 | // Aurelia
2 | import {
3 | bindable,
4 | bindingMode
5 | } from 'aurelia-framework';
6 |
7 | import icons from 'configs/icons';
8 |
9 | export class SvgIcon {
10 | @bindable({ defaultBindingMode: bindingMode.oneWay }) iconId; // eslint-disable-line
11 | @bindable({ defaultBindingMode: bindingMode.oneWay }) iconMirrorH; // eslint-disable-line
12 | @bindable({ defaultBindingMode: bindingMode.oneWay }) iconMirrorV; // eslint-disable-line
13 |
14 | constructor () {
15 | this.icon = {
16 | viewBox: '0 0 16 16',
17 | fillRule: '',
18 | svg: ''
19 | };
20 | }
21 |
22 | attached () {
23 | const id = this.iconId.toUpperCase().replace(/-/g, '_');
24 | this.icon = icons[id] ? icons[id] : icons.WARNING;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/assets/styles/grid.scss:
--------------------------------------------------------------------------------
1 | @import 'colors';
2 | @import 'transitions';
3 |
4 | .grid {
5 | position: absolute;
6 | z-index: 1;
7 | overflow: hidden;
8 | display: none;
9 | opacity: 0;
10 | transition: opacity 0 $ease-in-out-cubic;
11 |
12 | &.full-dim {
13 | display: block;
14 | bottom: 100%;
15 | }
16 |
17 | &.is-active {
18 | display: block;
19 | opacity: 1;
20 | transition: opacity $transition-fast $ease-in-out-cubic;
21 |
22 | &.full-dim {
23 | bottom: 0;
24 | }
25 | }
26 |
27 | .column {
28 | position: relative;
29 | border-left: 1px solid $primary;
30 |
31 | &:first-child {
32 | border-left: 0;
33 | }
34 | }
35 |
36 | .row {
37 | position: relative;
38 | border-top: 1px solid $primary;
39 |
40 | &:first-child {
41 | border-top: 0;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { LogManager } from 'aurelia-framework';
2 | import { ConsoleAppender } from 'aurelia-logging-console';
3 |
4 | //Configure Bluebird Promises.
5 | if (Promise.config) {
6 | Promise.config({
7 | warnings: {
8 | wForgottenReturn: false
9 | }
10 | });
11 | }
12 |
13 | export function configure (aurelia) {
14 | aurelia.use
15 | .standardConfiguration()
16 | .plugin('aurelia-validation')
17 | .feature('resources');
18 |
19 | if (!window.hipilerConfig) {
20 | window.hipilerConfig = {};
21 | }
22 |
23 | if (window.hipilerConfig.debug) {
24 | LogManager.addAppender(new ConsoleAppender());
25 | LogManager.setLevel(LogManager.logLevel.debug);
26 | }
27 |
28 | if (window.hipilerConfig.testing) {
29 | aurelia.use.plugin('aurelia-testing');
30 | }
31 |
32 | aurelia.start().then(() => aurelia.setRoot());
33 | }
34 |
35 | export default {
36 | configure
37 | };
38 |
--------------------------------------------------------------------------------
/src/assets/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import '../../../node_modules/higlass/dist/hglib.css';
2 |
3 | #hipiler {
4 | height: 100vh;
5 | margin: 0;
6 | padding: 0;
7 | background: #fff;
8 |
9 | // Mixins
10 | @import 'mixins/truncate';
11 |
12 | // Components
13 | @import 'colors';
14 | @import 'transitions';
15 | @import 'flexbox';
16 | @import 'range';
17 | @import 'multi-select';
18 | @import 'range-select';
19 | @import 'svg-icon';
20 | @import 'axis';
21 | @import 'grid';
22 | @import 'pile-context-menu';
23 | @import 'dialog';
24 | @import 'columns';
25 |
26 | // Base style
27 | @import 'base';
28 |
29 | // Global app styles
30 | @import 'app';
31 |
32 | // Specific component styles
33 | @import 'navigation';
34 | @import 'home';
35 | @import 'about';
36 | @import 'docs';
37 | @import 'explore';
38 | @import 'configurator';
39 | @import 'higlass';
40 | @import 'fragments';
41 | @import 'pile-details';
42 | }
43 |
--------------------------------------------------------------------------------
/src/utils/throttle.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Throttle a function call.
3 | *
4 | * @description
5 | * Function calls are delayed by `wait` milliseconds but at least every `wait`
6 | * milliseconds a call is triggered.
7 | *
8 | * @param {functiom} func - Function to be debounced
9 | * @param {number} wait - Number of milliseconds to debounce the function call.
10 | * @param {boolean} immediate - If `true` function is not debounced.
11 | * @return {functiomn} Throttled function.
12 | */
13 | export default function throttle (func, wait = 250, immediate = false) {
14 | let last;
15 | let deferTimer;
16 |
17 | const throttled = (...args) => {
18 | const now = Date.now();
19 |
20 | if (last && now < last + wait) {
21 | clearTimeout(deferTimer);
22 |
23 | deferTimer = setTimeout(() => {
24 | last = now;
25 | func(...args);
26 | }, wait);
27 | } else {
28 | last = now;
29 | func(...args);
30 | }
31 | };
32 |
33 | return throttled;
34 | }
35 |
--------------------------------------------------------------------------------
/src/components/spinner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/components/chartlet/chartlet.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 |
16 | ${index}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
${range[1]}
24 |
${range[0]}
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/components/footer-main/footer-main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
28 |
29 |
--------------------------------------------------------------------------------
/src/utils/query-obj.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Simple object query function.
3 | *
4 | * @description
5 | * Assuming that you have a deeply nested object o:
6 | * ```
7 | * const o = {
8 | * a: {
9 | * b: {
10 | * c: 'HiPiler rocks!'
11 | * }
12 | * }
13 | * }
14 | * ```
15 | * This object can be safely queried as follows:
16 | * ```
17 | * queryObj(o, ['a', 'b', 'c']) // 'HiPiler rocks!'
18 | * queryObj(o, ['a', 'b', 'd'], 'Oh no!') // 'Oh no!'
19 | * ```
20 | *
21 | * @param {object} obj - Object to be queried.
22 | * @param {array} queries - Array of queries.
23 | * @param {*} defVal - Default value to be returned when query fails.
24 | * @return {*} Value of the queried property or `undefined`.
25 | */
26 | export default function queryObj (obj, queries, defVal) {
27 | try {
28 | const query = queries[0];
29 | const nextQueries = queries.slice(1);
30 |
31 | if (nextQueries.length) {
32 | return queryObj(obj[query], nextQueries);
33 | }
34 |
35 | return obj[query];
36 | } catch (e) {
37 | return defVal;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/utils/debounce.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Debounce a function call.
3 | *
4 | * @description
5 | * Function calls are delayed by `wait` milliseconds and only one out of
6 | * multiple function calls is executed.
7 | *
8 | * @param {functiom} func - Function to be debounced
9 | * @param {number} wait - Number of milliseconds to debounce the function call.
10 | * @param {boolean} immediate - If `true` function is not debounced.
11 | * @return {functiomn} Debounced function.
12 | */
13 | export default function debounce (func, wait, immediate) {
14 | let timeout;
15 |
16 | const debounced = (...args) => {
17 | const later = () => {
18 | timeout = null;
19 | if (!immediate) {
20 | func(...args);
21 | }
22 | };
23 |
24 | const callNow = immediate && !timeout;
25 | clearTimeout(timeout);
26 | timeout = setTimeout(later, wait);
27 |
28 | if (callNow) {
29 | func(...args);
30 | }
31 | };
32 |
33 | debounced.cancel = () => {
34 | clearTimeout(timeout);
35 | timeout = null;
36 | };
37 |
38 | return debounced;
39 | }
40 |
--------------------------------------------------------------------------------
/src/assets/styles/configurator.scss:
--------------------------------------------------------------------------------
1 | @import 'colors';
2 | @import 'transitions';
3 |
4 | .configurator {
5 | h4 {
6 | margin: 1.5rem 0 0.5rem 0;
7 | color: $black;
8 | font-weight: bold;
9 | }
10 |
11 | label {
12 | span {
13 | font-size: 0.8em;
14 | }
15 |
16 | input {
17 | margin-bottom: 0.25rem;
18 | padding: 0.25rem;
19 | font-size: 0.9em;
20 |
21 | &[type=text],
22 | &[type=number] {
23 | border: 1px solid $gray-lightest;
24 | border-radius: 0.25rem;
25 | }
26 |
27 | &[type=checkbox] {
28 | height: 1.5rem;
29 | }
30 | }
31 | }
32 |
33 | .info-panel {
34 | margin-bottom: 0.5rem;
35 | }
36 |
37 | .configurator-main > .column-1-2 {
38 | &:first-child {
39 | padding-right: 0.5rem;
40 | border-right: 1px solid $gray-lightest;
41 | }
42 | &:last-child {
43 | padding-left: 0.5rem;
44 | }
45 | }
46 |
47 | .snippets-settings .column-1-3 {
48 | padding-left: 0.5rem;
49 |
50 | &.no-p {
51 | padding-left: 0;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/utils/worker-tsne.js:
--------------------------------------------------------------------------------
1 | /* eslint no-var:0, prefer-arrow-callback:0, no-undef:0 */
2 |
3 | self.onmessage = function (event) {
4 | var msg = event.data;
5 | var currcost = 100;
6 | var results;
7 |
8 | var model = new TSNE({
9 | dim: msg.dim || 2,
10 | perplexity: msg.perplexity || 25.0,
11 | earlyExaggeration: msg.earlyExaggeration || 4.0,
12 | learningRate: msg.learningRate || 25.0,
13 | nIter: msg.nIter || 1000,
14 | metric: msg.metric || 'euclidean'
15 | });
16 |
17 | model.init({
18 | data: msg.data,
19 | type: 'dense'
20 | });
21 |
22 | model.on('progressData', function (pos) {
23 | self.postMessage({ pos: model.getOutputScaled() });
24 | });
25 |
26 | model.on('progressIter', function (iter) {
27 | currcost = (currcost * 0.9) + iter[1];
28 | self.postMessage({
29 | iterations: iter[0],
30 | cost: iter[1],
31 | stop: currcost < 20
32 | });
33 | });
34 |
35 | results = model.run();
36 |
37 | self.postMessage({
38 | err: results[0],
39 | iterations: results[1],
40 | stop: true
41 | });
42 | };
43 |
--------------------------------------------------------------------------------
/aurelia_project/tasks/transpile.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp';
2 | import changedInPlace from 'gulp-changed-in-place';
3 | import plumber from 'gulp-plumber';
4 | import babel from 'gulp-babel';
5 | import sourcemaps from 'gulp-sourcemaps';
6 | import notify from 'gulp-notify';
7 | import rename from 'gulp-rename';
8 | import project from '../aurelia.json';
9 | import {CLIOptions, build} from 'aurelia-cli';
10 |
11 | function configureEnvironment() {
12 | let env = CLIOptions.getEnvironment();
13 |
14 | return gulp.src(`aurelia_project/environments/${env}.js`)
15 | .pipe(changedInPlace({firstPass: true}))
16 | .pipe(rename('environment.js'))
17 | .pipe(gulp.dest(project.paths.root));
18 | }
19 |
20 | function buildJavaScript() {
21 | return gulp.src(project.transpiler.source)
22 | .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
23 | .pipe(changedInPlace({firstPass: true}))
24 | .pipe(sourcemaps.init())
25 | .pipe(babel(project.transpiler.options))
26 | .pipe(build.bundle());
27 | }
28 |
29 | export default gulp.series(
30 | configureEnvironment,
31 | buildJavaScript
32 | );
33 |
--------------------------------------------------------------------------------
/src/components/fragments/pile-menu.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
28 |
29 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '8'
4 | branches:
5 | only:
6 | - master
7 | - develop
8 | - "/^v.*$/"
9 | before_install:
10 | - export CHROME_BIN=chromium-browser
11 | services:
12 | - xvfb
13 | before_script:
14 | - npm install
15 | before_deploy:
16 | - npm run prerelease
17 | deploy:
18 | provider: releases
19 | api_key:
20 | secure: wccs2hgDobPEwKUqzSIWGbDUGUFrGaLMu3NFTttgVgDsXQnqEXBg7XqTHML6yW4vdk1ug/frKvKrasyvTxNZ16kFQWNutUL8wiyoUc9dkYyCOt1oUdH9rv8L4ZhJPC/XQ+Y5A9Yi/V+Imcw+P0woukMQYsbADWFvlJh4FF2ObDklxghUp/KbeaKANrqlRg99JvAcJE0JdADuAbtyJUBzMJPqtaFdGW6q8sJzwdSY1tP18Hk8YWZl+kDErRQW/KA/YMDAkf0S009eeLcy3CZkTT1OcO3TuddboevfgxQnXufbEmDg+O4/gzZN9fSSllBnDM+UUyb4ebzlKmPQmXHcH7ab00IKyNfGUNQJ2g/tfHBPLnuL3k2+NKtLzXASu2Lbkp11Ycsgcb3XGE93Nzf/qaPj7KOz88WpWohTs2y3xbSK6i9xn4bdotL4Af9OgVg6NCNoH5z9NLgJO+bUEdyxXXpCghR5iSGu3bir898PU7pLCU8218VNMGRCREptUneYv58X1CQmqdtzyszfx0UkM8sw3TZS7zJUciEulaJElV7Ab60Cd3JqMgdyL2QpQa9sgqpFgZuVko1RANqFgdCLuk5tubFuBS4ZM68sNx+VB5iqmC7f25ltaviVjI7i+C12ZTpbI3U6l2tpWnW15t3DjZPRHKWzVtwerMAnVmfWEBs=
21 | file: build.zip
22 | skip_cleanup: true
23 | on:
24 | repo: flekschas/hipiler
25 | tags: true
26 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "extends": [
7 | "airbnb/base",
8 | "./node_modules/aurelia-tools/.eslintrc.json"
9 | ],
10 | "globals": {
11 | "describe": false,
12 | "expect": false,
13 | "it": false
14 | },
15 | "parser": "babel-eslint",
16 | "rules": {
17 | "new-cap": ["error", { "capIsNew": false }],
18 | "class-methods-use-this": 0,
19 | "function-paren-newline": 0,
20 | "import/no-extraneous-dependencies": 0,
21 | "import/prefer-default-export": 0,
22 | "import/extensions": 0,
23 | "import/no-unresolved": 0,
24 | "indent": ["error", 2, { "SwitchCase": 1 }],
25 | "no-console": ["error", { "allow": ["warn", "error"] }],
26 | "no-mixed-operators": ["error", { "allowSamePrecedence": true }],
27 | "no-multi-spaces": ["error", { "ignoreEOLComments": true }],
28 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
29 | "no-restricted-globals": 0,
30 | "no-undef": ["error"],
31 | "object-curly-newline": 0,
32 | "prefer-destructuring": 0,
33 | "space-before-function-paren": ["error", "always"]
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/range-select/range-select.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
${from}
4 |
24 |
${to}
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 The President and Fellows of Harvard College
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/aurelia_project/generators/value-converter.js:
--------------------------------------------------------------------------------
1 | import {inject} from 'aurelia-dependency-injection';
2 | import {Project, ProjectItem, CLIOptions, UI} from 'aurelia-cli';
3 |
4 | @inject(Project, CLIOptions, UI)
5 | export default class ValueConverterGenerator {
6 | constructor(project, options, ui) {
7 | this.project = project;
8 | this.options = options;
9 | this.ui = ui;
10 | }
11 |
12 | execute() {
13 | return this.ui
14 | .ensureAnswer(this.options.args[0], 'What would you like to call the value converter?')
15 | .then(name => {
16 | let fileName = this.project.makeFileName(name);
17 | let className = this.project.makeClassName(name);
18 |
19 | this.project.valueConverters.add(
20 | ProjectItem.text(`${fileName}.js`, this.generateSource(className))
21 | );
22 |
23 | return this.project.commitChanges()
24 | .then(() => this.ui.log(`Created ${fileName}.`));
25 | });
26 | }
27 |
28 | generateSource(className) {
29 | return `export class ${className}ValueConverter {
30 | toView(value) {
31 |
32 | }
33 |
34 | fromView(value) {
35 |
36 | }
37 | }
38 |
39 | `;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/utils/deep-clone.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Deep clone an object
3 | *
4 | * @param {object} target - Target object or undefined to create a new object.
5 | * @param {object} source - Object to be cloned.
6 | * @return {object} Cloned `source` object
7 | */
8 | function extend (target, source) {
9 | if (source === null || typeof source !== 'object') {
10 | return source;
11 | }
12 |
13 | if (source.constructor !== Object && source.constructor !== Array) {
14 | return source;
15 | }
16 |
17 | if (
18 | source.constructor === Date ||
19 | source.constructor === RegExp ||
20 | source.constructor === Function ||
21 | source.constructor === String ||
22 | source.constructor === Number ||
23 | source.constructor === Boolean
24 | ) {
25 | return new source.constructor(source);
26 | }
27 |
28 | target = target || new source.constructor();
29 |
30 | Object.keys(source).forEach((attr) => {
31 | target[attr] = typeof target[attr] === 'undefined' ?
32 | extend(undefined, source[attr]) : target[attr];
33 | });
34 |
35 | return target;
36 | }
37 |
38 | export default function (source) {
39 | let target;
40 | return extend(target, source);
41 | }
42 |
--------------------------------------------------------------------------------
/aurelia_project/generators/binding-behavior.js:
--------------------------------------------------------------------------------
1 | import {inject} from 'aurelia-dependency-injection';
2 | import {Project, ProjectItem, CLIOptions, UI} from 'aurelia-cli';
3 |
4 | @inject(Project, CLIOptions, UI)
5 | export default class BindingBehaviorGenerator {
6 | constructor(project, options, ui) {
7 | this.project = project;
8 | this.options = options;
9 | this.ui = ui;
10 | }
11 |
12 | execute() {
13 | return this.ui
14 | .ensureAnswer(this.options.args[0], 'What would you like to call the binding behavior?')
15 | .then(name => {
16 | let fileName = this.project.makeFileName(name);
17 | let className = this.project.makeClassName(name);
18 |
19 | this.project.bindingBehaviors.add(
20 | ProjectItem.text(`${fileName}.js`, this.generateSource(className))
21 | );
22 |
23 | return this.project.commitChanges()
24 | .then(() => this.ui.log(`Created ${fileName}.`));
25 | });
26 | }
27 |
28 | generateSource(className) {
29 | return `export class ${className}BindingBehavior {
30 | bind(binding, source) {
31 |
32 | }
33 |
34 | unbind(binding, source) {
35 |
36 | }
37 | }
38 |
39 | `
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/aurelia_project/generators/attribute.js:
--------------------------------------------------------------------------------
1 | import {inject} from 'aurelia-dependency-injection';
2 | import {Project, ProjectItem, CLIOptions, UI} from 'aurelia-cli';
3 |
4 | @inject(Project, CLIOptions, UI)
5 | export default class AttributeGenerator {
6 | constructor(project, options, ui) {
7 | this.project = project;
8 | this.options = options;
9 | this.ui = ui;
10 | }
11 |
12 | execute() {
13 | return this.ui
14 | .ensureAnswer(this.options.args[0], 'What would you like to call the custom attribute?')
15 | .then(name => {
16 | let fileName = this.project.makeFileName(name);
17 | let className = this.project.makeClassName(name);
18 |
19 | this.project.attributes.add(
20 | ProjectItem.text(`${fileName}.js`, this.generateSource(className))
21 | );
22 |
23 | return this.project.commitChanges()
24 | .then(() => this.ui.log(`Created ${fileName}.`));
25 | });
26 | }
27 |
28 | generateSource(className) {
29 | return `import {inject} from 'aurelia-framework';
30 |
31 | @inject(Element)
32 | export class ${className}CustomAttribute {
33 | constructor(element) {
34 | this.element = element;
35 | }
36 |
37 | valueChanged(newValue, oldValue) {
38 |
39 | }
40 | }
41 |
42 | `;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/navigation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/app-actions.js:
--------------------------------------------------------------------------------
1 | import {
2 | updateConfig as updateHglConfig
3 | } from 'components/higlass/higlass-actions';
4 |
5 | import {
6 | setDataDims,
7 | setDataPadding,
8 | setDataPercentile,
9 | setDataIgnoreDiags,
10 | selectPile,
11 | updateConfig as updateFgmConfig
12 | } from 'components/fragments/fragments-actions';
13 |
14 | import FgmState from 'components/fragments/fragments-state';
15 |
16 | export const UPDATE_CONFIG = 'UPDATE_CONFIG';
17 |
18 | export const updateConfigs = config => (dispatch) => {
19 | // Reset entire fgm state
20 | FgmState.reset();
21 |
22 | dispatch(selectPile(null));
23 | dispatch(updateHglConfig(config.hgl));
24 | dispatch(updateFgmConfig(config.fgm));
25 |
26 | if (config.fgm.fragmentsDims) {
27 | dispatch(setDataDims(config.fgm.fragmentsDims));
28 | }
29 | if (config.fgm.fragmentsPadding) {
30 | dispatch(setDataPadding(config.fgm.fragmentsPadding));
31 | }
32 | if (config.fgm.fragmentsPercentile) {
33 | dispatch(setDataPercentile(config.fgm.fragmentsPercentile));
34 | }
35 | if (config.fgm.fragmentsIgnoreDiags) {
36 | dispatch(setDataIgnoreDiags(config.fgm.fragmentsIgnoreDiags));
37 | }
38 | };
39 |
40 |
41 | export const RESET_STATE = 'RESET_STATE';
42 |
43 | export const resetState = () => ({
44 | type: RESET_STATE,
45 | payload: undefined
46 | });
47 |
--------------------------------------------------------------------------------
/src/configs/app.js:
--------------------------------------------------------------------------------
1 | export const name = 'HiPiler';
2 |
3 | export const routes = [
4 | {
5 | route: '',
6 | href: '/',
7 | name: 'home',
8 | title: 'Home',
9 | moduleId: 'views/home',
10 | nav: false
11 | },
12 | // {
13 | // route: 'configurator/:anchor?',
14 | // href: '/configurator',
15 | // name: 'configurator',
16 | // title: 'Configurator',
17 | // moduleId: 'views/configurator',
18 | // nav: true,
19 | // icon: 'cog'
20 | // },
21 | {
22 | route: 'about/:anchor?',
23 | href: '/about',
24 | name: 'about',
25 | title: 'About',
26 | moduleId: 'views/about',
27 | nav: true,
28 | icon: 'info'
29 | },
30 | {
31 | route: 'docs/:anchor?/:anchor2?',
32 | href: '/docs',
33 | name: 'docs',
34 | title: 'Documentation',
35 | moduleId: 'views/docs',
36 | nav: true,
37 | navTitle: 'Docs',
38 | icon: 'help'
39 | },
40 | {
41 | route: 'explore',
42 | href: '/explore',
43 | name: 'explore',
44 | title: 'Explore',
45 | moduleId: 'views/explore',
46 | nav: false
47 | }
48 | ];
49 |
50 | export const transition = {
51 | veryFast: 150,
52 | fast: 200,
53 | semiFast: 250,
54 | normal: 330,
55 | slow: 660,
56 | slowest: 1000
57 | };
58 |
59 | export default {
60 | routes,
61 | transition
62 | };
63 |
--------------------------------------------------------------------------------
/aurelia_project/generators/task.js:
--------------------------------------------------------------------------------
1 | import {inject} from 'aurelia-dependency-injection';
2 | import {Project, ProjectItem, CLIOptions, UI} from 'aurelia-cli';
3 |
4 | @inject(Project, CLIOptions, UI)
5 | export default class TaskGenerator {
6 | constructor(project, options, ui) {
7 | this.project = project;
8 | this.options = options;
9 | this.ui = ui;
10 | }
11 |
12 | execute() {
13 | return this.ui
14 | .ensureAnswer(this.options.args[0], 'What would you like to call the task?')
15 | .then(name => {
16 | let fileName = this.project.makeFileName(name);
17 | let functionName = this.project.makeFunctionName(name);
18 |
19 | this.project.tasks.add(
20 | ProjectItem.text(`${fileName}.js`, this.generateSource(functionName))
21 | );
22 |
23 | return this.project.commitChanges()
24 | .then(() => this.ui.log(`Created ${fileName}.`));
25 | });
26 | }
27 |
28 | generateSource(functionName) {
29 | return `import gulp from 'gulp';
30 | import changed from 'gulp-changed';
31 | import project from '../aurelia.json';
32 |
33 | export default function ${functionName}() {
34 | return gulp.src(project.paths.???)
35 | .pipe(changed(project.paths.output, {extension: '.???'}))
36 | .pipe(gulp.dest(project.paths.output));
37 | }
38 |
39 | `;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/views/configurator.js:
--------------------------------------------------------------------------------
1 | import { inject } from 'aurelia-dependency-injection';
2 | import { Validator, ValidationControllerFactory, ValidationRules } from 'aurelia-validation';
3 |
4 | // Utils etc.
5 | import anchorLink, { getAnchor } from 'utils/anchor-link';
6 | import scrollToAnchor from 'utils/scroll-to-anchor';
7 |
8 |
9 | @inject(Validator, ValidationControllerFactory)
10 | export class Configurator {
11 | constructor (validator, validationControllerFactory) {
12 | this.anchorLink = anchorLink;
13 | this.getAnchor = getAnchor;
14 |
15 | this.controller = validationControllerFactory.createForCurrentScope(validator);
16 | this.controller.subscribe(event => this.validated());
17 |
18 | ValidationRules
19 | .ensure('fragmentsServer')
20 | .displayName('Server URL')
21 | .matches(/\d{3}-\d{2}-\d{4}/)
22 | .withMessage('My ASS')
23 | .on(this);
24 | }
25 |
26 | /* ----------------------- Aurelia-specific methods ----------------------- */
27 |
28 | activate (urlParams) {
29 | if (urlParams.anchor) {
30 | this.anchor = `/about/${urlParams.anchor}`;
31 | }
32 | }
33 |
34 | attached () {
35 | if (this.anchor) {
36 | scrollToAnchor(this.anchor);
37 | }
38 | }
39 |
40 | /* ----------------------- Custom methods ----------------------- */
41 |
42 | validated () {}
43 | }
44 |
--------------------------------------------------------------------------------
/src/assets/styles/dialog.scss:
--------------------------------------------------------------------------------
1 | @import 'colors';
2 | @import 'transitions';
3 |
4 | dialog {
5 | z-index: 99;
6 | width: auto;
7 | height: 0;
8 | margin: 0;
9 | padding: 0;
10 | border: 0;
11 | background: transparentize($black, 1);
12 | transition: background $transition-very-fast $ease-in-out-cubic;
13 |
14 | &.full-dim {
15 | position: fixed;
16 | }
17 |
18 | &.is-active {
19 | height: auto;
20 | background: transparentize($black, 0.5);
21 | }
22 |
23 | .dialog-window {
24 | max-width: 75%;
25 | height: 0;
26 | color: $gray-darker;
27 | padding: 1rem;
28 | border-radius: 0.25rem;
29 | background: $white;
30 | opacity: 0;
31 | transform: scale(0);
32 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05),
33 | 0 1px 3px 0 rgba(0, 0, 0, 0.075),
34 | 0 3px 9px 0 rgba(0, 0, 0, 0.075);
35 | transition: opacity $transition-very-fast $ease-in-out-cubic,
36 | transform $transition-very-fast $ease-in-out-cubic;
37 |
38 | &.is-open {
39 | height: auto;
40 | opacity: 1;
41 | transform: scale(1);
42 | }
43 | }
44 |
45 | .button {
46 | font-size: inherit;
47 | color: $gray-dark;
48 | }
49 |
50 | .button:first-child {
51 | margin-right: 0.5rem;
52 | }
53 |
54 | .button:last-child {
55 | margin-left: 0.5rem;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/assets/styles/navigation.scss:
--------------------------------------------------------------------------------
1 | @import 'colors';
2 |
3 | .primary-nav {
4 | text-transform: uppercase;
5 | line-height: 3.25rem;
6 |
7 | li {
8 | position: relative;
9 | margin: 0 0.25rem;
10 |
11 | &.is-active {
12 | &::before {
13 | position: absolute;
14 | content: '';
15 | right: 0;
16 | bottom: 0;
17 | left: 0;
18 | height: 3px;
19 | background: $primary;
20 | }
21 |
22 | a {
23 | color: $black;
24 | }
25 | }
26 |
27 | &:last-child {
28 | padding-right: 0;
29 | margin-right: 0;
30 | }
31 | }
32 |
33 | a {
34 | height: 3rem;
35 | padding: 0 0.25rem;
36 | border-bottom: 0;
37 | }
38 |
39 | svg-icon {
40 | height: 100%;
41 | padding: 0 0.125rem;
42 | }
43 |
44 | svg {
45 | width: 100%;
46 | height: 100%;
47 | }
48 |
49 | .is-icon-only {
50 | width: 2.5rem;
51 | height: 3rem;
52 | padding: 0 0.5rem;
53 |
54 | a {
55 | display: block;
56 | }
57 | }
58 |
59 | .is-left-separated {
60 | position: relative;
61 | margin-left: 0.25rem;
62 |
63 | &::before {
64 | position: absolute;
65 | content: '';
66 | top: 0.25rem;
67 | left: 0;
68 | bottom: 0.25rem;
69 | width: 1px;
70 | background: $gray-lighter;
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/aurelia_project/generators/element.js:
--------------------------------------------------------------------------------
1 | import {inject} from 'aurelia-dependency-injection';
2 | import {Project, ProjectItem, CLIOptions, UI} from 'aurelia-cli';
3 |
4 | @inject(Project, CLIOptions, UI)
5 | export default class ElementGenerator {
6 | constructor(project, options, ui) {
7 | this.project = project;
8 | this.options = options;
9 | this.ui = ui;
10 | }
11 |
12 | execute() {
13 | return this.ui
14 | .ensureAnswer(this.options.args[0], 'What would you like to call the custom element?')
15 | .then(name => {
16 | let fileName = this.project.makeFileName(name);
17 | let className = this.project.makeClassName(name);
18 |
19 | this.project.elements.add(
20 | ProjectItem.text(`${fileName}.js`, this.generateJSSource(className)),
21 | ProjectItem.text(`${fileName}.html`, this.generateHTMLSource(className))
22 | );
23 |
24 | return this.project.commitChanges()
25 | .then(() => this.ui.log(`Created ${fileName}.`));
26 | });
27 | }
28 |
29 | generateJSSource(className) {
30 | return `import {bindable} from 'aurelia-framework';
31 |
32 | export class ${className} {
33 | @bindable value;
34 |
35 | valueChanged(newValue, oldValue) {
36 |
37 | }
38 | }
39 |
40 | `;
41 | }
42 |
43 | generateHTMLSource(className) {
44 | return `
45 | \${value}
46 | `;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/components/multi-select/multi-select.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 | ${option.name}
11 |
12 |
13 |
14 |
15 |
16 |
26 |
27 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/components/higlass/higlass-actions.js:
--------------------------------------------------------------------------------
1 | export const SET_GRAYSCALE = 'SET_GRAYSCALE';
2 |
3 | export const setGrayscale = grayscale => ({
4 | type: SET_GRAYSCALE,
5 | payload: { grayscale }
6 | });
7 |
8 | export const SET_FRAGMENTS_HIGHLIGHT = 'SET_FRAGMENTS_HIGHLIGHT';
9 |
10 | export const setFragmentsHighlight = highlight => ({
11 | type: SET_FRAGMENTS_HIGHLIGHT,
12 | payload: { highlight }
13 | });
14 |
15 | export const SET_FRAGMENTS_SELECTION = 'SET_FRAGMENTS_SELECTION';
16 |
17 | export const setFragmentsSelection = selection => ({
18 | type: SET_FRAGMENTS_SELECTION,
19 | payload: { selection }
20 | });
21 |
22 | export const SET_FRAGMENTS_SELECTION_FADE_OUT = 'SET_FRAGMENTS_SELECTION_FADE_OUT';
23 |
24 | export const setFragmentsSelectionFadeOut = selectionFadeOut => ({
25 | type: SET_FRAGMENTS_SELECTION_FADE_OUT,
26 | payload: { selectionFadeOut }
27 | });
28 |
29 | export const SET_INTERACTIONS = 'SET_INTERACTIONS';
30 |
31 | export const setInteractions = interactions => ({
32 | type: SET_INTERACTIONS,
33 | payload: { interactions }
34 | });
35 |
36 | export const SET_SELECTION_VIEW = 'SET_SELECTION_VIEW';
37 |
38 | export const setSelectionView = domains => ({
39 | type: SET_SELECTION_VIEW,
40 | payload: { domains }
41 | });
42 |
43 | export const UPDATE_HGL_CONFIG = 'UPDATE_HGL_CONFIG';
44 |
45 | export const updateConfig = config => ({
46 | type: UPDATE_HGL_CONFIG,
47 | payload: {
48 | config
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/src/assets/styles/range-select.scss:
--------------------------------------------------------------------------------
1 | @import 'colors';
2 | @import 'transitions';
3 |
4 | range-select {
5 | position: relative;
6 | height: 1rem;
7 | }
8 |
9 | .range-select-from,
10 | .range-select-top {
11 | font-size: 0.8rem;
12 | line-height: 1rem;
13 | }
14 |
15 | .range-select-from {
16 | margin-right: 0.25rem;
17 | }
18 |
19 | .range-select-top {
20 | margin-left: 0.5rem;
21 | }
22 |
23 | .range-selector {
24 | z-index: 2;
25 | position: absolute;
26 | top: 0;
27 | width: 0.25rem;
28 | height: 1rem;
29 | margin-left: -0.25rem;
30 | padding: 0 0.25;
31 | border-radius: 0;
32 | background: transparent;
33 |
34 | &:active,
35 | &:focus,
36 | &:hover {
37 | z-index: 3;
38 |
39 | &:after {
40 | left: 0.125rem;
41 | right: 0.125rem;
42 | background: $black;
43 | }
44 | }
45 |
46 | &:after {
47 | position: absolute;
48 | content: '';
49 | top: 0;
50 | right: 0.25rem;
51 | bottom: 0;
52 | left: 0.25rem;
53 | border-radius: 0.25rem;
54 | background: $gray;
55 | transition: all $transition-very-fast $ease-in-out-cubic;
56 | }
57 | }
58 |
59 | .range-select-range {
60 | z-index: 0;
61 | position: absolute;
62 | top: 0.375rem;
63 | right: 0;
64 | bottom: 0.375rem;
65 | left: 0;
66 | border-radius: 0.25rem;
67 | }
68 |
69 | .selected-range {
70 | z-index: 1;
71 | background: $gray;
72 | }
73 |
74 | .available-range {
75 | background: $gray-lightest;
76 | }
77 |
--------------------------------------------------------------------------------
/src/utils/drag-drop.js:
--------------------------------------------------------------------------------
1 | import $ from './dom-el';
2 | import hasParent from './has-parent';
3 |
4 | /**
5 | * Add drag and drop behavior / listener to a DOM element.
6 | *
7 | * @param {object} baseEl - The base element.
8 | * @param {object} dropEl - The element stuff needs to be dropped of (including
9 | * children of this element).
10 | * @param {function} dropCallback - Function to be called when stuff is dropped.
11 | */
12 | export default function dragDrop (baseEl, dropEl, dropCallback) {
13 | const $baseEl = $(baseEl);
14 | let isDragging = false;
15 |
16 | document.addEventListener('dragenter', (event) => {
17 | if (hasParent(event.target, baseEl)) {
18 | $baseEl.addClass('is-dragging-over');
19 | isDragging = true;
20 | event.preventDefault();
21 | }
22 | });
23 |
24 | document.addEventListener('dragover', (event) => {
25 | if (isDragging) {
26 | event.preventDefault();
27 | }
28 | });
29 |
30 | document.addEventListener('dragleave', () => {
31 | if (isDragging && hasParent(event.target, dropEl)) {
32 | $baseEl.removeClass('is-dragging-over');
33 | isDragging = false;
34 | }
35 | });
36 |
37 | document.addEventListener('drop', (event) => {
38 | if (isDragging) {
39 | event.preventDefault();
40 |
41 | if (hasParent(event.target, baseEl)) {
42 | dropCallback(event);
43 | }
44 |
45 | $baseEl.removeClass('is-dragging-over');
46 | isDragging = false;
47 | }
48 | }, false);
49 | }
50 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const path = require('path');
3 | const project = require('./aurelia_project/aurelia.json');
4 |
5 | const testSrc = [
6 | { pattern: project.unitTestRunner.source, included: false },
7 | 'test/aurelia-karma.js'
8 | ];
9 |
10 | const output = project.platform.output;
11 | const appSrc = project.build.bundles.map(x => path.join(output, x.name));
12 | const entryIndex = appSrc.indexOf(path.join(output, project.build.loader.configTarget));
13 | const entryBundle = appSrc.splice(entryIndex, 1)[0];
14 | const files = [entryBundle].concat(testSrc).concat(appSrc);
15 |
16 | module.exports = function (config) {
17 | const cfg = {
18 | basePath: '',
19 | frameworks: [project.testFramework.id],
20 | files,
21 | exclude: [],
22 | preprocessors: {
23 | [project.unitTestRunner.source]: [project.transpiler.id]
24 | },
25 | babelPreprocessor: { options: project.transpiler.options },
26 | reporters: ['progress'],
27 | port: 9876,
28 | colors: true,
29 | logLevel: config.LOG_INFO,
30 | autoWatch: true,
31 | browsers: ['Chrome'],
32 | customLaunchers: {
33 | Chrome_travis_ci: {
34 | base: 'Chrome',
35 | flags: ['--no-sandbox']
36 | }
37 | },
38 | singleRun: false,
39 | // client.args must be a array of string.
40 | // Leave 'aurelia-root', project.paths.root in this order so we can find
41 | // the root of the aurelia project.
42 | client: {
43 | args: ['aurelia-root', project.paths.root]
44 | }
45 | };
46 |
47 | if (process.env.TRAVIS) {
48 | cfg.browsers = ['Chrome_travis_ci'];
49 | }
50 |
51 | config.set(cfg);
52 | };
53 |
--------------------------------------------------------------------------------
/src/configs/examples.js:
--------------------------------------------------------------------------------
1 | export const rao2014Gm12878Mbol1kbMresChr22Loops = {
2 | name: 'Loops in chromosome 22 of GM12878',
3 | data: 'Rao et al. (2014) GM12878 Mbol 1kb',
4 | url: 'https://gist.githubusercontent.com/flekschas/8b0163f25fd4ffb067aaba2a595da447/raw/12316079e1adb63450ad38e21fdb51950440e917/rao-2014-gm12878-mbol-1kb-mres-chr22-loops-hipiler-v140.json'
5 | };
6 |
7 | export const rao2014Gm12878Mbol1kbMresChr4Tads = {
8 | name: 'TADs in chromosome 4 of GM12878',
9 | data: 'Rao et al. (2014) GM12878 Mbol 1kb',
10 | url: 'https://gist.githubusercontent.com/flekschas/37b6293b82b5b3e5fb56de9827100797/raw/9d6f05d5b4dc783560337772a203d66f30a068aa/rao-2014-gm12878-mbol-1kb-mres-chr4-tads-hipiler-v140.json'
11 | };
12 |
13 | export const rao2014Gm12878Mbol1kbMresTelomeres = {
14 | name: 'Telomere contacts of GM2878',
15 | data: 'Rao et al. (2014) GM12878 Mbol 1kb',
16 | url: 'https://gist.githubusercontent.com/flekschas/3700ffa02aac69b52a2b10300299f967/raw/f0b93a08d0b3f2d7c7a6bcca31b502f1234d235e/rao-2014-gm12878-mbol-1kb-mres-telomeres-hipiler-v140.json'
17 | };
18 |
19 | export const rao2014Gm12878VsK562Mbol1kbMresTelomeres = {
20 | name: 'Telomere contacts of GM12878 vs K562',
21 | data: 'Rao et al. (2014) GM12878 and K562 Mbol 1kb',
22 | url: 'https://gist.githubusercontent.com/flekschas/a397dded3694fc3984c6acdae6a837fd/raw/021bd894b0d84df8df05e65b442669b39f17b7c7/rao-2014-gm12878-vs-k562-mbol-1kb-mres-telomeres-hipiler-v140.json'
23 | };
24 |
25 | export default [
26 | rao2014Gm12878Mbol1kbMresChr22Loops,
27 | rao2014Gm12878Mbol1kbMresChr4Tads,
28 | rao2014Gm12878Mbol1kbMresTelomeres,
29 | rao2014Gm12878VsK562Mbol1kbMresTelomeres
30 | ];
31 |
--------------------------------------------------------------------------------
/src/utils/hilbert-curve.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-plusplus,no-bitwise */
2 |
3 | // Adapted from Jason Davies: https://www.jasondavies.com/hilbert-curve/
4 | const hilbert = (function () {
5 | // From Mike Bostock: http://bl.ocks.org/597287
6 | // Adapted from Nick Johnson: http://bit.ly/biWkkq
7 | const pairs = [
8 | [[0, 3], [1, 0], [3, 1], [2, 0]],
9 | [[2, 1], [1, 1], [3, 0], [0, 2]],
10 | [[2, 2], [3, 3], [1, 2], [0, 1]],
11 | [[0, 0], [3, 2], [1, 3], [2, 3]]
12 | ];
13 |
14 | // d2xy and rot are from:
15 | // http://en.wikipedia.org/wiki/Hilbert_curve#Applications_and_mapping_algorithms
16 | function rot (n, x, y, rx, ry) {
17 | if (ry === 0) {
18 | if (rx === 1) {
19 | x = n - 1 - x;
20 | y = n - 1 - y;
21 | }
22 | return [y, x];
23 | }
24 | return [x, y];
25 | }
26 |
27 | return {
28 | xy2d (x, y, z) {
29 | let quad = 0;
30 | let pair;
31 | let i = 0;
32 |
33 | while (--z >= 0) {
34 | pair = pairs[quad][(x & (1 << z) ? 2 : 0) | (y & (1 << z) ? 1 : 0)];
35 | i = (i << 2) | pair[0];
36 | quad = pair[1];
37 | }
38 |
39 | return i;
40 | },
41 | d2xy (z, t) {
42 | let n = 1 << z;
43 | let x = 0;
44 | let y = 0;
45 | for (let s = 1; s < n; s *= 2) {
46 | let rx = 1 & (t / 2);
47 | let ry = 1 & (t ^ rx);
48 | let xy = rot(s, x, y, rx, ry);
49 | x = xy[0] + (s * rx);
50 | y = xy[1] + (s * ry);
51 | t /= 4;
52 | }
53 | return [x, y];
54 | }
55 | };
56 | })();
57 |
58 | export default function hilbertCurve (level, index) {
59 | return hilbert.d2xy(level, index);
60 | }
61 |
--------------------------------------------------------------------------------
/src/assets/styles/mixins/slide-out-in.scss:
--------------------------------------------------------------------------------
1 | @mixin slide-out-in($speed, $easing) {
2 | -webkit-animation: slide-out-in $speed $easing 1;
3 | -moz-animation: slide-out-in $speed $easing 1;
4 | animation: slide-out-in $speed $easing 1;
5 | -webkit-transform-origin: 50% 50%;
6 | -moz-transform-origin: 50% 50%;
7 | transform-origin: 50% 50%;
8 |
9 | @-moz-keyframes slide-out-in {
10 | 0% {
11 | -moz-transform: translateX(0);
12 | opacity: 1;
13 | }
14 | 50% {
15 | -moz-transform: translateX(75%);
16 | opacity: 0;
17 | }
18 | 51% {
19 | -moz-transform: translateX(-50%);
20 | opacity: 0;
21 | }
22 | 100% {
23 | -moz-transform: translateX(0);
24 | opacity: 1;
25 | }
26 | }
27 | @-webkit-keyframes slide-out-in {
28 | 0% {
29 | -webkit-transform: translateX(0);
30 | opacity: 1;
31 | }
32 | 50% {
33 | -webkit-transform: translateX(75%);
34 | opacity: 0;
35 | }
36 | 51% {
37 | -webkit-transform: translateX(-50%);
38 | opacity: 0;
39 | }
40 | 100% {
41 | -webkit-transform: translateX(0);
42 | opacity: 1;
43 | }
44 | }
45 | @keyframes slide-out-in {
46 | 0% {
47 | -webkit-transform: translateX(0);
48 | transform: translateX(0);
49 | opacity: 1;
50 | }
51 | 50% {
52 | -webkit-transform: translateX(75%);
53 | transform: translateX(75%);
54 | opacity: 0;
55 | }
56 | 51% {
57 | -webkit-transform: translateX(-50%);
58 | transform: translateX(-50%);
59 | opacity: 0;
60 | }
61 | 100% {
62 | -webkit-transform: translateX(0);
63 | transform: translateX(0);
64 | opacity: 1;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/aurelia_project/tasks/run.js:
--------------------------------------------------------------------------------
1 | import gulp from 'gulp';
2 | import browserSync from 'browser-sync';
3 | import historyApiFallback from 'connect-history-api-fallback/lib';
4 | import project from '../aurelia.json';
5 | import build from './build';
6 | import {CLIOptions} from 'aurelia-cli';
7 |
8 | function log(message) {
9 | console.log(message); //eslint-disable-line no-console
10 | }
11 |
12 | function onChange(path) {
13 | log(`File Changed: ${path}`);
14 | }
15 |
16 | function reload(done) {
17 | browserSync.reload();
18 | done();
19 | }
20 |
21 | let serve = gulp.series(
22 | build,
23 | done => {
24 | browserSync({
25 | online: false,
26 | open: false,
27 | port: 9000,
28 | logLevel: 'silent',
29 | server: {
30 | baseDir: ['.'],
31 | middleware: [historyApiFallback(), function(req, res, next) {
32 | res.setHeader('Access-Control-Allow-Origin', '*');
33 | next();
34 | }]
35 | }
36 | }, function(err, bs) {
37 | let urls = bs.options.get('urls').toJS();
38 | log(`Application Available At: ${urls.local}`);
39 | log(`BrowserSync Available At: ${urls.ui}`);
40 | done();
41 | });
42 | }
43 | );
44 |
45 | let refresh = gulp.series(
46 | build,
47 | reload
48 | );
49 |
50 | let watch = function() {
51 | gulp.watch(project.transpiler.source, refresh).on('change', onChange);
52 | gulp.watch(project.markupProcessor.source, refresh).on('change', onChange);
53 | gulp.watch(project.cssProcessor.source, refresh).on('change', onChange);
54 | };
55 |
56 | let run;
57 |
58 | if (CLIOptions.hasFlag('watch')) {
59 | run = gulp.series(
60 | serve,
61 | watch
62 | );
63 | } else {
64 | run = serve;
65 | }
66 |
67 | export default run;
68 |
--------------------------------------------------------------------------------
/src/configs/colors.js:
--------------------------------------------------------------------------------
1 | export const BLACK = 0x000000;
2 | export const GRAY = 0x999999;
3 | export const WHITE = 0xffffff;
4 |
5 | export const GRAY_LIGHT = 0xbfbfbf;
6 | export const GRAY_LIGHTER = 0xd9d9d9;
7 | export const GRAY_LIGHTEST = 0xe5e5e5;
8 | export const GRAY_DARK = 0x666666;
9 | export const GRAY_DARKER = 0x444444;
10 | export const GRAY_DARKEST = 0x222222;
11 |
12 | export const CYAN = 0x3ae0e5;
13 | export const CYAN_RGBA = [58, 224, 229, 255];
14 | export const GREEN = 0x40bf00;
15 | export const GREEN_RGBA = [64, 191, 0, 255];
16 | export const ORANGE = 0xff5500;
17 | export const ORANGE_RGBA = [255, 85, 0, 255];
18 | export const PINK = 0xec3bb6;
19 | export const PINK_RGBA = [236, 59, 182, 255];
20 | export const RED = 0xf60029;
21 | export const RED_RGBA = [246, 0, 41, 255];
22 | export const YELLOW = 0xffcc00;
23 | export const YELLOW_RGBA = [255, 204, 0, 255];
24 | export const BLUE = 0x4e40ff;
25 | export const BLUE_RGBA = [78, 64, 255, 255];
26 |
27 | export const PRIMARY = ORANGE;
28 | export const SECONDARY = BLUE;
29 |
30 | export const LOW_QUALITY_BLUE = 0xdcd9ff;
31 | export const LOW_QUALITY_BLUE_ARR = [0.862745098, 0.850980392, 1];
32 | export const LOW_QUALITY_BLUE_RGBA = [220, 217, 255, 255];
33 | export const LOW_QUALITY_ORANGE = 0xffd9cb;
34 | export const LOW_QUALITY_ORANGE_ARR = [1, 0.890196078, 0.835294118];
35 | export const LOW_QUALITY_ORANGE_RGBA = [255, 227, 213, 255];
36 |
37 | export default {
38 | BLACK,
39 | GRAY,
40 | WHITE,
41 | GRAY_LIGHT,
42 | GRAY_LIGHTER,
43 | GRAY_LIGHTEST,
44 | GRAY_DARK,
45 | GRAY_DARKER,
46 | GRAY_DARKEST,
47 | CYAN,
48 | CYAN_RGBA,
49 | GREEN,
50 | GREEN_RGBA,
51 | ORANGE,
52 | ORANGE_RGBA,
53 | PINK,
54 | PINK_RGBA,
55 | RED,
56 | RED_RGBA,
57 | YELLOW,
58 | YELLOW_RGBA,
59 | BLUE,
60 | BLUE_RGBA,
61 | PRIMARY,
62 | SECONDARY,
63 | LOW_QUALITY_BLUE,
64 | LOW_QUALITY_BLUE_ARR,
65 | LOW_QUALITY_BLUE_RGBA,
66 | LOW_QUALITY_ORANGE,
67 | LOW_QUALITY_ORANGE_ARR,
68 | LOW_QUALITY_ORANGE_RGBA
69 | };
70 |
--------------------------------------------------------------------------------
/src/services/states.js:
--------------------------------------------------------------------------------
1 | // Aurelia
2 | import { LogManager } from 'aurelia-framework';
3 |
4 | // Third party
5 | import localforage from 'localForage';
6 | import { applyMiddleware, compose, createStore } from 'redux';
7 | import { autoRehydrate, persistStore, purgeStoredState } from 'redux-persist';
8 | import thunk from 'redux-thunk';
9 | // import freeze from 'redux-freeze';
10 | import undoable, { ActionCreators, groupByActionTypes } from 'redux-undo';
11 | import { enableBatching } from 'redux-batched-actions';
12 |
13 | import { resetState } from 'app-actions';
14 | import appReducer from 'app-reducer';
15 |
16 | import {
17 | ANNOTATE_PILE,
18 | SELECT_PILE
19 | } from 'components/fragments/fragments-actions';
20 |
21 | import logger from 'utils/redux-logger';
22 |
23 | const config = {
24 | storage: localforage,
25 | debounce: 25,
26 | keyPrefix: 'hipiler.'
27 | };
28 |
29 | const debug = LogManager.getLevel() === LogManager.logLevel.debug;
30 |
31 | const middlewares = [autoRehydrate(), applyMiddleware(thunk)];
32 |
33 | if (debug) {
34 | middlewares.push(applyMiddleware(logger));
35 | // middleware.push(applyMiddleware(freeze));
36 | }
37 |
38 | export default class States {
39 | constructor () {
40 | this.store = createStore(
41 | undoable(enableBatching(appReducer), {
42 | groupBy: groupByActionTypes([ANNOTATE_PILE, SELECT_PILE]),
43 | limit: 25
44 | }),
45 | undefined,
46 | compose(...middlewares)
47 | );
48 |
49 | persistStore(this.store, config, (err, state) => {
50 | // Rehydration is done
51 | this.isRehydrated = Object.keys(true).length > 0;
52 | });
53 | }
54 |
55 | undo () {
56 | this.store.dispatch(ActionCreators.undo());
57 | }
58 |
59 | redo () {
60 | this.store.dispatch(ActionCreators.redo());
61 | }
62 |
63 | reset () {
64 | // Reset state to default values
65 | this.store.dispatch(resetState());
66 |
67 | // Clear history
68 | this.store.dispatch(ActionCreators.clearHistory());
69 |
70 | // Purge persistent store
71 | return purgeStoredState(config);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
1 | �PNG
2 |
3 |
IHDR szz� bKGD �C� pHYs �� vpAg ���� TIDATXå�y�W��?���[�u�Pk��K�4�օ��AVqXZ6Q���Z�*��-BEaA�"��*��mSMm�Xm�E��� ��03�w��?���Y=�ͽ����~��{ν�]T����f������*�p�1k��xd���}��n��x��M\��MT�x�g�����{��H�]gj:8b���v�uޠ+�,��v�;fm��fm�#��G�;�x�ƈ�i!,i��t�ڳ\��>�1!�+��ܐ�M`{��US7WB P�yM��w�Zu����x��������qk�8���5�\0��IS���žZJ7������&j��������Vwv��;b��2f[���;Pۢ�ϝQY�9j��X9�bǔO�R`v�
4 | p�=�7�^�y���O=�BsZ�M� c���Eg���|��P��k+�}�~�;V�e(@D
���̮Zq �cvՊ^ ��"�\F���Ӏor�vS�5 I.�i����rͤ��O��r���;ܿ2��-���[��4\���Y�������� ]k��AZrh�P�h�h�>��C��;�=�~������e���W �VN�.�O�aA��i��1�]s��Y�N`:pЧ�Y�džo��j��>�]֧��1�1�p.���n���*��|p�"�B�r(eu� V�e~D
N�U��'=�o*��C�[�ՎX���%�����oN�}�4@b7�rvh�5|TY��})�X��@$BN�ȭ y� ��ƚ�m�ޱ�{M��e��.x_,&��d{����)}>��&��/z��- U