@domoinc/image-tooltip
Version:
ImageTooltip - Domo Widget
1,334 lines (1,198 loc) • 1.02 MB
JavaScript
/*! Copyright 2016 Domo Inc. */
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory(require("d3"), require("d3.chart"), require("@domoinc/base-widget"));
else if(typeof define === 'function' && define.amd)
define(["d3", "d3.chart", "base-widget"], factory);
else if(typeof exports === 'object')
exports["ImageTooltip"] = factory(require("d3"), require("d3.chart"), require("@domoinc/base-widget"));
else
root["ImageTooltip"] = factory(root["d3"], root["d3.chart"], root["BaseWidget"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_3__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/dist/";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
'use strict';
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------
// ImageTooltip:
© 2011 - 2015 DOMO, INC.
----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
var d3 = __webpack_require__(1);
var d3Chart = __webpack_require__(2);
var BaseWidget = __webpack_require__(3);
var daTheme2 = __webpack_require__(4);
var navigate = __webpack_require__(7);
var Tooltip = __webpack_require__(8);
module.exports = BaseWidget.extend('ImageTooltip', {
//**********************************************************************************
// Initialization
//**********************************************************************************
initialize: function initialize() {
'use strict';
var _Chart = this;
//----------------------------------------------------------------------------------
// Config helper functions
//----------------------------------------------------------------------------------
function getMinWidthHeight() {
return d3.min([_Chart.c('width'), _Chart.c('height')]);
}
//----------------------------------------------------------------------------------
// Config
//----------------------------------------------------------------------------------
_Chart._newConfig = {
// Configurable configs
// Image configs
width: {
name: 'Image Width',
description: 'Width of the image',
value: 400,
type: 'number',
units: 'px'
},
height: {
name: 'Image Height',
description: 'Height of the image',
value: 400,
type: 'number',
units: 'px'
},
// Tooltip configs
tooltipWidth: {
name: 'Tooltip Width',
description: 'Width of the tooltip',
value: 200,
type: 'number',
units: 'px'
},
tooltipBackgroundColor: daTheme2.themeElements.tooltipBackground(),
font: daTheme2.themeElements.tooltipFontFamily(),
// Tooltip title configs
tooltipTitleFontWeight: daTheme2.themeElements.tooltipFontWeight({
name: 'Tooltip Title Font Weight'
}),
tooltipTitleFontSize: daTheme2.themeElements.tooltipFontSize({
name: 'Tooltip Title Font Size',
description: 'Font size for the tooltip title'
}),
tooltipTitleFontColor: daTheme2.themeElements.tooltipFontColor({
name: 'Tooltip Title Font Color',
description: 'Font color for the tooltip title'
}),
// Tooltip body configs
tooltipBodyFontWeight: daTheme2.themeElements.tooltipFontWeight({
name: 'Tooltip Body Font Weight'
}),
tooltipBodyFontSize: daTheme2.themeElements.tooltipFontSize({
name: 'Tooltip Body Font Size',
description: 'Font size for the tooltip body text'
}),
tooltipBodyFontColor: daTheme2.themeElements.tooltipFontColor({
name: 'Tooltip Body Font Color',
description: 'Font color for the tooltip body text'
}),
// Non configurable configs
chartName: {
description: 'Name of chart for Reporting.',
type: 'string',
value: 'ImageTooltip'
},
showErrorMessage: {
type: 'boolean',
value: true
}
};
_Chart.mergeConfig(_Chart._newConfig);
//----------------------------------------------------------------------------------
// Data Definition
//----------------------------------------------------------------------------------
_Chart._newDataDefinition = {
'Title': {
type: 'string',
validate: function validate(d) {
return true;
},
accessor: function accessor(line) {
return line[0];
}
},
'Text': {
type: 'string',
validate: function validate(d) {
return true;
},
accessor: function accessor(line) {
return line[1];
}
},
'ImageURL': {
type: 'string',
validate: function validate(d) {
return true;
},
accessor: function accessor(line) {
return line[2];
}
},
'LinkURL': {
type: 'string',
validate: function validate(d) {
return true;
},
accessor: function accessor(line) {
return line[3];
}
}
};
_Chart.mergeDataDefinition(_Chart._newDataDefinition);
//----------------------------------------------------------------------------------
// Local variables
//----------------------------------------------------------------------------------
var imageGroup = _Chart._layerGroup.append('g').classed('imageGroup', true);
_Chart._svg = findSvg(_Chart._layerGroup.node());
var tooltipSvg = d3.select('body').append('div').classed('tooltipDiv', true).style({
width: '100%',
height: '100%',
position: 'absolute',
top: 0,
left: 0,
'z-index': 10000, // needs to be over 9000 to go over the dropdown menu
'pointer-events': 'none'
}).append('svg').attr({
height: '100%',
width: '100%'
});
_Chart._tooltip = tooltipSvg.chart('DomoTooltip').c('container', tooltipSvg).c('format', 'multiTextBlock');
_Chart._tooltip._appends.append('text').classed('title', true);
_Chart._tooltip._appends.append('text').classed('body', true);
//----------------------------------------------------------------------------------
// Triggers and event listeners
//----------------------------------------------------------------------------------
//**********************************************************************************
// d3.Chart Transform
//**********************************************************************************
var superTransform = _Chart.transform;
_Chart.transform = function (data) {
var validData = superTransform(data);
insertImage(data);
updateTooltipStyles();
return validData;
};
//----------------------------------------------------------------------------------
// Event functions
//----------------------------------------------------------------------------------
function hoverMove(d) {
var mouse = d3.mouse(_Chart._svg.node());
var point = { x: mouse[0], y: mouse[1] };
_Chart._tooltip._appends.select('.title').text(_Chart.a('Title')(d));
_Chart._tooltip._appends.select('.body').text(_Chart.a('Text')(d));
_Chart._tooltip.trigger('draw');
_Chart._tooltip.trigger('moveTo', point);
}
function hoverOff() {
_Chart._tooltip.trigger('remove');
}
//----------------------------------------------------------------------------------
// Tooltip functions
//----------------------------------------------------------------------------------
function updateTooltipStyles() {
_Chart._tooltip.c('maxWidth', _Chart.c('tooltipWidth')).c('tooltipBackgroundColor', _Chart.c('tooltipBackgroundColor'));
_Chart._tooltip._appends.select('.title').style({
'font-family': _Chart.c('font'),
'font-weight': _Chart.c('tooltipTitleFontWeight').value,
'font-size': _Chart.c('tooltipTitleFontSize'),
'fill': _Chart.c('tooltipTitleFontColor')
});
_Chart._tooltip._appends.select('.body').style({
'font-family': _Chart.c('font'),
'font-weight': _Chart.c('tooltipBodyFontWeight').value,
'font-size': _Chart.c('tooltipBodyFontSize'),
'fill': _Chart.c('tooltipBodyFontColor')
});
}
//----------------------------------------------------------------------------------
// Helper functions
//----------------------------------------------------------------------------------
function findSvg(selection) {
var selectionParent = selection.parentNode;
if (selectionParent.tagName === 'svg') {
return d3.select(selectionParent);
} else {
return findSvg(selectionParent);
}
}
//----------------------------------------------------------------------------------
// Image functions
//----------------------------------------------------------------------------------
/**
* load image if there is a valid url
* otherwise give an error image
*/
function insertImage(data) {
imageGroup.selectAll('*').remove();
var container = addAnchor(imageGroup, _Chart.a('LinkURL')(data[0]));
var img = new Image();
img.src = _Chart.a('ImageURL')(data[0]);
img.onload = function () {
updateImageAttrs(container, this, data[0]);
};
img.onerror = function () {
if (_Chart.c('showErrorMessage')) {
addImageFailedMessage(container);
}
};
}
function updateImageAttrs(container, imgObj, data) {
var svgImage = container.append('image').datum(data).attr({
'image-rendering': 'optimizeQuality',
'x': 0,
'y': 0
}).style({
opacity: 1,
display: 'inline'
}).on('mousemove', hoverMove).on('mouseout', hoverOff);
var chartRatio = _Chart.c('width') / _Chart.c('height');
var width;
var height;
var transform;
var imgWidth = imgObj.width;
var imgHeight = imgObj.height;
var imageRatio = imgWidth / imgHeight;
if (imageRatio > chartRatio) {
width = _Chart.c('width');
height = _Chart.c('width') * imgHeight / imgWidth;
transform = 'translate(0, ' + (_Chart.c('height') - height) / 2 + ')';
} else {
width = _Chart.c('height') * imgWidth / imgHeight;
height = _Chart.c('height');
transform = 'translate(' + (_Chart.c('width') - width) / 2 + ', 0)';
}
svgImage.attr({
'xlink:href': imgObj.src,
'width': width,
'height': height,
'transform': transform
});
}
function addImageFailedMessage(container) {
var chartRatio = _Chart.c('width') / _Chart.c('height');
var imageWidth = 143.804;
var imageHeight = 185.958;
var imageRatio = imageWidth / imageHeight;
var scale;
var imageShapePath = 'M131.82,12.096H11.984v96.767H131.82V12.096L131.82,12.096z M143.804,119.761 c0,0.661-0.535,1.197-1.2,1.197H1.2c-0.663,0-1.2-0.539-1.2-1.197V1.197C0,0.536,0.535,0,1.2,0h141.403c0.663,0,1.2,0.539,1.2,1.197 L143.804,119.761L143.804,119.761z M119.836,96.767H23.968V77.759l28.76-47.52l38.348,47.519l28.761-19.008L119.836,96.767 L119.836,96.767z M85.437,38.783c0-5.213,2.755-10.03,7.228-12.637c4.473-2.607,9.983-2.607,14.456,0 c4.473,2.607,7.228,7.424,7.228,12.637c0,5.213-2.755,10.03-7.228,12.637c-4.473,2.607-9.983,2.607-14.456,0 S85.437,43.996,85.437,38.783L85.437,38.783z';
var exclamationShape = 'M0,22.82h26.365L13.183,0L0,22.82z M13.183,4.795l9.025,15.623H4.158L13.183,4.795z M14.381,19.217h-2.397v-2.402h2.397V19.217z M11.984,15.614V9.609h2.397v6.005H11.984z';
var fontSize = 10;
var transformX;
var transformY;
if (imageRatio > chartRatio) {
scale = _Chart.c('width') * 0.8 / imageWidth;
} else {
scale = _Chart.c('height') * 0.8 / imageHeight;
}
transformX = (_Chart.c('width') - imageWidth * scale) / 2;
transformY = (_Chart.c('height') - imageHeight * scale) / 2;
var imageFailedGroupScale = container.append('g').attr('transform', 'translate(' + transformX + ', ' + transformY + ')');
var imageFailedGroup = imageFailedGroupScale.append('g').attr('transform', 'scale(' + scale + ')');
imageFailedGroup.append('path').attr({
'd': exclamationShape,
'transform': 'translate(' + 59 + ', ' + 0 + ')'
}).style({
'fill': '#fb9995'
});
var textGroup = imageFailedGroup.append('g').attr({
'transform': 'translate(' + 72 + ', ' + 40 + ')'
});
textGroup.append('text').text('Your image has failed to load.').style({
'font-size': fontSize + 'px',
'text-anchor': 'middle',
'fill': '#808080'
});
textGroup.append('text').text('Try checking your image URL.').attr({
'y': fontSize * 1.35
}).style({
'font-size': fontSize + 'px',
'text-anchor': 'middle',
'fill': '#999999'
});
imageFailedGroup.append('path').attr({
'd': imageShapePath,
'transform': 'translate(' + 0 + ', ' + 65 + ')'
}).style({
'fill': '#e6e6e6'
});
// an invisible rect which captures mouse events
imageFailedGroup.append('rect').attr({
width: imageWidth,
height: imageHeight
}).style({
opacity: 0
});
}
function clickLink(url) {
return function () {
d3.event.preventDefault();
navigate(url, true);
};
}
function addAnchor(selection, url) {
if (url) {
return selection.append('a').attr({
'xlink:href': url,
'target': '_blank'
}).on('click', clickLink(url));
}
return selection;
}
}
});
/***/ }),
/* 1 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
/***/ }),
/* 2 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_2__;
/***/ }),
/* 3 */
/***/ (function(module, exports) {
module.exports = __WEBPACK_EXTERNAL_MODULE_3__;
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
/*! Copyright 2016 Domo Inc. */
(function webpackUniversalModuleDefinition(root, factory) {
if(true)
module.exports = factory(__webpack_require__(5));
else if(typeof define === 'function' && define.amd)
define(["lodash"], factory);
else if(typeof exports === 'object')
exports["theme2"] = factory(require("lodash"));
else
root["da"] = root["da"] || {}, root["da"]["theme2"] = factory(root["_"]);
})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) {
return /******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "/dist/";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
var _ = __webpack_require__(1);
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
// DA THEME
//
// Organization:
// * Private Functions and Definitions
// * Public Functions and Definitions
// * Config Theme Elements Constructors
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
var daTheme2 = (function () {
var theme = {};
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------
// PRIVATE FUNCTION and DEFINITIONS
----------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
var largeIndexMap = {
0: [],
1: [[0, 3]],
2: [[0, 3], [1, 1]],
3: [[0, 3], [1, 1], [2, 3]],
4: [[0, 3], [1, 1], [2, 2], [2, 4]],
5: [[0, 3], [1, 2], [1, 4], [2, 2], [2, 4]],
6: [[0, 2], [0, 4], [1, 2], [1, 4], [2, 2], [2, 4]],
7: [[0, 2], [0, 4], [1, 2], [1, 4], [2, 1], [2, 4], [2, 3]],
8: [[0, 2], [0, 4], [1, 1], [1, 4], [1, 3], [2, 1], [2, 4], [2, 3]],
9: [[0, 1], [0, 4], [0, 3], [1, 1], [1, 4], [1, 3], [2, 1], [2, 4], [2, 3]],
10: [[0, 1], [0, 4], [0, 3], [1, 1], [1, 4], [1, 3], [2, 0], [2, 3], [2, 1], [2, 4]],
11: [[0, 1], [0, 4], [0, 3], [1, 0], [1, 3], [1, 1], [1, 4], [2, 0], [2, 3], [2, 1], [2, 4]],
12: [[0, 0], [0, 3], [0, 1], [0, 4], [1, 0], [1, 3], [1, 1], [1, 4], [2, 0], [2, 3], [2, 1], [2, 4]],
13: [[0, 0], [0, 3], [0, 1], [0, 4], [1, 0], [1, 3], [1, 1], [1, 4], [2, 0], [2, 3], [2, 1], [2, 4], [2, 2]],
14: [[0, 0], [0, 3], [0, 1], [0, 4], [1, 0], [1, 3], [1, 1], [1, 4], [1, 2], [2, 0], [2, 3], [2, 1], [2, 4], [2, 2]],
15: [[0, 0], [0, 3], [0, 1], [0, 4], [0, 2], [1, 0], [1, 3], [1, 1], [1, 4], [1, 2], [2, 0], [2, 3], [2, 1], [2, 4], [2, 2]],
16: [[0, 0], [0, 3], [0, 1], [0, 4], [0, 2], [1, 0], [1, 3], [1, 1], [1, 4], [1, 2], [2, 0], [2, 3], [2, 1], [2, 4], [2, 2], [2, 5]],
17: [[0, 0], [0, 3], [0, 1], [0, 4], [0, 2], [1, 0], [1, 3], [1, 1], [1, 4], [1, 2], [1, 5], [2, 0], [2, 3], [2, 1], [2, 4], [2, 2], [2, 5]],
18: [[0, 0], [0, 3], [0, 1], [0, 4], [0, 2], [0, 5], [1, 0], [1, 3], [1, 1], [1, 4], [1, 2], [1, 5], [2, 0], [2, 3], [2, 1], [2, 4], [2, 2], [2, 5]]
};
//**********************************************************************************
//**********************************************************************************
function getPrimaryColorForSeriesCountFrom2DThemeColors(seriesCount, primaryColors) {
if (primaryColors === null || primaryColors === undefined || primaryColors.length < 1) return null;
var colors = [];
if (seriesCount <= 18) {
var colorIndexes = largeIndexMap[seriesCount];
var i = 0;
for (; i < colorIndexes.length; i++) {
colors.push(primaryColors[colorIndexes[i][0]][colorIndexes[i][1]])
}
}
else //More than 18
{
var hueIndex;
var tmpShadeIndex = 0;
var shadeOffset = 3;
for (hueIndex = 0; hueIndex < primaryColors.length && colors.length < seriesCount; hueIndex++) {
tmpShadeIndex = 0;
for (; tmpShadeIndex + shadeOffset < primaryColors[hueIndex].length && colors.length < seriesCount; tmpShadeIndex++) {
colors.push(primaryColors[hueIndex][tmpShadeIndex]);
if (colors.length < seriesCount) {
colors.push(primaryColors[hueIndex][tmpShadeIndex + shadeOffset]);
}
}
}
}
return colors;
}
//**********************************************************************************
//**********************************************************************************
function getSecondaryColorForSeriesCountFrom2DThemeColors(seriesCount, primaryColors) {
if (primaryColors === null || primaryColors === undefined || primaryColors.length < 1) return null;
var colors = [];
var maxShadeIndex;
var secondShadeOffset;
if (seriesCount <= 18) {
var i = 0;
var colorIndexes = largeIndexMap[seriesCount];
maxShadeIndex = primaryColors[0].length - 1;
secondShadeOffset = maxShadeIndex - 1;
for (; i < colorIndexes.length; i++) {
colors.push(primaryColors[colorIndexes[i][0]][(colorIndexes[i][1] + secondShadeOffset > maxShadeIndex ? 0 : colorIndexes[i][1] + secondShadeOffset)])
}
}
else //More than 18
{
var tmpShadeIndex = 0;
var shadeOffset = 3;
maxShadeIndex = primaryColors[0].length - 1; // ASSUMING that there are an equal # of shades in each Hue.
secondShadeOffset = maxShadeIndex - 1;
for (var hueIndex = 0; hueIndex < primaryColors.length && colors.length < seriesCount; hueIndex++) {
tmpShadeIndex = 0;
for (; tmpShadeIndex + shadeOffset < primaryColors[hueIndex].length && colors.length < seriesCount; tmpShadeIndex++) {
colors.push(primaryColors[hueIndex][(tmpShadeIndex + secondShadeOffset > maxShadeIndex ? 0 : tmpShadeIndex + secondShadeOffset)]);
if (colors.length < seriesCount) {
colors.push(primaryColors[hueIndex][(tmpShadeIndex + shadeOffset + secondShadeOffset > maxShadeIndex ? 0 : tmpShadeIndex + shadeOffset + secondShadeOffset)]);
}
}
}
}
return colors;
}
//**********************************************************************************
//**********************************************************************************
function themeArrayIs2D(primaryColors) {
return primaryColors !== undefined && primaryColors.length > 0 && typeof primaryColors[0] === 'object';
}
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------
// PUBLIC FUNCTIONS and DEFINITIONS
----------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
//domoColorsKey is deprecated
theme.domoColorsKey = {
blues: 0, greens: 1, oranges: 2,
purples: 3, pinks: 4, teals: 5, reds: 6
};
theme.domoColors = [
['#D9EBFD', '#B7DAF5', '#90c4e4', '#73B0D7', '#4E8CBA', '#31689B'], // Blue
['#DDF4BA', '#BBE491', '#A0D771', '#80C25D', '#559E38', '#387B26'], // Green
['#FDECAD', '#FCCF84', '#FBAD56', '#FB8D34', '#E45621', '#A43724'], // Orange
['#F3E4FE', '#DDC8EF', '#C5ACDE', '#B391CA', '#8F6DC0', '#7940A1'], // Purple
['#FCD7E6', '#FBB6DD', '#F395CD', '#EE76BF', '#CF51AC', '#A62A92'], // Pink
['#D8F4DE', '#ABE4CA', '#8DD5BE', '#68BEA8', '#46998A', '#227872'], // Teal
['#FDDDDD', '#FCBCB7', '#FD9A93', '#FD7F76', '#e45850', '#c92e25'] // Red
];
/*----------------------------------------------------------------------------------
// getPrimaryColorsForNumberOfSeries
----------------------------------------------------------------------------------*/
theme.getPrimaryColorsForNumberOfSeries = function (number, primaryColors) {
if (themeArrayIs2D(primaryColors)) {
return getPrimaryColorForSeriesCountFrom2DThemeColors(number, primaryColors);
}
else {
return primaryColors.slice(0, number);
}
};
/*----------------------------------------------------------------------------------
//getSecondaryColorsForNumberOfSeries
----------------------------------------------------------------------------------*/
theme.getSecondaryColorsForNumberOfSeries = function (number, primaryColors, secondaryColors) {
if (themeArrayIs2D(primaryColors)) {
return getSecondaryColorForSeriesCountFrom2DThemeColors(number, primaryColors);
}
else {
return secondaryColors.slice(0, number);
}
};
/*----------------------------------------------------------------------------------
// Gets a set of colors
// These colors are pulled from the given colorSet in a staggared repeating pattern.
// (1) 2 3 (4) 5 6
// 1 (2) 3 4 (5) 6
// 1 2 (3) 4 5 (6)
//(1) 2 3 (4) 5 6
----------------------------------------------------------------------------------*/
theme.getNumStaggeredColors = function (num, colorSet) {
var colors = [];
var i = 0;
var half = Math.floor(colorSet.length / 2.0);
while (colors.length < num) {
colors.push(colorSet[i%colorSet.length]);
colors.push(colorSet[(i+half)%colorSet.length]);
i++;
}
return colors;
};
/*----------------------------------------------------------------------------------
// Update an ordinal scale while preserving the previous domain and range
// e.g. When updating a color scale, this can add new items to the domain, while
// ensuring that previous color mappings are not changed
----------------------------------------------------------------------------------*/
function updateOrdinalScale(scale, newDomain, newRange) {
var domain;
var range;
var primaryColors;
domain = _.uniq(scale.domain().concat(newDomain));
primaryColors = theme.getPrimaryColorsForNumberOfSeries(domain.length, newRange);
range = _.uniq(scale.range().concat(primaryColors)).slice(0, domain.length);
return scale.domain(domain)
.range(range);
}
/*----------------------------------------------------------------------------------
// Given a primary color scale with domain and range set, set the domain and range
// of the secondary color scale with appropriate complementary colors
----------------------------------------------------------------------------------*/
function syncSecondaryColorScale(primaryScale, secondaryScale, primaryColors, secondaryColors) {
var primaryRange = primaryScale.range();
var primaryDomain = primaryScale.domain();
var range = primaryRange.map(function(color){
return secondaryColors[primaryColors.indexOf(color)];
});
secondaryScale.domain(primaryDomain)
.range(range);
}
/*----------------------------------------------------------------------------------
// Update a color scale with the given domain and range of colors. Ensures that
// previously defined color mappings are preserved
// @param primaryScale - A D3 ordinal scale for primary colors
// @param domain - An array of values to be merged into the current scale's domain
// @param colors1 - An array of primary colors. Can be a simple array of hex values, or a 2D array of colors using Domo's color sequence rules
----------------------------------------------------------------------------------*/
theme.updatePrimaryColorScale = function(primaryScale, domain, colors1) {
updateOrdinalScale(primaryScale, domain, colors1);
}
/*----------------------------------------------------------------------------------
// Update both the primary and secondary color scales simultaneously
// @param primaryScale - A D3 ordinal scale for primary colors
// @param secondaryScale - A D3 ordinal scale for secondary colors
// @param domain - An array of values to be merged into the current scale domain
// @param colors1 - An array of primary colors. Can be a simple array of hex values,
// or a 2D array of colors using domo's color sequence rules
// @param colors2 - An array of secondary, complementary colors. Can be a simple array
// of hex values, or a 2D array of colors using domo's color
// sequence rules
----------------------------------------------------------------------------------*/
theme.updateBothColorScales = function(primaryScale, secondaryScale, domain, colors1, colors2) {
var length = _.flatten(colors1).length;
var allPrimaryColors = theme.getPrimaryColorsForNumberOfSeries(length, colors1);
var allSecondaryColors = theme.getSecondaryColorsForNumberOfSeries(length, colors1, colors2);
updateOrdinalScale(primaryScale, domain, colors1);
syncSecondaryColorScale(primaryScale, secondaryScale, allPrimaryColors, allSecondaryColors);
}
/*----------------------------------------------------------------------------------
// Reset the domain and range of any scale
// Use this when you do not need a scale to keep constancy
----------------------------------------------------------------------------------*/
theme.resetScale = function(scale) {
return scale.domain([]).range([]);
}
theme.getThemeDefaults = function() {
return _.mapValues(theme.themeElements, function(currentValue) {
return currentValue().value;
});
}
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------
// THEME COMPONENTS
----------------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------------
----------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
// THEME CONFIG ELEMENT CONSTRUCTOR FUNCTIONS
//
// Functions that will return the value of each theme config element.
//
// Example:
// _Chart.config = {barColor: da.theme2.themeElements.fillOne()}
---------------------------------------------------------------------------------------*/
theme.themeElements = {
//----------------------------------------------------------------------------------
// General
//----------------------------------------------------------------------------------
background: function (options) {
return _.assign({
theme: {
name: 'background'
},
name: 'Background Color',
description: 'Color for the background',
category: 'General',
value: '#FFFFFF', //white
type: 'color'
}, options);
},
//----------------------------------------------------------------------------------
// Fills
//----------------------------------------------------------------------------------
// Note: if two of the same type of theme elements are used in one widget, change the names to '{{Element}} + {{Theme Name}}'
// i.g. primaryFill & secondaryFill: 'Fill Color' & 'Fill Color' -> 'Bar Fill Color' & 'Circle Fill Color'
// i.g. goodFill & goodFill2: 'Good Fill Color' & 'Good Fill Color' -> 'Bar Good Fill Color' & 'Circle Good Fill Color'
// i.g. badFill & badFill2: 'Bad Fill Color' & 'Bad Fill Color' -> 'Bar Bad Fill Color' & 'Circle Bad Fill Color'
// The description should generally stay unchanged
primaryFill: function (options) {
return _.assign({
theme: {
name: 'primaryFill'
},
name: 'Fill Color',
description: 'Fill color for each element',
category: 'General',
value: '#73B0D7', //blue
type: 'color'
}, options);
},
secondaryFill: function (options) {
return _.assign({
theme: {
name: 'secondaryFill'
},
name: 'Fill Color',
description: 'Fill color for each element',
category: 'General',
value: '#DAEAF8', //light blue
type: 'color'
}, options);
},
goodFill: function (options) {
return _.assign({
theme: {
name: 'goodFill'
},
name: 'Good Fill Color',
description: 'Fill color that indicates something is good', //use for small elements
category: 'General',
value: '#80C25D', //green
type: 'color'
}, options);
},
goodFill2: function (options) {
return _.assign({
theme: {
name: 'goodFill2'
},
name: 'Good Fill Color',
description: 'Fill color that indicates something is good', //use for large elements
category: 'General',
value: '#91D0BC', //teal
type: 'color'
}, options);
},
badFill: function (options) {
return _.assign({
theme: {
name: 'badFill'
},
name: 'Bad Fill Color',
description: 'Fill color that indicates something is bad', //use for small elements
category: 'General',
value: '#E4584F', //red
type: 'color'
}, options);
},
badFill2: function (options) {
return _.assign({
theme: {
name: 'badFill2'
},
name: 'Bad Fill Color',
description: 'Fill color that indicates something is bad', //use for large elements
category: 'General',
value: '#F27E79', //pinkish red
type: 'color'
}, options);
},
neutralFill: function (options) {
return _.assign({
theme: {
name: 'neutralFill'
},
name: 'Neutral Fill Color',
description: 'Fill color that indicates something is neutral',
category: 'General',
value: '#E5E5E5', //light gray
type: 'color'
}, options);
},
notFilledColor: function (options) {
return _.assign({
theme: {
name: 'notFilledColor'
},
name: 'Empty Fill Color',
description: 'Fill color that indicates something is empty', // i.g. gauges
category: 'General',
value: '#E4E5E5', //light gray
type: 'color'
}, options);
},
noDataColor: function (options) {
return _.assign({
theme: {
name: 'noDataColor'
},
name: 'No Data Fill Color',
description: 'Fill color that indicates something has no data',
category: 'General',
value: '#D9D9D9',
type: 'color'
}, options);
},
washoutColor: function (options) {
return _.assign({
theme: {
name: 'washoutColor'
},
name: 'Washout Fill Color',
description: 'Fill color that indicates something is not highlighted',
category: 'General',
value: '#E4E5E5',
type: 'color'
}, options);
},
//----------------------------------------------------------------------------------
// Strokes
//----------------------------------------------------------------------------------
// Note: if two of the same type of theme elements are used in one widget, change the names to '{{Element}} + {{Theme Name}}'
// i.g. primaryStroke + secondaryStroke: 'Border Color' & 'Border Color' -> 'Bar Border Color' & 'Circle Border Color'
// i.g. primaryStrokeWidth + secondaryStrokeWidth: 'Border Width' & 'Border Width' -> 'Bar Border Width' & 'Circle Border Width'
// The description should generally stay unchanged
primaryStroke: function (options) {
return _.assign({
theme: {
name: 'primaryStroke'
},
name: 'Border Color',
description: 'Border color for each element',
category: 'General',
value: '#73B0D7', //blue
type: 'color'
}, options);
},
secondaryStroke: function (options) {
return _.assign({
theme: {
name: 'secondaryStroke'
},
name: 'Border Color',
description: 'Border color for each element',
category: 'General',
value: '#DAEAF8', //light blue
type: 'color'
}, options);
},
goodStroke: function (options) {
return _.assign({
theme: {
name: 'goodStroke'
},
name: 'Good Border Color',
description: 'Border color that indicates something is good',
category: 'General',
value: '#559E38', //dark green
type: 'color'
}, options);
},
badStroke: function (options) {
return _.assign({
theme: {
name: 'badStroke'
},
name: 'Bad Border Color',
description: 'Border color that indicates something is bad',
category: 'General',
value: '#C92E25', //dark red
type: 'color'
}, options);
},
neutralStroke: function (options) {
return _.assign({
theme: {
name: 'neutralStroke'
},
name: 'Neutral Border Color',
description: 'Border color that indicates something is neutral',
category: 'General',
value: '#D7D9DA', //gray
type: 'color'
}, options);
},
primaryStrokeWidth: function (options) {
return _.assign({
name: 'Border Width',
description: 'Width of the element\'s border',
category: 'General',
value: 1,
type: 'number',
units: 'px'
}, options);
},
secondaryStrokeWidth: function (options) {
return _.assign({
name: 'Border Width',
description: 'Width of the element\'s border',
category: 'General',
value: 2,
type: 'number',
units: 'px'
}, options);
},
separatorColor: function (options) {
return _.assign({
theme: {
name: 'separatorColor'
},
name: 'Separator Color',
description: 'Border color for separating elements', //i.g. pie, donut, grouped bar chart
category: 'General',
value: '#FFFFFF',
type: 'color'
}, options);
},
separatorStrokeWidth: function (options) {
return _.assign({
name: 'Separator Width',
description: 'Border width for separating elements',
category: 'General',
value: 1,
type: 'number',
units: 'px'
}, options);
},
/*----------------------------------------------------------------------------------
// ChartSeriesColors
----------------------------------------------------------------------------------*/
colorTheme: function (options) {
return _.assign({
name: 'Color Theme',
description: 'The color theme used by the elements',
category: 'General',
value: {name: 'Default', defaultTheme: true, directory: ''},
type: 'colorTheme',
options: []
}, options);
},
primaryColorSeries: function (options) {
return _.assign({
theme: {
name: 'primaryColorSeries'
},
name: 'Primary Color Series ',
description: 'Primary colors used to represent series data',
category: 'Series Colors',
value: theme.domoColors,
type: 'colorArray'
}, options);
},
secondaryColorSeries: function (options) {
return _.assign({
theme: {
name: 'secondaryColorSeries'
},
name: 'Secondary Color Series',
description: 'Secondary colors used to represent series data',
category: 'Series Colors',
value: ['#4E8CBA', '#D9EBFD', '#31689B', '#D9EBFD', '#D9EBFD', '#559E38', '#DDF4BA', '#387B26', '#DDF4BA', '#DDF4BA', '#E45621', '#FDECAD', '#A43724', '#FDECAD', '#FDECAD', '#FDECAD'],
type: 'colorArray'
}, options);
},
divergentColorSeries: function (options) {
return _.assign({
theme: {
name: 'divergentColorSeries'
},
name: 'Divergent Color Series',
description: '',
category: 'Series Colors',
value: ['#336088', '#71A7C7', '#B7D7E6', '#D8EBF3', '#FFFFFF', '#F7DAD7', '#EFB7B1', '#E37872', '#B53230'],
type: 'colorArray'
}, options);
},
valueColorSeries: function (options) {
return _.assign({
theme: {
name: 'valueColorSeries'
},
name: 'Value Color Series',
description: '',
category: 'Series Colors',
value: ["#FDECAD", "#FCCF84", "#FBAD56", "#FB8D34", "#E45621", "#A43724"],
type: 'colorArray'
}, options);
},
/*----------------------------------------------------------------------------------
// Tooltip
----------------------------------------------------------------------------------*/
tooltipBackground: function (options) {
return _.assign({
theme: {
name: 'tooltipBackground'
},
name: 'Tooltip Color',
description: 'Background color for the tooltip',
category: 'Tooltip',
value: '#555555', //dark gray
type: 'color'
}, options);
},
tooltipFontFamily: function (options) {
return _.assign({
name: 'Tooltip Font',
description: 'Font type for the tooltip text',
category: 'Text',
value: 'Open Sans',
type: 'font'
}, options);
},
tooltipFontColor: function (options) {
return _.assign({
theme: {
name: 'tooltipFontColor'
},
name: 'Tooltip Font Color',
description: 'Font color for the tooltip text',
category: 'Tooltip',
value: '#FFFFFF', //white
type: 'color'
}, options);
},
tooltipFontSize: function (options) {
return _.assign({
name: 'Tooltip Font Size',
description: 'Font size for the tooltip text',
category: 'Text',
value: 12,
type: 'number',
units: 'px'
}, options);
},
tooltipFontWeight: function (options) {
return _.assign({
name: 'Tooltip Font Weight',
description: 'Greater values correspond to increased font boldness (some fonts do not support every value)',
category: 'Text',
value: {name:'300 - Light' , value:300},
type: 'select',
options: [
{name: '100', value:100},
{name: '200', value:200},
{name: '300 - Light' , value:300},
{name: '400 - Regular', value:400},
{name: '500', value:500},
{name: '600', value:600},
{name: '700 - Bold' , value:700},
{name: '800', value:800},
{name: '900', value:900}
]
}, options);
},
tooltipLetterSpacing: function (options) {
return _.assign({
name: 'Tooltip Letter Spacing',
description: 'Space between characters in the text',
category: 'Text',
value: 0,
type: 'number',
units: 'px'
}, options);
},
/*----------------------------------------------------------------------------------
// Axes
----------------------------------------------------------------------------------*/
axesLineColor: function (options) {
return _.assign({
theme: {
name: 'axesLineColor'
},
name: 'Axes Line Color',
description: 'Line color for the axes',
category: 'General',
value: '#E3E3E3',
type: 'color',
});
},
axesFontFamily: function (options) {
return _.assign({
name: 'Axes Font',
description: 'Font type for the axes labels',
category: 'Text',
value: 'Open Sans',
type: 'font'
}, options);
},
axesFontColor: function (options) {
return _.assign({
theme: {
name: 'axesFontColor'
},
name: 'Axes Font Color',
description: 'Font color for the axes labels',
category: 'Text',
value: '#8A8D8E', //gray
type: 'color'
}, options);
},
axesFontSize: function (options) {
return _.assign({
name: 'Axes Font Size',
description: 'Font size for the axes labels',
category: 'Text',
value: 11,
type: 'number',
units: 'px'
}, options);
},
axesFontWeight: function (options) {
return _.assign({
name: 'Axes Font Weight',
description: 'Greater values correspond to increased font boldness (some fonts do not support every value)',
category: 'Text',
value: {name:'300 - Light' , value:300},
type: 'select',
options: [
{name: '100', value:100},
{name: '200', value:200},
{name: '300 - Light' , value:300},
{name: '400 - Regular', value:400},
{name: '500', value:500},
{name: '600', value:600},
{name: '700 - Bold' , value:700},
{name: '800', value:800},
{name: '900', value:900}
]
}, options);
},
axesLetterSpacing: function (options) {
return _.assign({
name: 'Axes Letter Spacing',
description: 'Space between characters in the text',
category: 'Text',
value: 0,
type: 'number',
units: 'px'
}, options);
},
/*----------------------------------------------------------------------------------
// Legend
----------------------------------------------------------------------------------*/
legendLineColor: function (options) {
return _.assign({
theme: {
name: 'legendLineColor'
},
name: 'Legend Line Color',
description: 'Dividing line color for the legend',
category: 'General',
value: '#E3E3E3',
type: 'color',
}, options);
},
legendWashoutColor: function (options) {
return _.assign({
theme: {
name: 'legendWashoutColor'
},
name: 'Legend Washout Fill Color',
description: 'Fill color that indicates something is not highlighted',
category: 'General',
value: '#E4E5E5',
type: 'color',
}, options);
},
legendFontFamily: function (options) {
return _.assign({
name: 'Legend Font',
description: 'Font type for the legend labels',
category: 'Text',
value: 'Open Sans',
type: 'font'
}, options);
},
legendFontColor: function (options) {
return _.assign({