UNPKG

biojs-vis-inchlib

Version:
1,312 lines (1,108 loc) 604 kB
require=(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ /** /* * biojs-vis-inchlib * https://github.com/skutac/biojs-vis-inchlib * * Copyright (c) 2014 Ctibor Škuta * Licensed under the MIT license. */ /** * @class InChlib * InCHlib is an interactive JavaScript library which facilitates data * visualization and exploration by means of a cluster heatmap. InCHlib * is a versatile tool, and its use is not limited only to chemical or * biological data. Source code, tutorial, documentation, and example * data are freely available from InCHlib website <a * href="http://openscreen.cz/software/inchlib" * target=blank>http://openscreen.cz/software/inchlib</a>. At the * website, you can also find a Python script <a * href="http://openscreen.cz/software/inchlib/inchlib_clust" * target=blank>inchlib_clust</a> which performs data clustering and * prepares <a href="http://openscreen.cz/software/inchlib/input_format" * target=blank>input data for InCHlib</a>. * * @author <a href="mailto:ctibor.skuta@img.cas.cz">Ctibor Škuta</a> * @author <a href="mailto:petr.bartunek@img.cas.cz">Petr Bartůněk</a> * @author <a href="mailto:svozild@vscht.cz">Daniel Svozil</a> * @version 1.1.2 * @category 1 * @license InCHlib - Interactive Cluster Heatmap Library http://openscreen.cz/software/inchlib Copyright 2014, Ctibor Škuta, Petr Bartůněk, Daniel Svozil Licensed under the MIT license. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * @requires <a href='http://code.jquery.com/jquery-2.0.3.min.js'>jQuery Core 2.0.3</a> * @dependency <script language="JavaScript" type="text/javascript" src="http://code.jquery.com/jquery-2.0.3.min.js"></script> * * @requires <a href='http://kineticjs.com/'>KineticJS 5.1.0</a> * @dependency <script language="JavaScript" type="text/javascript" src="http://openscreen.cz/software/inchlib/static/js/kinetic-v5.1.0.min.js"></script> * * @param {Object} options An object with the options for the InCHlib component. * * @option {string} target * identifier of the DIV tag where the component should be displayed * @option {boolean} [column_dendrogram=false] * turn on/off the column dendrogram * @option {boolean} [count_column=false] * turn on/off the count column * @option {boolean} [dendrogram=true] * turn on/off the row dendrogram * @option {string} [font="Trebuchet&nbsp;MS"] * font family * @option {string} [heatmap_colors="Greens"] * the heatmap color scale * @option {number} [heatmap_part_width=0.7] * define the heatmap part width from the width of the whole graph * @option {string} [highlight_colors="Reds"] * color scale for highlighted rows * @option {obejct} [highlighted_rows=[]] * array of row IDs to highlight * @option {boolean} [independent_columns=true] * determines whether the color scale is based on the values from all columns together or for each column separately * @option {string} [label_color=grey] * color of column label * @option {number} [max_column_width=100] * maximum column width in pixels * @option {number} [max_height=800] * maximum graph height in pixels * @option {number} [max_row_height=25] * maximum row height in pixels * @option {boolean} [metadata=false] * turn on/off the metadata * @option {string} [metadata_colors="Oranges"] * the metadata color scale * @option {number} [min_row_height=false] * minimum row height in pixels * @option {number} [width="the width of target DIV"] * width of the graph in pixels * @option {boolean} [heatmap=true] * turn on/off the heatmap * @option {string} [heatmap_font_color="black"] * the color of the text values in the heatmap * @option {string} [count_column_colors="Reds"] * the color scale of count column * @option {boolean} [draw_row_ids=false] * draws the row IDs next to the heatmap when there is enough space to visualize them * @option {number} [max_percentile=100] * the value percentile above which the color will be equal to the terminal color of the color scale * @option {number} [min_percentile=0] * the value percentile below which the color will be equal to the beginning color of the color scale * @option {number} [middle_percentile=50] * the value percentile which defines where the middle color of the color scale will be used * * @example * window.instance = new InCHlib({ * target : "YourOwnDivId", * metadata: true, * max_height: 800, * width: 700, * metadata_colors: "RdLrBu" * }); * instance.read_data_from_file("../biojs/data/chembl_gr.json"); * instance.draw(); */ var _ = require("backbone"); var InCHlib, _this; var _date = new Date(); module.exports = InCHlib = function(settings){ _this = this; _this.user_settings = settings; _this.target_element = $("#" + settings.target); var target_width = _this.target_element.width(); _this.target_element.css({"position": "relative"}); /** * Default values for the settings * @name InCHlib#settings */ _this.settings = { "target" : "YourOwnDivId", "heatmap" : true, "heatmap_header": true, "dendrogram": true, "metadata": false, "column_metadata": false, "column_metadata_row_height": 8, "column_metadata_colors": "RdLrBu", "max_height" : 800, "width" : target_width, "heatmap_colors" : "Greens", "heatmap_font_color" : "black", "heatmap_part_width" : 0.7, "column_dendrogram" : false, "independent_columns" : true, "metadata_colors" : "Reds", "highlight_colors" : "Oranges", "highlighted_rows" : [], "label_color": "#9E9E9E", "count_column": false, "count_column_colors": "Reds", "min_row_height": false, "max_row_height": 25, "max_column_width": 150, "font": "Helvetica", "draw_row_ids": false, "show_export_button": true, "max_percentile": 100, "min_percentile": 0, "middle_percentile": 50, }; $.extend(_this.settings, settings); _this.settings.width = (settings.max_width && settings.max_width < target_width)?settings.max_width:_this.settings.width; _this.settings.heatmap_part_width = (_this.settings.heatmap_part_width>0.9)?0.9:_this.settings.heatmap_part_width; _this.header_height = 150; _this.footer_height = 70; _this.dendrogram_heatmap_distance = 5; /** * Default color scales * @name InCHlib#colors */ _this.colors = { "YlGn": {"start": {"r":255, "g": 255, "b": 204}, "end": {"r": 35, "g": 132, "b": 67}}, "GnBu": {"start": {"r":240, "g": 249, "b": 232}, "end": {"r": 43, "g": 140, "b": 190}}, "BuGn": {"start": {"r":237, "g": 248, "b": 251}, "end": {"r": 35, "g": 139, "b": 69}}, "PuBu": {"start": {"r":241, "g": 238, "b": 246}, "end": {"r": 5, "g": 112, "b": 176}}, "BuPu": {"start": {"r":237, "g": 248, "b": 251}, "end": {"r": 136, "g": 65, "b": 157}}, "RdPu": {"start": {"r":254, "g": 235, "b": 226}, "end": {"r": 174, "g": 1, "b": 126}}, "PuRd": {"start": {"r":241, "g": 238, "b": 246}, "end": {"r": 206, "g": 18, "b": 86}}, "OrRd": {"start": {"r":254, "g": 240, "b": 217}, "end": {"r": 215, "g": 48, "b": 31}}, "Purples2": {"start": {"r":242, "g": 240, "b": 247}, "end": {"r": 106, "g": 81, "b": 163}}, "Blues": {"start": {"r":239, "g": 243, "b": 255}, "end": {"r": 33, "g": 113, "b": 181}}, "Greens": {"start": {"r":237, "g": 248, "b": 233}, "end": {"r": 35, "g": 139, "b": 69}}, "Oranges": {"start": {"r":254, "g": 237, "b": 222}, "end": {"r": 217, "g": 71, "b": 1}}, "Reds": {"start": {"r":254, "g": 229, "b": 217}, "end": {"r": 203, "g": 24, "b": 29}}, "Greys": {"start": {"r":247, "g": 247, "b": 247}, "end": {"r": 82, "g": 82, "b": 82}}, "PuOr": {"start": {"r":230, "g": 97, "b": 1}, "end": {"r": 94, "g": 60, "b": 153}}, "BrBG": {"start": {"r":166, "g": 97, "b": 26}, "end": {"r": 1, "g": 133, "b": 113}}, "RdBu": {"start": {"r":202, "g": 0, "b": 32}, "end": {"r": 5, "g": 113, "b": 176}}, "RdGy": {"start": {"r":202, "g": 0, "b": 32}, "end": {"r": 64, "g": 64, "b": 64}}, "BuYl": {"start": {"r": 5, "g": 113, "b": 176}, "end": {"r": 250, "g": 233, "b": 42}}, "YlOrR": {"start": {"r":255, "g": 255, "b": 178}, "end": {"r": 227, "g": 26, "b": 28}, "middle": {"r": 204, "g": 76, "b": 2}}, "YlOrB": {"start": {"r":255, "g": 255, "b": 212}, "end": {"r": 5, "g": 112, "b": 176}, "middle": {"r": 204, "g": 76, "b": 2}}, "PRGn2": {"start": {"r":123, "g": 50, "b": 148}, "end": {"r": 0, "g": 136, "b": 55}, "middle": {"r":202, "g": 0, "b": 32}}, "PiYG2": {"start": {"r":208, "g": 28, "b": 139}, "end": {"r": 77, "g": 172, "b": 38}, "middle": {"r":255, "g": 255, "b": 178},}, "YlGnBu": {"start": {"r":255, "g": 255, "b": 204}, "end": {"r": 34, "g": 94, "b": 168}, "middle": {"r": 35, "g": 132, "b": 67}}, "RdYlBu": {"start": {"r":215, "g": 25, "b": 28}, "end": {"r": 44, "g": 123, "b": 182}, "middle": {"r":255, "g": 255, "b": 178}}, "RdYlGn": {"start": {"r":215, "g": 25, "b": 28}, "end": {"r": 26, "g": 150, "b": 65}, "middle": {"r":255, "g": 255, "b": 178}}, "BuWhRd": {"start": {"r": 33, "g": 113, "b": 181}, "middle": {"r": 255, "g": 255, "b": 255}, "end": {"r":215, "g": 25, "b": 28}}, "RdLrBu": {"start": {"r":215, "g": 25, "b": 28}, "middle": {"r":254, "g": 229, "b": 217}, "end": {"r": 44, "g": 123, "b": 182}}, "RdBkGr": {"start": {"r":215, "g": 25, "b": 28}, "middle": {"r": 0, "g": 0, "b": 0}, "end": {"r": 35, "g": 139, "b": 69}}, "RdLrGr": {"start": {"r":215, "g": 25, "b": 28}, "middle": {"r":254, "g": 229, "b": 217}, "end": {"r": 35, "g": 139, "b": 69}}, }; /** * Default kineticjs objects references * @name InCHlib#objects_ref */ _this.objects_ref = { "tooltip_label": new Kinetic.Label({ opacity: 1, listening: false, }), "tooltip_tag": new Kinetic.Tag({ fill: _this.settings.label_color, pointerWidth: 10, pointerHeight: 10, lineJoin: 'round', listening: false, }), "tooltip_text": new Kinetic.Text({ fontFamily: _this.settings.font, fontSize: 12, padding: 8, fill: 'white', fontStyle: "bold", listening: false, align: "center", lineHeight: 1.2, }), "node": new Kinetic.Line({ stroke: "grey", strokeWidth: 2, lineCap: 'sqare', lineJoin: 'round', listening: false }), "node_rect" : new Kinetic.Rect({ fill: "white", opacity: 0, }), "icon_overlay": new Kinetic.Rect({ width: 32, height: 32, opacity: 0, }), "heatmap_value": new Kinetic.Text({ fontFamily: _this.settings.font, fill: _this.settings.heatmap_font_color, fontStyle: "bold", listening: false, }), "heatmap_line": new Kinetic.Line({ lineCap: 'butt', value: false, }), "column_header": new Kinetic.Text({ fontFamily: _this.settings.font, fontStyle: "bold", fill: 'black', }), "count": new Kinetic.Text({ fontSize: 10, fill: "#6d6b6a", fontFamily: _this.settings.font, fontStyle: 'bold', listening: false, }), "cluster_overlay": new Kinetic.Rect({ fill: "white", opacity: 0.5, }), "cluster_border": new Kinetic.Line({ stroke: "black", strokeWidth: 1, dash: [6,2] }), "icon": new Kinetic.Path({ fill: "grey", }), "rect_gradient": new Kinetic.Rect({ x: 0, y: 80, width: 100, height: 20, fillLinearGradientStartPoint: {x: 0, y: 80}, fillLinearGradientEndPoint: {x: 100, y: 80}, stroke: "#D2D2D2", strokeWidth: "1px" }), }; _this.paths_ref = { "zoom_icon": "M22.646,19.307c0.96-1.583,1.523-3.435,1.524-5.421C24.169,8.093,19.478,3.401,13.688,3.399C7.897,3.401,3.204,8.093,3.204,13.885c0,5.789,4.693,10.481,10.484,10.481c1.987,0,3.839-0.563,5.422-1.523l7.128,7.127l3.535-3.537L22.646,19.307zM13.688,20.369c-3.582-0.008-6.478-2.904-6.484-6.484c0.006-3.582,2.903-6.478,6.484-6.486c3.579,0.008,6.478,2.904,6.484,6.486C20.165,17.465,17.267,20.361,13.688,20.369zM15.687,9.051h-4v2.833H8.854v4.001h2.833v2.833h4v-2.834h2.832v-3.999h-2.833V9.051z", "unzoom_icon": "M22.646,19.307c0.96-1.583,1.523-3.435,1.524-5.421C24.169,8.093,19.478,3.401,13.688,3.399C7.897,3.401,3.204,8.093,3.204,13.885c0,5.789,4.693,10.481,10.484,10.481c1.987,0,3.839-0.563,5.422-1.523l7.128,7.127l3.535-3.537L22.646,19.307zM13.688,20.369c-3.582-0.008-6.478-2.904-6.484-6.484c0.006-3.582,2.903-6.478,6.484-6.486c3.579,0.008,6.478,2.904,6.484,6.486C20.165,17.465,17.267,20.361,13.688,20.369zM8.854,11.884v4.001l9.665-0.001v-3.999L8.854,11.884z", "lightbulb": "M15.5,2.833c-3.866,0-7,3.134-7,7c0,3.859,3.945,4.937,4.223,9.499h5.553c0.278-4.562,4.224-5.639,4.224-9.499C22.5,5.968,19.366,2.833,15.5,2.833zM15.5,28.166c1.894,0,2.483-1.027,2.667-1.666h-5.334C13.017,27.139,13.606,28.166,15.5,28.166zM12.75,25.498h5.5v-5.164h-5.5V25.498z" }; } InCHlib.prototype._update_user_settings = function(settings){ var updated_settings = {}, key; for(var i = 0, keys=Object.keys(settings), len = keys.length; i < len; i++){ key = keys[i]; if(_this.user_settings[key] !== undefined && _this.user_settings[key] !== settings[key] && _this.user_settings[key] === true){ updated_settings[key] = false; } else if(_this.user_settings[key] === undefined){ updated_settings[key] = settings[key]; } } $.extend(_this.settings, updated_settings); } /** * Read data from JSON variable. * * @param {object} [variable] Clustering in proper JSON format. */ InCHlib.prototype.read_data = function(json){ _this.json = json; _this.data = _this.json.data; var settings = {}; if(_this.json["metadata"] !== undefined){ _this.metadata = _this.json.metadata; settings.metadata = true; } else{ settings.metadata = false; } if(_this.json["column_dendrogram"] !== undefined){ _this.column_dendrogram = _this.json.column_dendrogram; settings.column_dendrogram = true; } else{ settings.column_dendrogram = false; } if(_this.json["column_metadata"] !== undefined){ _this.column_metadata = _this.json.column_metadata; settings.column_metadata = true; } else{ settings.column_metadata = false; } _this._update_user_settings(settings); _this._add_prefix(); } /** * Read data from JSON file. * * @param {string} [filename] Path to the JSON data file. * */ InCHlib.prototype.read_data_from_file = function(json){ $.ajax({ type: 'GET', url: json, dataType: 'json', success: function(json_file){ _this.read_data(json_file); }, async: false }); } InCHlib.prototype._add_prefix = function(){ _this.data.nodes = _this._add_prefix_to_data(_this.data.nodes); if(_this.settings.metadata){ var metadata = {}; for(var i = 0, keys = Object.keys(_this.metadata.nodes), len = keys.length; i < len; i++){ id = [_this.settings.target, keys[i]].join("#"); metadata[id] = _this.metadata.nodes[keys[i]]; } _this.metadata.nodes = metadata; } if(_this.column_dendrogram){ _this.column_dendrogram.nodes = _this._add_prefix_to_data(_this.column_dendrogram.nodes); } } InCHlib.prototype._add_prefix_to_data = function(data){ var id, prefixed_data = {}; for(var i = 0, keys = Object.keys(data), len = keys.length; i < len; i++){ id = [_this.settings.target, keys[i]].join("#"); prefixed_data[id] = data[keys[i]]; if(prefixed_data[id]["parent"] !== undefined){ prefixed_data[id].parent = [_this.settings.target, prefixed_data[id].parent].join("#"); } if(prefixed_data[id]["count"] != 1){ prefixed_data[id].left_child = [_this.settings.target, prefixed_data[id].left_child].join("#"); prefixed_data[id].right_child = [_this.settings.target, prefixed_data[id].right_child].join("#"); } } return prefixed_data; } InCHlib.prototype._get_root_id = function(nodes){ var root_id; for(var i = 0, keys = Object.keys(nodes), len = keys.length; i < len; i++){ if(nodes[keys[i]]["parent"] === undefined){ root_id = keys[i]; break; } } return root_id; } InCHlib.prototype._get_dimensions = function(){ var dimensions = {"data": 0, "metadata": 0, "overall": 0}, key, keys, i; for(i = 0, keys = Object.keys(_this.data.nodes), len = keys.length; i < len; i++){ key = keys[i]; if(_this.data.nodes[key].count == 1){ dimensions["data"] = _this.data.nodes[key].features.length; break; } } if(_this.settings.metadata){ key = Object.keys(_this.metadata.nodes)[0]; dimensions["metadata"] = _this.metadata.nodes[key].length; } dimensions["overall"] = dimensions["data"] + dimensions["metadata"]; return dimensions; } InCHlib.prototype._get_min_max_middle = function(data){ var i, len; var min_max_middle = []; var all = []; for(i = 0, len = data.length; i<len; i++){ all = all.concat(data[i].filter(function(x){return x !== null})); } var len = all.length; all.sort(function(a,b){return a - b}); min_max_middle.push((_this.settings.min_percentile > 0)?all[_this._hack_round(len*_this.settings.min_percentile/100)]:Math.min.apply(null, all)); min_max_middle.push((_this.settings.max_percentile < 100)?all[_this._hack_round(len*_this.settings.max_percentile/100)]:Math.max.apply(null, all)); min_max_middle.push((_this.settings.middle_percentile != 50)?all[_this._hack_round(len*_this.settings.middle_percentile/100)]:all[_this._hack_round((len-1)/2)]); return min_max_middle; } InCHlib.prototype._get_data_min_max_middle = function(data, axis){ if(axis === undefined){ axis = "column"; } var i, j, value, len, columns; var data_length = data[0].length; if(axis == "column"){ columns = []; for(i = 0; i<data_length; i++){ columns.push([]); } for(i = 0; i<data.length; i++){ for(j = 0; j < data_length; j++){ value = data[i][j]; if(value !== null && value !== undefined){ columns[j].push(value); } } } } else{ columns = data.slice(0); } var data2descs = {} var data_min_max_middle = [], min, max, middle; for(i = 0; i<columns.length; i++){ if(_this._is_number(columns[i][0])){ columns[i] = columns[i].map(parseFloat); columns[i].sort(function(a,b){return a - b}); len = columns[i].length; max = (_this.settings.max_percentile < 100)?columns[i][_this._hack_round(len*_this.settings.max_percentile/100)]:Math.max.apply(null, columns[i]); min = (_this.settings.min_percentile > 0)?columns[i][_this._hack_round(len*_this.settings.min_percentile/100)]:Math.min.apply(null, columns[i]); middle = (_this.settings.middle_percentile != 50)?columns[i][_this._hack_round(len*_this.settings.middle_percentile/100)]:columns[i][_this._hack_round((len-1)/2)]; data2descs[i] = {"min": min, "max": max, "middle": middle}; } else{ var hash_object = _this._get_hash_object(columns[i]); min = 0; max = _this._hack_size(hash_object)-1; middle = max/2; data2descs[i] = {"min": min, "max": max, "middle": middle, "str2num": hash_object}; } } return data2descs; } InCHlib.prototype._get_hash_object = function(array){ var i, count=0, hash_object = {}; for(i = 0; i<array.length; i++){ if(hash_object[array[i]] === undefined){ hash_object[array[i]] = count; count++; } } return hash_object; } InCHlib.prototype._get_max_length = function(items){ var lengths = items.map(function(x){return (""+x).length}); var max = Math.max.apply(Math, lengths); return max; } InCHlib.prototype._get_max_value_length = function(){ var nodes = _this.data.nodes; var max_length = 0; var node_data, key; for(var i = 0, keys = Object.keys(nodes), len = keys.length; i < len; i++){ key = keys[i]; if(nodes[key].count == 1){ node_data = nodes[key].features; for(var j = 0, len_2 = node_data.length; j < len_2; j++){ if((""+node_data[j]).length > max_length){ max_length = (""+node_data[j]).length; } } } } if(_this.settings.metadata){ nodes = _this.metadata.nodes; for(var i = 0, keys = Object.keys(nodes), len = keys.length; i < len; i++){ key = keys[i]; node_data = nodes[key]; for(var j = 0, len_2 = node_data.length; j < len_2; j++){ if((""+node_data[j]).length > max_length){ max_length = (""+node_data[j]).length; } } } } return max_length; } InCHlib.prototype._preprocess_heatmap_data = function(){ var heatmap_array = [], i, j = 0, keys, key, len, data, node; for(i = 0, keys = Object.keys(_this.data.nodes), len = keys.length; i < len; i++){ key = keys[i]; node = _this.data.nodes[key]; if(node.count == 1){ data = node.features; heatmap_array.push([key]); heatmap_array[j].push.apply(heatmap_array[j], data); if(_this.settings.metadata){ heatmap_array[j].push.apply(heatmap_array[j], _this.metadata.nodes[key]); } j++; } } return heatmap_array; } InCHlib.prototype._reorder_heatmap = function(column_index){ _this.leaves_y_coordinates = {}; column_index++; if(_this.ordered_by_index == column_index){ _this.heatmap_array.reverse(); } else{ if(_this._is_number(_this.heatmap_array[0][column_index])){ _this.heatmap_array.sort(function(a,b){return (a[column_index] == null)?-1:(b[column_index] == null)?1:a[column_index] - b[column_index]}); } else{ _this.heatmap_array.sort(function(a,b){return (a[column_index] == null)?-1:(b[column_index] == null)?1:(a[column_index] > b[column_index])?1:(a[column_index] < b[column_index])?-1:0}); } } var y = _this.pixels_for_leaf/2 + _this.header_height; for(var i = 0, len = _this.heatmap_array.length; i<len; i++){ _this.leaves_y_coordinates[_this.heatmap_array[i][0]] = y; y += _this.pixels_for_leaf; } _this.ordered_by_index = column_index; } /** * Draw already read data (from file/JSON variable). */ InCHlib.prototype.draw = function(){ _this.zoomed_clusters = {"row": [], "column": []}; _this.last_highlighted_cluster = null; _this.current_object_ids = []; _this.current_column_ids = []; _this.heatmap_array = _this._preprocess_heatmap_data(); if(_this.settings.heatmap){ _this.last_column = null; _this.on_features = {"data":[], "metadata":[]} _this.dimensions = _this._get_dimensions(); _this.column_metadata_rows = (_this.settings.column_metadata)?_this.column_metadata.features.length:0; _this.column_metadata_height = _this.column_metadata_rows * _this.settings.column_metadata_row_height; _this._set_heatmap_settings(); _this._adjust_leaf_size(_this.heatmap_array.length); } else{ _this._adjust_horizontal_sizes(); _this.dimensions = {"data": 0, "metadata": 0, "overall": 0}; } if(_this.settings.column_dendrogram && _this.heatmap_header){ _this.footer_height = 150; } _this.stage = new Kinetic.Stage({ container: _this.settings.target, }); _this.settings.height = _this.heatmap_array.length*_this.pixels_for_leaf+_this.header_height+_this.footer_height; _this.stage.setWidth(_this.settings.width); _this.stage.setHeight(_this.settings.height); _this._draw_stage_layer(); if(_this.settings.dendrogram){ _this.timer = 0; _this._draw_dendrogram_layers(); _this.root_id = _this._get_root_id(_this.data.nodes); _this._draw_row_dendrogram(_this.root_id); if(_this.settings.column_dendrogram && _this.settings.dendrogram){ _this.column_root_id = _this._get_root_id(_this.column_dendrogram.nodes); _this.nodes2columns = false; _this.columns_start_index = 0; _this._draw_column_dendrogram(_this.column_root_id); } } else{ _this.settings.column_dendrogram = false; _this._reorder_heatmap(0); _this.ordered_by_index = 0; } _this._draw_heatmap(); _this._draw_heatmap_header(); _this._draw_navigation(); _this.highlight_rows(_this.settings.highlighted_rows); } InCHlib.prototype._draw_dendrogram_layers = function(){ _this.cluster_layer = new Kinetic.Layer(); _this.dendrogram_hover_layer = new Kinetic.Layer(); _this.stage.add(_this.cluster_layer, _this.dendrogram_hover_layer); _this.cluster_layer.on("click", function(evt){ _this.unhighlight_cluster(); _this.unhighlight_column_cluster(); _this.trigger("empty_space_onclick", evt); }); }; InCHlib.prototype._draw_row_dendrogram = function(node_id){ _this.dendrogram_layer = new Kinetic.Layer(); var node = _this.data.nodes[node_id]; var count = node.count; _this.distance_step = _this.distance/node.distance; _this.leaves_y_coordinates = {}; _this.objects2leaves = {}; _this._adjust_leaf_size(count); _this.settings.height = count*_this.pixels_for_leaf+_this.header_height+_this.footer_height+_this.column_metadata_height; _this.stage.setWidth(_this.settings.width); _this.stage.setHeight(_this.settings.height); var current_left_count = 0; var current_right_count = 0; var y = _this.header_height + _this.column_metadata_height + _this.pixels_for_leaf/2; if(node.count > 1){ current_left_count = _this.data.nodes[node.left_child].count; current_right_count = _this.data.nodes[node.right_child].count; } _this._draw_row_dendrogram_node(node_id, node, current_left_count, current_right_count, 0, y); _this.middle_item_count = (_this.min_item_count+_this.max_item_count)/2; _this._draw_distance_scale(node.distance); _this.stage.add(_this.dendrogram_layer); _this._bind_dendrogram_hover_events(_this.dendrogram_layer); _this.dendrogram_layer.on("click", function(evt){ _this._dendrogram_layers_click(this, evt); }); _this.dendrogram_layer.on("mousedown", function(evt){ _this._dendrogram_layers_mousedown(this, evt); }); _this.dendrogram_layer.on("mouseup", function(evt){ _this._dendrogram_layers_mouseup(this, evt); }); } InCHlib.prototype._draw_row_dendrogram_node = function(node_id, node, current_left_count, current_right_count, x, y){ if(node.count != 1){ var node_neighbourhood = _this._get_node_neighbourhood(node, _this.data.nodes); var right_child = _this.data.nodes[node.right_child]; var left_child = _this.data.nodes[node.left_child]; var y1 = _this._get_y1(node_neighbourhood, current_left_count, current_right_count); var y2 = _this._get_y2(node_neighbourhood, current_left_count, current_right_count); var x1 = _this._hack_round(_this.distance - _this.distance_step*node.distance); x1 = (x1 == 0)? 2: x1; var x2 = x1; var left_distance = _this.distance - _this.distance_step*_this.data.nodes[node.left_child].distance; var right_distance = _this.distance - _this.distance_step*_this.data.nodes[node.right_child].distance; if(right_child.count == 1){ y2 = y2 + _this.pixels_for_leaf/2; } _this.dendrogram_layer.add(_this._draw_horizontal_path(node_id, x1, y1, x2, y2, left_distance, right_distance)); _this._draw_row_dendrogram_node(node.left_child, left_child, current_left_count - node_neighbourhood.left_node.right_count, current_right_count + node_neighbourhood.left_node.right_count, left_distance, y1); _this._draw_row_dendrogram_node(node.right_child, right_child, current_left_count + node_neighbourhood.right_node.left_count, current_right_count - node_neighbourhood.right_node.left_count, right_distance, y2); } else{ var objects = node.objects; _this.leaves_y_coordinates[node_id] = y; for(var i = 0, len = objects.length; i<len; i++){ _this.objects2leaves[objects[i]] = node_id; } var count = node.objects.length; if(count<_this.min_item_count){ _this.min_item_count = count; } if(count>_this.max_item_count){ _this.max_item_count = count; } } } InCHlib.prototype._draw_stage_layer = function(){ _this.stage_layer = new Kinetic.Layer(); var stage_rect = new Kinetic.Rect({ x: 0, y: 0, width: _this.settings.width, height: _this.settings.height, opacity: 0, }); _this.stage_layer.add(stage_rect); stage_rect.moveToBottom(); _this.stage.add(_this.stage_layer); _this.stage_layer.on("click", function(evt){ _this.unhighlight_cluster(); _this.unhighlight_column_cluster(); _this.trigger("empty_space_onclick", evt); }); } InCHlib.prototype._draw_column_dendrogram = function(node_id){ _this.column_dendrogram_layer = new Kinetic.Layer(); _this.column_x_coordinates = {}; var node = _this.column_dendrogram.nodes[node_id]; _this.current_column_count = node.count; _this.vertical_distance = _this.header_height; _this.vertical_distance_step = _this.vertical_distance/node.distance; _this.last_highlighted_column_cluster = null; var current_left_count = _this.column_dendrogram.nodes[node.left_child].count; var current_right_count = _this.column_dendrogram.nodes[node.right_child].count; _this._draw_column_dendrogram_node(node_id, node, current_left_count, current_right_count, 0, 0); _this.stage.add(_this.column_dendrogram_layer); if(!_this.nodes2columns){ _this.nodes2columns = _this._get_nodes2columns(); } _this._bind_dendrogram_hover_events(_this.column_dendrogram_layer); _this.column_dendrogram_layer.on("click", function(evt){ _this._column_dendrogram_layers_click(this, evt); }); _this.column_dendrogram_layer.on("mousedown", function(evt){ _this._column_dendrogram_layers_mousedown(this, evt); }); _this.column_dendrogram_layer.on("mouseup", function(evt){ _this._dendrogram_layers_mouseup(this, evt); }); } InCHlib.prototype._get_nodes2columns = function(){ var coordinates = []; var coordinates2nodes = {}; var nodes2columns = {}; var key, value, i; for(i = 0, keys = Object.keys(_this.column_x_coordinates), len = keys.length; i < len; i++){ key = keys[i]; value = _this.column_x_coordinates[key]; coordinates2nodes[value] = key; coordinates.push(value); } coordinates.sort(function(a,b){return a - b}); for(i = 0, len = coordinates.length; i<len; i++){ nodes2columns[coordinates2nodes[coordinates[i]]] = i; } return nodes2columns; } InCHlib.prototype._bind_dendrogram_hover_events = function(layer){ layer.on("mouseover", function(evt){ _this._dendrogram_layers_mouseover(this, evt); }); layer.on("mouseout", function(evt){ _this._dendrogram_layers_mouseout(this, evt); }); } InCHlib.prototype._delete_layers = function(to_destroy, to_remove_children){ for(var i = 0, len = to_destroy.length; i < len; i++){ if(to_destroy[i] !== undefined){ to_destroy[i].destroy(); } } if(to_remove_children !== undefined){ for(var i = 0, len = to_remove_children.length; i < len; i++){ to_remove_children[i].removeChildren(); to_remove_children[i].draw(); } } } InCHlib.prototype._delete_all_layers = function(){ _this.stage.destroyChildren(); } InCHlib.prototype._adjust_leaf_size = function(leaves){ _this.pixels_for_leaf = (_this.settings.max_height-_this.header_height-_this.footer_height-_this.column_metadata_height-5)/leaves; if(_this.pixels_for_leaf > 2*_this.pixels_for_dimension){ _this.pixels_for_leaf = 2*_this.pixels_for_dimension; } if(_this.pixels_for_leaf > _this.settings.max_row_height){ _this.pixels_for_leaf = _this.settings.max_row_height; } if(_this.settings.min_row_height > _this.pixels_for_leaf){ _this.pixels_for_leaf = _this.settings.min_row_height; } } InCHlib.prototype._adjust_horizontal_sizes = function(dimensions){ if(dimensions === undefined){ dimensions = _this._get_visible_count(); } _this.right_margin = 100; if(_this.settings.dendrogram){ _this.heatmap_width = (_this.settings.width - _this.right_margin - _this.dendrogram_heatmap_distance)*_this.settings.heatmap_part_width; _this.distance = _this.settings.width - _this.heatmap_width - _this.right_margin; _this.heatmap_distance = _this.distance + _this.dendrogram_heatmap_distance; } else{ _this.heatmap_width = _this.settings.width - _this.right_margin; _this.distance = _this.right_margin/2; _this.heatmap_distance = _this.distance; } _this.pixels_for_dimension = dimensions?_this.heatmap_width/dimensions:0; if(_this.settings.max_column_width && _this.settings.max_column_width < _this.pixels_for_dimension){ _this.pixels_for_dimension = _this.settings.max_column_width; _this.heatmap_width = dimensions*_this.pixels_for_dimension; if(_this.settings.dendrogram){ _this.distance = _this.settings.width - _this.heatmap_width - _this.right_margin - _this.dendrogram_heatmap_distance; _this.heatmap_distance = _this.distance + _this.dendrogram_heatmap_distance; } else{ _this.distance = _this._hack_round((_this.settings.width - _this.heatmap_width)/2); _this.right_margin = _this.distance; _this.heatmap_distance = _this.distance; } } } InCHlib.prototype._set_color_settings = function(){ var data = []; for(i = 0, keys = Object.keys(_this.data.nodes), len = keys.length; i < len; i++){ node = _this.data.nodes[keys[i]]; if(node.count == 1){ data.push(node.features); }; } _this.data_descs = {}; if(_this.settings.independent_columns){ _this.data_descs = _this._get_data_min_max_middle(data); } else{ var min_max_middle = _this._get_min_max_middle(data); for(i = 0; i < _this.dimensions["data"]; i++){ _this.data_descs[i] = {"min": min_max_middle[0], "max": min_max_middle[1], "middle": min_max_middle[2]}; } } if(_this.settings.metadata){ var metadata = []; for(i = 0, keys = Object.keys(_this.metadata.nodes), len = keys.length; i < len; i++){ metadata.push(_this.metadata.nodes[keys[i]]); } _this.metadata_descs = _this._get_data_min_max_middle(metadata); } } InCHlib.prototype._set_heatmap_settings = function(){ var i, keys, key, len, node; _this.header = []; for(i = 0; i<_this.dimensions["overall"]; i++){ _this.header.push(""); } _this.heatmap_header = false; _this.metadata_header = false; _this.current_label = null; _this._set_color_settings(); if(_this.data.feature_names !== undefined){ _this.heatmap_header = _this.data.feature_names; for(i=0; i<_this.dimensions["data"]; i++){ _this.header[i] = _this.heatmap_header[i]; } } if(_this.settings.metadata){ if(_this.metadata.feature_names){ _this.metadata_header = _this.metadata.feature_names; for(i=0; i<_this.dimensions["metadata"]; i++){ _this.header[_this.dimensions["data"]+i] = _this.metadata_header[i]; } } } if(_this.settings.column_metadata){ if(_this.column_metadata.feature_names !== undefined){ _this.column_metadata_header = _this.column_metadata.feature_names; } } if(_this.settings.count_column){ _this.max_item_count = 1; _this.min_item_count = 1; _this.dimensions["overall"]++; _this.header.push("Count"); } _this.features = {}; for(i=0; i<_this.dimensions["overall"]; i++){ _this.features[i] = true; } _this._set_on_features(); _this._adjust_horizontal_sizes(); _this.top_heatmap_distance = _this.header_height + _this.column_metadata_height + _this.settings.column_metadata_row_height/2; } InCHlib.prototype._set_on_features = function(features){ var key; if(features === undefined){ var features = []; for(var i = 0, keys = Object.keys(_this.features), len = keys.length; i < len; i++){ key = keys[i]; if(_this.features[key]){ features.push(key); } } } _this.on_features = {"data":[], "metadata":[], "count_column":[]} for(var i = 0, len = features.length; i < len; i++){ key = features[i]; if(key < _this.dimensions["data"]){ _this.on_features["data"].push(key); } else if(key <= _this.dimensions["data"] + _this.dimensions["metadata"] - 1){ _this.on_features["metadata"].push(key-_this.dimensions["data"]); } else{ _this.on_features["count_column"].push(0); } } } InCHlib.prototype._draw_heatmap = function(){ if(!_this.settings.heatmap || _this.dimensions["overall"]==0){ return; } var heatmap_row, row_id, col_number, col_label, row_values, y; _this.heatmap_layer = new Kinetic.Layer(); _this.heatmap_overlay = new Kinetic.Layer(); _this.highlighted_rows_y = []; _this.current_draw_values = true; _this.max_value_length = _this._get_max_value_length(); _this.value_font_size = _this._get_font_size(_this.max_value_length, _this.pixels_for_dimension, _this.pixels_for_leaf, 12); if(_this.value_font_size < 4){ _this.current_draw_values = false; } var x1 = _this.heatmap_distance; var current_leaves_y = []; for(var i = 0, keys = Object.keys(_this.leaves_y_coordinates), len = keys.length; i < len; i++){ key = keys[i]; y = _this.leaves_y_coordinates[key]; heatmap_row = _this._draw_heatmap_row(key, x1, y); _this.heatmap_layer.add(heatmap_row); current_leaves_y.push([key, y]); _this._bind_row_events(heatmap_row); } if(_this.settings.column_metadata){ _this.column_metadata_descs = _this._get_data_min_max_middle(_this.column_metadata.features, "row"); y1 = _this.header_height + 0.5*_this.settings.column_metadata_row_height; for(var i = 0, len = _this.column_metadata.features.length; i < len; i++){ heatmap_row = _this._draw_column_metadata_row(_this.column_metadata.features[i], i, x1, y1); _this.heatmap_layer.add(heatmap_row); _this._bind_row_events(heatmap_row); y1 = y1 + _this.settings.column_metadata_row_height; } } if(_this.settings.draw_row_ids){ _this._draw_row_ids(current_leaves_y); } _this.highlighted_rows_layer = new Kinetic.Layer(); _this.stage.add(_this.heatmap_layer, _this.heatmap_overlay, _this.highlighted_rows_layer); _this.highlighted_rows_layer.moveToTop(); _this.row_overlay = _this.objects_ref.heatmap_line.clone(); _this.column_overlay = _this.objects_ref.heatmap_line.clone(); _this.heatmap_layer.on("mouseleave", function(evt){ _this.last_header = null; _this.heatmap_overlay.destroyChildren(); _this.heatmap_overlay.draw(); _this.trigger("heatmap_onmouseout", evt); }); } InCHlib.prototype._draw_heatmap_row = function(node_id, x1, y1){ var node = _this.data.nodes[node_id]; var row = new Kinetic.Group({id:node_id}); var x2, y2, color, line, value, text, text_value, col_index; for (var i = 0, len = _this.on_features["data"].length; i < len; i++){ col_index = _this.on_features["data"][i]; x2 = x1 + _this.pixels_for_dimension; y2 = y1; value = node.features[col_index]; if(value !== null){ color = _this._get_color_for_value(value, _this.data_descs[col_index]["min"], _this.data_descs[col_index]["max"], _this.data_descs[col_index]["middle"], _this.settings.heatmap_colors); line = _this.objects_ref.heatmap_line.clone({ stroke: color, points: [x1, y1, x2, y2], value: value, column: ["d", col_index].join("_"), strokeWidth: _this.pixels_for_leaf, }); row.add(line); if(_this.current_draw_values){ text = _this.objects_ref.heatmap_value.clone({ x: _this._hack_round((x1 + x2)/2-(""+value).length*(_this.value_font_size/4)), y: _this._hack_round(y1-_this.value_font_size/2), fontSize: _this.value_font_size, text: value, }); row.add(text); } } x1 = x2; } if(_this.settings.metadata){ var metadata = _this.metadata.nodes[node_id]; if(metadata !== undefined){ for (var i = 0, len = _this.on_features["metadata"].length; i < len; i++){ col_index = _this.on_features["metadata"][i]; value = metadata[col_index]; x2 = x1 + _this.pixels_for_dimension; y2 = y1; if(value !== null && value !== undefined){ text_value = value; if(_this.metadata_descs[col_index]["str2num"] !== undefined){ value = _this.metadata_descs[col_index]["str2num"][value]; } color = _this._get_color_for_value(value, _this.metadata_descs[col_index]["min"], _this.metadata_descs[col_index]["max"], _this.metadata_descs[col_index]["middle"], _this.settings.metadata_colors); line = _this.objects_ref.heatmap_line.clone({ stroke: color, points: [x1, y1, x2, y2], value: text_value, column: ["m", col_index].join("_"), strokeWidth: _this.pixels_for_leaf, }); row.add(line); if(_this.current_draw_values){ text = _this.objects_ref.heatmap_value.clone({ text: text_value, fontSize: _this.value_font_size, }); width = text.getWidth(); x = _this._hack_round((x1+x2)/2-width/2); y = _this._hack_round(y1-_this.value_font_size/2); text.position({x:x, y:y}); row.add(text); } } x1 = x2; } } } if(_this.settings.count_column && _this.features[_this.dimensions["overall"]-1]){ x2 = x1 + _this.pixels_for_dimension; var count = node.objects.length; color = _this._get_color_for_value(count, _this.min_item_count, _this.max_item_count, _this.middle_item_count, _this.settings.count_column_colors); line = _this.objects_ref.heatmap_line.clone({ stroke: color, points: [x1, y1, x2, y2], value: count, column: "Count", strokeWidth: _this.pixels_for_leaf, }); row.add(line); if(_this.current_draw_values){ text = _this.objects_ref.heatmap_value.clone({ text: count, }); width = text.getWidth(); x = _this._hack_round((x1+x2)/2-width/2); y = _this._hack_round(y1-_this.value_font_size/2); text.position({x:x, y:y}); row.add(text); } } return row; } InCHlib.prototype._draw_column_metadata_row = function(data, row_index, x1, y1){ var row = new Kinetic.Group({"class": "column_metadata"}); var x2, y2, color, line, value, text, text_value, width, col_index; var str2num = (_this.column_metadata_descs[row_index]["str2num"] === undefined)?false:true; for (var i = 0, len = _this.on_features["data"].length; i < len; i++){ col_index = _this.on_features["data"][i]; value = data[col_index]; text_value = value; if(str2num){ value = _this.column_metadata_descs[row_index]["str2num"][value]; } color = _this._get_color_for_value(value, _this.column_metadata_descs[row_index]["min"], _this.column_metadata_descs[row_index]["max"], _this.column_metadata_descs[row_index]["middle"], _this.settings.column_metadata_colors); x2 = x1 + _this.pixels_for_dimension; y2 = y1; line = _this.objects_ref.heatmap_line.clone({ strokeWidth: _this.settings.column_metadata_row_height, stroke: color, value: text_value, points: [x1, y1, x2, y2], column: ["cm", row_index].join("_"), }); row.add(line); x1 = x2; } return row; } InCHlib.prototype._bind_row_events = function(row){ row.on("mouseenter", function(evt){ _this._row_mouseenter(evt); }); row.on("mouseleave", function(evt){ _this._row_mouseleave(evt); }); row.on("mouseover", function(evt){ _this._draw_col_label(evt); }); row.on("mouseout", function(evt){ _this.heatmap_overlay.find("#col_label")[0].destroy(); }); row.on("click", function(evt){ var row_id = evt.target.parent.attrs.id; if(evt.target.parent.attrs.class !== "column_metadata"){ var items = _this.data.nodes[row_id].objects; var item_ids = []; for(i = 0; i < items.length; i++){ item_ids.push(items[i]); } _this.trigger("row_onclick", item_ids, evt); } }); } InCHlib.prototype._draw_row