ShapeWidget.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2014, 2019
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['./StaticWidget', '../../../lib/@waca/dashboard-common/dist/utils/HtmlXSSUtils', 'jquery', 'underscore', '../../../app/nls/StringResources'], function (StaticWidget, HtmlCleanser, $, _, resources) {
  8. var defaultViewBox = '0 0 100 100';
  9. var SVGWRAPPER_START = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="staticContent" height="100%" width="100%" preserveAspectRatio="none" viewBox="$viewBox$">';
  10. var SVGWRAPPER_START_LINE = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" class="staticContent shapesLine" height="100%" width="100%" preserveAspectRatio="none" viewBox="$viewBox$">';
  11. var SVGWRAPPER_END = '</svg>';
  12. var DEFAULT_ATTRIBUTES = ' vector-effect="non-scaling-stroke" $1';
  13. var WHITELISTED_SVG_ELEMENTS = ['<altGlyph>', '<altGlyphDef>', '<altGlyphItem>', '<animate>', '<animateMotion>', '<animateTransform>', '<circle>', '<clipPath>', '<color-profile>', '<cursor>', '<defs>', '<desc>', '<ellipse>', '<feBlend>', '<feColorMatrix>', '<feComponentTransfer>', '<feComposite>', '<feConvolveMatrix>', '<feDiffuseLighting>', '<feDisplacementMap>', '<feDistantLight>', '<feFlood>', '<feFuncA>', '<feFuncB>', '<feFuncG>', '<feFuncR>', '<feGaussianBlur>', '<feImage>', '<feMerge>', '<feMergeNode>', '<feMorphology>', '<feOffset>', '<fePointLight>', '<feSpecularLighting>', '<feSpotLight>', '<feTile>', '<feTurbulence>', '<filter>', '<font>', '<font-face>', '<font-face-format>', '<font-face-name>', '<font-face-src>', '<font-face-uri>', '<foreignObject>', '<g>', '<glyph>', '<glyphRef>', '<hkern>', '<image>', '<line>', '<linearGradient>', '<marker>', '<mask>', '<missing-glyph>', '<mpath>', '<path>', '<pattern>', '<polygon>', '<polyline>', '<radialGradient>', '<rect>', '<set>', '<stop>', '<svg>', '<switch>', '<symbol>', '<text>', '<textPath>', '<title>', '<tref>', '<tspan>', '<use>', '<view>', '<vkern>'];
  14. var _getDefaultSvgHtml = function _getDefaultSvgHtml(sName) {
  15. var svgWrapper = sName === 'Line' ? SVGWRAPPER_START_LINE : SVGWRAPPER_START;
  16. var viewBox = document.getElementById(sName).getAttribute('viewBox');
  17. svgWrapper = svgWrapper.replace('$viewBox$', viewBox);
  18. var svgHtml = $('body #' + sName).children()[1];
  19. // IE does not support outer/innerHTML on SVG elements
  20. // eslint-disable-next-line no-undef
  21. svgHtml = new XMLSerializer().serializeToString(svgHtml);
  22. return svgWrapper + svgHtml + SVGWRAPPER_END;
  23. };
  24. /**
  25. * Update the specified avatar node with the fill and border color defined in
  26. * the specified spec.
  27. * @param avatar - JQuery node of the avatar of interest
  28. * @param spec - spec representing the shape shown in the avatar
  29. * @param spec.model - model holding the possible fillColor and borderColor
  30. */
  31. var addFillAndBorderToAvatarIfNeeded = function addFillAndBorderToAvatarIfNeeded(avatar, spec) {
  32. /* (Older) JQuery has issues with add classes to SVG objects so we have to be a
  33. * little more basic when sgetting the SVG border and fill colors
  34. */
  35. // Get the element to manipulate and retrieve its current classes
  36. var element = avatar.find('svg.staticContent')[0];
  37. var elClass = element.getAttribute('class').split(/\s+/g);
  38. // Add the fill color and border color if one is needed to the class array
  39. if (spec.model['fillColor']) {
  40. elClass.push('fill-' + spec.model['fillColor']);
  41. }
  42. if (spec.model['borderColor']) {
  43. elClass.push('border-' + spec.model['borderColor']);
  44. }
  45. // set the list of classes.
  46. element.setAttribute('class', elClass.join(' '));
  47. };
  48. var getDefaultSpec = function getDefaultSpec(sName, options) {
  49. var coloredProperty = sName === 'shapesLine' ? 'borderColor' : 'fillColor';
  50. var transparentProperty = sName === 'shapesLine' ? 'fillColor' : 'borderColor';
  51. var title = options.ext ? sName : resources.get(sName);
  52. var content;
  53. // Custom widget definition
  54. if (options.template) {
  55. var sTemplate = options.template;
  56. var attributes = DEFAULT_ATTRIBUTES;
  57. var viewBox = options && options.viewBox || defaultViewBox;
  58. var svgWrapper = sName === 'shapesLine' ? SVGWRAPPER_START_LINE : SVGWRAPPER_START;
  59. svgWrapper = svgWrapper.replace('$viewBox$', viewBox);
  60. content = HtmlCleanser.cleanseContentElements(svgWrapper + sTemplate.replace(/(\/?>)/g, attributes) + SVGWRAPPER_END, WHITELISTED_SVG_ELEMENTS, true);
  61. } else {
  62. // Bootstrapped widget definition using svg spritesheet
  63. content = HtmlCleanser.cleanseContentElements(_getDefaultSvgHtml(options.icon), WHITELISTED_SVG_ELEMENTS, true);
  64. }
  65. var spec = {
  66. model: {
  67. type: 'shape',
  68. name: title,
  69. content: content
  70. },
  71. layoutProperties: {
  72. style: {
  73. width: '120px',
  74. height: '120px'
  75. }
  76. }
  77. };
  78. // Get the default color for shape widget being dropped onto the canvas
  79. if (options.dashboardApi) {
  80. var colorsService = options.dashboardApi.getFeature('Colors');
  81. var dashboardColors = colorsService.getDashboardColorSet();
  82. if (options.fillColor && colorsService.isColorIdValidForDashboadColorSet(options.fillColor)) {
  83. spec.model[coloredProperty] = options.fillColor;
  84. } else {
  85. spec.model[coloredProperty] = dashboardColors[dashboardColors.length - 1].id;
  86. }
  87. } else {
  88. spec.model[coloredProperty] = options.fillColor;
  89. }
  90. spec.model[transparentProperty] = options.borderColor;
  91. return Promise.resolve(spec);
  92. };
  93. var ShapeWidget = null;
  94. ShapeWidget = StaticWidget.extend({
  95. init: function init(options) {
  96. ShapeWidget.inherited('init', this, arguments);
  97. this.model = this.model || {};
  98. this.model.content = options && options.initialConfigJSON && options.initialConfigJSON.content;
  99. if (this.model.content && this.isValidHtmlContent(this.model.content)) {
  100. this.model.borderColor = options && options.initialConfigJSON && options.initialConfigJSON.borderColor;
  101. this.model.fillColor = options && options.initialConfigJSON && options.initialConfigJSON.fillColor;
  102. this.model.name = options && options.initialConfigJSON && options.initialConfigJSON.name;
  103. this.$el.append(HtmlCleanser.cleanseContentElements(this.model.content, WHITELISTED_SVG_ELEMENTS, true));
  104. this.applyCommonProperties();
  105. }
  106. },
  107. getHtmlRender: function getHtmlRender() {
  108. var shapeEl = this.$el;
  109. // svg.outerHTML does not work on safari ipad. We use the XMLSerializer when available
  110. var newContent;
  111. if (window.XMLSerializer) {
  112. // eslint-disable-next-line no-undef
  113. var s = new XMLSerializer();
  114. newContent = s.serializeToString(shapeEl[0]);
  115. } else {
  116. newContent = shapeEl.outerHTML;
  117. }
  118. return newContent;
  119. },
  120. getWidgetStyleNode: function getWidgetStyleNode() {
  121. return this.$el.children('.staticContent');
  122. },
  123. registerEventGroup: function registerEventGroup() {
  124. // Shape widgets are not added to event groups
  125. },
  126. /**
  127. * Updates the model version of the markup of this widget
  128. * @param updatedHtml An optional parameter of the markup.
  129. * This is to avoid regenerating the markup if it is already known.
  130. */
  131. updateModelContent: function updateModelContent(updatedHtml, transactionId) {
  132. // regenerate the HTML if not passed
  133. if (!updatedHtml) {
  134. updatedHtml = HtmlCleanser.cleanseContentElements(this.getHtmlRender(), WHITELISTED_SVG_ELEMENTS, true);
  135. } else {
  136. updatedHtml = HtmlCleanser.cleanseContentElements(updatedHtml, WHITELISTED_SVG_ELEMENTS, true);
  137. }
  138. // update the model to persist the changes. Use transactionId to combine undos
  139. var data = {};
  140. if (transactionId) {
  141. _.extend(data, {
  142. payloadData: {
  143. undoRedoTransactionId: transactionId
  144. }
  145. });
  146. } else {
  147. _.extend(data, {
  148. silent: true
  149. });
  150. }
  151. this.set({
  152. content: updatedHtml
  153. }, data);
  154. }
  155. });
  156. ShapeWidget.getDefaultSpec = getDefaultSpec;
  157. ShapeWidget.addFillAndBorderToAvatarIfNeeded = addFillAndBorderToAvatarIfNeeded;
  158. return ShapeWidget;
  159. });
  160. //# sourceMappingURL=ShapeWidget.js.map