UNPKG

ucsc-xena-client

Version:

UCSC Xena Client. Functional genomics visualizations.

190 lines (159 loc) 7 kB
'use strict'; var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var _reduxDevtoolsLogMonitor = require('redux-devtools-log-monitor'); var _reduxDevtoolsLogMonitor2 = _interopRequireDefault(_reduxDevtoolsLogMonitor); var _reduxDevtoolsDockMonitor = require('redux-devtools-dock-monitor'); var _reduxDevtoolsDockMonitor2 = _interopRequireDefault(_reduxDevtoolsDockMonitor); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var _ = require('./underscore_ext'); var Rx = require('./rx'); var React = require('react'); var ReactDOM = require('react-dom'); var _require = require('./controllers/devtools'), createDevTools = _require.createDevTools; var nostate = require('./nostate'); var urlParams = require('./urlParams'); var LZ = require('./lz-string'); var _require2 = require('./compactData'), compactState = _require2.compactState, expandState = _require2.expandState; var migrateState = require('./migrateState'); var _require3 = require('./schemaCheck'), schemaCheckThrow = _require3.schemaCheckThrow; function logError(err) { if ((typeof window === 'undefined' ? 'undefined' : _typeof(window)) === 'object' && typeof window.chrome !== 'undefined') { // In Chrome, rethrowing provides better source map support setTimeout(function () { throw err; }); } else { console.error(err.stack || err); } } var dropTransient = function dropTransient(state) { return _.assoc(state, 'wizard', {}, 'datapages', undefined); }; // serialization function stringify(state) { return LZ.compressToUTF16(JSON.stringify(_extends({}, _.omit(state, 'computedStates', 'committedState'), { committedState: compactState(dropTransient(state.committedState)) }))); } function parse(str) { var state = JSON.parse(LZ.decompressFromUTF16(str)); return _extends({}, state, { committedState: schemaCheckThrow(expandState(migrateState(state.committedState))) }); } // var unwrapDevState = function unwrapDevState(state) { return _.last(state.computedStates).state; }; function getSavedState(persist) { delete sessionStorage.xena; // Free up space & don't try to share with prod. if (persist && nostate('debugSession')) { try { return parse(sessionStorage.debugSession); } catch (err) { console.log("Unable to load saved debug session", err); } } return null; } var historyObs = Rx.Observable.fromEvent(window, 'popstate').map(function () { return ['history', { path: location.pathname, params: urlParams() }]; }); module.exports = function (_ref) { var Page = _ref.Page, controller = _ref.controller, persist = _ref.persist, initialState = _ref.initialState, serverBus = _ref.serverBus, serverCh = _ref.serverCh, uiBus = _ref.uiBus, uiCh = _ref.uiCh, main = _ref.main, selector = _ref.selector; var dom = { main: main }, updater = function updater(ac) { return uiBus.next(ac); }, devBus = new Rx.Subject(), devCh = devBus, devtoolsVisible = false; // Change this to turn on the debug window at start. var DevTools = createDevTools(React.createElement( _reduxDevtoolsDockMonitor2.default, { defaultIsVisible: devtoolsVisible, toggleVisibilityKey: 'ctrl-h', changePositionKey: 'ctrl-q' }, React.createElement(_reduxDevtoolsLogMonitor2.default, { preserveScrollTop: false, expandStateRoot: false }) )), devReducer = DevTools.instrument(controller, initialState), savedState = getSavedState(persist), // Here we need not just the initial state, but to know if we have a // saved state. The initial state is used in devtools as the 'reset' // target. So, we can't replace initial state with saved state. devInitialState = devReducer(null, savedState ? { type: 'IMPORT_STATE', nextLiftedState: savedState } : {}); // Side-effects (e.g. async) happen here. Ideally we wouldn't call this // from 'scan', since 'scan' should be side-effect free. However we've lost // the action by the time scan is complete, so we do it in the scan. var inEffectsReducer = false; var effectsReducer = function effectsReducer(state, ac) { if (inEffectsReducer) { throw new Error("Reentry in reducer. Reducers must not invoke actions."); } inEffectsReducer = true; var nextState = devReducer(state, ac); if (ac.type === 'PERFORM_ACTION') { try { controller.postAction(serverBus, unwrapDevState(state), unwrapDevState(nextState), ac.action); } catch (err) { logError(err); } } // We have an implicit async action on page load: 'init'. redux-devtools // 'RESET' command will return us to the initial state, but never // re-issues async actions (which would break the devtools functionality). // This leaves our app in an unusable state after RESET: initial state w/o any // way of issuing the 'init' action. The effect is the cohort list never // loads. Here we intercept the devtools actions & re-issue 'init' on // RESET. if (ac.type === 'RESET') { setTimeout(function () { return uiBus.next(['init', location.pathname, urlParams()]); }, 0); } inEffectsReducer = false; return nextState; }; var devStateObs = Rx.Observable.merge(serverCh, uiCh, historyObs).map(function (ac) { return { type: 'PERFORM_ACTION', action: ac }; }).merge(devCh).scan(effectsReducer, devInitialState) // XXX side effects! .share(); // XXX double check that this expression is doing what we want: don't draw faster // than rAF. // pass the selector into Page, so we catch errors while rendering & can display an error message. devStateObs.debounceTime(0, Rx.Scheduler.animationFrame).subscribe(function (devState) { return ReactDOM.render(React.createElement( 'div', null, React.createElement(Page, { callback: updater, selector: selector, state: unwrapDevState(devState) }), React.createElement(DevTools, _extends({ dispatch: devBus.next.bind(devBus) }, devState)) ), dom.main); }, function (err) { return console.log('err', err); }); if (persist) { // Save state in sessionStorage on page unload. devStateObs.sample(Rx.Observable.fromEvent(window, 'beforeunload')).map(function (state) { return sessionStorage.saveDevState ? state : effectsReducer(state, { type: 'COMMIT' }); }).subscribe(function (state) { return sessionStorage.debugSession = stringify(state); }); } // This causes us to always load cohorts on page load. This is important after // setting hubs, for example. uiBus.next(['init', location.pathname, urlParams()]); return dom; };