UNPKG

@domoinc/image-tooltip

Version:

ImageTooltip - Domo Widget

1,334 lines (1,198 loc) 1.02 MB
/*! 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({