UNPKG

@dd-decaf/escher

Version:

Escher: A Web Application for Building, Sharing, and Embedding Data-Rich Visualizations of Metabolic Pathways

1,570 lines (1,410 loc) 1.34 MB
(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define([], factory); else if(typeof exports === 'object') exports["escher"] = factory(); else root["escher"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 117); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__src_creator__ = __webpack_require__(36); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "creator", function() { return __WEBPACK_IMPORTED_MODULE_0__src_creator__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_1__src_local__ = __webpack_require__(133); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "local", function() { return __WEBPACK_IMPORTED_MODULE_1__src_local__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_2__src_matcher__ = __webpack_require__(60); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "matcher", function() { return __WEBPACK_IMPORTED_MODULE_2__src_matcher__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_3__src_mouse__ = __webpack_require__(134); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "mouse", function() { return __WEBPACK_IMPORTED_MODULE_3__src_mouse__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_4__src_namespace__ = __webpack_require__(37); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "namespace", function() { return __WEBPACK_IMPORTED_MODULE_4__src_namespace__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_5__src_namespaces__ = __webpack_require__(38); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "namespaces", function() { return __WEBPACK_IMPORTED_MODULE_5__src_namespaces__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_6__src_point__ = __webpack_require__(26); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "clientPoint", function() { return __WEBPACK_IMPORTED_MODULE_6__src_point__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_7__src_select__ = __webpack_require__(135); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "select", function() { return __WEBPACK_IMPORTED_MODULE_7__src_select__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_8__src_selectAll__ = __webpack_require__(163); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "selectAll", function() { return __WEBPACK_IMPORTED_MODULE_8__src_selectAll__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_9__src_selection_index__ = __webpack_require__(6); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "selection", function() { return __WEBPACK_IMPORTED_MODULE_9__src_selection_index__["b"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_10__src_selector__ = __webpack_require__(41); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "selector", function() { return __WEBPACK_IMPORTED_MODULE_10__src_selector__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_11__src_selectorAll__ = __webpack_require__(61); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "selectorAll", function() { return __WEBPACK_IMPORTED_MODULE_11__src_selectorAll__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_12__src_selection_style__ = __webpack_require__(64); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "style", function() { return __WEBPACK_IMPORTED_MODULE_12__src_selection_style__["b"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_13__src_touch__ = __webpack_require__(164); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "touch", function() { return __WEBPACK_IMPORTED_MODULE_13__src_touch__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_14__src_touches__ = __webpack_require__(165); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "touches", function() { return __WEBPACK_IMPORTED_MODULE_14__src_touches__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_15__src_window__ = __webpack_require__(42); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "window", function() { return __WEBPACK_IMPORTED_MODULE_15__src_window__["a"]; }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_16__src_selection_on__ = __webpack_require__(40); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "event", function() { return __WEBPACK_IMPORTED_MODULE_16__src_selection_on__["c"]; }); /* harmony reexport (binding) */ __webpack_require__.d(__webpack_exports__, "customEvent", function() { return __WEBPACK_IMPORTED_MODULE_16__src_selection_on__["a"]; }); /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; /* global Blob, XMLSerializer, Image, btoa */ var vkbeautify = __webpack_require__(56); var _ = __webpack_require__(2); var Consts = __webpack_require__(57); var d3_json = __webpack_require__(24).json; var d3_text = __webpack_require__(24).text; var d3_csvParseRows = __webpack_require__(34).csvParseRows; var d3_selection = __webpack_require__(0).selection; try { var saveAs = __webpack_require__(166).saveAs; } catch (e) { console.warn('Not a browser, so FileSaver.js not available.'); } module.exports = { set_options: set_options, remove_child_nodes: remove_child_nodes, load_css: load_css, load_files: load_files, load_the_file: load_the_file, make_class: make_class, class_with_optional_new: class_with_optional_new, setup_defs: setup_defs, draw_an_object: draw_an_object, draw_a_nested_object: draw_a_nested_object, make_array: make_array, make_array_ref: make_array_ref, compare_arrays: compare_arrays, arrayToObject: arrayToObject, clone: clone, extend: extend, uniqueConcat: uniqueConcat, unique_strings_array: unique_strings_array, debounce: debounce, object_slice_for_ids: object_slice_for_ids, object_slice_for_ids_ref: object_slice_for_ids_ref, c_plus_c: c_plus_c, c_minus_c: c_minus_c, c_times_scalar: c_times_scalar, download_json: download_json, load_json: load_json, load_json_or_csv: load_json_or_csv, downloadSvg: downloadSvg, downloadPng: downloadPng, rotate_coords_recursive: rotate_coords_recursive, rotate_coords: rotate_coords, get_angle: get_angle, to_degrees: to_degrees, angleNorm: angleNorm, to_radians: to_radians, to_radians_norm: to_radians_norm, angle_for_event: angle_for_event, distance: distance, check_undefined: check_undefined, compartmentalize: compartmentalize, decompartmentalize: decompartmentalize, mean: mean, median: median, quartiles: quartiles, random_characters: random_characters, generate_map_id: generate_map_id, check_for_parent_tag: check_for_parent_tag, name_to_url: name_to_url, get_document: get_document, get_window: get_window, d3_transform_catch: d3_transform_catch, // check_browser: check_browser calculate_fva_opacity: calculate_fva_opacity, findMap: findMap, isMetabolite: isMetabolite, htmlToElement: htmlToElement, object_slice_for_bigg: object_slice_for_bigg, get_central_or_last_nodes: get_central_or_last_nodes /** * Check if Blob is available, and alert if it is not. */ };function _check_filesaver() { try { var isFileSaverSupported = !!new Blob(); } catch (e) { alert('Blob not supported'); } } function set_options(options, defaults, must_be_float) { if (options === undefined || options === null) { return defaults; } var i = -1; var out = {}; for (var key in defaults) { var has_key = key in options && options[key] !== null && options[key] !== undefined; var val = has_key ? options[key] : defaults[key]; if (must_be_float && key in must_be_float) { val = parseFloat(val); if (isNaN(val)) { if (has_key) { console.warn('Bad float for option ' + key); val = parseFloat(defaults[key]); if (isNaN(val)) { console.warn('Bad float for default ' + key); val = null; } } else { console.warn('Bad float for default ' + key); val = null; } } } out[key] = val; } return out; } function remove_child_nodes(selection) { /** Removes all child nodes from a d3 selection */ var node = selection.node(); while (node.hasChildNodes()) { node.removeChild(node.lastChild); } } function load_css(css_path, callback) { var css = ""; if (css_path) { d3_text(css_path, function (error, text) { if (error) { console.warn(error); } css = text; callback(css); }); } return false; } function _ends_with(str, suffix) { return str.indexOf(suffix, str.length - suffix.length) !== -1; } /** * Load a file. * @param {} t - this context for callback. Should be an object. * @param {} files_to_load - A filename to load. Must be JSON or CSS. * @param {} callback - Function to run after the file is loaded. Takes the * arguments error and data. * @param {} value - If the value is specified, just assign it and do not * execute the ajax query. */ function load_the_file(t, file, callback, value) { if (value) { if (file) console.warn('File ' + file + ' overridden by value.'); callback.call(t, null, value); return; } if (!file) { callback.call(t, 'No filename', null); return; } if (_ends_with(file, 'json')) { d3_json(file, function (e, d) { callback.call(t, e, d); }); } else if (_ends_with(file, 'css')) { d3_text(file, function (e, d) { callback.call(t, e, d); }); } else { callback.call(t, 'Unrecognized file type', null); } return; } function load_files(t, files_to_load, final_callback) { /** Load multiple files asynchronously by calling utils.load_the_file. t: this context for callback. Should be an object. files_to_load: A list of objects with the attributes: { file: a_filename.json, callback: a_callback_fn } File must be JSON or CSS. final_callback: Function that runs after all files have loaded. */ if (files_to_load.length === 0) final_callback.call(t); var i = -1, remaining = files_to_load.length; while (++i < files_to_load.length) { load_the_file(t, files_to_load[i].file, function (e, d) { this.call(t, e, d); if (! --remaining) final_callback.call(t); }.bind(files_to_load[i].callback), files_to_load[i].value); } } /** * Create a constructor that returns a new object with our without the 'new' * keyword. * * Adapted from Hubert Kauker (MIT Licensed), John Resig (MIT Licensed). * http://stackoverflow.com/questions/7892884/simple-class-instantiation */ function make_class() { var is_internal; var constructor = function constructor(args) { if (this instanceof constructor) { if (typeof this.init === 'function') { this.init.apply(this, is_internal ? args : arguments); } } else { is_internal = true; var instance = new constructor(arguments); is_internal = false; return instance; } }; return constructor; } /** * Return a class that can be instantiated without the new keyword. * @param {Class} AClass - Any ES6 class. */ function class_with_optional_new(AClass) { return new Proxy(AClass, { apply: function apply(Target, thisArg, args) { return new (Function.prototype.bind.apply(Target, [null].concat(args)))(); } }); } function setup_defs(svg, style) { // add stylesheet svg.select("defs").remove(); var defs = svg.append("defs"); // make sure the defs is the first node var node = defs.node(); node.parentNode.insertBefore(node, node.parentNode.firstChild); defs.append("style").attr("type", "text/css").text(style); return defs; } /** * Run through the d3 data binding steps for an object. Also checks to make sure * none of the values in the *object* are undefined, and ignores those. * * The create_function, update_function, and exit_function CAN modify the input * data object. * * @param {} container_sel - A d3 selection containing all objects. * * @param {} parent_node_selector - A selector string for a subselection of * container_sel. * * @param {} children_selector - A selector string for each DOM element to bind. * * @param {} object - An object to bind to the selection. * * @param {} id_key - The key that will be used to store object IDs in the bound * data points. * * @param {} create_function - A function for enter selection. Create function * must return a selection of the new nodes. * * @param {} update_function - A function for update selection. * * @param {} exit_function - A function for exit selection. */ function draw_an_object(container_sel, parent_node_selector, children_selector, object, id_key, create_function, update_function, exit_function) { var draw_object = {}; for (var id in object) { if (object[id] === undefined) { console.warn('Undefined value for id ' + id + ' in object. Ignoring.'); } else { draw_object[id] = object[id]; } } var sel = container_sel.select(parent_node_selector).selectAll(children_selector).data(make_array_ref(draw_object, id_key), function (d) { return d[id_key]; }); // enter: generate and place reaction var update_sel = create_function ? create_function(sel.enter()).merge(sel) : sel; // update: update when necessary if (update_function) { update_sel.call(update_function); } // exit if (exit_function) { sel.exit().call(exit_function); } } /** * Run through the d3 data binding steps for an object that is nested within * another element with D3 data. * * The create_function, update_function, and exit_function CAN modify the input * data object. * * @param {} container_sel - A d3 selection containing all objects. * * @param {} children_selector - A selector string for each DOM element to bind. * * @param {} object_data_key - A key for the parent object containing data for * the new selection. * * @param {} id_key - The key that will be used to store object IDs in the bound * data points. * * @param {} create_function - A function for enter selection. Create function * must return a selection of the new nodes. * * @param {} update_function - A function for update selection. * * @param {} exit_function - A function for exit selection. */ function draw_a_nested_object(container_sel, children_selector, object_data_key, id_key, create_function, update_function, exit_function) { var sel = container_sel.selectAll(children_selector).data(function (d) { return make_array_ref(d[object_data_key], id_key); }, function (d) { return d[id_key]; }); // enter: generate and place reaction var update_sel = create_function ? create_function(sel.enter()).merge(sel) : sel; // update: update when necessary if (update_function) { update_sel.call(update_function); } // exit if (exit_function) { sel.exit().call(exit_function); } } function make_array(obj, id_key) { // is this super slow? var array = []; for (var key in obj) { // copy object var it = clone(obj[key]); // add key as 'id' it[id_key] = key; // add object to array array.push(it); } return array; } function make_array_ref(obj, id_key) { /** Turn the object into an array, but only by reference. Faster than make_array. */ var array = []; for (var key in obj) { // copy object var it = obj[key]; // add key as 'id' it[id_key] = key; // add object to array array.push(it); } return array; } function compare_arrays(a1, a2) { /** Compares two simple (not-nested) arrays. */ if (!a1 || !a2) return false; if (a1.length != a2.length) return false; for (var i = 0, l = a1.length; i < l; i++) { if (a1[i] != a2[i]) { // Warning - two different object instances will never be equal: {x:20} != {x:20} return false; } } return true; } /** * Convert an array of objects to an object with all keys and values * that are arrays of the same length as arr. Fills in spaces with null. * * For example, [ { a: 1 }, { b: 2 }] becomes { a: [1, null], b: [null, 2] }. */ function arrayToObject(arr) { // new object var obj = {}; // for each element of the array for (var i = 0, l = arr.length; i < l; i++) { var column = arr[i]; var keys = Object.keys(column); for (var k = 0, nk = keys.length; k < nk; k++) { var id = keys[k]; if (!(id in obj)) { var n = []; // fill spaces with null for (var j = 0; j < l; j++) { n[j] = null; } n[i] = column[id]; obj[id] = n; } else { obj[id][i] = column[id]; } } } return obj; } /** * Deep copy for array and object types. All other types are returned by * reference. * @param {T<Object|Array|*>} obj - The object to copy. * @return {T} The copied object. */ function clone(obj) { if (_.isArray(obj)) return _.map(obj, function (t) { return clone(t); });else if (_.isObject(obj)) return _.mapObject(obj, function (t, k) { return clone(t); });else return obj; } function extend(obj1, obj2, overwrite) { /** Extends obj1 with keys/values from obj2. Performs the extension cautiously, and does not override attributes, unless the overwrite argument is true. Arguments --------- obj1: Object to extend obj2: Object with which to extend. overwrite: (Optional, Default false) Overwrite attributes in obj1. */ if (overwrite === undefined) overwrite = false; for (var attrname in obj2) { if (!(attrname in obj1) || overwrite) // UNIT TEST This obj1[attrname] = obj2[attrname];else throw new Error('Attribute ' + attrname + ' already in object.'); } } function uniqueConcat(arrays) { var newArray = []; arrays.forEach(function (a) { a.forEach(function (x) { if (newArray.indexOf(x) < 0) { newArray.push(x); } }); }); return newArray; } /** * Return unique values in array of strings. * * http://stackoverflow.com/questions/1960473/unique-values-in-an-array */ function unique_strings_array(arr) { var a = []; for (var i = 0, l = arr.length; i < l; i++) { if (a.indexOf(arr[i]) === -1) { a.push(arr[i]); } } return a; } /** * Returns a function, that, as long as it continues to be invoked, will not be * triggered. The function will be called after it stops being called for N * milliseconds. If "immediate" is passed, trigger the function on the leading * edge, instead of the trailing. */ function debounce(func, wait, immediate) { var timeout; return function () { var context = this; var args = arguments; var later = function later() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; } /** * Return a copy of the object with just the given ids. * @param {} obj - An object * @param {} ids - An array of id strings */ function object_slice_for_ids(obj, ids) { var subset = {}; var i = -1; while (++i < ids.length) { subset[ids[i]] = clone(obj[ids[i]]); } if (ids.length !== Object.keys(subset).length) { console.warn('did not find correct reaction subset'); } return subset; } /** * Return a reference of the object with just the given ids. Faster than * object_slice_for_ids. * @param {} obj - An object. * @param {} ids - An array of id strings. */ function object_slice_for_ids_ref(obj, ids) { var subset = {}; var i = -1; while (++i < ids.length) { subset[ids[i]] = obj[ids[i]]; } if (ids.length !== Object.keys(subset).length) { console.warn('did not find correct reaction subset'); } return subset; } function c_plus_c(coords1, coords2) { if (coords1 === null || coords2 === null || coords1 === undefined || coords2 === undefined) { return null; } return { x: coords1.x + coords2.x, y: coords1.y + coords2.y }; } function c_minus_c(coords1, coords2) { if (coords1 === null || coords2 === null || coords1 === undefined || coords2 === undefined) { return null; } return { x: coords1.x - coords2.x, y: coords1.y - coords2.y }; } function c_times_scalar(coords, scalar) { return { x: coords.x * scalar, y: coords.y * scalar }; } /** * Download JSON file in a blob. */ function download_json(json, name) { // Alert if blob isn't going to work _check_filesaver(); var j = JSON.stringify(json); var blob = new Blob([j], { type: 'application/json' }); saveAs(blob, name + '.json'); } /** * Try to load the file as JSON. * @param {} f - The file path * @param {} callback - A callback function that accepts arguments: error, data. * @param {} pre_fn (optional) - A function to call before loading the data. * @param {} failure_fn (optional) - A function to call if the load fails or is * aborted. */ function load_json(f, callback, pre_fn, failure_fn) { // Check for the various File API support if (!(window.File && window.FileReader && window.FileList && window.Blob)) { callback('The File APIs are not fully supported in this browser.', null); } var reader = new window.FileReader(); // Closure to capture the file information. reader.onload = function (event) { var result = event.target.result; var data; // Try JSON try { data = JSON.parse(result); } catch (e) { // If it failed, return the error callback(e, null); return; } // If successful, return the data callback(null, data); }; if (pre_fn !== undefined && pre_fn !== null) { try { pre_fn(); } catch (e) { console.warn(e); } } reader.onabort = function (event) { try { failure_fn(); } catch (e) { console.warn(e); } }; reader.onerror = function (event) { try { failure_fn(); } catch (e) { console.warn(e); } }; // Read in the image file as a data URL reader.readAsText(f); } /** * Try to load the file as JSON or CSV (JSON first). * @param {String} f - The file path * @param {Function} csv_converter - A function to convert the CSV output to equivalent JSON. * @param {Function} callback - A callback function that accepts arguments: error, data. * @param {} pre_fn (optional) - A function to call before loading the data. * @param {} failure_fn (optional) - A function to call if the load fails or is * aborted. * @param {} debug_event (optional) - An event, with a string at * event.target.result, to load as though it was the contents of a loaded file. */ function load_json_or_csv(f, csv_converter, callback, pre_fn, failure_fn, debug_event) { // Capture the file information. var onload_function = function onload_function(event) { var result = event.target.result; var data; var errors; // try JSON try { data = JSON.parse(result); } catch (e) { errors = 'JSON error: ' + e; // try csv try { data = csv_converter(d3_csvParseRows(result)); } catch (e) { // if both failed, return the errors callback(errors + '\nCSV error: ' + e, null); return; } } // if successful, return the data callback(null, data); }; if (debug_event !== undefined && debug_event !== null) { console.warn('Debugging load_json_or_csv'); return onload_function(debug_event); } // Check for the various File API support. if (!(window.File && window.FileReader && window.FileList && window.Blob)) callback("The File APIs are not fully supported in this browser.", null); var reader = new window.FileReader(); if (pre_fn !== undefined && pre_fn !== null) { try { pre_fn(); } catch (e) { console.warn(e); } } reader.onabort = function (event) { try { failure_fn(); } catch (e) { console.warn(e); } }; reader.onerror = function (event) { try { failure_fn(); } catch (e) { console.warn(e); } }; // Read in the image file as a data URL. reader.onload = onload_function; reader.readAsText(f); } /** * Download an svg file using FileSaver.js. * @param {String} name - The filename (without extension) * @param {D3 Selection} svg_sel - The d3 selection for the SVG element * @param {Boolean} do_beautify - If true, then beautify the SVG output */ function downloadSvg(name, svg_sel, do_beautify) { // Alert if blob isn't going to work _check_filesaver(); // Make the xml string var xml = new XMLSerializer().serializeToString(svg_sel.node()); if (do_beautify) xml = vkbeautify.xml(xml); xml = '<?xml version="1.0" encoding="utf-8"?>\n' + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"\n' + ' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' + xml; // Save var blob = new Blob([xml], { type: 'image/svg+xml' }); saveAs(blob, name + '.svg'); } /** * Download a png file using FileSaver.js. * @param {String} name - The filename (without extension). * @param {D3 Selection} svg_sel - The d3 selection for the SVG element. */ function downloadPng(name, svg_sel) { // Alert if blob isn't going to work _check_filesaver(); // Make the xml string var xml = new XMLSerializer().serializeToString(svg_sel.node()); xml = '<?xml version="1.0" encoding="utf-8"?>\n' + '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"\n' + ' "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">\n' + xml; // Canvas to hold the image var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); // Get SVG size var svg_size = svg_sel.node().getBBox(); var svg_width = svg_size.width + svg_size.x; var svg_height = svg_size.height + svg_size.y; // Canvas size = SVG size. Constrained to 10000px for very large SVGs if (svg_width < 10000 && svg_height < 10000) { canvas.width = svg_width; canvas.height = svg_height; } else { if (canvas.width > canvas.height) { canvas.width = 10000; canvas.height = 10000 * (svg_height / svg_width); } else { canvas.width = 10000 * (svg_width / svg_height); canvas.height = 10000; } } // Image element appended with data var base_image = new Image(); base_image.src = 'data:image/svg+xml;base64,' + btoa(xml); base_image.onload = function () { // Draw image to canvas with white background context.fillStyle = '#FFF'; context.fillRect(0, 0, canvas.width, canvas.height); context.drawImage(base_image, 0, 0, canvas.width, canvas.height); // Save image canvas.toBlob(function (blob) { saveAs(blob, name + '.png'); }); }; } function rotate_coords_recursive(coords_array, angle, center) { return coords_array.map(function (c) { return rotate_coords(c, angle, center); }); } /** * Calculates displacement { x: dx, y: dy } based on rotating point c around * center with angle. */ function rotate_coords(c, angle, center) { var dx = Math.cos(-angle) * (c.x - center.x) + Math.sin(-angle) * (c.y - center.y) + center.x - c.x; var dy = -Math.sin(-angle) * (c.x - center.x) + Math.cos(-angle) * (c.y - center.y) + center.y - c.y; return { x: dx, y: dy }; } /** * Get the angle between coordinates * @param {Object} coords - Array of 2 coordinate objects { x: 1, y: 1 } * @return {Number} angle between 0 and 2PI. */ function get_angle(coords) { var denominator = coords[1].x - coords[0].x; var numerator = coords[1].y - coords[0].y; if (denominator === 0 && numerator >= 0) { return Math.PI / 2; } else if (denominator === 0 && numerator < 0) { return 3 * Math.PI / 2; } else if (denominator >= 0 && numerator >= 0) { return Math.atan(numerator / denominator); } else if (denominator >= 0) { return Math.atan(numerator / denominator) + 2 * Math.PI; } else { return Math.atan(numerator / denominator) + Math.PI; } } function to_degrees(radians) { return radians * 180 / Math.PI; } /** * Force to domain -PI to PI */ function angleNorm(radians) { if (radians < -Math.PI) { return radians + Math.floor((radians - Math.PI) / (-2 * Math.PI)) * 2 * Math.PI; } else if (radians > Math.PI) { return radians - Math.floor((radians + Math.PI) / (2 * Math.PI)) * 2 * Math.PI; } else { return radians; } } function to_radians(degrees) { return Math.PI / 180 * degrees; } /** * Convert to radians, and force to domain -PI to PI */ function to_radians_norm(degrees) { var radians = to_radians(degrees); return angleNorm(radians); } function angle_for_event(displacement, point, center) { var gamma = Math.atan2(point.x - center.x, center.y - point.y); var beta = Math.atan2(point.x - center.x + displacement.x, center.y - point.y - displacement.y); var angle = beta - gamma; return angle; } function distance(start, end) { return Math.sqrt(Math.pow(end.y - start.y, 2) + Math.pow(end.x - start.x, 2)); } /** * Report an error if any of the arguments are undefined. Call by passing in * "arguments" from any function and an array of argument names. */ function check_undefined(args, names) { names.forEach(function (name, i) { if (args[i] === undefined) { console.error('Argument is undefined: ' + names[i]); } }); } function compartmentalize(bigg_id, compartment_id) { return bigg_id + '_' + compartment_id; } /** * Returns an array of [bigg_id, compartment id]. Matches compartment ids with * length 1 or 2. Return [ id, null ] if no match is found. */ function decompartmentalize(id) { var reg = /(.*)_([a-z0-9]{1,2})$/; var result = reg.exec(id); return result !== null ? result.slice(1, 3) : [id, null]; } function mean(array) { var sum = array.reduce(function (a, b) { return a + b; }); var avg = sum / array.length; return avg; } function median(array) { array.sort(function (a, b) { return a - b; }); var half = Math.floor(array.length / 2); if (array.length % 2 == 1) { return array[half]; } else { return (array[half - 1] + array[half]) / 2.0; } } function quartiles(array) { array.sort(function (a, b) { return a - b; }); var half = Math.floor(array.length / 2); if (array.length === 1) { return [array[0], array[0], array[0]]; } else if (array.length % 2 === 1) { return [median(array.slice(0, half)), array[half], median(array.slice(half + 1))]; } else { return [median(array.slice(0, half)), (array[half - 1] + array[half]) / 2.0, median(array.slice(half))]; } } /** * Generate random characters * * Thanks to @csharptest.net * http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript */ function random_characters(num) { var text = ''; var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; for (var i = 0; i < num; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; } function generate_map_id() { return random_characters(12); } /** * Check that the selection has the given parent tag. * @param {D3 Selection|DOM Node} el - A D3 Selection or DOM Node to check. * @param {String} tag - A tag name (case insensitive). */ function check_for_parent_tag(el, tag) { // make sure it is a node if (el instanceof d3_selection) { el = el.node(); } while (el.parentNode !== null) { el = el.parentNode; if (el.tagName === undefined) { continue; } if (el.tagName.toLowerCase() === tag.toLowerCase()) { return true; } } return false; } /** * Convert model or map name to url. * @param {String} name - The short name, e.g. e_coli.iJO1366.central_metabolism. * @param {String} download_url (optional) - The url to prepend. */ function name_to_url(name, download_url) { if (download_url !== undefined && download_url !== null) { // strip download_url download_url = download_url.replace(/^\/|\/$/g, ''); name = [download_url, name].join('/'); } // strip final path return name.replace(/^\/|\/$/g, '') + '.json'; } /** * Get the document for the node */ function get_document(node) { return node.ownerDocument; } /** * Get the window for the node */ function get_window(node) { return get_document(node).defaultView; } /** * Get translation and rotation values for a transform string. This used to be * in d3, but since v4, I just adapted a solution from SO: * * http://stackoverflow.com/questions/38224875/replacing-d3-transform-in-d3-v4 * * To get skew and scale out, go back to that example. * * TODO rename function without "catch" * * @param {String} transform_attr - A transform string. */ function d3_transform_catch(transform_attr) { if (transform_attr.indexOf('skew') !== -1 || transform_attr.indexOf('matrix') !== -1) { throw new Error('d3_transform_catch does not work with skew or matrix'); } var translate_res = /translate\s*\(\s*([0-9.-]+)\s*,\s*([0-9.-]+)\s*\)/.exec(transform_attr); var tn = _.isNull(translate_res); var tx = tn ? 0.0 : Number(translate_res[1]); var ty = tn ? 0.0 : Number(translate_res[2]); var rotate_res = /rotate\s*\(\s*([0-9.-]+)\s*\)/.exec(transform_attr); var rn = _.isNull(rotate_res); var r = rn ? 0.0 : Number(rotate_res[1]); var scale_res = /scale\s*\(\s*([0-9.-]+)\s*\)/.exec(transform_attr); var sn = _.isNull(scale_res); var s = sn ? 0.0 : Number(scale_res[1]); return { translate: [tx, ty], rotate: r, scale: s // // Create a dummy g for calculation purposes only. This will new be appended // // to the DOM and will be discarded once this function returns. // var g = document.createElementNS('http://www.w3.org/2000/svg', 'g') // // Set the transform attribute to the provided string value. // g.setAttributeNS(null, 'transform', transform_attr) // // Consolidate the SVGTransformList containing all Try to a single // // SVGTransform of type SVG_TRANSFORM_MATRIX and get its SVGMatrix. // var matrix = g.transform.baseVal.consolidate().matrix // // Below calculations are taken and adapted from the private func // // transform/decompose.js of D3's module d3-interpolate. // var a = matrix.a // var b = matrix.b // var c = matrix.c // var d = matrix.d // var e = matrix.e // var f = matrix.f // var scaleX = Math.sqrt(a * a + b * b) // if (scaleX) { // a /= scaleX // b /= scaleX // } // if (a * d < b * c) { // a = -a // b = -b // } // return { // translate: [ e, f ], // rotate: Math.atan2(b, a) * Math.PI / 180, // } }; } /** * Look for name in the user agent string. */ // function check_browser (name) { // var browser = function() { // // Thanks to // // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript // var ua = navigator.userAgent // var M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [] // var tem // if (/trident/i.test(M[1])) { // tem = /\brv[ :]+(\d+)/g.exec(ua) || [] // return 'IE '+ (tem[1] || '') // } // if (M[1] === 'Chrome') { // tem = ua.match(/\b(OPR|Edge)\/(\d+)/) // if (tem != null) return tem.slice(1).join(' ').replace('OPR', 'Opera') // } // M = M[2] ? [ M[1], M[2] ]: [ navigator.appName, navigator.appVersion, '-?' ] // if ((tem = ua.match(/version\/(\d+)/i)) !== null) { // M.splice(1, 1, tem[1]) // } // return M.join(' ') // } // try { // // navigator.userAgent is deprecated, so don't count on it // return browser().toLowerCase().indexOf(name) > -1 // } catch (e) { // return false // } // } function calculate_fva_opacity(flux, lower, upper) { if (Math.abs(lower - upper) < 1e-6) { return 1; } else if (lower < 0 && upper > 0) { return 0.2; } else if (lower <= 1e-6 || upper >= -1e-6) { return 0.5; } else if (lower > 1e-6 || upper < -1e-6) { return 0.8; } } // returns the first element function findMap(array, pred) { return array.reduce(function (accumulator, next) { return accumulator || pred(next); }, null); } function isMetabolite(metabolite) { var cofactors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Consts.cofactors; return !cofactors.includes(decompartmentalize(metabolite)[0]); } function htmlToElement(html) { var template = document.createElement('template'); html = html.trim(); // Never return a text node of whitespace as the result template.innerHTML = html; return template.content.firstChild; } function object_slice_for_bigg(obj, bigg_ids) { /** Return a copy of the object with just the given bigg ids. Arguments --------- obj: An object. ids: An array of bigg id strings. */ return _.pick(obj, function (r) { return _.contains(bigg_ids, r.bigg_id); }); } /** * * @param {*} reactions */ function get_central_or_last_nodes(reactions) { return Object.assign.apply(Object, [{}].concat(Object.entries(reactions).map(function (_ref) { var _ref2; var r_id = _ref[0], segments = _ref[1].segments; segments = Object.values(segments); if (segments.length >= 5) { segments = segments.filter(function (segment) { return segment.b1 === null && segment.b2 === null; }); } var nodes = segments.map(function (seg) { return [seg.from_node_id, seg.to_node_id]; }); var node = void 0; if (nodes.length === 1) { // Use the last node for reactions with one segment node = nodes[0][1]; } else { // Extract a not ending node node = nodes[0].filter(function (x) { return new Set(nodes[1]).has(x); }); } return _ref2 = {}, _ref2[r_id] = [node], _ref2; }))); } /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Underscore.js 1.8.3 // http://underscorejs.org // (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. var previousUnderscore = root._; // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. var push = ArrayProto.push, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind, nativeCreate = Object.create; // Naked function reference for surrogate-prototype-swapping. var Ctor = function(){}; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we're in // the browser, add `_` as a global object. if (true) { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } // Current version. _.VERSION = '1.8.3'; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore // functions. var optimizeCb = function(func, context, argCount) { if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { return func.call(context, value); }; case 2: return function(value, other) { return func.call(context, value, other); }; case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; }; // A mostly-internal function to generate callbacks that can be applied // to each element in a collection, returning the desired result — either // identity, an arbitrary callback, a property matcher, or a property accessor. var cb = function(value, context, argCount) { if (value == null) return _.identity; if (_.isFunction(value)) return optimizeCb(value, context, argCount); if (_.isObject(value)) return _.matcher(value); return _.property(value); }; _.iteratee = function(value, context) { return cb(value, context, Infinity); }; // An internal function for creating assigner functions. var createAssigner = function(keysFunc, undefinedOnly) { return function(obj) { var length = arguments.length; if (length < 2 || obj == null) return obj; for (var index = 1; index < length; index++) { var source = arguments[index], keys = keysFunc(source), l = keys.length; for (var i = 0; i < l; i++) { var key = keys[i]; if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key]; } } return obj; }; }; // An internal function for creating a new object that inherits from another. var baseCreate = function(prototype) { if (!_.isObject(prototype)) return {}; if (nativeCreate) return nativeCreate(prototype); Ctor.prototype = prototype; var result = new Ctor; Ctor.prototype = null; return result; }; var property = function(key) { return function(obj) { return obj == null ? void 0 : obj[key]; }; }; // Helper for collection methods to determine whether a collection // should be iterated as an array or as an object // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094 var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; var getLength = property('length'); var isArrayLike = function(collection) { var length = getLength(collection); return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX; }; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. // Handles raw objects in addition to array-likes. Treats all // sparse array-likes as if they were dense. _.each = _.forEach = function(obj, iteratee, context) { iteratee = optimizeCb(iteratee, context); var i, length; if (isArrayLike(obj)) { for (i = 0, length = obj.length; i < length; i++) { iteratee(obj[i], i, obj); } } else { var keys = _.keys(obj); for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj); } } return obj; }; // Return the results of applying the iteratee to each element. _.map = _.collect = function(obj, iteratee, context) { iteratee = cb(iteratee, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, results = Array(length); for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; results[index] = iteratee(obj[currentKey], currentKey, obj); } return results; }; // Create a reducing function iterating left or right. function createReduce(dir) { // Optimized iterator function as using arguments.length // in the main function will deoptimize the, see #1991. function iterator(obj, iteratee, memo, keys, index, length) { for (; index >= 0 && index < length; index += dir) { var currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; } return function(obj, iteratee, memo, context) { iteratee = optimizeCb(iteratee, context, 4); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, index = dir > 0 ? 0 : length - 1; // Determine the initial value if none is provided. if (arguments.length < 3) { memo = obj[keys ? keys[index] : index]; index += dir; } return iterator(obj, iteratee, memo, keys, index, length); }; } // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. _.reduce = _.foldl = _.inject = createReduce(1); // The right-associative version of reduce, also known as `foldr`. _.reduceRight = _.foldr = createReduce(-1); // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, predicate, context) { var key; if (isArrayLike(obj)) { key = _.findIndex(obj, predicate, context); } else { key = _.findKey(obj, predicate, context); } if (key !== void 0 && key !== -1) return obj[key]; }; // Return all the elements that pass a truth test. // Aliased as `select`. _.filter = _.select = function(obj, predicate, context) { var results = []; predicate = cb(predicate, context); _.each(obj, function(value, index, list) { if (predicate(value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, predicate, context) { return _.filter(obj, _.negate(cb(predicate)), context); }; // Determine whether all of the elements match a truth test. // Aliased as `all`. _.every = _.all = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true; }; // Determine if at least one element in the object matches a truth test. // Aliased as `any`. _.some = _.any = function(obj, predicate, context) { predicate = cb(predicate, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length; for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; if (predicate(obj[currentKey], currentKey, obj)) return true; } return false;