/**
* rows and columns can contain field, renameTo, display, order
*
*/
morpheus.HeatMap = function (options) {
morpheus.Util.loadTrackingCode();
var _this = this;
options = $
.extend(
true,
{},
{
/**
* The element in which to render to the heat map.
*/
el: null,
/**
* A File or URL to a GCT
* 1.3, ' + 'GCT
* 1.2, ' + 'MAF, ' + 'GMT, ' + '
* or a tab-delimitted text file. Can also be an array
* of File or URLs in which case the datasets are
* combined by matching on column ids.
*/
dataset: undefined,
/**
*
* @description Array of file, datasetField, fileField,
* and include (optional fields to include
* from file). File can be xlsx file,
* tab-delimitted text file, or gmt file.
*
* Example: Annotate rows matching
* 'name' field in dataset to 'id' field in
* file.
*
* [{file:'https://MY_URL', datasetField:'name', fileField:'id'}]
*/
rowAnnotations: undefined,
/**
* Array of file, datasetField, fileField, and include
* (optional fields to include from file). File can be
* xlsx file, tab-delimitted text file, or gmt file.
*/
columnAnnotations: undefined,
/**
* Array of column metadata names to group the heat map
* by.
*
*
* Example: Group by the type and gender
* metadata field.
*
*
* ['type', 'gender']
*/
columnGroupBy: undefined,
/**
* Array of row metadata names to group the heat map by.
*
*
* Example: Group by the gene metadata field.
*
* ['gene']
*/
rowGroupBy: undefined,
/**
* Object that describes mapping of values to colors.
* Type can be 'fixed' or 'relative'. Stepped indicates
* whether color scheme is continuous (false) or
* discrete (true).
*
* Example: Use a fixed color scheme with color
* stops at -100, -90, 90, and 100.
*
* { type : 'fixed', stepped:false, map : [ { value : -100, color :
* 'blue' }, { value : -90, color : 'white' }, { value :
* 90, color : 'white' }, { value : 100, color : 'red' } ] };
*/
colorScheme: undefined,
/**
* Array of metadata names and sort order. Use 0 for
* ascending and 1 for descending. To sort by values use
* modelIndices.
*
*
* Example: Sort ascending by gene, and then
* descending by stdev
*
* [{field:'gene', order:0}, {field:'stdev',
* order:1}]
*/
rowSortBy: undefined,
/**
* Array of metadata names and sort order. Use 0 for
* ascending and 1 for descending.
*
*
* Example: to sort ascending by gene, and then
* descending by stdev
*
* [{name:'gene',
* order:0}, {name:'stdev', order:1}]
*/
columnSortBy: undefined,
/**
* URL to a dendrogram in Newick
* format
*/
rowDendrogram: undefined,
/**
* URL to a dendrogram in Newick
* format
*/
columnDendrogram: undefined,
/**
* Column metadata field in dataset used to match leaf
* node ids in column dendrogram Newick file
*/
columnDendrogramField: 'id',
/**
* Row metadata field in dataset used to match leaf node
* ids in row dendrogram Newick file
*/
rowDendrogramField: 'id',
/**
* Array of objects describing how to display row
* metadata fields. Each object in the array must have
* field, and optionally display, order, and renameTo.
* Field is the metadata field name. Display is a comma
* delimited string that describes how to render a
* metadata field. Options are text, color, stacked_bar,
* bar, highlight, shape, discrete, and continuous.
* Order is a number that indicates the order in which
* the field should appear in the heat map. RenameTo
* allows you to rename a field.
*/
rows: [],
/**
* Array of objects describing how to display column
* metadata fields. Each object in the array must have
* field, and optionally display, order, and renameTo.
* Field is the metadata field name. Display is a comma
* delimited string that describes how to render a
* metadata field. Options are text, color, stacked_bar,
* bar, highlight, shape, discrete, and continuous.
* Order is a number that indicates the order in which
* the field should appear in the heat map. RenameTo
* allows you to rename a field.
*/
columns: [],
/**
* Optional array of tools to run at load time. For
* example: tools : [ {
* name : 'Marker Selection',
* params : {
* field : [ comparisonVector.getName() ],
* class_a : [ 'A' ], class_b : [ 'B' ] }} ]
*/
tools: undefined,
/**
* Optional array of {name:string, values:[]}
*/
rowFilter: undefined,
columnFilter: undefined,
customUrls: undefined, // Custom urls for File>Open.
height: 'window', // set the available height for the
// heat map. If not
// set, it will be determined automatically
width: undefined, // set the available width for the
// heat map. If not
// set, it will be determined automatically
inheritFromParent: true,
inheritFromParentOptions: {
transpose: false
},
structureUrlProvider: undefined,
promises: undefined, // additional promises to wait
// for
renderReady: undefined,
datasetReady: undefined,
loadedCallback: undefined,
name: undefined,
rowsSortable: true,
columnsSortable: true,
popupEnabled: true,
symmetric: false,
keyboard: true,
inlineTooltip: true,
$loadingImage: $('Loading...
'),
/**
* Whether this heat map tab can be closed
*/
closeable: true,
toolbar: {
zoom: true,
tools: true,
searchRows: true,
searchColumns: true,
sort: true,
options: true,
saveImage: true,
saveDataset: true,
openFile: true,
filter: true,
colorKey: true,
searchValues: false
}
}, options);
this.options = options;
this.tooltipProvider = morpheus.HeatMapTooltipProvider;
if (!options.el) {
this.$el = $('');
} else {
this.$el = $(options.el);
}
this.$el.addClass('morpheus');
if (!options.landingPage) {
options.landingPage = new morpheus.LandingPage();
options.landingPage.$el.prependTo(this.$el);
}
if (!this.options.name) {
this.options.name = morpheus.Util
.getBaseFileName(morpheus.Util
.getFileName(this.options.dataset.file ? this.options.dataset.file
: this.options.dataset));
}
var isPrimary = this.options.parent == null;
if (this.options.parent == null) {
this.tabManager = this.options.tabManager ? this.options.tabManager
: new morpheus.TabManager({
landingPage: this.options.landingPage
});
// if (window.location.hostname.indexOf('clue.io') === -1
// && window.location.pathname.indexOf('cancer/software/morpheus') ===
// -1) {
if (!morpheus.HelpMenu.ADDED) { // only show once per page
morpheus.HelpMenu.ADDED = true;
var $a = $('
');
$a.tooltip({
placement: 'auto'
});
// var $img = $a.find('img');
var $right = $('');
$a.appendTo($right);
new morpheus.HelpMenu().$el.appendTo($right);
$right.appendTo(this.tabManager.$nav);
}
if (!this.options.tabManager) {
this.tabManager.$nav.appendTo(this.$el);
this.tabManager.$tabContent.appendTo(this.$el);
}
} else {
if (this.options.inheritFromParent) {
this.popupItems = this.options.parent.popupItems;
if (!this.options.tabOpened) {
this.options.tabOpened = this.options.parent.options.tabOpened;
}
this.options.drawCallback = this.options.parent.options.drawCallback;
}
this.tabManager = this.options.parent.tabManager;
}
this.$content = $('');
this.$content.css({
'width': '100%',
'user-select': 'none',
'-webkit-user-select': 'none',
'-webkit-user-drag': 'none',
'-webkit-tap-highlight-color': 'rgba(0, 0, 0, 0)',
'-moz-user-select': 'none',
'-moz-user-drag': 'none',
'-moz-tap-highlight-color': 'rgba(0, 0, 0, 0)',
'-ms-user-select': 'none',
'-ms-user-drag': 'none',
'-ms-tap-highlight-color': 'rgba(0, 0, 0, 0)',
'-o-user-select': 'none',
'-o-user-drag': 'none',
'-o-tap-highlight-color': 'rgba(0, 0, 0, 0)',
'overflow-x': 'visible',
'overflow-y': 'visible'
});
var tab = this.tabManager.add({
$el: this.$content,
closeable: this.options.closeable,
rename: true,
title: this.options.name,
object: this,
focus: true
});
if (options.$loadingImage) {
options.$loadingImage.appendTo(this.$content);
}
this.tabId = tab.id;
this.$tabPanel = tab.$panel;
this.options.dataSource = !options.dataset ? ''
: (options.dataset.file ? options.dataset.file : options.dataset);
this._togglingInfoWindow = false;
this.tooltipMode = 0; // 0=docked, 1=dialog, 2=follow
var promises = [];
if (options.promises) {
for (var i = 0; i < options.promises.length; i++) {
promises.push(options.promises[i]);
}
}
this.whenLoaded = [];
if (options.rowAnnotations) {
var rowDef = morpheus.DatasetUtil.annotate({
annotations: options.rowAnnotations,
isColumns: false
});
rowDef.done(function (callbacks) {
_this.whenLoaded = _this.whenLoaded.concat(callbacks);
});
promises.push(rowDef);
}
if (options.columnAnnotations) {
var columnDef = morpheus.DatasetUtil.annotate({
annotations: options.columnAnnotations,
isColumns: true
});
columnDef.done(function (callbacks) {
_this.whenLoaded = _this.whenLoaded.concat(callbacks);
});
promises.push(columnDef);
}
if (options.rowDendrogram !== undefined
&& _.isString(options.rowDendrogram)) {
var rowDendrogramDeferred = morpheus.Util
.getText(options.rowDendrogram);
rowDendrogramDeferred.done(function (text) {
_this.options.rowDendrogram = morpheus.AbstractDendrogram
.parseNewick(text);
});
promises.push(rowDendrogramDeferred);
}
if (options.columnDendrogram !== undefined
&& _.isString(options.columnDendrogram)) {
var columnDendrogramDeferred = morpheus.Util
.getText(options.columnDendrogram);
columnDendrogramDeferred.done(function (text) {
_this.options.columnDendrogram = morpheus.AbstractDendrogram
.parseNewick(text);
});
promises.push(columnDendrogramDeferred);
}
var heatMapLoaded = function () {
if (typeof window !== 'undefined') {
var resize = function () {
_this.revalidate();
};
$(window).on('orientationchange resize', resize);
_this.$content.on('remove', function () {
$(window).off('orientationchange resize', resize);
});
}
_this.revalidate();
if (options.loadedCallback) {
options.loadedCallback(_this);
}
_this.tabManager.setActiveTab(tab.id);
_this.$el.trigger('heatMapLoaded', _this);
};
if (morpheus.Util.isArray(options.dataset)) {
var d = morpheus.DatasetUtil.readDatasetArray(options.dataset);
d.fail(function (message) {
if (_this.options.$loadingImage) {
_this.options.$loadingImage.remove();
}
morpheus.FormBuilder.showInModal({
title: 'Error',
html: message
});
});
d
.done(function (joined) {
if (_this.options.$loadingImage) {
_this.options.$loadingImage.remove();
}
_this.options.dataset = joined;
_this._init();
if (joined.getRowMetadata().getByName('Source') != null
&& !_this.options.colorScheme) {
_this.heatmap.getColorScheme()
.setSeparateColorSchemeForRowMetadataField(
'Source');
}
_
.each(
options.dataset,
function (option) {
if (option.colorScheme) {
_this.heatmap
.getColorScheme()
.setCurrentValue(
morpheus.Util
.getBaseFileName(morpheus.Util
.getFileName(option.dataset)));
_this.heatmap
.getColorScheme()
.setColorSupplierForCurrentValue(
morpheus.HeatMapColorScheme
.createColorSupplier(option.colorScheme));
} else {
try {
_this
.autoDisplay({
extension: morpheus.Util
.getExtension(morpheus.Util
.getFileName(option.dataset)),
filename: morpheus.Util
.getBaseFileName(morpheus.Util
.getFileName(option.dataset))
});
} catch (x) {
console
.log('Autodisplay errror');
}
}
});
heatMapLoaded();
});
} else {
var deferred = options.dataset.file ? morpheus.DatasetUtil.read(
options.dataset.file, options.dataset.options)
: morpheus.DatasetUtil.read(options.dataset);
deferred.done(function (dataset) {
_this.options.dataset = dataset;
});
deferred.fail(function (err) {
_this.options.$loadingImage.remove();
var message = ['Error opening '
+ (options.dataset.file ? morpheus.Util
.getFileName(options.dataset.file) : morpheus.Util
.getFileName(options.dataset)) + '.'];
if (err.message) {
message.push('
Cause: ');
message.push(err.message);
}
morpheus.FormBuilder.showInModal({
title: 'Error',
html: message.join('')
});
});
promises.push(deferred);
$.when.apply($, promises).then(function () {
if (_this.options.$loadingImage) {
_this.options.$loadingImage.remove();
}
_this._init();
heatMapLoaded();
});
}
};
morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS = 6;
morpheus.HeatMap.showTool = function (tool, controller, callback) {
if (tool.gui) {
var gui = tool.gui(controller.getProject());
var formBuilder = new morpheus.FormBuilder();
_.each(gui, function (item) {
formBuilder.append(item);
});
var tabId = controller.getTabManager().getActiveTabId();
if (tool.init) {
tool.init(controller.getProject(), formBuilder, {
controller: controller
});
}
var okCallback = function () {
var task = {
name: tool.toString(),
tabId: tabId
};
controller.getTabManager().addTask(task);
var input = {};
_.each(gui, function (item) {
input[item.name] = formBuilder.getValue(item.name);
});
// give a chance for ui to update
setTimeout(function () {
try {
var value = tool.execute({
controller: controller,
project: controller.getProject(),
input: input
});
if (value instanceof Worker) {
value.onerror = function (e) {
task.worker.terminate();
morpheus.FormBuilder.showInModal({
title: 'Error',
html: e,
close: 'Close'
});
if (e.stack) {
console.log(e.stack);
}
};
var terminate = _.bind(value.terminate, value);
task.worker = value;
value.terminate = function () {
terminate();
try {
controller.getTabManager().removeTask(task);
} catch (x) {
console.log('Error removing task');
}
if (callback) {
callback(input);
}
};
} else {
if (callback) {
callback(input);
}
}
} catch (e) {
morpheus.FormBuilder.showInModal({
title: 'Error',
html: e,
close: 'Close'
});
if (e.stack) {
console.log(e.stack);
}
} finally {
if (task.worker === undefined) {
try {
controller.getTabManager().removeTask(task);
} catch (x) {
console.log('Error removing task');
}
}
if (tool.dispose) {
tool.dispose();
}
}
}, 0);
};
var $formDiv;
tool.ok = function () {
okCallback();
$formDiv.modal('hide');
};
var guiOptions = $.extend({}, {
ok: true
}, gui.options);
$formDiv = morpheus.FormBuilder.showOkCancel({
title: tool.toString(),
apply: tool.apply,
ok: guiOptions.ok,
size: guiOptions.size,
draggable: true,
content: formBuilder.$form,
align: 'right',
okCallback: okCallback
});
} else { // run headless
try {
var value = tool.execute({
controller: controller,
project: controller.getProject(),
input: {}
});
if (callback) {
callback({});
}
} catch (e) {
morpheus.FormBuilder.showInModal({
title: 'Error',
html: e,
close: 'Close'
});
if (e.stack) {
console.log(e.stack);
}
} finally {
if (tool.dispose) {
tool.dispose();
}
}
}
var toolName = tool.toString();
var parenIndex = toolName.indexOf('(');
if (parenIndex !== -1) {
toolName = toolName.substring(0, parenIndex).trim();
}
morpheus.Util.trackEvent({
eventCategory: 'Tool',
eventAction: toolName
});
};
morpheus.HeatMap.getSpaces = function (groupByKeys, length, gapSize) {
var previousArray = [];
var nkeys = groupByKeys.length;
for (var keyIndex = 0; keyIndex < nkeys; keyIndex++) {
var key = groupByKeys[keyIndex];
previousArray.push(key.getValue(0));
}
var spaces = [];
var sum = 0;
spaces.push(sum);
for (var i = 1; i < length; i++) {
var isEqual = true;
for (var keyIndex = 0; keyIndex < nkeys; keyIndex++) {
var key = groupByKeys[keyIndex];
var comparator = key.getComparator();
var val = key.getValue(i);
var c = comparator(val, previousArray[keyIndex]);
if (c !== 0) { // not equal, add space
isEqual = false;
for (var keyIndex2 = 0; keyIndex2 < nkeys; keyIndex2++) {
previousArray[keyIndex2] = groupByKeys[keyIndex2]
.getValue(i);
}
break;
}
}
if (!isEqual) {
sum += gapSize;
}
spaces.push(sum);
}
return spaces;
};
morpheus.HeatMap.createGroupBySpaces = function (dataset, groupByKeys, gapSize) {
if (groupByKeys.length > 0) {
var nkeys = groupByKeys.length;
for (var keyIndex = 0; keyIndex < nkeys; keyIndex++) {
groupByKeys[keyIndex].init(dataset);
}
return morpheus.HeatMap.getSpaces(groupByKeys, dataset.getRowCount(),
gapSize);
}
};
morpheus.HeatMap.isDendrogramVisible = function (project, isColumns) {
var sortKeys = isColumns ? project.getColumnSortKeys() : project
.getRowSortKeys();
// var filter = isColumns ? this.project.getColumnFilter()
// : this.project.getRowFilter();
var size = isColumns ? project.getSortedFilteredDataset().getColumnCount()
: project.getSortedFilteredDataset().getRowCount();
if (sortKeys.length === 1) {
return sortKeys[0] instanceof morpheus.SpecifiedModelSortOrder
&& sortKeys[0].name === 'dendrogram'
&& sortKeys[0].nvisible === size;
}
};
morpheus.HeatMap.prototype = {
gapSize: 10,
updatingScroll: false,
autoDisplay: function (options) {
if (options.filename == null) {
options.filename = '';
}
var colorScheme;
if (options.extension === 'segtab' || options.extension === 'seg') {
colorScheme = {
type: 'fixed',
map: morpheus.HeatMapColorScheme.Predefined.CN().map
.map(function (item) {
return {
value: Math.pow(2, 1 + item.value),
color: item.color
};
})
};
} else if (options.extension === 'maf') {
colorScheme = morpheus.HeatMapColorScheme.Predefined.MAF();
var colorMap = morpheus.HeatMapColorScheme.Predefined.MAF().map;
var rowMutProfile = this.project.getFullDataset().getRowMetadata()
.getByName('mutation_summary');
var columnMutProfile = this.project.getFullDataset()
.getColumnMetadata().getByName('mutation_summary');
var track = this.getTrack('mutation_summary', false);
if (track) {
track.settingFromConfig('stacked_bar');
}
track = this.getTrack('mutation_summary', true);
if (track) {
track.settingFromConfig('stacked_bar');
}
for (var i = 1; i < colorMap.length; i++) {
if (rowMutProfile) {
this.getProject().getRowColorModel().setMappedValue(
rowMutProfile, i - 1, colorMap[i].color);
}
if (columnMutProfile) {
this.getProject().getColumnColorModel().setMappedValue(
columnMutProfile, i - 1, colorMap[i].color);
}
}
} else if (options.extension === 'gmt') {
colorScheme = morpheus.HeatMapColorScheme.Predefined.BINARY();
} else if (options.filename === 'all_lesions.conf_99'
|| options.filename === 'all_data_by_genes.txt') {
colorScheme = {
type: 'fixed',
map: [{
value: -0.5,
color: 'blue'
}, {
value: 0,
color: 'white'
}, {
value: 0.5,
color: 'red'
}]
};
} else if (options.filename.toLowerCase().indexOf('copynumber') !== -1 || options.filename.toLowerCase().indexOf('copy number') !== -1) {
colorScheme = {
type: 'fixed',
map: [{
value: -1.5,
color: 'blue'
}, {
value: 0,
color: 'white'
}, {
value: 1.5,
color: 'red'
}]
};
} else if (options.filename.toLowerCase().indexOf('achilles') !== -1) {
colorScheme = {
type: 'fixed',
map: [{
value: -3,
color: 'blue'
}, {
value: -1,
color: 'white'
}, {
value: 1,
color: 'white'
}, {
value: 3,
color: 'red'
}]
};
}
if (colorScheme && options.filename) {
this.heatmap.getColorScheme().setCurrentValue(options.filename);
this.heatmap.getColorScheme().setColorSupplierForCurrentValue(
morpheus.HeatMapColorScheme
.createColorSupplier(colorScheme));
}
return colorScheme;
},
/**
*
* @param sortOrder
* @param isColumns
* Whether sorting based on column selection
* @param append
* Whether to add to existing sort order
*/
sortBasedOnSelection: function (sortOrder, isColumns, append) {
// if isColumns, sort rows
var project = this.project;
var selectionModel = isColumns ? project.getColumnSelectionModel()
: project.getRowSelectionModel();
var modelIndices = selectionModel.toModelIndices();
if (modelIndices.length === 0) {
return;
}
var priorSortKeyIndex = -1;
if (sortOrder == null) {
// toggle sort order?
var existingSortKeys = isColumns ? project.getRowSortKeys()
: project.getColumnSortKeys();
for (var i = 0, length = existingSortKeys.length; i < length; i++) {
var key = existingSortKeys[i];
if (key instanceof morpheus.SortByValuesKey
&& morpheus.Util.arrayEquals(key.modelIndices,
modelIndices)) {
priorSortKeyIndex = i;
if (key.getSortOrder() === morpheus.SortKey.SortOrder.UNSORTED) {
sortOrder = morpheus.SortKey.SortOrder.DESCENDING; // 1st
// click
} else if (key.getSortOrder() === morpheus.SortKey.SortOrder.DESCENDING) {
sortOrder = morpheus.SortKey.SortOrder.ASCENDING; // 2nd
// click
} else if (key.getSortOrder() === morpheus.SortKey.SortOrder.ASCENDING) {
sortOrder = morpheus.SortKey.SortOrder.TOP_N; // 3rd
// click
} else if (key.getSortOrder() === morpheus.SortKey.SortOrder.TOP_N) {
sortOrder = morpheus.SortKey.SortOrder.UNSORTED; // 4th
// click
}
break;
}
}
}
if (sortOrder == null) {
sortOrder = morpheus.SortKey.SortOrder.DESCENDING;
}
var sortKeys;
if (append) {
sortKeys = !isColumns ? project.getColumnSortKeys() : project
.getRowSortKeys();
if (priorSortKeyIndex !== -1) {
if (sortOrder === morpheus.SortKey.SortOrder.UNSORTED) {
// remove existing sort key
sortKeys.splice(priorSortKeyIndex, 1);
} else {
sortKeys[priorSortKeyIndex].setSortOrder(sortOrder);
}
} else {
if (sortOrder !== morpheus.SortKey.SortOrder.UNSORTED) {
sortKeys.push(new morpheus.SortByValuesKey(modelIndices,
sortOrder, !isColumns));
}
// add new sort key
}
sortKeys = morpheus.SortKey.keepExistingSortKeys(sortKeys,
!isColumns ? project.getColumnSortKeys() : project
.getRowSortKeys());
} else {
var newSortKeys = sortOrder === morpheus.SortKey.SortOrder.UNSORTED ? []
: [new morpheus.SortByValuesKey(modelIndices, sortOrder,
!isColumns)];
sortKeys = morpheus.SortKey.keepExistingSortKeys(newSortKeys,
!isColumns ? project.getColumnSortKeys() : project
.getRowSortKeys());
}
if (!isColumns) { // sort columns by selected rows
project.setColumnSortKeys(sortKeys, true);
this.scrollLeft(0);
} else { // sort rows by selected column
project.setRowSortKeys(sortKeys, true);
this.scrollTop(0);
}
morpheus.Util.trackEvent({
eventCategory: 'Tool',
eventAction: isColumns ? 'sortRowsBasedOnSelection' : 'sortColumnsBasedOnSelection'
});
},
getToolbarElement: function () {
return this.toolbar.$el;
},
getToolbar: function () {
return this.toolbar;
},
setName: function (name) {
this.options.name = name;
},
getName: function () {
return this.options.name;
},
showOptions: function () {
new morpheus.HeatMapOptions(this);
},
getProject: function () {
return this.project;
},
/**
* @param tree
* An object with maxHeight, a rootNode, leafNodes, and
* nLeafNodes
*/
setDendrogram: function (tree, isColumns, viewIndices) {
var dendrogram = isColumns ? this.columnDendrogram : this.rowDendrogram;
if (dendrogram) {
dendrogram.dispose();
dendrogram = null;
}
if (tree != null) {
var modelIndices = [];
var modelIndexSet = new morpheus.Set();
var size = isColumns ? this.project.getFullDataset()
.getColumnCount() : this.project.getFullDataset()
.getRowCount();
for (var i = 0; i < size; i++) {
modelIndexSet.add(i);
}
for (var i = 0, length = viewIndices.length; i < length; i++) {
var modelIndex = isColumns ? this.project
.convertViewColumnIndexToModel(viewIndices[i])
: this.project
.convertViewRowIndexToModel(viewIndices[i]);
modelIndices.push(modelIndex);
modelIndexSet.remove(modelIndex);
}
var nvisible = modelIndices.length;
// add model indices that weren't visible when clustering
if (modelIndexSet.size() > 0) {
var indices = modelIndexSet.values();
for (var i = 0, length = indices.length; i < length; i++) {
modelIndices.push(indices[i]);
}
}
if (isColumns) {
dendrogram = new morpheus.ColumnDendrogram(this, tree,
this.heatmap.getColumnPositions(), this.project);
dendrogram.filter = this.project.getColumnFilter()
.shallowClone();
this.columnDendrogram = dendrogram;
this.project.setColumnSortKeys(
[new morpheus.SpecifiedModelSortOrder(modelIndices,
nvisible, 'dendrogram')], true);
} else {
dendrogram = new morpheus.RowDendrogram(this, tree,
this.heatmap.getRowPositions(), this.project);
dendrogram.filter = this.project.getRowFilter().shallowClone();
this.rowDendrogram = dendrogram;
this.project.setRowSortKeys(
[new morpheus.SpecifiedModelSortOrder(modelIndices,
nvisible, 'dendrogram')], true);
}
dendrogram.appendTo(this.$parent);
dendrogram.$label.appendTo(this.$parent);
dendrogram.$squishedLabel.appendTo(this.$parent);
} else { // no more dendrogram
var sortKeys = isColumns ? this.project.getColumnSortKeys()
: this.project.getRowSortKeys();
// remove dendrogram sort key
for (var i = 0; i < sortKeys.length; i++) {
if (sortKeys[i] instanceof morpheus.SpecifiedModelSortOrder
&& sortKeys[i].name === 'dendrogram') {
sortKeys.splice(i, 1);
i--;
}
}
if (isColumns) {
this.heatmap.getColumnPositions().setSquishedIndices(null);
delete this.columnDendrogram;
this.project.setColumnSortKeys(sortKeys, true);
} else {
delete this.rowDendrogram;
this.project.setRowSortKeys(sortKeys, true);
this.heatmap.getRowPositions().setSquishedIndices(null);
}
}
// FIXME update grouping
this.trigger('dendrogramChanged', {
isColumns: isColumns
});
},
setCustomUrls: function (customUrls) {
this._customUrls = customUrls;
},
getTabManager: function () {
return this.tabManager;
},
getSelectedElementsText: function () {
var _this = this;
var project = this.project;
var selectedViewIndices = project.getElementSelectionModel()
.getViewIndices();
if (selectedViewIndices.size() > 0) {
var tipText = [];
var dataset = project.getSortedFilteredDataset();
var rowTracks = _this.rowTracks.filter(function (t) {
return t.settings.inlineTooltip;
});
var columnTracks = _this.columnTracks.filter(function (t) {
return t.settings.inlineTooltip;
});
selectedViewIndices.forEach(function (id) {
var rowIndex = id.getArray()[0];
var columnIndex = id.getArray()[1];
tipText.push(morpheus.Util.nf(dataset.getValue(rowIndex,
columnIndex)));
rowTracks.forEach(function (track) {
tipText.push('\t');
tipText.push(morpheus.Util.formatObject(dataset
.getRowMetadata().getByName(track.name).getValue(
rowIndex)));
});
columnTracks.forEach(function (track) {
tipText.push('\t');
tipText.push(morpheus.Util.formatObject(dataset
.getColumnMetadata().getByName(track.name)
.getValue(columnIndex)));
});
tipText.push('\n');
});
return tipText.join('');
}
},
_init: function () {
var _this = this;
morpheus.MetadataUtil.renameFields(this.options.dataset, this.options);
var dataset = this.options.dataset;
var rowDendrogram = this.options.rowDendrogram;
var columnDendrogram = this.options.columnDendrogram;
_.each(this.whenLoaded, function (f) {
f(_this.options.dataset);
});
if (this.options.datasetReady) {
var updatedDataset = this.options.datasetReady(dataset);
if (updatedDataset) {
dataset = updatedDataset;
}
}
this.project = this.options.symmetric ? new morpheus.SymmetricProject(
dataset) : new morpheus.Project(dataset);
this.tabManager.setTabTitle(this.tabId, this.project.getFullDataset()
.getRowCount()
+ ' row'
+ morpheus.Util.s(this.project.getFullDataset().getRowCount())
+ ' x '
+ this.project.getFullDataset().getColumnCount()
+ ' column'
+ morpheus.Util.s(this.project.getFullDataset()
.getColumnCount()));
if (this.options.inheritFromParent && this.options.parent != null) {
morpheus.HeatMap.copyFromParent(this.project, this.options);
}
var createFiltersFromOptions = function (filters, isColumns) {
// name, 1. set if string filter, 2. min, max if range filter
// 3. top and isTop if top n filter
_.each(filters, function (filter) {
if (filter.values) {
if ((isColumns ? _this.project.getFullDataset()
.getColumnMetadata().getByName(filter.name)
: _this.project.getFullDataset().getRowMetadata()
.getByName(filter.name)) != null) {
var set = new morpheus.Set();
for (var i = 0; i < filter.values.length; i++) {
set.add(filter.values[i]);
}
(isColumns ? _this.project.getColumnFilter()
: _this.project.getRowFilter())
.add(new morpheus.VectorFilter(set, -1,
filter.name));
}
} else {
(isColumns ? _this.project.getColumnFilter()
: _this.project.getRowFilter()).add(filter);
}
});
// filter ui will be initialized automatically
if (isColumns) {
_this.project.setColumnFilter(_this.project.getColumnFilter(),
true);
} else {
_this.project.setRowFilter(_this.project.getRowFilter());
}
};
if (this.options.rowFilter) {
createFiltersFromOptions(this.options.rowFilter, false);
}
if (this.options.columnFilter) {
createFiltersFromOptions(this.options.columnFilter, true);
}
this.whenLoaded = null;
this.$parent = $('').css('position', 'relative');
this.$parent.appendTo(this.$content);
this.toolbar = new morpheus.HeatMapToolBar(this);
if (this.options.customUrls) {
this.setCustomUrls(this.options.customUrls);
}
// scroll bars at the bottom of the heatmap, and right of the heatmap
// TODO along bottom of row metadata, and along left of column metadata
// the viewport is the size of the visible region, the view is the full
// size of the heat map
this.vscroll = new morpheus.ScrollBar(true);
this.vscroll.appendTo(this.$parent);
this.vscroll.on('scroll', function () {
if (_this.updatingScroll) {
return;
}
_this.paintAll({
paintRows: true,
paintColumns: false,
invalidateRows: true,
invalidateColumns: false
});
});
// for resizing column dendrogram
this.beforeColumnTrackDivider = new morpheus.Divider(false);
this.beforeColumnTrackDivider.appendTo(this.$parent);
var dragStartHeight = 0;
this.beforeColumnTrackDivider.on('resizeStart', function (e) {
dragStartHeight = _this.columnDendrogram.getUnscaledHeight();
}).on('resize', function (e) {
// grow or shrink the column dendrogram
var newHeight = Math.max(8, dragStartHeight + e.delta);
_this.columnDendrogram.setPrefHeight(newHeight);
_this.revalidate();
}).on('resizeEnd', function () {
dragStartHeight = 0;
});
// for resizing row dendrogram
this.afterRowDendrogramDivider = new morpheus.Divider(true);
this.afterRowDendrogramDivider.appendTo(this.$parent);
var rowDendrogramStartWidth = 0;
this.afterRowDendrogramDivider.on('resizeStart', function (e) {
rowDendrogramStartWidth = _this.rowDendrogram.getUnscaledWidth();
}).on('resize', function (e) {
// grow or shrink the column dendrogram
var newWidth = Math.max(8, rowDendrogramStartWidth + e.delta);
_this.rowDendrogram.setPrefWidth(newWidth);
_this.revalidate();
}).on('resizeEnd', function () {
rowDendrogramStartWidth = 0;
});
this.afterVerticalScrollBarDivider = new morpheus.Divider(true);
this.afterVerticalScrollBarDivider.appendTo(this.$parent);
var resizeStartHeatMapWidth = 0;
this.afterVerticalScrollBarDivider.on('resizeStart', function (e) {
resizeStartHeatMapWidth = _this.heatmap.getUnscaledWidth();
}).on('resize', function (e) {
// grow or shrink the heat map
_this.heatmap.prefWidth = resizeStartHeatMapWidth + e.delta;
_this.revalidate();
});
// horizontal scroll
this.hscroll = new morpheus.ScrollBar(false);
this.hscroll.appendTo(this.$parent);
this.hscroll.on('scroll', function () {
if (_this.updatingScroll) {
return;
}
_this.paintAll({
paintRows: false,
paintColumns: true,
invalidateRows: false,
invalidateColumns: true
});
});
var heatmap = new morpheus.HeatMapElementCanvas(this.project);
if (this.options.drawCallback) {
heatmap.setDrawCallback(this.options.drawCallback);
}
$(heatmap.canvas)
.on(
'contextmenu',
function (e) {
morpheus.Popup
.showPopup(
[
{
name: 'Copy',
disabled: _this.project
.getElementSelectionModel()
.count() === 0
},
{
name: 'Save Image (Ctrl-S)'
},
{
separator: true
},
{
name: 'Show Inline Tooltip',
checked: _this.options.inlineTooltip
}],
{
x: e.pageX,
y: e.pageY
},
e.target,
function (event, item) {
if (item === 'Show Inline Tooltip') {
_this.options.inlineTooltip = !_this.options.inlineTooltip;
} else if (item === 'Save Image (Ctrl-S)') {
morpheus.HeatMap
.showTool(
new morpheus.SaveImageTool(),
_this);
} else if ('Copy') {
var text = _this
.getSelectedElementsText();
if (text !== '') {
event.clipboardData
.setData(
'text/plain',
text);
}
}
});
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
});
heatmap.appendTo(this.$parent);
this.heatmap = heatmap;
var rowDendrogramSortKey = null;
if (rowDendrogram != undefined) {
var tree = rowDendrogram;
if (tree.leafNodes.length !== this.project.getFullDataset()
.getRowCount()) {
throw '# leaf nodes in row dendrogram ' + tree.leafNodes.length
+ ' != ' + this.project.getFullDataset().getRowCount();
}
if (this.options.rowDendrogramField != null) {
var vector = dataset.getRowMetadata().getByName(
this.options.rowDendrogramField);
var rowIndices = [];
var map = new morpheus.Map();
var re = /[,:]/g;
for (var j = 0, size = vector.size(); j < size; j++) {
var key = vector.getValue(j);
map.set(key.replace(re, ''), j);
}
// need to replace special characters to match ids in newick
// file
for (var i = 0, length = tree.leafNodes.length; i < length; i++) {
var index = map.get(tree.leafNodes[i].name);
if (index === undefined) {
throw 'Unable to find row dendrogram id '
+ tree.leafNodes[i].name
+ ' in row annotations';
}
rowIndices.push(index);
}
} else {
for (var i = 0, length = tree.leafNodes.length; i < length; i++) {
rowIndices.push(i);
}
}
this.rowDendrogram = new morpheus.RowDendrogram(this, tree, heatmap
.getRowPositions(), this.project, true);
rowDendrogramSortKey = new morpheus.SpecifiedModelSortOrder(
rowIndices, rowIndices.length, 'dendrogram');
this.rowDendrogram.appendTo(this.$parent);
this.rowDendrogram.$label.appendTo(this.$parent);
this.rowDendrogram.$squishedLabel.appendTo(this.$parent);
}
var columnDendrogramSortKey = null;
if (columnDendrogram !== undefined) {
var tree = columnDendrogram;
if (tree.leafNodes.length !== this.project.getFullDataset()
.getColumnCount()) {
throw '# leaf nodes ' + tree.leafNodes.length + ' != '
+ this.project.getFullDataset().getColumnCount();
}
var columnIndices = [];
if (this.options.columnDendrogramField != null) {
var vector = dataset.getColumnMetadata().getByName(
this.options.columnDendrogramField);
var map = new morpheus.Map();
var re = /[,:]/g;
for (var j = 0, size = vector.size(); j < size; j++) {
var key = vector.getValue(j);
map.set(key.replace(re, ''), j);
}
for (var i = 0, length = tree.leafNodes.length; i < length; i++) {
var index = map.get(tree.leafNodes[i].name);
if (index === undefined) {
throw 'Unable to find column dendrogram id '
+ tree.leafNodes[i].name
+ ' in column annotations';
}
columnIndices.push(index);
}
} else {
for (var i = 0, length = tree.leafNodes.length; i < length; i++) {
columnIndices.push(i);
}
}
this.columnDendrogram = new morpheus.ColumnDendrogram(this, tree,
heatmap.getColumnPositions(), this.project, true);
columnDendrogramSortKey = new morpheus.SpecifiedModelSortOrder(
columnIndices, columnIndices.length, 'dendrogram');
this.columnDendrogram.appendTo(this.$parent);
this.columnDendrogram.$label.appendTo(this.$parent);
this.columnDendrogram.$squishedLabel.appendTo(this.$parent);
}
if (rowDendrogramSortKey !== null) {
this.project.setRowSortKeys([rowDendrogramSortKey]);
}
if (columnDendrogramSortKey !== null) {
this.project.setColumnSortKeys([columnDendrogramSortKey]);
}
if (this.options.rowGroupBy != null) {
for (var i = 0; i < this.options.rowGroupBy.length; i++) {
var key = new morpheus.SortKey(this.options.rowGroupBy[i],
morpheus.SortKey.SortOrder.UNSORTED);
this.project.groupRows.push(key);
}
}
if (this.options.rowSortBy) {
var keys = [];
for (var i = 0; i < this.options.rowSortBy.length; i++) {
var sortBy = this.options.rowSortBy[i];
if (sortBy.modelIndices != null
&& morpheus.Util.isArray(sortBy.modelIndices)) {
// sort by values
keys.push(new morpheus.SortByValuesKey(
this.options.rowSortBy[i].modelIndices,
this.options.rowSortBy[i].order, false));
} else {
// name is deprecated, use field
var name = sortBy.name != null ? sortBy.name : sortBy.field;
if (this.project.getFullDataset().getRowMetadata()
.getByName(name) != null) {
keys.push(new morpheus.SortKey(name, sortBy.order));
}
}
}
this.project.setRowSortKeys(keys, false);
}
if (this.options.columnGroupBy != null) {
for (var i = 0; i < this.options.columnGroupBy.length; i++) {
var key = new morpheus.SortKey(this.options.columnGroupBy[i],
morpheus.SortKey.SortOrder.UNSORTED);
this.project.groupColumns.push(key);
}
}
if (this.options.columnSortBy) {
var keys = [];
for (var i = 0; i < this.options.columnSortBy.length; i++) {
var sortBy = this.options.columnSortBy[i];
if (sortBy.modelIndices != null
&& morpheus.Util.isArray(sortBy.modelIndices)) {
keys.push(new morpheus.SortByValuesKey(sortBy.modelIndices,
sortBy.order, true));
} else {
// name is deprecated, use field
var name = sortBy.name != null ? sortBy.name : sortBy.field;
if (this.project.getFullDataset().getColumnMetadata()
.getByName(name) != null) {
keys.push(new morpheus.SortKey(name, sortBy.order));
}
}
}
this.project.setColumnSortKeys(keys, false);
}
this.vSortByValuesIndicator = new morpheus.SortByValuesIndicator(
this.project, true, heatmap.getRowPositions());
this.vSortByValuesIndicator.appendTo(this.$parent);
this.hSortByValuesIndicator = new morpheus.SortByValuesIndicator(
this.project, false, heatmap.getColumnPositions());
this.hSortByValuesIndicator.appendTo(this.$parent);
this.verticalSearchBar = new morpheus.ScentedSearch(this.project
.getRowSelectionModel(), heatmap.getRowPositions(), true,
this.vscroll, this);
this.horizontalSearchBar = new morpheus.ScentedSearch(this.project
.getColumnSelectionModel(), heatmap.getColumnPositions(),
false, this.hscroll, this);
this.rowTracks = [];
this.rowTrackHeaders = [];
this.columnTracks = [];
this.columnTrackHeaders = [];
var setInitialDisplay = function (isColumns, options) {
var nameToOption = new morpheus.Map();
// at
// least
// one
// display option
// supplied
var displaySpecified = false || (_this.options.parent != null && _this.options.inheritFromParent);
_.each(options, function (option) {
if (!displaySpecified) {
displaySpecified = option.display != null;
}
nameToOption.set(option.renameTo != null ? option.renameTo
: option.field, option);
});
var displayMetadata = isColumns ? dataset.getColumnMetadata()
: dataset.getRowMetadata();
// see if default fields found
if (!displaySpecified) {
var defaultFieldsToShow = new morpheus.Set();
['pert_iname', 'moa', 'target', 'description']
.forEach(function (field) {
defaultFieldsToShow.add(field);
});
for (var i = 0, metadataCount = displayMetadata
.getMetadataCount(); i < metadataCount; i++) {
var v = displayMetadata.get(i);
if (defaultFieldsToShow.has(v.getName())) {
nameToOption.set(v.getName(), {
display: 'text'
});
displaySpecified = true;
}
}
}
var isFirst = true;
for (var i = 0, metadataCount = displayMetadata.getMetadataCount(); i < metadataCount; i++) {
var display = displaySpecified ? 'None' : undefined;
var v = displayMetadata.get(i);
var name = v.getName();
var option = nameToOption.get(name);
if (morpheus.MetadataUtil.DEFAULT_HIDDEN_FIELDS.has(name)
&& option == null) {
continue;
}
var count = isColumns ? dataset.getColumnCount() : dataset
.getRowCount();
if (!option && !displaySpecified && count > 1
&& !morpheus.VectorUtil.containsMoreThanOneValue(v)) {
continue;
}
if (option == null) {
option = {};
}
if (option.title) {
v.getProperties().set(morpheus.VectorKeys.TITLE,
option.title);
}
if (option.display) {
if (typeof option.display == 'function') {
display = option.display(name);
} else {
display = option.display;
}
}
var add = display !== 'None';
if (add) {
if (display == null) {
if (name === 'pert_iname' || name === 'id' || isFirst) {
display = 'text,tooltip';
} else {
display = isColumns ? 'color,highlight' : 'text';
}
}
isFirst = false;
var track = isColumns ? _this.addColumnTrack(name, display)
: _this.addRowTrack(name, display);
if (track.isRenderAs(morpheus.VectorTrack.RENDER.COLOR)
&& option.color) {
var m = isColumns ? _this.project.getColumnColorModel()
: _this.project.getRowColorModel();
if (track.isDiscrete()) {
_.each(options.color, function (p) {
m.setMappedValue(v, p.value, p.color);
});
} else {
var cs = m.createContinuousColorMap(v);
var min = Number.MAX_VALUE;
var max = -Number.MAX_VALUE;
_.each(options.color, function (p) {
min = Math.min(min, p.value);
max = Math.max(max, p.value);
});
cs.setMin(min);
cs.setMax(max);
var valueToFraction = d3.scale.linear().domain(
[cs.getMin(), cs.getMax()]).range(
[0, 1]).clamp(true);
var fractions = [];
var colors = [];
_.each(options.color, function (p) {
fractions.push(valueToFraction(p.value));
colors.push(p.color);
});
cs.setFractions({
fractions: fractions,
colors: colors
});
}
}
if (track.isRenderAs(morpheus.VectorTrack.RENDER.SHAPE)
&& option.shape) {
var m = isColumns ? _this.project.getColumnShapeModel()
: _this.project.getRowShapeModel();
_.each(options.shape, function (p) {
m.setMappedValue(v, p.value, p.shape);
});
}
}
}
};
setInitialDisplay(false, this.options.rows);
setInitialDisplay(true, this.options.columns);
function reorderTracks(array, isColumns) {
var nameOrderPairs = [];
var found = false;
_.each(array, function (item) {
var name = item.renameTo || item.field;
var order = 999;
if (item.order != null) {
order = item.order;
found = true;
}
nameOrderPairs.push({
name: name,
order: order
});
});
if (found) {
nameOrderPairs.sort(function (a, b) {
return (a.order === b.order ? 0 : (a.order < b.order ? -1
: 1));
});
for (var i = 0, counter = 0; i < nameOrderPairs.length; i++) {
var index = _this.getTrackIndex(nameOrderPairs[i].name,
isColumns);
if (index !== -1) {
_this.moveTrack(index, counter, isColumns);
counter++;
}
}
}
}
reorderTracks(this.options.rows, false);
reorderTracks(this.options.columns, true);
var colorSchemeSpecified = this.options.colorScheme != null;
if (this.options.colorScheme == null) {
var ext = '';
if (this.options.dataSource) {
try {
ext = morpheus.Util.getExtension(morpheus.Util
.getFileName(this.options.dataSource));
} catch (x) {
}
}
var colorScheme = this.autoDisplay({
extension: ext
});
if (colorScheme == null) {
colorScheme = {
type: 'relative'
};
}
this.options.colorScheme = colorScheme;
var name = this.project.getFullDataset().getName();
if (ext === 'maf' && !this.options.rowSortBy) {
var sortKeys = [];
if (this.project.getFullDataset().getRowMetadata().getByName(
'order')) {
sortKeys.push(new morpheus.SortKey('order',
morpheus.SortKey.SortOrder.ASCENDING));
}
sortKeys.push(new morpheus.SortKey('id',
morpheus.SortKey.SortOrder.ASCENDING));
this.project.setRowSortKeys(sortKeys, false);
}
if (morpheus.DatasetUtil.getSeriesIndex(this.project
.getFullDataset(), 'allelic_fraction') !== -1) {
this.options.sizeBy = 'allelic_fraction';
}
}
if (this.options.parent && this.options.inheritFromParent
&& !colorSchemeSpecified) {
heatmap.setColorScheme(this.options.parent.heatmap.getColorScheme()
.copy(this.project));
} else {
heatmap.setColorScheme(new morpheus.HeatMapColorScheme(
this.project, this.options.colorScheme));
if (this.options.dataset.getRowMetadata().getByName('Source') != null) {
// separate color scheme for each source file
var sourcesSet = morpheus.VectorUtil
.getSet(this.options.dataset.getRowMetadata()
.getByName('Source'));
this.heatmap.getColorScheme()
.setSeparateColorSchemeForRowMetadataField('Source');
sourcesSet.forEach(function (source) {
_this.autoDisplay({
extension: morpheus.Util.getExtension(source),
filename: '' + source
});
});
}
}
if (this.options.sizeBy) {
heatmap.getColorScheme().getSizer().setSeriesName(
this.options.sizeBy);
}
this.updateDataset();
if (this.options.uiReady) {
this.options.uiReady(this);
}
if (this.options.tabOpened) {
try {
this.options.tabOpened(this);
} catch (x) {
console.log('Error in tabOpened');
if (x.stack) {
console.log(x.stack);
}
}
this.updateDataset();
}
if (this.options.renderReady) {
try {
this.options.renderReady(this);
} catch (x) {
console.log('Error in renderReady');
if (x.stack) {
console.log(x.stack);
}
}
this.updateDataset();
}
if (this.options.rowSize != null) {
if (this.options.rowSize === 'fit') {
this.heatmap.getRowPositions().setSize(this.getFitRowSize());
} else {
this.heatmap.getRowPositions().setSize(this.options.rowSize);
}
this.revalidate({
paint: false
});
}
if (this.options.columnSize != null) {
if (this.options.columnSize === 'fit') {
this.heatmap.getColumnPositions().setSize(
this.getFitColumnSize());
} else {
this.heatmap.getColumnPositions().setSize(
this.options.columnSize);
}
this.revalidate({
paint: false
});
}
if (this.options.rowSize != null && this.options.columnSize != null) {
// note that we have to revalidate twice because column sizes are
// dependent on row sizes and vice versa
if (this.options.columnSize === 'fit') {
this.heatmap.getColumnPositions().setSize(
this.getFitColumnSize());
this.revalidate({
paint: false
});
}
if (this.options.rowSize === 'fit') {
this.heatmap.getRowPositions().setSize(this.getFitRowSize());
this.revalidate({
paint: false
});
}
this.paintAll({
paintRows: true,
paintColumns: true,
invalidateRows: true,
invalidateColumns: true
});
}
this.options.parent = null;
this.$tipFollow = $('');
this.$tipFollow.appendTo(this.$parent);
this.$tipInfoWindow = $('');
this.$tipInfoWindow.appendTo(this.$parent);
this.$tipInfoWindow.dialog({
close: function (event, ui) {
if (!_this._togglingInfoWindow) {
_this.toggleInfoWindow();
}
},
autoOpen: false,
width: 220,
height: 280,
minHeight: 38,
minWidth: 10,
collision: 'fit',
position: {
my: 'right-30 bottom',
at: 'right top',
of: this.$parent
},
title: 'Info'
});
this
.getProject()
.on(
'rowFilterChanged columnFilterChanged rowGroupByChanged columnGroupByChanged rowSortOrderChanged columnSortOrderChanged datasetChanged',
function (e) {
if (e.type === 'datasetChanged') { // remove
// tracks
// that are no
// longer in the
// dataset
var dataset = _this.getProject()
.getFullDataset();
for (var i = 0; i < _this.rowTracks.length; i++) {
var track = _this.rowTracks[i];
if (!dataset.getRowMetadata().getByName(
track.getName())) {
_this.removeTrack(track.getName(),
false);
i--;
}
}
for (var i = 0; i < _this.columnTracks.length; i++) {
var track = _this.columnTracks[i];
if (!dataset.getColumnMetadata().getByName(
track.getName())) {
_this
.removeTrack(track.getName(),
true);
i--;
}
}
}
_this.updateDataset();
_this.revalidate();
});
this.getProject().on('trackChanged', function (e) {
var columns = e.columns;
_.each(e.vectors, function (v, i) {
var index = _this.getTrackIndex(v.getName(), columns);
if (index === -1) {
if (columns) {
_this.addColumnTrack(v.getName(), e.render[i]);
} else {
_this.addRowTrack(v.getName(), e.render[i]);
}
} else {
// repaint
var track = _this.getTrackByIndex(index, columns);
var render = e.render[i];
if (render) {
track.settingFromConfig(render);
}
track.setInvalid(true);
}
});
_this.revalidate();
});
this.getProject().on('rowTrackRemoved', function (e) {
_this.removeTrack(e.vector.getName(), false);
_this.revalidate();
});
this.getProject().on('columnTrackRemoved', function (e) {
_this.removeTrack(e.vector.getName(), true);
_this.revalidate();
});
this
.getProject()
.getRowSelectionModel()
.on(
'selectionChanged',
function () {
// repaint tracks that indicate selection
for (var i = 0; i < _this.columnTracks.length; i++) {
var track = _this.columnTracks[i];
if (track.settings.stackedBar
&& track
.isRenderAs(morpheus.VectorTrack.RENDER.BAR)) {
track.setInvalid(true);
track.repaint();
}
}
_this.verticalSearchBar.update();
_this.paintAll({
paintRows: true,
paintColumns: false,
invalidateRows: false,
invalidateColumns: false
});
});
this.getProject().getColumnSelectionModel().on('selectionChanged',
function () {
_this.horizontalSearchBar.update();
_this.paintAll({
paintRows: false,
paintColumns: true,
invalidateRows: false,
invalidateColumns: false
});
});
$(window)
.on(
'paste.morpheus',
function (e) {
if (_this.isActiveComponent()) {
var text = e.originalEvent.clipboardData
.getData('text/plain');
if (text != null && text.length > 0) {
var blob = new Blob([text]);
var url = window.URL.createObjectURL(blob);
e.preventDefault();
e.stopPropagation();
morpheus.HeatMap.showTool(
new morpheus.OpenFileTool({
file: url
}), _this);
}
}
})
.on('beforecopy.morpheus', function (e) {
if (_this.isActiveComponent()) {
e.preventDefault();
}
})
.on(
'copy.morpheus',
function (ev) {
if (_this.isActiveComponent()) {
var activeComponent = _this
.getActiveComponent();
var project = _this.project;
if (activeComponent === 2) {
var text = _this.getSelectedElementsText();
if (text !== '') {
ev.originalEvent.clipboardData.setData(
'text/plain', text);
ev.preventDefault();
ev.stopImmediatePropagation();
return;
}
}
// copy all selected rows and columns
var dataset = project.getSelectedDataset({
emptyToAll: false
});
var columnMetadata = dataset
.getColumnMetadata();
var rowMetadata = dataset.getRowMetadata();
// only copy visible tracks
var visibleColumnFields = _this
.getVisibleTrackNames(true);
var columnFieldIndices = [];
_.each(visibleColumnFields, function (name) {
var index = morpheus.MetadataUtil.indexOf(
columnMetadata, name);
if (index !== -1) {
columnFieldIndices.push(index);
}
});
columnMetadata = new morpheus.MetadataModelColumnView(
columnMetadata, columnFieldIndices);
var rowMetadata = dataset.getRowMetadata();
// only copy visible tracks
var visibleRowFields = _this
.getVisibleTrackNames(false);
var rowFieldIndices = [];
_.each(visibleRowFields, function (name) {
var index = morpheus.MetadataUtil.indexOf(
rowMetadata, name);
if (index !== -1) {
rowFieldIndices.push(index);
}
});
rowMetadata = new morpheus.MetadataModelColumnView(
rowMetadata, rowFieldIndices);
var text = [];
var rowsSelected = dataset.getRowCount() > 0;
var columnsSelected = dataset.getColumnCount() > 0;
if (rowsSelected && columnsSelected) { // copy
// as
// gct
// 1.3
text = new morpheus.GctWriter()
.write(dataset);
} else {
var text = [];
var model = rowsSelected ? rowMetadata
: columnMetadata;
for (var i = 0, count = model
.getItemCount(); i < count; i++) {
for (var j = 0, nfields = model
.getMetadataCount(); j < nfields; j++) {
var v = model.get(j);
if (j > 0) {
text.push('\t');
}
text.push(morpheus.Util.toString(v
.getValue(i)));
}
text.push('\n');
}
text = text.join('');
}
ev.originalEvent.clipboardData.setData(
'text/plain', text);
ev.preventDefault();
ev.stopImmediatePropagation();
}
});
if (this.options.keyboard) {
new morpheus.HeatMapKeyListener(this);
}
var dragStartScrollTop;
var dragStartScrollLeft;
this.hammer = morpheus.Util
.hammer(_this.heatmap.canvas, ['pan', 'pinch', 'tap'])
.on('panmove', function (event) {
_this.updatingScroll = true;
var rows = false;
var columns = false;
if (event.deltaY !== 0) {
var pos = dragStartScrollTop + event.deltaY;
_this.scrollTop(pos);
rows = true;
}
if (event.deltaX !== 0) {
var pos = dragStartScrollLeft + event.deltaX;
_this.scrollLeft(pos);
columns = true;
}
_this.updatingScroll = false;
_this.paintAll({
paintRows: rows,
paintColumns: rows,
invalidateRows: rows,
invalidateColumns: columns
});
event.preventDefault();
})
.on('panstart', function (event) {
dragStartScrollTop = _this.scrollTop();
dragStartScrollLeft = _this.scrollLeft();
})
.on(
'tap',
function (event) {
var commandKey = morpheus.Util.IS_MAC ? event.srcEvent.metaKey
: event.srcEvent.ctrlKey;
if (morpheus.Util.IS_MAC && event.srcEvent.ctrlKey) { // right-click
// on
// Mac
return;
}
var position = morpheus.CanvasUtil
.getMousePosWithScroll(event.target, event,
_this.scrollLeft(), _this
.scrollTop());
var rowIndex = _this.heatmap.getRowPositions()
.getIndex(position.y, false);
var columnIndex = _this.heatmap
.getColumnPositions().getIndex(position.x,
false);
_this.project.getElementSelectionModel().click(
rowIndex, columnIndex,
event.srcEvent.shiftKey || commandKey);
})
.on(
'pinch',
function (event) {
var scale = event.scale;
_this.heatmap.getRowPositions().setSize(13 * scale);
_this.heatmap.getColumnPositions().setSize(
13 * scale);
var reval = {};
if (_this.project.getHoverRowIndex() !== -1) {
reval.scrollTop = this.heatmap
.getRowPositions()
.getPosition(
this.project.getHoverRowIndex());
}
if (_this.project.getHoverColumnIndex() !== -1) {
reval.scrollLeft = this.heatmap
.getColumnPositions().getPosition(
this.project
.getHoverColumnIndex());
}
_this.revalidate(reval);
event.preventDefault();
});
var heatMapMouseMoved = function (event) {
var mouseI, mouseJ;
if (event.type === 'mouseout') {
mouseI = -1;
mouseJ = -1;
} else {
var position = morpheus.CanvasUtil.getMousePosWithScroll(
event.target, event, _this.scrollLeft(), _this
.scrollTop());
mouseI = _this.heatmap.getRowPositions().getIndex(position.y,
false);
mouseJ = _this.heatmap.getColumnPositions().getIndex(
position.x, false);
}
_this.setMousePosition(mouseI, mouseJ, {
event: event
});
};
$(_this.heatmap.canvas).on('mouseout', heatMapMouseMoved).on(
'mousemove', heatMapMouseMoved);
_.each(this.options.tools, function (item) {
var tool = _this.toolbar.getToolByName(item.name);
if (tool == null) {
console.log(item.name + ' not found.');
} else {
try {
var gui = tool.gui(_this.getProject());
var formBuilder = new morpheus.FormBuilder();
_.each(gui, function (item) {
formBuilder.append(item);
});
var input = {};
_.each(gui, function (item) {
input[item.name] = formBuilder.getValue(item.name);
});
if (item.params) {
// overide default values
for (var key in item.params) {
input[key] = item.params[key];
}
}
tool.execute({
controller: _this,
project: _this.getProject(),
input: input
});
} catch (x) {
if (x.stack) {
console.log(x.stack);
}
console.log('Error running ' + item.name);
} finally {
if (tool.dispose) {
tool.dispose();
}
}
}
});
},
setMousePosition: function (i, j, options) {
this.mousePositionOptions = options;
var updateColumns = this.project.getHoverColumnIndex() !== j;
var updateRows = this.project.getHoverRowIndex() !== i;
if (updateColumns || updateRows) {
this.project.setHoverRowIndex(i);
this.project.setHoverColumnIndex(j);
this.setToolTip(i, j, options);
this.paintAll({
paintRows: updateRows,
paintColumns: updateColumns,
invalidateRows: false,
invalidateColumns: false
});
} else {
this._updateTipFollowPosition(options);
}
// else if (this.tooltipMode === 2 &&
// (this.project.getHoverColumnIndex() !== -1 || this.project
// .getHoverRowIndex() !== -1)) {
//
// }
this.trigger('change', {
name: 'setMousePosition',
source: this,
arguments: arguments
});
},
setTooltipMode: function (mode) {
this._togglingInfoWindow = true;
this.tooltipMode = mode;
this.$tipInfoWindow.html('');
this.toolbar.$tip.html('');
this.$tipFollow.html('').css({
left: -1000,
top: -1000
});
this.setToolTip(-1, -1);
if (this.tooltipMode === 1) {
this.$tipInfoWindow.dialog('open');
} else {
this.$tipInfoWindow.dialog('close');
}
this._togglingInfoWindow = false;
},
toggleInfoWindow: function () {
this.setTooltipMode(this.tooltipMode == 1 ? 0 : 1);
},
_setTipText: function (tipText, tipFollowText, options) {
if (this.tooltipMode === 0) {
this.toolbar.$tip.html(tipText.join(''));
} else if (this.tooltipMode === 1) {
this.$tipInfoWindow.html(tipText.join(''));
}
if (tipFollowText.length > 0) {
this.tipFollowHidden = false;
this.$tipFollow.html('' + tipFollowText.join('') + '');
this._updateTipFollowPosition(options);
} else {
this.tipFollowHidden = true;
this.$tipFollow.html('').css({
left: -1000,
top: -1000
});
}
this.trigger('change', {
name: 'setToolTip',
source: this,
arguments: arguments
});
},
setToolTip: function (rowIndex, columnIndex, options) {
options = options || {};
if (this.options.showSeriesNameInTooltip) {
options.showSeriesNameInTooltip = true;
}
if (options.heatMapLens) {
// don't draw lens if currently visible
// row lens
var $wrapper = $('');
var wrapperHeight = 0;
var wrapperWidth = 0;
var found = false;
var inline = [];
if (rowIndex != null && rowIndex.length > 0) {
for (var hoverIndex = 0; hoverIndex < rowIndex.length; hoverIndex++) {
var row = rowIndex[hoverIndex];
if (row >= 0 && (row >= this.heatmap.lastPosition.bottom || row < this.heatmap.lastPosition.top)) {
found = true;
var heatMapWidth = this.heatmap.getUnscaledWidth();
var top = row; // Math.max(0, rowIndex - 1);
var bottom = row + 1; //Math.min(rowIndex + 1, this.heatmap.rowPositions.getLength());
var startPix = this.heatmap.rowPositions.getPosition(top);
var endPix = startPix + this.heatmap.rowPositions.getItemSize(top);
var heatMapHeight = endPix - startPix;
var canvas = morpheus.CanvasUtil.createCanvas();
var trackWidth = 0;
for (var i = 0, ntracks = this.rowTracks.length; i < ntracks; i++) {
var track = this.rowTracks[i];
if (track.isVisible()) {
trackWidth += track.getUnscaledWidth();
}
}
var canvasWidth = trackWidth + heatMapWidth + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
canvas.width = canvasWidth * morpheus.CanvasUtil.BACKING_SCALE;
canvas.style.width = canvasWidth + 'px';
canvas.height = heatMapHeight * morpheus.CanvasUtil.BACKING_SCALE;
canvas.style.height = heatMapHeight + 'px';
var context = canvas.getContext('2d');
morpheus.CanvasUtil.resetTransform(context);
context.save();
context.translate(-this.heatmap.lastClip.x, -startPix);
context.rect(this.heatmap.lastClip.x, startPix, this.heatmap.lastClip.width, this.heatmap.lastClip.height);
context.clip();
this.heatmap._draw({
left: this.heatmap.lastPosition.left,
right: this.heatmap.lastPosition.right,
top: top,
bottom: bottom,
context: context
});
context.restore();
context.translate(heatMapWidth + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS, -startPix);
trackWidth = 0;
for (var i = 0, ntracks = this.rowTracks.length; i < ntracks; i++) {
var track = this.rowTracks[i];
if (track.isVisible()) {
context.save();
context.translate(trackWidth, 0);
context.rect(0, startPix, track.getUnscaledWidth(), track.lastClip.height);
context.clip();
track._draw({
start: top,
end: bottom,
vector: track.getVector(),
context: context,
availableSpace: track.getUnscaledWidth()
});
context.restore();
trackWidth += track.getUnscaledWidth();
}
}
$(canvas).appendTo($wrapper);
canvas.style.top = wrapperHeight + 'px';
wrapperHeight += parseFloat(canvas.style.height);
wrapperWidth = parseFloat(canvas.style.width);
} else {
inline.push(row);
}
}
if (found) {
$wrapper.css({
height: wrapperHeight,
width: wrapperWidth
});
var rect = this.$parent[0].getBoundingClientRect();
this.$tipFollow.html($wrapper).css({
left: parseFloat(this.heatmap.canvas.style.left) - 1,
top: options.event.clientY - rect.top - wrapperHeight / 2
});
return;
} else {
var tipText = [];
var tipFollowText = [];
for (var hoverIndex = 0; hoverIndex < inline.length; hoverIndex++) {
this.tooltipProvider(this, inline[hoverIndex], -1,
options, this.tooltipMode === 0 ? ' '
: '
', false, tipText);
if (this.options.inlineTooltip) {
this.tooltipProvider(this, inline[hoverIndex], -1,
options, '
', true, tipFollowText);
}
}
this._setTipText(tipText, tipFollowText, options);
}
}
if (columnIndex != null && columnIndex.length > 0) {
for (var hoverIndex = 0; hoverIndex < columnIndex.length; hoverIndex++) {
var column = columnIndex[hoverIndex];
if (column >= 0 && (column >= this.heatmap.lastPosition.right || column < this.heatmap.lastPosition.left)) {
found = true;
var heatMapHeight = this.heatmap.getUnscaledHeight();
var left = column; // Math.max(0, rowIndex - 1);
var right = column + 1; //Math.min(rowIndex + 1, this.heatmap.rowPositions.getLength());
var startPix = this.heatmap.columnPositions.getPosition(left);
var endPix = startPix + this.heatmap.columnPositions.getItemSize(left);
var heatMapWidth = endPix - startPix;
var canvas = morpheus.CanvasUtil.createCanvas();
var trackHeight = 0;
for (var i = 0, ntracks = this.columnTracks.length; i < ntracks; i++) {
var track = this.columnTracks[i];
if (track.isVisible()) {
trackHeight += track.getUnscaledHeight();
}
}
var canvasHeight = trackHeight + heatMapHeight + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
canvas.width = heatMapWidth * morpheus.CanvasUtil.BACKING_SCALE;
canvas.style.width = heatMapWidth + 'px';
canvas.height = canvasHeight * morpheus.CanvasUtil.BACKING_SCALE;
canvas.style.height = canvasHeight + 'px';
var context = canvas.getContext('2d');
morpheus.CanvasUtil.resetTransform(context);
context.translate(-startPix, 0);
context.save();
context.rect(startPix, trackHeight + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS, this.heatmap.lastClip.width, this.heatmap.lastClip.height + trackHeight + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS);
context.clip();
context.translate(0, trackHeight + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS - this.heatmap.lastClip.y);
this.heatmap._draw({
top: this.heatmap.lastPosition.top,
bottom: this.heatmap.lastPosition.bottom,
left: left,
right: right,
context: context
});
context.restore();
trackHeight = 0;
for (var i = 0, ntracks = this.columnTracks.length; i < ntracks; i++) {
var track = this.columnTracks[i];
if (track.isVisible()) {
context.save();
context.translate(0, trackHeight);
context.rect(startPix, 0, track.lastClip.width, track.getUnscaledHeight());
context.clip();
track._draw({
start: left,
end: right,
vector: track.getVector(),
context: context,
availableSpace: track.getUnscaledHeight(),
clip: {
x: track.lastClip.x,
y: track.lastClip.y
}
});
context.restore();
trackHeight += track.getUnscaledHeight();
}
}
canvas.style.left = wrapperWidth + 'px';
wrapperWidth += parseFloat(canvas.style.width);
wrapperHeight = parseFloat(canvas.style.height);
$(canvas).appendTo($wrapper);
} else {
inline.push(column);
}
}
if (found) {
$wrapper.css({
height: wrapperHeight,
width: wrapperWidth
});
var rect = this.$parent[0].getBoundingClientRect();
this.$tipFollow.html($wrapper).css({
top: parseFloat(this.heatmap.canvas.style.top) - trackHeight - morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS - 1,
left: (options.event.clientX - rect.left) - (wrapperWidth / 2)
});
return;
} else {
var tipText = [];
var tipFollowText = [];
for (var hoverIndex = 0; hoverIndex < inline.length; hoverIndex++) {
this.tooltipProvider(this, -1, inline[hoverIndex],
options, this.tooltipMode === 0 ? ' '
: '
', false, tipText);
if (this.options.inlineTooltip) {
this.tooltipProvider(this, -1, inline[hoverIndex],
options, '
', true, tipFollowText);
}
}
this._setTipText(tipText, tipFollowText, options);
}
}
// column lens
}
// tooltipMode=0 top, 1=window, 2=inline
var tipText = [];
this.tooltipProvider(this, rowIndex, columnIndex,
options, this.tooltipMode === 0 ? ' '
: '
', false, tipText);
var tipFollowText = [];
if (this.options.inlineTooltip) {
this.tooltipProvider(this, rowIndex, columnIndex,
options, '
', true, tipFollowText);
}
this._setTipText(tipText, tipFollowText, options);
}
,
_updateTipFollowPosition: function (options) {
if (this.tipFollowHidden) {
return;
}
var rect = this.$parent[0].getBoundingClientRect();
var tipWidth = this.$tipFollow.width();
var tipHeight = this.$tipFollow.height();
var left = options.event.clientX - rect.left + 16;
// default is bottom-right
if ((left + tipWidth) >= rect.right) { // offscreen right
left = options.event.clientX - rect.left - 16 - tipWidth;
}
var top = options.event.clientY - rect.top + 16;
if ((top + tipHeight) >= (rect.bottom - rect.top)) { // offscreen
top = options.event.clientY - rect.top - 16 - tipHeight;
}
this.$tipFollow.css({
left: left,
top: top
});
}
,
setTrackVisibility: function (tracks) {
var _this = this;
_.each(tracks, function (track) {
var existingTrack = _this.getTrack(track.name, track.isColumns);
if (track.visible && existingTrack != null
&& _.keys(existingTrack.renderSettings).length === 0) {
existingTrack.settingFromConfig('Text');
}
_this.setTrackVisible(track.name, track.visible, track.isColumns);
});
this.revalidate();
this.trigger('change', {
name: 'setTrackVisibility',
source: this,
arguments: arguments
});
}
,
setTrackVisible: function (name, visible, isColumns) {
var trackIndex = this.getTrackIndex(name, isColumns);
if (trackIndex === -1) { // not currently visible
if (!visible) {
return;
}
if (!isColumns) {
this.addRowTrack(name);
} else {
this.addColumnTrack(name);
}
} else {
var track = isColumns ? this.columnTracks[trackIndex]
: this.rowTracks[trackIndex];
var header = isColumns ? this.columnTrackHeaders[trackIndex]
: this.rowTrackHeaders[trackIndex];
if (track.isVisible() !== visible) {
track.setVisible(visible);
header.setVisible(visible);
} else {
return;
}
}
this.trigger('change', {
name: 'setTrackVisible',
source: this,
arguments: arguments
});
}
,
addRowTrack: function (name, renderSettings) {
if (name === undefined) {
throw 'Name not specified';
}
if ('None' === renderSettings) {
return;
}
if (renderSettings == null) {
renderSettings = morpheus.VectorUtil.getDataType(this.project
.getFullDataset().getRowMetadata().getByName(name)) === '[number]' ? 'bar'
: morpheus.VectorTrack.RENDER.TEXT;
}
var track = new morpheus.VectorTrack(this.project, name, this.heatmap
.getRowPositions(), false, this);
track.settingFromConfig(renderSettings);
this.rowTracks.push(track);
track.appendTo(this.$parent);
$(track.canvas).css('z-index', '0');
var header = new morpheus.VectorTrackHeader(this.project, name, false,
this);
this.rowTrackHeaders.push(header);
header.appendTo(this.$parent);
$(header.canvas).css('z-index', '0');
track._selection = new morpheus.TrackSelection(track, this.heatmap
.getRowPositions(), this.project.getRowSelectionModel(), false,
this);
return track;
}
,
addTrack: function (name, isColumns, renderSettings) {
if (isColumns) {
this.addColumnTrack(name, renderSettings);
} else {
this.addRowTrack(name, renderSettings);
}
}
,
addColumnTrack: function (name, renderSettings) {
if (name === undefined) {
throw 'Name not specified';
}
if ('None' === renderSettings) {
return;
}
if (renderSettings == null) {
renderSettings = morpheus.VectorUtil.getDataType(this.project
.getFullDataset().getColumnMetadata().getByName(name)) === '[number]' ? 'bar'
: morpheus.VectorTrack.RENDER.TEXT;
}
var track = new morpheus.VectorTrack(this.project, name, this.heatmap
.getColumnPositions(), true, this);
track.settingFromConfig(renderSettings);
this.columnTracks.push(track);
track.appendTo(this.$parent);
var header = new morpheus.VectorTrackHeader(this.project, name, true,
this);
this.columnTrackHeaders.push(header);
header.appendTo(this.$parent);
track._selection = new morpheus.TrackSelection(track, this.heatmap
.getColumnPositions(), this.project.getColumnSelectionModel(),
true, this);
return track;
}
,
addPopup: function (item) {
if (!this.popupItems) {
this.popupItems = [];
}
this.popupItems.push(item);
}
,
getPopupItems: function () {
return this.popupItems || [];
}
,
removeTrack: function (name, isColumns) {
var index = this.getTrackIndex(name, isColumns);
var tracks = isColumns ? this.columnTracks : this.rowTracks;
if (isNaN(index) || index < 0 || index >= tracks.length) {
console.log('removeTrack: ' + name + ' not found.');
return;
}
var headers = isColumns ? this.columnTrackHeaders
: this.rowTrackHeaders;
var track = tracks[index];
var header = headers[index];
track.dispose();
track._selection.dispose();
header.dispose();
tracks.splice(index, 1);
headers.splice(index, 1);
this.trigger('change', {
name: 'removeTrack',
source: this,
arguments: arguments
});
}
,
updateDataset: function () {
var dataset = this.project.getSortedFilteredDataset();
this.verticalSearchBar.update();
this.horizontalSearchBar.update();
this.heatmap.setDataset(dataset);
this.heatmap.getRowPositions().spaces = morpheus.HeatMap
.createGroupBySpaces(dataset, this.project.getGroupRows(),
this.gapSize);
this.heatmap.getColumnPositions().spaces = morpheus.HeatMap
.createGroupBySpaces(
new morpheus.TransposedDatasetView(dataset),
this.project.getGroupColumns(), this.gapSize);
this.trigger('change', {
name: 'updateDataset',
source: this,
arguments: arguments
});
}
,
zoom: function (isZoomIn, options) {
options = $.extend({}, {
rows: true,
columns: true
}, options);
if (isZoomIn) {
if (options.rows) {
this.heatmap.getRowPositions().setSize(
this.heatmap.getRowPositions().getSize() * 1.5);
}
if (options.columns) {
this.heatmap.getColumnPositions().setSize(
this.heatmap.getColumnPositions().getSize() * 1.5);
}
} else {
if (options.rows) {
this.heatmap.getRowPositions().setSize(
this.heatmap.getRowPositions().getSize() / 1.5);
}
if (options.columns) {
this.heatmap.getColumnPositions().setSize(
this.heatmap.getColumnPositions().getSize() / 1.5);
}
}
var reval = {};
if (options.rows && this.project.getHoverRowIndex() !== -1) {
reval.scrollTop = this.heatmap.getRowPositions().getPosition(
this.project.getHoverRowIndex());
}
if (options.columns && this.project.getHoverColumnIndex() !== -1) {
reval.scrollLeft = this.heatmap.getColumnPositions().getPosition(
this.project.getHoverColumnIndex());
}
this.revalidate(reval);
this.trigger('change', {
name: 'zoom',
source: this,
arguments: arguments
});
}
,
getTrackIndex: function (name, isColumns) {
var tracks = isColumns ? this.columnTracks : this.rowTracks;
for (var i = 0, length = tracks.length; i < length; i++) {
if (tracks[i].name !== undefined && tracks[i].name === name) {
return i;
}
}
return -1;
}
,
getNumTracks: function (isColumns) {
return isColumns ? this.columnTracks.length : this.rowTracks.length;
}
,
moveTrack: function (index, newIndex, isColumns) {
var tracks = isColumns ? this.columnTracks : this.rowTracks;
var headers = isColumns ? this.columnTrackHeaders
: this.rowTrackHeaders;
var track = tracks[index];
tracks.splice(index, 1);
var header = headers[index];
headers.splice(index, 1);
tracks.splice(newIndex, 0, track);
headers.splice(newIndex, 0, header);
this.trigger('change', {
name: 'moveTrack',
source: this,
arguments: arguments
});
}
,
getTrackByIndex: function (index, isColumns) {
return isColumns ? this.columnTracks[index] : this.rowTracks[index];
}
,
getTrackHeaderByIndex: function (index, isColumns) {
return isColumns ? this.columnTrackHeaders[index]
: this.rowTrackHeaders[index];
}
,
getTrack: function (name, isColumns) {
var index = this.getTrackIndex(name, isColumns);
if (index === -1) {
return undefined;
}
return isColumns ? this.columnTracks[index] : this.rowTracks[index];
}
,
/**
* @return true if active element is an ancestor of this heat map.
*/
isActiveComponent: function () {
var active = document.activeElement;
var tagName = active.tagName;
if (tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA') {
return false;
}
return this.$tabPanel[0].contains(active);
}
,
getActiveComponent: function () {
var active = document.activeElement;
if (active.tagName === 'CANVAS') {
for (var i = 0, ntracks = this.columnTracks.length; i < ntracks; i++) {
if (this.columnTracks[i].canvas === active) {
return 1;
}
}
for (var i = 0, ntracks = this.rowTracks.length; i < ntracks; i++) {
if (this.rowTracks[i].canvas === active) {
return 0;
}
}
if (this.heatmap.canvas === active) {
return 2;
}
}
return -1;
}
,
getVisibleTrackNames: function (isColumns) {
var names = [];
var tracks = isColumns ? this.columnTracks : this.rowTracks;
for (var i = 0, length = tracks.length; i < length; i++) {
if (tracks[i].isVisible()) {
names.push(tracks[i].name);
}
}
return names;
}
,
resizeTrack: function (name, width, height, isColumns) {
var index = this.getTrackIndex(name, isColumns);
if (index === -1) {
throw name + ' not found in resize track';
}
if (!isColumns) {
var track = this.rowTracks[index];
var header = this.rowTrackHeaders[index];
track.setPrefWidth(width); // can only set width
header.setPrefWidth(width);
} else {
var track = this.columnTracks[index];
var header = this.columnTrackHeaders[index];
if (height) {
track.setPrefHeight(height);
header.setPrefHeight(height);
}
if (width) {
for (var i = 0; i < this.columnTracks.length; i++) {
this.columnTracks[i].setPrefWidth(width);
this.columnTrackHeaders[i].setPrefWidth(width);
}
// set width for all tracks
}
}
this.revalidate();
this.trigger('change', {
name: 'resizeTrack',
source: this,
arguments: arguments
});
}
,
isDendrogramVisible: function (isColumns) {
var dendrogram = isColumns ? this.columnDendrogram : this.rowDendrogram;
if (dendrogram !== undefined) {
return morpheus.HeatMap
.isDendrogramVisible(this.project, isColumns);
}
}
,
/**
*
* Paint all the components
*
* @param options.paintRows
* @param options.paintColumns
* @param options.invalidateRows
* @param options.invalidateColumns
*/
paintAll: function (options) {
var unscaledHeight = this.heatmap.getUnscaledHeight();
var unscaledWidth = this.heatmap.getUnscaledWidth();
var y = this.scrollTop();
var x = this.scrollLeft();
this.hscroll.paint();
this.vscroll.paint(); // FIXME
var rows = options.paintRows;
var columns = options.paintColumns;
var invalidateRows = options.invalidateRows;
var invalidateColumns = options.invalidateColumns;
// FIXME double buffer search bars
this.hSortByValuesIndicator.setInvalid(invalidateRows
|| invalidateColumns);
this.hSortByValuesIndicator.paint({
x: x,
y: 0,
width: unscaledWidth,
height: this.hSortByValuesIndicator.getUnscaledHeight()
});
this.vSortByValuesIndicator.setInvalid(invalidateRows
|| invalidateColumns);
this.vSortByValuesIndicator.paint({
x: 0,
y: y,
width: this.vSortByValuesIndicator.getUnscaledWidth(),
height: unscaledHeight
});
if (rows) {
for (var i = 0, length = this.rowTracks.length; i < length; i++) {
var track = this.rowTracks[i];
track.setInvalid(invalidateRows);
if (track.isVisible()) {
track.paint({
x: 0,
y: y,
height: unscaledHeight,
width: unscaledWidth
});
this.rowTrackHeaders[i].paint();
}
}
if (this.rowDendrogram != null) {
this.rowDendrogram.setInvalid(invalidateRows);
if (this.isDendrogramVisible(false)) {
this.rowDendrogram.setVisible(true);
this.rowDendrogram.paint({
x: 0,
y: y,
height: unscaledHeight,
width: this.rowDendrogram.getUnscaledWidth()
});
} else {
this.rowDendrogram.setVisible(false);
}
}
}
if (columns) {
for (var i = 0, length = this.columnTracks.length; i < length; i++) {
var track = this.columnTracks[i];
track.setInvalid(invalidateColumns);
track.paint({
x: x,
y: 0,
width: unscaledWidth,
height: track.getUnscaledHeight()
});
this.columnTrackHeaders[i].paint();
}
if (this.columnDendrogram != null) {
this.columnDendrogram.setInvalid(invalidateColumns);
if (this.isDendrogramVisible(true)) {
this.columnDendrogram.setVisible(true);
this.columnDendrogram.paint({
x: x,
y: 0,
width: unscaledWidth,
height: this.columnDendrogram.getUnscaledHeight()
});
} else {
this.columnDendrogram.setVisible(false);
}
}
}
if (invalidateRows || invalidateColumns) {
this.heatmap.setInvalid(true);
}
this.heatmap.paint({
x: x,
y: y,
width: unscaledWidth,
height: unscaledHeight
});
this.trigger('change', {
name: 'paintAll',
source: this,
arguments: arguments
});
}
,
scrollTop: function (pos) {
if (pos === undefined) {
return this.vscroll.getValue();
}
this.vscroll.setValue(pos, true);
this.trigger('change', {
name: 'scrollTop',
source: this,
arguments: arguments
});
}
,
scrollLeft: function (pos) {
if (pos === undefined) {
return this.hscroll.getValue();
}
this.trigger('change', {
name: 'scrollLeft',
source: this,
arguments: arguments
});
this.hscroll.setValue(pos, true);
}
,
setSelectedTrack: function (name, isColumns) {
if (name !== this.selectedTrackName
|| isColumns !== this.selectedTrackIsColumns) {
var index = this.getTrackIndex(this.selectedTrackName,
this.selectedTrackIsColumns);
if (index !== -1) {
this.getTrackHeaderByIndex(index, this.selectedTrackIsColumns)
.setSelected(false);
}
this.selectedTrackName = name;
this.selectedTrackIsColumns = isColumns;
var index = this.getTrackIndex(this.selectedTrackName,
this.selectedTrackIsColumns);
if (index !== -1) {
this.getTrackHeaderByIndex(index, this.selectedTrackIsColumns)
.setSelected(true);
}
this.trigger('change', {
name: 'setSelected',
source: this,
arguments: arguments
});
}
}
,
saveImage: function (file, format) {
var bounds = this.getTotalSize();
if (format === 'svg') {
var context = new C2S(bounds.width, bounds.height);
this.snapshot(context);
var svg = context.getSerializedSvg();
var blob = new Blob([svg], {
type: 'text/plain;charset=utf-8'
});
saveAs(blob, file, true);
} else {
var canvas = $('')[0];
var height = bounds.height;
var width = bounds.width;
canvas.height = height;
canvas.width = width;
var context = canvas.getContext('2d');
this.snapshot(context);
canvas.toBlob(function (blob) {
if (blob == null || blob.size === 0) {
morpheus.FormBuilder.showInModal({
title: 'Save Image',
html: 'Image is too large to save.'
});
return;
}
saveAs(blob, file, true);
});
}
}
,
getTotalSize: function (options) {
options = $.extend({}, {
legend: true
}, options);
var _this = this;
var heatmapPrefSize = this.heatmap.getPreferredSize();
var totalSize = {
width: heatmapPrefSize.width,
height: heatmapPrefSize.height
};
if (this.isDendrogramVisible(false)) { // row dendrogram
totalSize.width += this.rowDendrogram.getUnscaledWidth() + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
}
if (this.isDendrogramVisible(true)) {
totalSize.height += this.columnDendrogram.getUnscaledHeight() + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
}
var maxRowHeaderHeight = 0;
for (var i = 0, length = this.rowTracks.length; i < length; i++) {
var track = this.rowTracks[i];
if (track.isVisible()) {
var headerSize = this.rowTrackHeaders[i].getPrintSize();
totalSize.width += Math.max(headerSize.width, track
.getPrintSize().width);
maxRowHeaderHeight = Math.max(maxRowHeaderHeight, headerSize.height);
}
}
var maxColumnHeaderWidth = 0;
var columnTrackHeightSum = 0;
for (var i = 0, length = this.columnTracks.length; i < length; i++) {
var track = this.columnTracks[i];
if (track.isVisible()) {
columnTrackHeightSum += track.getPrintSize().height;
maxColumnHeaderWidth = Math.max(maxColumnHeaderWidth,
this.columnTrackHeaders[i].getPrintSize().width);
}
}
totalSize.height += Math.max(columnTrackHeightSum, maxRowHeaderHeight) + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
totalSize.width += maxColumnHeaderWidth + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
// color legend
if (options.legend) {
var legendHeight = this.heatmap.getColorScheme().getNames() != null ? this.heatmap
.getColorScheme().getNames().length * 14
: 40;
totalSize.height += legendHeight + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
}
var trackLegendSize = new morpheus.HeatMapTrackColorLegend(
_
.filter(
this.columnTracks,
function (track) {
return track.isVisible()
&& (track
.isRenderAs(morpheus.VectorTrack.RENDER.COLOR) || track
.isRenderAs(morpheus.VectorTrack.RENDER.TEXT_AND_COLOR));
}), this.getProject().getColumnColorModel())
.getPreferredSize();
totalSize.height += trackLegendSize.height;
totalSize.width = Math.max(totalSize.width, trackLegendSize.width);
trackLegendSize = new morpheus.HeatMapTrackColorLegend(
_
.filter(
this.rowTracks,
function (track) {
return track.isVisible()
&& (track
.isRenderAs(morpheus.VectorTrack.RENDER.COLOR) || track
.isRenderAs(morpheus.VectorTrack.RENDER.TEXT_AND_COLOR));
}), this.getProject().getRowColorModel())
.getPreferredSize();
totalSize.height += morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS + trackLegendSize.height;
totalSize.width = morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS + Math.max(totalSize.width, trackLegendSize.width);
return totalSize;
}
,
getHeatMapElementComponent: function () {
return this.heatmap;
}
,
snapshot: function (context, options) {
options = $.extend({}, {
legend: true
}, options);
var heatmapPrefSize = this.heatmap.getPreferredSize();
var totalSize = this.getTotalSize(options);
var legendHeight = 0;
if (options.legend) {
context.save();
context.translate(50, 0);
morpheus.HeatMapColorSchemeLegend.drawColorScheme(context,
this.heatmap.getColorScheme(), 200, true);
context.restore();
legendHeight = this.heatmap.getColorScheme().getNames() != null ? this.heatmap
.getColorScheme().getNames().length * 14
: 40;
}
context.save();
context.translate(4, legendHeight);
// column color legend
var columnTrackLegend = new morpheus.HeatMapTrackColorLegend(
_
.filter(
this.columnTracks,
function (track) {
return track.isVisible()
&& (track
.isRenderAs(morpheus.VectorTrack.RENDER.COLOR) || track
.isRenderAs(morpheus.VectorTrack.RENDER.TEXT_AND_COLOR));
}), this.getProject().getColumnColorModel());
columnTrackLegend.draw({}, context);
context.restore();
// row color legend to the right of column color legend
var columnTrackLegendSize = columnTrackLegend.getPreferredSize();
context.save();
context.translate(4 + columnTrackLegendSize.width, legendHeight);
var rowTrackLegend = new morpheus.HeatMapTrackColorLegend(
_
.filter(
this.rowTracks,
function (track) {
return track.isVisible()
&& (track
.isRenderAs(morpheus.VectorTrack.RENDER.COLOR) || track
.isRenderAs(morpheus.VectorTrack.RENDER.TEXT_AND_COLOR));
}), this.getProject().getRowColorModel());
rowTrackLegend.draw({}, context);
context.restore();
legendHeight += Math.max(rowTrackLegend.getPreferredSize().height,
columnTrackLegendSize.height);
var heatmapY = this.isDendrogramVisible(true) ? (this.columnDendrogram
.getUnscaledHeight() + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS) : 0;
heatmapY += legendHeight;
var columnTrackY = heatmapY;
var heatmapX = this.isDendrogramVisible(false) ? (this.rowDendrogram
.getUnscaledWidth() + morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS) : 0;
var isColumnTrackVisible = false;
for (var i = 0, length = this.columnTracks.length; i < length; i++) {
var track = this.columnTracks[i];
if (track.isVisible()) {
var header = this.columnTrackHeaders[i];
heatmapX = Math.max(heatmapX, header.getPrintSize().width);
heatmapY += track.getPrintSize().height;
isColumnTrackVisible = true;
}
}
if (isColumnTrackVisible) {
heatmapY += morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
}
// check if row headers are taller than column tracks
for (var i = 0, length = this.rowTracks.length; i < length; i++) {
var track = this.rowTracks[i];
if (track.isVisible()) {
var header = this.rowTrackHeaders[i];
heatmapY = Math.max(heatmapY, header.getPrintSize().height);
}
}
if (this.isDendrogramVisible(true)) {
var columnDendrogramClip = {
x: 0,
y: 0,
height: this.columnDendrogram.getUnscaledHeight(),
width: heatmapPrefSize.width
};
context.save();
context.translate(heatmapX, legendHeight);
this.columnDendrogram.prePaint(columnDendrogramClip, context);
this.columnDendrogram.draw(columnDendrogramClip, context);
context.restore();
}
if (this.isDendrogramVisible(false)) {
var rowDendrogramClip = {
x: 0,
y: 0,
width: this.rowDendrogram.getUnscaledWidth(),
height: heatmapPrefSize.height
};
context.save();
context.translate(0, heatmapY);
this.rowDendrogram.prePaint(rowDendrogramClip, context);
this.rowDendrogram.draw(rowDendrogramClip, context);
context.restore();
}
for (var i = 0, length = this.columnTracks.length; i < length; i++) {
var track = this.columnTracks[i];
if (track.isVisible()) {
context.save();
context.translate(heatmapX, columnTrackY);
var trackClip = {
x: 0,
y: 0,
width: heatmapPrefSize.width,
height: track.getPrintSize().height
};
track.print(trackClip, context);
context.restore();
// draw header
var header = this.columnTrackHeaders[i];
context.save();
var headerSize = header.getPrintSize();
var headerClip = {
x: 0,
y: 0,
width: headerSize.width,
height: trackClip.height
};
context.translate(heatmapX - 2, columnTrackY + trackClip.height);
header.print(headerClip, context);
context.restore();
columnTrackY += Math.max(headerClip.height, trackClip.height);
}
}
context.save();
context.translate(heatmapX, heatmapY);
this.heatmap.draw({
x: 0,
y: 0,
width: heatmapPrefSize.width,
height: heatmapPrefSize.height
}, context);
context.restore();
var rowTrackWidthSum = 0;
for (var i = 0, length = this.rowTracks.length; i < length; i++) {
var track = this.rowTracks[i];
if (track.isVisible()) {
context.save();
var tx = morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS + heatmapX + heatmapPrefSize.width + rowTrackWidthSum;
var ty = heatmapY;
var trackClip = {
x: 0,
y: 0,
width: track.getPrintSize().width,
height: heatmapPrefSize.height
};
context.translate(tx, ty);
context.strokeStyle = 'white';
context.rect(0, 0, trackClip.width, trackClip.height);
// stroke is needed for clip to work for svg export
context.stroke();
context.clip();
track.print(trackClip, context);
context.restore();
// draw header
var header = this.rowTrackHeaders[i];
context.save();
var headerSize = header.getPrintSize();
var headerClip = {
x: 0,
y: 0,
width: headerSize.width,
height: headerSize.height
};
context.translate(tx, ty - 4);
header.print(headerClip, context);
context.restore();
rowTrackWidthSum += Math.max(headerSize.width, trackClip.width);
}
}
}
,
resetZoom: function () {
var heatmap = this.heatmap;
var rowSizes = heatmap.getRowPositions();
var columnSizes = heatmap.getColumnPositions();
rowSizes.setSize(13);
columnSizes.setSize(13);
var reval = {};
if (this.project.getHoverRowIndex() !== -1) {
reval.scrollTop = this.heatmap.getRowPositions().getPosition(
this.project.getHoverRowIndex());
}
if (this.project.getHoverColumnIndex() !== -1) {
reval.scrollLeft = this.heatmap.getColumnPositions().getPosition(
this.project.getHoverColumnIndex());
}
this.revalidate(reval);
}
,
getFitColumnSize: function () {
var heatmap = this.heatmap;
var availablePixels = this.getAvailableWidth();
if (this.rowDendrogram) {
availablePixels -= this.rowDendrogram.getUnscaledWidth();
}
var trackPixels = 12; // spacer
for (var i = 0, length = this.rowTracks.length; i < length; i++) {
var track = this.rowTracks[i];
if (track.isVisible()) {
trackPixels += track.getUnscaledWidth();
}
}
for (var i = 0, length = this.columnTracks.length; i < length; i++) {
var track = this.columnTracks[i];
if (track.isVisible()) { // all column track headers have the
// same width
trackPixels += this.columnTrackHeaders[i].getUnscaledWidth();
break;
}
}
availablePixels -= trackPixels;
var positions = heatmap.getColumnPositions();
var totalCurrent = positions.getItemSize(positions.getLength() - 1)
+ positions.getPosition(positions.getLength() - 1);
var size = positions.getSize();
size = size * (availablePixels / totalCurrent);
size = Math.min(13, size);
return size;
}
,
getFitRowSize: function () {
var heatmap = this.heatmap;
var availablePixels = this.getAvailableHeight();
if (this.columnDendrogram) {
availablePixels -= this.columnDendrogram.getUnscaledHeight();
}
var trackPixels = 12;
for (var i = 0, length = this.columnTracks.length; i < length; i++) {
var track = this.columnTracks[i];
if (track.isVisible()) {
trackPixels += track.getUnscaledHeight();
}
}
availablePixels -= trackPixels;
var positions = heatmap.getRowPositions();
var totalCurrent = positions.getItemSize(positions.getLength() - 1)
+ positions.getPosition(positions.getLength() - 1);
var size = positions.getSize();
size = size * (availablePixels / totalCurrent);
size = Math.min(13, size);
return size;
}
,
fitToWindow: function (repaint) {
this.heatmap.getRowPositions().setSize(this.getFitRowSize());
this.heatmap.getColumnPositions().setSize(this.getFitColumnSize());
if (repaint) {
var reval = {};
if (this.project.getHoverRowIndex() !== -1) {
reval.scrollTop = this.heatmap.getRowPositions().getPosition(
this.project.getHoverRowIndex());
}
if (this.project.getHoverColumnIndex() !== -1) {
reval.scrollLeft = this.heatmap.getColumnPositions()
.getPosition(this.project.getHoverColumnIndex());
}
this.revalidate(reval);
}
}
,
getAvailableHeight: function () {
if (_.isNumber(this.options.height)) {
return this.options.height;
}
var height = $(window).height() - this.$parent.offset().top - 24;
if (this.options.height === 'window') {
return height;
}
return Math.max(Math.round(screen.height * 0.7), height);
}
,
getAvailableWidth: function () {
if (this.options.width) {
return this.options.width;
}
// (this.$el.parent().outerWidth() - 30);
// return this.$el.width() - 30;
return this.tabManager.getWidth() - 30;
}
,
/**
* Layout all the components
*/
revalidate: function (options) {
options = $.extend({}, {
paint: true
}, options);
this.updatingScroll = true;
var availableHeight = this.getAvailableHeight();
var availableWidth = this.getAvailableWidth();
var heatmapPrefSize = this.heatmap.getPreferredSize();
var columnDendrogramHeight = 0;
var rowDendrogramWidth = 0;
if (this.columnDendrogram) {
columnDendrogramHeight = morpheus.CanvasUtil
.getPreferredSize(this.columnDendrogram).height;
}
if (this.rowDendrogram) {
rowDendrogramWidth = morpheus.CanvasUtil
.getPreferredSize(this.rowDendrogram).width;
}
var rowTrackWidthSum = 0;
for (var i = 0, length = this.rowTracks.length; i < length; i++) {
if (this.rowTracks[i].isVisible()) {
rowTrackWidthSum += Math
.max(
morpheus.CanvasUtil
.getPreferredSize(this.rowTrackHeaders[i]).width,
morpheus.CanvasUtil
.getPreferredSize(this.rowTracks[i]).width);
}
}
var ypos = columnDendrogramHeight;
var maxHeaderWidth = 0;
for (var i = 0, length = this.columnTracks.length; i < length; i++) {
if (this.columnTracks[i].isVisible()) {
var width = morpheus.CanvasUtil
.getPreferredSize(this.columnTrackHeaders[i]).width;
maxHeaderWidth = Math.max(maxHeaderWidth, width);
}
}
var xpos = Math.max(rowDendrogramWidth, maxHeaderWidth);
var heatMapWidth = heatmapPrefSize.width;
var maxHeatMapWidth = Math.max(50, availableWidth - rowTrackWidthSum
- xpos
- morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS);
if (maxHeatMapWidth > 0 && heatMapWidth > maxHeatMapWidth) {
heatMapWidth = maxHeatMapWidth;
heatMapWidth = Math.min(heatMapWidth, heatmapPrefSize.width); // can't
// go
// bigger
// than
// pref
// width
}
if (this.heatmap.prefWidth !== undefined) { // heat map was manually
// resized
heatMapWidth = Math.min(heatmapPrefSize.width,
this.heatmap.prefWidth);
}
if (this.columnDendrogram !== undefined) {
this.columnDendrogram.setBounds({
width: heatMapWidth,
height: columnDendrogramHeight,
left: xpos,
top: 0
});
this.columnDendrogram.$label.css('left',
xpos + this.columnDendrogram.getUnscaledWidth() + 10).css(
'top', 2);
this.columnDendrogram.$squishedLabel.css('left',
xpos + this.columnDendrogram.getUnscaledWidth() + 10).css(
'top', 18);
this.beforeColumnTrackDivider.setVisible(true);
this.beforeColumnTrackDivider.setBounds({
left: xpos - maxHeaderWidth,
top: ypos,
width: maxHeaderWidth
});
ypos++;
} else {
this.beforeColumnTrackDivider.setVisible(false);
}
for (var i = 0, length = this.columnTracks.length; i < length; i++) {
var track = this.columnTracks[i];
if (track.isVisible()) {
var size = morpheus.CanvasUtil.getPreferredSize(track);
var headerSize = morpheus.CanvasUtil
.getPreferredSize(this.columnTrackHeaders[i]);
size.height = Math.max(size.height, headerSize.height);
track.setBounds({
width: heatMapWidth,
height: size.height,
left: xpos,
top: ypos
});
this.columnTrackHeaders[i].setBounds({
width: maxHeaderWidth,
height: size.height,
left: xpos - maxHeaderWidth,
top: ypos
});
ypos += size.height;
}
}
ypos += morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS;
var heatMapHeight = heatmapPrefSize.height;
if (heatMapHeight > (availableHeight - ypos)) {
heatMapHeight = Math.max(100, Math.min(heatmapPrefSize.height,
availableHeight - ypos));
}
if (ypos < 0) {
ypos = 0;
}
if (this.rowDendrogram) {
this.rowDendrogram.setBounds({
width: Math.max(rowDendrogramWidth, maxHeaderWidth),
height: heatMapHeight,
left: 0,
top: ypos
});
this.rowDendrogram.$label.css('left', 0).css('top', 2);
this.afterRowDendrogramDivider.setVisible(true);
this.afterRowDendrogramDivider.setBounds({
height: heatMapHeight,
left: this.rowDendrogram.getUnscaledWidth(),
top: ypos
});
xpos++;
} else {
this.afterRowDendrogramDivider.setVisible(false);
}
this.heatmap.setBounds({
width: heatMapWidth,
height: heatMapHeight,
left: xpos,
top: ypos
});
this.hSortByValuesIndicator.setBounds({
height: 4,
width: heatMapWidth,
left: xpos,
top: ypos - 4
});
this.hscroll.setVisible(heatMapWidth < heatmapPrefSize.width);
this.hscroll.setExtent(heatMapWidth, heatmapPrefSize.width,
options.scrollLeft !== undefined ? options.scrollLeft
: (heatmapPrefSize.width === this.hscroll
.getTotalExtent() ? this.hscroll.getValue()
: heatmapPrefSize.width
* this.hscroll.getValue()
/ this.hscroll.getMaxValue()));
this.hscroll.setBounds({
left: xpos,
top: ypos + heatMapHeight + 2
});
xpos += heatMapWidth;
var nvisibleRowTracks = 0;
for (var i = 0, length = this.rowTracks.length; i < length; i++) {
var track = this.rowTracks[i];
if (track.isVisible()) {
nvisibleRowTracks++;
break;
}
}
this.vSortByValuesIndicator.setBounds({
width: 4,
height: heatMapHeight,
left: xpos,
top: ypos
});
if (nvisibleRowTracks > 0) {
xpos = xpos
+ morpheus.HeatMap.SPACE_BETWEEN_HEAT_MAP_AND_ANNOTATIONS; // leave
// space
// after
// afterVerticalScrollBarDivider
}
var rowAnnotationXStart = xpos;
for (var i = 0, length = this.rowTracks.length; i < length; i++) {
var track = this.rowTracks[i];
if (track.isVisible()) {
var size = morpheus.CanvasUtil.getPreferredSize(track);
var headerSize = morpheus.CanvasUtil
.getPreferredSize(this.rowTrackHeaders[i]);
size.width = Math.max(headerSize.width, size.width);
size.height = heatMapHeight;
track.setBounds({
width: size.width,
height: size.height,
left: xpos,
top: ypos
});
this.rowTrackHeaders[i].setBounds({
width: size.width,
left: xpos,
top: ypos - headerSize.height - 5,
height: headerSize.height
});
xpos += size.width;
}
}
this.afterVerticalScrollBarDivider
.setVisible(nvisibleRowTracks > 0 ? true : false);
this.afterVerticalScrollBarDivider.setBounds({
left: rowAnnotationXStart - 2,
top: ypos - 18
});
this.vscroll.setVisible(heatMapHeight < heatmapPrefSize.height);
this.vscroll.setExtent(heatMapHeight, heatmapPrefSize.height,
options.scrollTop !== undefined ? options.scrollTop
: (heatmapPrefSize.height === this.vscroll
.getTotalExtent() ? this.vscroll.getValue()
: heatmapPrefSize.height
* this.vscroll.getValue()
/ this.vscroll.getMaxValue()));
xpos += 2;
this.vscroll.setBounds({
left: xpos,
top: ypos
});
xpos += this.vscroll.getUnscaledWidth();
if (this.hscroll.isVisible()) {
ypos += this.hscroll.getUnscaledHeight() + 2;
}
var totalHeight = 2 + ypos + heatMapHeight;
if (options.paint) {
this.paintAll({
paintRows: true,
paintColumns: true,
invalidateRows: true,
invalidateColumns: true
});
}
this.$parent.css({
height: Math.ceil(totalHeight) + 'px'
});
this.updatingScroll = false;
this.trigger('change', {
name: 'revalidate',
source: this,
arguments: arguments
});
}
};
morpheus.HeatMap.copyFromParent = function (project, options) {
// TODO persist sort order, grouping, dendrogram
project.rowColorModel = options.parent.getProject().getRowColorModel()
.copy();
project.columnColorModel = options.parent.getProject()
.getColumnColorModel().copy();
project.rowShapeModel = options.parent.getProject().getRowShapeModel()
.copy();
project.columnShapeModel = options.parent.getProject()
.getColumnShapeModel().copy();
var parentRowTracks = options.parent.rowTracks || [];
var parentColumnTracks = options.parent.columnTracks || [];
if (options.inheritFromParentOptions.rows) { // row similarity matrix
project.columnShapeModel = project.rowShapeModel;
project.columnColorModel = project.rowColorModel;
parentColumnTracks = parentRowTracks.slice().reverse();
}
if (options.inheritFromParentOptions.columns) { // column similarity matrix
project.rowShapeModel = project.columnShapeModel;
project.rowColorModel = project.columnColorModel;
parentRowTracks = parentColumnTracks.slice().reverse();
}
if (options.inheritFromParentOptions.transpose) {
var tmp = project.rowShapeModel;
project.rowShapeModel = project.columnShapeModel;
project.columnShapeModel = tmp;
tmp = project.rowColorModel;
project.rowColorModel = project.columnColorModel;
project.columnColorModel = tmp;
tmp = parentRowTracks.slice().reverse();
// swap tracks
parentRowTracks = parentColumnTracks.slice().reverse();
parentColumnTracks = tmp;
}
// copy track rendering options and order
// from parent
options.rows = options.rows || [];
for (var i = 0; i < parentRowTracks.length; i++) {
var track = parentRowTracks[i];
if (track.isVisible()) {
options.rows.push({
order: options.rows.length,
field: track.getName(),
display: $.extend(true, {}, track.settings),
force: true
});
}
}
options.columns = options.columns || [];
for (var i = 0; i < parentColumnTracks.length; i++) {
var track = parentColumnTracks[i];
if (track.isVisible()) {
options.columns.push({
order: options.columns.length,
field: track.getName(),
display: $.extend(true, {}, track.settings),
force: true
});
}
}
};
morpheus.Util.extend(morpheus.HeatMap, morpheus.Events);