ucsc-xena-client
Version:
UCSC Xena Client. Functional genomics visualizations.
190 lines (159 loc) • 7 kB
JavaScript
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;
};
;