LiveWidget.js 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905
  1. 'use strict';
  2. /*
  3. * +------------------------------------------------------------------------+
  4. * | Licensed Materials - Property of IBM
  5. * | IBM Cognos Products: Dashboard
  6. * | (C) Copyright IBM Corp. 2014, 2020
  7. * |
  8. * | US Government Users Restricted Rights - Use, duplication or disclosure
  9. * | restricted by GSA ADP Schedule Contract with IBM Corp.
  10. * +------------------------------------------------------------------------+
  11. */
  12. define(['./DeprecateWidgetBase', '../../widgets/livewidget/nls/StringResources', 'jquery', '../../lib/@waca/core-client/js/core-client/utils/Deferred', 'underscore', './VisualizationCreator', './expandmode/VisExpandMode', './models/LiveWidgetModel', './features/FeatureLoader', '../../api/impl/Visualization', '../../visualizations/renderer/filter/FilterLabelHelper', '../../visualizations/vipr/VIPR', '../../visualizations/vipr/VIPRLibraries', './SdkShowError', './util/VisUtil', './nls/StringResources', '../../view/features/content/LiveWidgetInternal/LiveWidgetInternal', '../../dataSources/utils/DatasourceUtil', '../../lib/@waca/dashboard-common/dist/utils/MemUtil', 'react', 'react-dom', '../../lib/@waca/dashboard-common/dist/utils/SkeletonPlaceholder'], function (DeprecateWidgetBase, resources, $, Deferred, _, VisualizationCreator, VisExpandMode, LiveWidgetModel, FeatureLoader, VisualizationImpl, FilterLabelHelper, VIPR, VIPRLibraries, SdkShowError, VisUtil, StringResources, LiveWidgetInternal, DatasourceUtil, MemUtil, React, ReactDOM, SkeletonPlaceholder) {
  13. 'use strict';
  14. /**
  15. * INTENT: LiveWidget: The Live widget (LightWeight Interactive Visualization and Exploration widget) provides the controlling API
  16. * for creating, modifying and rendering a live widget.
  17. * TODO: The functionality in this class should be investigated and moved into more focused sub-objects.
  18. */
  19. var LiveWidget = DeprecateWidgetBase.extend({
  20. /**
  21. * Ajax service instance
  22. * @type {AjaxService}
  23. */
  24. ajaxSvc: null,
  25. palettePropertyToTypeMap: {
  26. condColorPalette: 'ConditionalPalette',
  27. contColorPalette: 'HeatPalette',
  28. colorPalette: 'ColorPalette'
  29. },
  30. /**
  31. * create the visualization given the spec.
  32. */
  33. init: function init(options) {
  34. var _this = this;
  35. // arguments: id, initialConfigJSON, el, eventRouter
  36. LiveWidget.inherited('init', this, arguments);
  37. this.contentFeatureLoader = options.contentFeatureLoader;
  38. this.visualization = this.content.getFeature('Visualization');
  39. this.predictData = options.predictData;
  40. this.interactivitySettings = options.interactivitySettings || {};
  41. this.forceDisabledThumbnail = options.forceDisabledThumbnail;
  42. this.isPreview = options.isPreview;
  43. this.isPredictPreview = options.isPredictPreview;
  44. this.isFocusModeDisabled = !!options.focusModeDisabled;
  45. this._managesOwnQueries = options.managesOwnQueries || false;
  46. this.logger = this.dashboardApi.getGlassCoreSvc('.Logger');
  47. this.ajaxSvc = this.dashboardApi.getGlassCoreSvc('.Ajax');
  48. this.transaction = this.dashboardApi.getFeature('Transaction');
  49. this.featureSet = options.featureSet;
  50. if (options.widgetModel) {
  51. this.model = options.widgetModel;
  52. } else if (options.widgetSpec) {
  53. this.model = new LiveWidgetModel(options.widgetSpec);
  54. } else {
  55. this.logger.error('LiveWidget expects its widgetModel in the constructor.');
  56. }
  57. this.renderCompleteDeferred = new Deferred();
  58. // Avoids clearing the renderCompleteDeferred in renderStart if first renderStart
  59. // is not the one with parameter 'initial' (e.g: resize event)
  60. this._isCurrentlyRendering = true;
  61. // Track the number of complete loads on this widget
  62. this._renderCount = 0;
  63. //If true, widget render will be trigger on show, regardless of whether it was already rendered
  64. this.rerenderOnShow = null;
  65. this.updateDecorationsOnShow = false;
  66. //holds any dnd drop targets we create
  67. this._dropTargets = [];
  68. if (this.initialConfigJSON) {
  69. this.bOpeningBoard = !!this.initialConfigJSON.visId;
  70. this.options = options;
  71. this.createFeatureLoaderAndVisualizationPromise = this.createFeatureLoaderAndVisualizationAPI();
  72. }
  73. // Expose the following interface methods to the data widget
  74. // TODO: Remove once the API refactoring is complete
  75. this.api = {
  76. getId: this.getId.bind(this),
  77. changeVisType: this.onPropertyUpdate.bind(this),
  78. aggregate: this.changeAggregation.bind(this),
  79. getDOM: this.getDOM.bind(this),
  80. myClass: 'LiveWidget - api'
  81. };
  82. this._extendAPI({
  83. getVisualization: this.getVisualizationApi.bind(this)
  84. });
  85. /**
  86. * @deprecated
  87. * Deprecated Widget APIs
  88. */
  89. this._extendAPI({
  90. getWidgetLocalFilters: this.getWidgetLocalFilters.bind(this),
  91. getWidgetGlobalFilters: this.getWidgetGlobalFilters.bind(this),
  92. getWidgetTopBottom: this.getWidgetTopBottom.bind(this),
  93. getDataGridHelpers: this.getDataGridHelpers.bind(this),
  94. getMatchingFeatures: this.getMatchingFeatures.bind(this),
  95. getVisApi: function getVisApi() {
  96. return _this.visAPI;
  97. },
  98. getVisId: this.getVisId.bind(this),
  99. render: this.render.bind(this),
  100. whenRenderStart: this.whenRenderStart.bind(this),
  101. whenRenderComplete: this.whenRenderComplete.bind(this),
  102. updateTitle: this.updateTitle.bind(this),
  103. allowShowTabs: this.allowShowTabs.bind(this),
  104. getSavedPrompts: this.getSavedPrompts.bind(this)
  105. });
  106. this._setupDND(options.widgetContainer);
  107. this.colorsService = this.dashboardApi.getFeature('Colors');
  108. },
  109. getWidgetTopBottom: function getWidgetTopBottom() {
  110. var topBottoms = [];
  111. var formattedTopBottom = [];
  112. if (this.visAPI) {
  113. var filterLabelHelper = new FilterLabelHelper({ 'dataSource': this.visualization.getDataSource(), 'visAPI': this.visAPI });
  114. topBottoms = this.visAPI.getTopBottomInfo();
  115. if (topBottoms.length > 0) {
  116. formattedTopBottom = filterLabelHelper.generateTopBottomListItems(topBottoms);
  117. }
  118. }
  119. return formattedTopBottom;
  120. },
  121. /**
  122. * Execute a callback. The method will only execute the callback if the object is not destroyed
  123. * Typpically used in a promise callback because we might have destroyed the object before the promise was resolved.
  124. * @param {Function} callback
  125. */
  126. _prepareAsyncCallback: function _prepareAsyncCallback(callback) {
  127. return function () {
  128. if (!this._destroyed) {
  129. return callback.apply(undefined, arguments);
  130. }
  131. return Promise.reject('The live widget object was destroyed');
  132. }.bind(this);
  133. },
  134. registerLiveWidgetInternal: function registerLiveWidgetInternal() {
  135. // State feature is a core feature that requires to be available prior to initialize
  136. this.contentFeatureLoader.registerFeature(this.id, 'livewidget.internal', new LiveWidgetInternal({
  137. widget: this
  138. }));
  139. },
  140. initialize: function initialize() {
  141. var _this2 = this;
  142. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  143. this.registerLiveWidgetInternal();
  144. if (!this.visualization.getType()) {
  145. var transaction = this.dashboardApi.getFeature('Transaction');
  146. var transactionToken = transaction.startTransactionById(options.transactionId);
  147. this.showLoadingAnimation({ loadingText: StringResources.get('processing') });
  148. return this.content.getFeature('Visualization.SmartsRecommender').recommendBestVisualization(transactionToken).then(function () {
  149. var featureLWSegment = _this2.dashboardApi.getFeature('LiveWidgetSegment');
  150. if (featureLWSegment) {
  151. var visId = _this2.visualization.getDefinition().getId();
  152. featureLWSegment.track({ category: 'fromRecommendation', recommendation: visId });
  153. }
  154. return _this2._initialize(options);
  155. }).finally(function () {
  156. transaction.endTransaction(transactionToken);
  157. });
  158. } else {
  159. return this._initialize(options);
  160. }
  161. },
  162. _initialize: function _initialize() {
  163. var _this3 = this;
  164. return this.createFeatureLoaderAndVisualizationPromise.then(this._prepareAsyncCallback(function () {
  165. _this3.visualization = _this3.content.getFeature('Visualization');
  166. if (!_this3._ondataSourceChange) {
  167. _this3._ondataSourceChange = _this3.visualization.on('change:dataSource', function () {
  168. _this3.registerShapingModelEvents();
  169. });
  170. }
  171. _this3.registerShapingModelEvents();
  172. return _this3.createVisualization(_this3.content.getFeature('Visualization.legacy').getLegacyManagers());
  173. }));
  174. },
  175. /**
  176. * @private
  177. **/
  178. registerShapingModelEvents: function registerShapingModelEvents() {
  179. var _this4 = this;
  180. this._removeShapingModelEvents(); // Make sure we remove any existing events
  181. var dataSource = this.visualization.getDataSource();
  182. if (dataSource) {
  183. // @todo we should introduce events for DataSourceAPI (ie. refresh)
  184. var deprecatedDataSources = this.dashboardApi.getFeature('dataSources.deprecated');
  185. if (deprecatedDataSources) {
  186. deprecatedDataSources.getModule(dataSource.getId()).then(this._prepareAsyncCallback(function (module) {
  187. _this4.shapingEventHandler = module.on('shapingmodel:changed', _this4._onShapingModelChange.bind(_this4));
  188. }));
  189. }
  190. }
  191. },
  192. _onShapingModelChange: function _onShapingModelChange(event) {
  193. if (this.visAPI) {
  194. this.visAPI.clearModelInvalid();
  195. this.visAPI.validateFilters();
  196. }
  197. //Stop widget refresh on noRefresh flag
  198. if (!event.noRefresh) {
  199. this._refreshWidgets(event);
  200. }
  201. this.trigger('shapingmodel:changed', event);
  202. },
  203. _removeShapingModelEvents: function _removeShapingModelEvents() {
  204. if (this.shapingEventHandler) {
  205. this.shapingEventHandler.remove();
  206. this.shapingEventHandler = null;
  207. }
  208. },
  209. createFeatureLoaderAndVisualizationAPI: function createFeatureLoaderAndVisualizationAPI() {
  210. this.featureLoader = new FeatureLoader(this.getAPI(), this.dashboardApi);
  211. return this.featureLoader.loadFeatures(this.featureSet);
  212. },
  213. getVisualizationApi: function getVisualizationApi() {
  214. return this.content.getFeature('Visualization');
  215. },
  216. setFeatureEnabled: function setFeatureEnabled(name, isEnabled) {
  217. this.featureLoader.setFeatureEnabled(name, isEnabled);
  218. },
  219. getFeature: function getFeature(name) {
  220. return this.featureLoader.getFeature(name);
  221. },
  222. getMatchingFeatures: function getMatchingFeatures(matchCriteria) {
  223. return this.featureLoader.getMatchingFeatures(matchCriteria);
  224. },
  225. getExtraRenderSequenceSteps: function getExtraRenderSequenceSteps() {
  226. return this.featureLoader.getExtraRenderSequenceSteps();
  227. },
  228. isOptimizeForSize: function isOptimizeForSize() {
  229. return this.optimizeForSize;
  230. },
  231. getInteractivitySettings: function getInteractivitySettings() {
  232. return this.interactivitySettings;
  233. },
  234. /**
  235. * NOTE: this is temporarily exposed to expose necessary APIs for the contextual grid. Do not use
  236. * as these helpers will and should be removed.
  237. *
  238. * @returns an API of helpers useful for the contextual data grid
  239. */
  240. getDataGridHelpers: function getDataGridHelpers() {
  241. var _this5 = this;
  242. return {
  243. getData: function getData() {
  244. return _this5.get('data');
  245. },
  246. getPageContextApi: function getPageContextApi() {
  247. return _this5.pageContextAPI;
  248. },
  249. getVisApi: function getVisApi() {
  250. return _this5.visAPI;
  251. }
  252. };
  253. },
  254. /**
  255. * @param n - the n'th decoratorAPI (most visualizations have only 1 decoratorAPI
  256. * (ie: 1 overall set (dataSet), 1 set of items, 1 set of points)
  257. * Maps can have more.
  258. * @returns the n'th decoratorAPI from the view (default to the first)
  259. */
  260. getDecoratorAPI: function getDecoratorAPI(n) {
  261. n = n || 0;
  262. return this._callVisFunction('getDecoratorAPI', n);
  263. },
  264. getDecoratorAPIs: function getDecoratorAPIs() {
  265. return this._callVisFunction('getDecoratorAPIs');
  266. },
  267. getCustomData: function getCustomData() {
  268. return this._currVis && this._currVis.predictHandler && this._currVis.predictHandler.getCustomData && this._currVis.predictHandler.getCustomData();
  269. },
  270. /**
  271. * Generate thumbnail
  272. *
  273. * @param {Object} [options] - generate options
  274. * @param {Object} [options.size] - aspect ratio to generate
  275. * @param {number} [options.size.width=150] - aspect ratio width
  276. * @param {number} [options.size.height=100] - aspect ratio height
  277. * @param {string} [options.type=svg] - type of thumbnail to generate
  278. *
  279. * @return {Promise}
  280. */
  281. generateThumbnail: function generateThumbnail(options) {
  282. return this._callVisFunction('generateThumbnail', options, Promise.resolve(undefined));
  283. },
  284. _initializeAppSettings: function _initializeAppSettings() {
  285. //get the name of the host application service, so that live widget can
  286. //'publish' features to it
  287. this._hostApplicationName = this.dashboardApi.getAppConfig('hostApplicationName');
  288. this._thumbnailConfig = this.dashboardApi.getAppConfig('thumbnail');
  289. if ([false, 'false'].indexOf(this._thumbnailConfig) !== -1) {
  290. this._thumbnailConfig = false;
  291. }
  292. },
  293. /**
  294. * @returns the fredIsRedAPI which implements api's to get colours by value.
  295. */
  296. getFredIsRed: function getFredIsRed() {
  297. if (this.boardModel && this.boardModel.properties && this.boardModel.properties.fredIsRed) {
  298. return this._fredIsRed;
  299. }
  300. },
  301. /**
  302. * create a visualization.
  303. * 1) create a module
  304. * 2) load (or select) a visualization definition.
  305. * 3) create the VisModelManager for this definition.
  306. * 4) apply properties.
  307. *
  308. */
  309. createVisualization: function createVisualization(legacyManagers) {
  310. var _this6 = this;
  311. if (!this._visualizationCreator) {
  312. this._visualizationCreator = new VisualizationCreator({
  313. legacyManagers: legacyManagers,
  314. ownerWidget: this,
  315. widgetModel: this.model,
  316. dashboardAPI: this.dashboardApi,
  317. logger: this.logger,
  318. initialConfigJSON: this.options.initialConfigJSON,
  319. content: this.content,
  320. contentFeatureLoader: this.contentFeatureLoader
  321. });
  322. }
  323. var promise;
  324. if (this.dashboardApi) {
  325. promise = Promise.all([this.dashboardApi.getDashboardSvc('SynchronizeDataService')]).then(this._prepareAsyncCallback(function (_ref) {
  326. var synchronizeDataService = _ref[0];
  327. var synchData = synchronizeDataService.getSynchronizeData();
  328. var boardModel = _this6.boardModel;
  329. // get & init fredIsRed feature
  330. _this6._fredIsRed = _this6.dashboardApi.getFeature('FredIsRed');
  331. var pageContext = boardModel.get('pageContext');
  332. if (_this6._visualizationCreator) {
  333. _this6._visualizationCreator.setPageContext(pageContext && pageContext.getAPI());
  334. _this6._visualizationCreator.setSynchronizeData(synchData);
  335. return _this6._visualizationCreator.createVisualization();
  336. }
  337. }));
  338. } else {
  339. promise = this._visualizationCreator.createVisualization();
  340. }
  341. return promise;
  342. },
  343. /**
  344. * Called when module used by this visualization is altered. As it affects a wide variety of objects, it's better to
  345. * simply destroy the existing visualization and its models completely and build a new one of the same type
  346. *
  347. * @param {boolean} undoRedo -when true, this method will not persist values to the widget model (as they are already part of the undo
  348. * information). when false or not defined, the VisualizationCreator.recreateVisualization() is responsible for
  349. * persisting the dataset/mapping information to the widget model.
  350. *
  351. * @param {boolean} forceRerender true: force data widget to render even it was rendered already. It is introduced for
  352. * situations such as when data widget responds to dataset ID change event.
  353. */
  354. _rebuildVis: function _rebuildVis(event) {
  355. var _this7 = this;
  356. this.setNotRendered();
  357. this.clearCurrentVisualization();
  358. return this._visualizationCreator.recreateVisualization(event).then(this._prepareAsyncCallback(function () {
  359. return _this7.render();
  360. }));
  361. },
  362. setVisModelManager: function setVisModelManager(visModelManager, visAPI) {
  363. this.visModelManager = visModelManager;
  364. this.visAPI = visAPI;
  365. this.visualization.on('change:slots', this._onChangeSlots, this);
  366. },
  367. /**
  368. * Function used for setting the RerenderOnShow variable.
  369. * @param isResizing we make this option available because when a user toggles
  370. * we sometimes lose the dimensions of the visualization and then it will render
  371. * with weird dimensions. This happens with xtab and table.
  372. */
  373. setReRenderOnShow: function setReRenderOnShow(options) {
  374. this.rerenderOnShow = options;
  375. },
  376. /**
  377. * check if showTab is allowed
  378. * @returns true if showTabs config is set true and not a in a preview or in focus mode
  379. * otherwise return false
  380. */
  381. allowShowTabs: function allowShowTabs() {
  382. var showTabs = this.getDashboardApi().getAppConfig('showTabs') || false;
  383. return showTabs ? !this.isPreview : this.isInFocusMode && this.isInFocusMode();
  384. },
  385. /**
  386. * Update widget title based on recommendation from smarts
  387. * @returns a promise which resolves when async request is done or fails.
  388. */
  389. // updateTitle: function(event) {
  390. // const options = {
  391. // refresh : event && event.info && event.info.refresh || {},
  392. // extraInfo: {
  393. // payloadData : {
  394. // transactionToken : event && event.transactionToken || {}
  395. // }
  396. // }
  397. // };
  398. // const transactionApi = this.dashboardApi.getFeature('Transaction');
  399. // const transactionToken = event && event.transactionToken;
  400. // if (!transactionToken || transactionApi.isValidTransaction(transactionToken)) {
  401. // const handlerId = 'titleUpdate' + this.id;
  402. // return transactionApi.registerTransactionHandler(transactionToken || {}, handlerId, function(opt) {
  403. // return this._updateTitle(opt);
  404. // }.bind(this), options);
  405. // }
  406. // return Promise.resolve();
  407. // },
  408. _onChangeSlots: function _onChangeSlots(event) {
  409. this.updateTitle();
  410. // when slot mapping becomes incomplete, clear custom data selections
  411. if (this.visualization && !this.visualization.getSlots().isMappingComplete()) {
  412. this._currVis && this._currVis.eventHandler && this._currVis.eventHandler.clearCustomDataSelection({
  413. payloadData: {
  414. transactionToken: event && event.transactionToken
  415. }
  416. });
  417. }
  418. },
  419. updateTitle: function updateTitle() {
  420. var _this8 = this;
  421. var smartTitleFeature = this.content.getFeature('SmartTitle');
  422. var smartTitleEnabled = smartTitleFeature && smartTitleFeature.shouldUseGeneratedTitle();
  423. if (smartTitleEnabled === true) {
  424. /**
  425. * setting SMART title should pass runTimeOnly:true flag in payloadData because
  426. * a) the action alone should not mark board dirty
  427. * b) the action alone should not be added to undo redo stack
  428. * c) we don’t want undo/redo and silent is too much of a hammer
  429. *
  430. * title is stored in widget model. title change happens together with widget action (eg. replace slot/ adding a widget)
  431. * the board will be dirty after the widget action (vis transation)
  432. * when the user undos the widget action, the previous model is restored with the older title
  433. */
  434. var options = {
  435. payloadData: {
  436. runtimeOnly: true
  437. }
  438. };
  439. if (this.visualization.getSlots().isMappingComplete()) {
  440. return this.content.getFeature('Visualization.SmartsRecommender.deprecated').getRecommendedTitle().then(this._prepareAsyncCallback(function (title) {
  441. var shouldSetTitle = smartTitleFeature && smartTitleFeature.shouldSetTitle(title);
  442. if (shouldSetTitle) {
  443. var titleHtml = smartTitleFeature && smartTitleFeature.getTitleHtmlWithPreviousFormat(title);
  444. _this8._setTitle(title, titleHtml, options);
  445. }
  446. })).catch(this._prepareAsyncCallback(function (e) {
  447. _this8.logger.error('Could not get title for vis');
  448. _this8.logger.error(e);
  449. _this8._setTitle(null, null, options);
  450. }));
  451. } else {
  452. options.silent = true;
  453. //Show no title when showing vis placeholder
  454. this._setTitle(null, null, options);
  455. return Promise.resolve();
  456. }
  457. } else {
  458. return Promise.resolve();
  459. }
  460. },
  461. _setTitle: function _setTitle(title, titleHtml, options) {
  462. if (this.visModelManager) {
  463. this.visModelManager.setTitle(title, titleHtml, options);
  464. }
  465. },
  466. /**
  467. * get widget title
  468. * @returns a title
  469. */
  470. getTitle: function getTitle() {
  471. return this.visModelManager.getTitle();
  472. },
  473. /**
  474. * @returns true if the property is not a 'general' property (i.e. is a
  475. * visualization property that can be overriden by a user)
  476. */
  477. isAnOverridableVisProperty: function isAnOverridableVisProperty(property) {
  478. var commonProps = {
  479. 'fillColor': true,
  480. 'borderColor': true,
  481. 'transparency': true,
  482. 'showTitle': true,
  483. 'titleMode': true,
  484. 'saveTitle': true,
  485. 'queryRefresh': true
  486. };
  487. return !commonProps[property];
  488. },
  489. /**
  490. * Re send the query when user clicks on Retry link
  491. */
  492. _onRetry: function _onRetry() {
  493. this.visAPI.clearModelInvalid();
  494. this.visAPI.getRenderSequence().reRender({
  495. refreshAll: true
  496. });
  497. },
  498. /**
  499. * Convert error type to warning for prompted sign on error
  500. */
  501. _updateErrorType: function _updateErrorType(errorMsg) {
  502. if (errorMsg && errorMsg === 'dwPromptSignonCancelWarning') {
  503. return 'warning';
  504. }
  505. return LiveWidget.inherited('_updateErrorType', this, arguments);
  506. },
  507. /**
  508. * Add Retry link after the warning message if message is a prompted sign on one.
  509. */
  510. _updateErrorContainer: function _updateErrorContainer(msg, $errorContainer) {
  511. if (msg === 'dwPromptSignonCancelWarning') {
  512. var $retry = $('<div class="retry">' + resources.get('retry') + '</div>');
  513. $retry.on('primaryaction', this._onRetry.bind(this));
  514. // add Retry link
  515. $errorContainer.append($retry);
  516. }
  517. },
  518. destroy: function destroy() {
  519. if (this._ondataSourceChange) {
  520. this._ondataSourceChange.remove();
  521. }
  522. if (this.visualization) {
  523. this.visualization.off('change:slots', this.updateTitle, this);
  524. }
  525. if (this._currVis) {
  526. this._currVis.remove(true);
  527. }
  528. if (this.visModelManager) {
  529. this.dashboardApi.getDashboardCoreSvc('TranslationService').deregisterView(this.visModelManager.id);
  530. }
  531. if (this.featureLoader) {
  532. //Unload features loaded with the widget's feature loader as defined in com.ibm.bi.dashboard.live-features.
  533. //eg: For explore: Visualization, vis-badges, NLT, summarizer, title fetcher, vis-image-capture etc.
  534. // For liveWidget, only the summaryFeature is loaded in this way.
  535. this.featureLoader.unloadFeatures();
  536. }
  537. this._dropTargets && this._dropTargets.forEach(function (target) {
  538. return target.remove();
  539. });
  540. if (this.visExpandMode) {
  541. this.visExpandMode.destroy();
  542. }
  543. if (this._visualizationCreator) {
  544. this._visualizationCreator.destroy();
  545. }
  546. this._destroyCustomVisShowError();
  547. LiveWidget.inherited('destroy', this, arguments);
  548. // Clear an content to avoid an memory leak in case some objects are holding on to this
  549. MemUtil.destroy(this);
  550. },
  551. _destroyCustomVisShowError: function _destroyCustomVisShowError() {
  552. if (this.sdkShowError) {
  553. this.sdkShowError.destroy();
  554. this.sdkShowError = null;
  555. }
  556. },
  557. setEventRouter: function setEventRouter() /*eventRouter, options*/{
  558. LiveWidget.inherited('setEventRouter', this, arguments);
  559. },
  560. /**
  561. * @returns the scope of this widget for selection purposes.
  562. * TODO: The scope is currently just the tabId but should be the tabId + the eventGroupId.
  563. * Need to understand why the groupId is based on the dataSetId.
  564. */
  565. getScope: function getScope() {
  566. return this.getContainerPageId();
  567. },
  568. /**
  569. * Return the widget model that is persisted as part of the pin definition.
  570. * This is the same as the original widget model EXCEPT special cases:
  571. * - When filters from the source dashboard need to be merged to produce 'portable filters' in the pin.
  572. * In this case, the model is cloned, widget-to-widget filters are cleared and local filters are assigned the merged result.
  573. * - ContainerPageId should be removed so that new containerPageId get assigned PinOnDrop
  574. * @returns the widget model that is persisted as part of the pin definition.
  575. */
  576. getWidgetModelForPinning: function getWidgetModelForPinning() {
  577. var widgetModelClone = $.extend(true, {}, this.model);
  578. delete widgetModelClone.containerPageId;
  579. var portableFilters = this.visAPI.getAllFiltersAsLocalFiltersForPinning();
  580. if (portableFilters) {
  581. widgetModelClone.localFilters = portableFilters;
  582. delete widgetModelClone.filters;
  583. return widgetModelClone;
  584. }
  585. return widgetModelClone;
  586. },
  587. /**
  588. * @param {boolean} bForceRender, we only change this.isRendered to be true if needed.
  589. */
  590. setNotRendered: function setNotRendered(bForceRender) {
  591. if (bForceRender) {
  592. this.isRendered = false;
  593. }
  594. },
  595. /**
  596. * This will clear the loading class, removing the icon.
  597. */
  598. removeLoadingAnimation: function removeLoadingAnimation() {
  599. clearTimeout(this.showLoadingAnimationTimer);
  600. if (this.$loadingIndicatorEl) {
  601. this.$loadingIndicatorEl.remove();
  602. this.$loadingIndicatorEl = null;
  603. }
  604. },
  605. /**
  606. * This will add the loading class to the view $el, showing a loading icon
  607. */
  608. showLoadingAnimation: function showLoadingAnimation() {
  609. var _this9 = this;
  610. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  611. if (this.showLoadingAnimationTimer) {
  612. clearTimeout(this.showLoadingAnimationTimer);
  613. }
  614. // todo livewidget_cleanup - doesn't seem right to access the internal of the livewidget here.
  615. this.showLoadingAnimationTimer = setTimeout(function () {
  616. if (!MemUtil.isDestroyed(_this9) && !_this9.$loadingIndicatorEl) {
  617. _this9.$loadingIndicatorEl = $('<div class="skeletonPlaceholderContainer"></div>');
  618. _this9.$el.append(_this9.$loadingIndicatorEl);
  619. var placeholderImage = void 0;
  620. if (!options.loadingText) {
  621. placeholderImage = _this9._getPlaceholder() || 'dashboard-analytics/images/placeholders/autoviz.svg';
  622. //only show the placeholder icon on first render
  623. if (_this9._firstRenderComplete) {
  624. placeholderImage = null;
  625. }
  626. _this9.skeletonPlaceholder = ReactDOM.render(React.createElement(SkeletonPlaceholder, { shown: true, placeholderImage: placeholderImage }), _this9.$loadingIndicatorEl[0]);
  627. } else {
  628. _this9.skeletonPlaceholder = ReactDOM.render(React.createElement(SkeletonPlaceholder, { shown: true, placeholderText: options.loadingText }), _this9.$loadingIndicatorEl[0]);
  629. }
  630. }
  631. }, 1000);
  632. },
  633. /**
  634. * @override
  635. */
  636. renderError: function renderError(msg) {
  637. var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  638. var customVisErrorContext = this._getCustomVisErrorContext(params.errorInfo);
  639. this._destroyCustomVisShowError();
  640. if (customVisErrorContext) {
  641. var hasError = this.hasError();
  642. var $originalContentBeforeError = null;
  643. if (!hasError) {
  644. $originalContentBeforeError = this.$el.children();
  645. $originalContentBeforeError.detach();
  646. }
  647. if (this.el) {
  648. this.$el.find('.errorContainer').remove();
  649. this.errorMessage = msg;
  650. this.updateWidgetArialabel(msg);
  651. this.sdkShowError = new SdkShowError(this.$el[0], customVisErrorContext.msgTitle, customVisErrorContext.msgBody, customVisErrorContext.msgIcon);
  652. this.sdkShowError.showError();
  653. this.addErrorDetailsHandler();
  654. if (this.renderComplete) {
  655. //If a renderComplete function is available, call it as we have rendered what we can and anything waiting on this widget to render shouldn't be blocked.
  656. this.renderComplete();
  657. }
  658. if ($originalContentBeforeError) {
  659. this._$originalContentBeforeError = $originalContentBeforeError;
  660. }
  661. }
  662. } else {
  663. LiveWidget.inherited('renderError', this, arguments);
  664. }
  665. },
  666. _getCustomVisErrorContext: function _getCustomVisErrorContext() {
  667. var errorInfo = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  668. var context = void 0;
  669. if (errorInfo && errorInfo.errorCode === VIPRLibraries.LOAD_DEFINITION_ERROR) {
  670. var isCustomVisPermissionError = void 0;
  671. var isCustomVisDisconnectError = errorInfo.id === 'visualizationPreview';
  672. if (!isCustomVisDisconnectError) {
  673. isCustomVisPermissionError = !VIPRLibraries.isSystemLib(errorInfo.id, VIPR.getSystemLibIds());
  674. }
  675. if (isCustomVisPermissionError || isCustomVisDisconnectError) {
  676. errorInfo.isRendered = true;
  677. context = {
  678. msgTitle: isCustomVisPermissionError ? StringResources.get('extPermissionTitle') : StringResources.get('extVisNoSDKConnection'),
  679. msgBody: isCustomVisPermissionError ? StringResources.get('extPermissionBody') : StringResources.get('extVisNoSDKPreviewMsg'),
  680. msgIcon: isCustomVisPermissionError ? 'permission' : 'disconnect'
  681. };
  682. }
  683. }
  684. return context;
  685. },
  686. clearCurrentVisualization: function clearCurrentVisualization() {
  687. if (this._currVis) {
  688. this._currVis = null;
  689. }
  690. },
  691. /**
  692. * TODO: this api should not be in LiveWidget.js [Post-m31]
  693. * @private
  694. * @return {SlotAPI} Slot Model APIs for the slot that applies the infographic shape
  695. **/
  696. _getShapableSlotAPI: function _getShapableSlotAPI(options) {
  697. options = options || {};
  698. var oSlotAPIs = this.visualization.getSlots().getMappedSlotList();
  699. if (!oSlotAPIs || oSlotAPIs.length === 0) {
  700. return null;
  701. }
  702. var oSlotAPI;
  703. var oDropNode = options.dropNode; //Drop node that accepts dropped in shape widget
  704. if (oDropNode && oDropNode.dataset && oDropNode.dataset.slotId) {
  705. oSlotAPI = this.content.getFeature('Visualization').getSlots().getSlot(oDropNode.dataset.slotId);
  706. } else {
  707. oSlotAPI = _.find(oSlotAPIs, function (slotAPI) {
  708. return slotAPI.getDefinition().getProperty('shapable') === true; //Find the first shapable slot
  709. });
  710. }
  711. return oSlotAPI;
  712. },
  713. onHeatScalePaletteChanged: function onHeatScalePaletteChanged() {
  714. LiveWidget.inherited('onHeatScalePaletteChanged', this, arguments);
  715. return this.visAPI.hasHeatByItem() ? this.visAPI.updateConditionalPalette() : Promise.resolve();
  716. },
  717. /**
  718. * A custom palette was just deleted, update any model properties that reference that palette
  719. * @param {string} paletteId - the id of the palette that was deleted
  720. * @param {string} action - the action on the palette, either update or delete
  721. */
  722. onCustomPaletteChanged: function onCustomPaletteChanged(paletteId, action) {
  723. var _this10 = this;
  724. LiveWidget.inherited('onCustomPaletteChanged', this, arguments);
  725. var rerender = false;
  726. var updateContionalPalette = false;
  727. for (var propertyName in this.palettePropertyToTypeMap) {
  728. if (this.visAPI.getPropertyValue(propertyName) === paletteId) {
  729. rerender = true;
  730. // Special case for conditional palettes, we need to update the cached colors in the
  731. // conditional palette model object
  732. if (propertyName === 'condColorPalette') {
  733. updateContionalPalette = true;
  734. }
  735. if (action === 'delete') {
  736. this.updateVisProperties({
  737. id: propertyName,
  738. value: this.colorsService.getDefaultPaletteName(this.palettePropertyToTypeMap[propertyName])
  739. }, {
  740. payloadData: {
  741. // Can't under a delete of a palette
  742. skipUndoRedo: true
  743. }
  744. });
  745. }
  746. }
  747. }
  748. var updateConditional = updateContionalPalette ? this.visAPI.updateConditionalPalette() : Promise.resolve();
  749. return updateConditional.then(this._prepareAsyncCallback(function () {
  750. if (rerender) {
  751. return _this10.visAPI.getRenderSequence().reRender();
  752. }
  753. }));
  754. },
  755. /**
  756. * Called when the page fill color changes
  757. * We need to reRender to apply the proper foreground colors
  758. */
  759. onPagefillColorChange: function onPagefillColorChange() {
  760. DeprecateWidgetBase.inherited('onPagefillColorChange', this, arguments);
  761. return this.visAPI.getRenderSequence().reRender();
  762. },
  763. onDashboardColorSetChanged: function onDashboardColorSetChanged() {
  764. LiveWidget.inherited('onDashboardColorSetChanged', this, arguments);
  765. return this.visAPI.getRenderSequence().reRender();
  766. },
  767. onColorPaletteChanged: function onColorPaletteChanged() {
  768. LiveWidget.inherited('onColorPaletteChanged', this, arguments);
  769. return this.visAPI.hasHeatByItem() ? this.visAPI.updateConditionalPalette() : Promise.resolve();
  770. },
  771. onFredIsRedChanged: function onFredIsRedChanged() {
  772. if (this.isVisible()) {
  773. return this.visAPI.getRenderSequence().reRender();
  774. } else {
  775. this.setReRenderOnShow({ resizing: true });
  776. }
  777. },
  778. /**
  779. * Register external events the data widget is interested in.
  780. *
  781. */
  782. // @Override event registration function called by WidgetBase.
  783. registerEvents: function registerEvents() {
  784. LiveWidget.inherited('registerEvents', this, arguments);
  785. this.dashboardApi.on('pagecontext:filterContextUpdated', this.onPageContextChanged, this);
  786. this.dashboardApi.on('pagecontext:brushingContextUpdated', this.onPageContextChanged, this);
  787. this.dashboardApi.on('synchronize:pageContextFilters', this._setSynchronizePageContextFilters, this);
  788. this.dashboardApi.on('ambiguousConnection:resolved', this.onAmbiguousConnectionResolved, this);
  789. this.colorsService.on('fredIsRed:changed', this.onFredIsRedChanged, this);
  790. },
  791. unregisterEvents: function unregisterEvents() {
  792. LiveWidget.inherited('unregisterEvents', this, arguments);
  793. this.dashboardApi.off('widget:onDeleteAction', this.onDeleteAction, this);
  794. this.dashboardApi.off('pagecontext:filterContextUpdated', this.onPageContextChanged, this);
  795. this.dashboardApi.off('pagecontext:brushingContextUpdated', this.onPageContextChanged, this);
  796. this.dashboardApi.off('synchronize:pageContextFilters', this._setSynchronizePageContextFilters, this);
  797. this.dashboardApi.off('ambiguousConnection:resolved', this.onAmbiguousConnectionResolved, this);
  798. this.colorsService.off('fredIsRed:changed', this.onFredIsRedChanged, this);
  799. },
  800. /**
  801. * Register external events the data widget is interested in.
  802. *
  803. * @param eventRouter The external event router.
  804. */
  805. registerWidgetChromeEvents: function registerWidgetChromeEvents(eventRouter) {
  806. LiveWidget.inherited('registerWidgetChromeEvents', this, arguments);
  807. // TODO: derived classes override this to setup handlers on view events or to fire requests to view
  808. if (eventRouter) {
  809. eventRouter.on('widget:onDeleteAction', this.onDeleteAction, this);
  810. }
  811. },
  812. isVisible: function isVisible() {
  813. return $(this.el).parent().is(':visible');
  814. },
  815. reRender: function reRender(extraInfo) {
  816. if (this.isVisible()) {
  817. return this.visAPI.reRender(extraInfo);
  818. } else {
  819. this.setReRenderOnShow({
  820. refresh: {
  821. data: true
  822. },
  823. extraInfo: extraInfo
  824. });
  825. return Promise.resolve({ isVisible: false });
  826. }
  827. },
  828. /**
  829. * render the visualization
  830. * @param renderOptions - options that are used in rendering. Usually these are renderSequence options (like refresh)
  831. * By default, render options are null and that performs a "full render". If it's not set, we assume we render for widgets support interactive.
  832. * @returns a promise which is resolved with the results of the render
  833. * (either the renderContext result or exception from the render sequence or a visibility indicator.)
  834. */
  835. render: function render() {
  836. var _this11 = this;
  837. var renderOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
  838. var result = void 0;
  839. if (this.isVisible()) {
  840. console.debug('waiting to Render...');
  841. this.isRendered = true;
  842. result = Promise.all([this.whenContainerIsReady.promise, this.readyDfd.promise]).then(this._prepareAsyncCallback(function () {
  843. // Ensure container is ready before calling setPreferred size
  844. console.debug('wait complete...containerIsReady/getRenderSequence and render');
  845. //Render won't be called until the visModelManager is created.
  846. if (!_this11.visModelManager) {
  847. _this11.visModelManager = _this11._visualizationCreator.visModelManager;
  848. }
  849. var options = {
  850. undoRedoTransactionId: _this11.addPayloadData && _this11.addPayloadData.undoRedoTransactionId,
  851. transactionToken: _this11.addPayloadData && _this11.addPayloadData.transactionToken
  852. };
  853. if (_this11._renderCount !== 0) {
  854. renderOptions = renderOptions || {
  855. refreshAll: true
  856. };
  857. }
  858. _this11.setPreferredSize(_this11.visualization.getDefinition().getPreferredSize(), options);
  859. return _this11.visAPI.getRenderSequence().render(renderOptions)
  860. //Note: the renderSequence resolves with its renderContext.
  861. .then(_this11._prepareAsyncCallback(function (renderSequenceRenderContext) {
  862. var initialExpanded = _this11.dashboardApi.getAppConfig('expandWidget') || false;
  863. return _this11.toggleExpanded(initialExpanded).then(function () {
  864. return renderSequenceRenderContext;
  865. });
  866. }));
  867. }));
  868. } else {
  869. console.debug('Widget Invisible. Skipping render');
  870. //The widgetLoader calls render when any widget creation is complete...but if this widget is invisible (ie on invisible tab or aborted),
  871. //render for a livewidget is designed to do nothing (for performance). It will render on show.
  872. result = Promise.resolve({ isVisible: false });
  873. }
  874. return result;
  875. },
  876. _getDnDTargetData: function _getDnDTargetData() {
  877. return {
  878. el: this.el,
  879. type: 'widget.live',
  880. info: {
  881. node: this.el,
  882. contentId: this.content.getId()
  883. }
  884. };
  885. },
  886. /**
  887. * NOTE: The widget accepts api is only called for A11Y support
  888. * (It is called by CanvasController:addDataItemsOrAddWidget if this widget is the currently selected content on the dashboard.)
  889. * For CA, this occurs when a user selects data in the tree and clicks Shift-rightarrow...other applications might have other gestures for this.
  890. * For "normal Drag&Drop", see _setupDND (below) and VisDnD.
  891. * @param {Object} source - the drag source object as defined by the caller and accepted by the implementation of VisDnD
  892. * @returns true if the source is accepted by this widget
  893. */
  894. accepts: function accepts(source) {
  895. var visDnD = this.content.getFeature('VisDnD');
  896. return visDnD && visDnD.accepts(source, this._getDnDTargetData());
  897. },
  898. /**
  899. * NOTE: The widget onDrop api is only called for A11Y support
  900. * (It is called by CanvasController:addDataItemsOrAddWidget if this widget is the currently selected content on the dashboard.)
  901. * For CA, this occurs when a user selects data in the tree and clicks Shift-rightarrow...other applications might have other gestures for this.
  902. * For "normal Drag&Drop", see _setupDND (below) and VisDnD.
  903. * @param {Object} source - the drag source object as defined by the caller and accepted by the implementation of VisDnD
  904. * @returns true if the source is accepted by this widget
  905. */
  906. onDrop: function onDrop(source) {
  907. var visDnD = this.content.getFeature('VisDnD');
  908. visDnD && visDnD.onDrop(source, this._getDnDTargetData());
  909. },
  910. _getDnD: function _getDnD() {
  911. // fall back to the old .DndManager
  912. return this.dashboardApi.getFeature('DashboardDnd.internal') || this.dashboardApi.getFeature('.DndManager');
  913. },
  914. /**
  915. * Creates our dnd nodes and handlers
  916. * @param widgetContainer [optional] if defined, the container of this widget
  917. */
  918. _setupDND: function _setupDND(widgetContainer) {
  919. var _this12 = this;
  920. this._initializeAppSettings();
  921. var dndManager = this._getDnD();
  922. var widgetFocus = '.widgetFocus *';
  923. var _onDragEnter = function _onDragEnter(target) {
  924. //if we're in focus mode
  925. var node = target && target.info && target.info.node;
  926. var isFocusMode = $(node).is(widgetFocus);
  927. var dropTargetNode = node;
  928. if (!isFocusMode && widgetContainer) {
  929. dropTargetNode = widgetContainer;
  930. }
  931. _this12._dropTargetNode = dropTargetNode;
  932. $(dropTargetNode).addClass('draggedOn');
  933. _this12._showVisualizationDropZones();
  934. };
  935. var _onDragLeave = function _onDragLeave(target) {
  936. $(_this12._dropTargetNode).removeClass('draggedOn');
  937. _this12._hideVisualizationDropZones(target);
  938. };
  939. // Register live widget as a drop target
  940. this._dropTargets.push(dndManager.registerDropTarget({
  941. el: this.el,
  942. type: 'widget.live',
  943. info: function info(domNode, event) {
  944. return {
  945. dragInfo: {
  946. position: {
  947. x: event.clientX,
  948. y: event.clientY
  949. }
  950. },
  951. node: domNode,
  952. contentId: _this12.content.getId(),
  953. onDragEnter: _onDragEnter,
  954. onDragLeave: _onDragLeave
  955. };
  956. }
  957. }));
  958. if (widgetContainer) {
  959. this._dropTargets.push(dndManager.registerDropTarget({
  960. el: widgetContainer,
  961. type: 'widget.live',
  962. info: function info(domNode, event) {
  963. return {
  964. dragInfo: {
  965. position: {
  966. x: event.clientX,
  967. y: event.clientY
  968. }
  969. },
  970. node: domNode,
  971. contentId: _this12.content.getId(),
  972. onDragEnter: _onDragEnter,
  973. onDragLeave: _onDragLeave
  974. };
  975. }
  976. }));
  977. }
  978. },
  979. /**
  980. * Returns the placeholder icon for the current vis definition
  981. */
  982. _getPlaceholder: function _getPlaceholder() {
  983. var def = this.visualization.getDefinition();
  984. return def && def.getPlaceholderIconUri();
  985. },
  986. _showVisualizationDropZones: function _showVisualizationDropZones() {
  987. if (this.visualization.getSlots().isMappingComplete()) {
  988. var dropZonesOverlayDOM = this._getContent().getFeature('DropZonesOverlayDOM');
  989. var dropZonesOverlayState = this._getContent().getFeature('DropZonesOverlayState');
  990. if (dropZonesOverlayState && dropZonesOverlayState.isEnabled()) {
  991. if (dropZonesOverlayDOM.isMounted()) {
  992. dropZonesOverlayState.show();
  993. } else {
  994. dropZonesOverlayDOM.render();
  995. }
  996. dropZonesOverlayDOM.reassessDropTarget(this._dropTargetNode);
  997. }
  998. }
  999. },
  1000. _hideVisualizationDropZones: function _hideVisualizationDropZones(target) {
  1001. var dropZonesOverlayState = this._getContent().getFeature('DropZonesOverlayState');
  1002. if (dropZonesOverlayState && dropZonesOverlayState.isEnabled() && this.visualization.getSlots().isMappingComplete()) {
  1003. var targetInfo = target && target.info;
  1004. //overlay drop zones should be hidden for:
  1005. //1) moving from this widget to the canvas (targetInfo is undefined in the new target in this case)
  1006. //2) moving from this widget to another widget (target will have a different contentId than ours)
  1007. //3) moving from this widget to an overlay in another widget (contentId's are defined in targets for both overlays and widgets)
  1008. if (!targetInfo || !targetInfo.node || !targetInfo.contentId || targetInfo.contentId !== this.content.getId()) {
  1009. dropZonesOverlayState.hide();
  1010. }
  1011. }
  1012. },
  1013. /*
  1014. * Called after card is animated into view
  1015. */
  1016. postRenderAnimation: function postRenderAnimation() {
  1017. setTimeout(function () {
  1018. // useful for IT test. We are adding a flag to indicate the Focus widget is done with its expanding animation.
  1019. // With IT tests, events like click and drag will miss their target if the elements are moving in the page.
  1020. $('.dialogBlocker .card').addClass('doneAnim');
  1021. }, 500);
  1022. setTimeout(this.resize.bind(this), 50);
  1023. },
  1024. /**
  1025. * Externally consumable api to access the local filters of the widget in text form.
  1026. * @returns an array of all the filters with the following params.
  1027. ariaDeleteLabel: "Press DELETE key to delete filter."
  1028. ariaEditLabel: "Press ENTER key to delete filter."
  1029. dataId: "{"origin":"visualization","sourceId":"model000001629afa21a4_00000002","scope":"page1","eventGroupId":"page1:1","huns":[{"hierarchyUniqueName":"sales_and_marketing_csv.Product_line"}]}"
  1030. deleteEnabled : true
  1031. deleteLabel : "DeleteFilter"
  1032. description : "Includes: Personal Accessories"
  1033. editEnabled : false
  1034. editLabel : "Edit filter"
  1035. svgHref : ""#common-filter""
  1036. title : "Product line"
  1037. toolTip : "Includes: Personal Accessories"
  1038. type : "Filter"
  1039. */
  1040. getWidgetLocalFilters: function getWidgetLocalFilters() {
  1041. var formattedFilters = [];
  1042. if (this.visAPI) {
  1043. var filters = this.visAPI.getFilterInfo();
  1044. if (filters && filters.localFilters && filters.localFilters.length > 0) {
  1045. var filterLabelHelper = new FilterLabelHelper({ 'dataSource': this.visualization.getDataSource(), 'dashboardApi': this.dashboardApi });
  1046. formattedFilters = filterLabelHelper.generateFilterListItems(filters.localFilters);
  1047. }
  1048. }
  1049. return formattedFilters;
  1050. },
  1051. /**
  1052. * Externally consumable api to access the global filters of the widget in text form.
  1053. * @returns an array of all the filters with the following params.
  1054. ariaDeleteLabel: "Press DELETE key to delete filter."
  1055. ariaEditLabel: "Press ENTER key to delete filter."
  1056. dataId: "{"origin":"visualization","sourceId":"model000001629afa21a4_00000002","scope":"page1","eventGroupId":"page1:1","huns":[{"hierarchyUniqueName":"sales_and_marketing_csv.Product_line"}]}"
  1057. deleteEnabled : true
  1058. deleteLabel : "DeleteFilter"
  1059. description : "Includes: Personal Accessories"
  1060. editEnabled : false
  1061. editLabel : "Edit filter"
  1062. svgHref : ""#common-filter""
  1063. title : "Product line"
  1064. toolTip : "Includes: Personal Accessories"
  1065. type : "Filter"
  1066. */
  1067. getWidgetGlobalFilters: function getWidgetGlobalFilters() {
  1068. var filters = [];
  1069. var formattedFilters = [];
  1070. var filterLabelHelper = new FilterLabelHelper({ 'dataSource': this.visualization.getDataSource(), 'dashboardApi': this.dashboardApi });
  1071. filters = this.visAPI.getFilterInfo();
  1072. if (filters.filters.length > 0) {
  1073. formattedFilters = filterLabelHelper.generateFilterListItems(filters.filters);
  1074. }
  1075. return formattedFilters;
  1076. },
  1077. getVisId: function getVisId() {
  1078. return this.model.visId;
  1079. },
  1080. /**
  1081. * Called when a hidden widget is shown
  1082. */
  1083. onShow: function onShow(options) {
  1084. var result = void 0;
  1085. if (!this.isRendered || this.rerenderOnShow) {
  1086. //If rendering non interactive (eg: thumbnails), disable features which are interactive-only - otherwise, enable them.
  1087. var isInteractive = !(options && options.isBackgroundRun);
  1088. this.featureLoader.setMatchingFeaturesEnabled('interactive-only', isInteractive);
  1089. //renderOptions are for this onShow...by default, do a full render (renderOptions=null))
  1090. //but honour renderOptions carried forward in the rerenderOnShow member.
  1091. var renderOptions = this.isRendered && this.rerenderOnShow ? this.rerenderOnShow : null;
  1092. if (isInteractive) {
  1093. this.rerenderOnShow = null;
  1094. } else {
  1095. //add rerenderOnShow options for the next, interactive onShow
  1096. this._addInteractiveStepsToRerenderOnShow();
  1097. }
  1098. result = this.render(renderOptions, isInteractive);
  1099. } else {
  1100. this._callVisFunction('onVisible');
  1101. result = Promise.resolve();
  1102. }
  1103. if (this.updateDecorationsOnShow) {
  1104. var decoratorAPI = this.getDecoratorAPI();
  1105. if (decoratorAPI) {
  1106. decoratorAPI.updateDecorations();
  1107. }
  1108. this.updateDecorationsOnShow = false;
  1109. }
  1110. return result;
  1111. },
  1112. //Get all of the render sequence steps that are associated with interactive-only features
  1113. //and set them to be refreshed.
  1114. _addInteractiveStepsToRerenderOnShow: function _addInteractiveStepsToRerenderOnShow() {
  1115. var _this13 = this;
  1116. var stepIds = this.featureLoader.getMatchingFeatureStepIds('interactive-only');
  1117. if (stepIds.length) {
  1118. this.rerenderOnShow = this.rerenderOnShow || {};
  1119. this.rerenderOnShow.refresh = this.rerenderOnShow.refresh || {};
  1120. stepIds.forEach(function (stepId) {
  1121. _this13.rerenderOnShow.refresh[stepId] = true;
  1122. });
  1123. }
  1124. },
  1125. _completeRenderStart: function _completeRenderStart(renderState) {
  1126. if (this.renderStartDeferred) {
  1127. this.renderStartDeferred.resolve(renderState);
  1128. this.renderStartDeferred = null;
  1129. }
  1130. },
  1131. /**
  1132. * Called everytime we start a new render
  1133. *
  1134. * @param {boolean} [initial=false] - whether is initial render or not
  1135. * @param {boolean} [resizing=false] - whether is rendering due to a resize
  1136. * @param {boolean} [hideToolbar=true] - whether the toolbar should be hidden as part of the render or not
  1137. *
  1138. * @return {boolean} - true if the renderCompleteDeferred was reset. Really only used for unit tests
  1139. */
  1140. renderStart: function renderStart() {
  1141. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
  1142. hideToolbar: true
  1143. };
  1144. var hideToolbar = options.hideToolbar === undefined ? true : !!options.hideToolbar;
  1145. var renderState = {
  1146. renderCount: this._renderCount,
  1147. isInitial: !!options.initial,
  1148. isResizing: !!options.resizing
  1149. };
  1150. if (renderState.isResizing && hideToolbar) {
  1151. // TODO: revisit triggering this event in livewidget
  1152. this.dashboardApi.triggerDashboardEvent('widget:hideToolbar');
  1153. }
  1154. //only try to trigger title preparation if the widget is being rendered (and visible)
  1155. this._triggerTitlePreparation();
  1156. this.trigger('renderStart', renderState);
  1157. var content = this._getContent();
  1158. if (content) {
  1159. var stateApi = content.getFeature('state.internal');
  1160. stateApi.setStatus(stateApi.STATUS.RENDERING);
  1161. }
  1162. // Don't do anything for the initial render since we've created renderCompleteDeferred was created in the constructor
  1163. if (options.initial || this._isCurrentlyRendering) {
  1164. this._isCurrentlyRendering = true;
  1165. this._completeRenderStart(renderState);
  1166. return false;
  1167. }
  1168. this._isCurrentlyRendering = true;
  1169. this.renderCompleteDeferred = new Deferred();
  1170. this._completeRenderStart(renderState);
  1171. return true;
  1172. },
  1173. /**
  1174. * Since the render process can be async, this method exists to allow views to notify their model (and, in turn, users of
  1175. * this widget) that render is complete. via the event router. NOTE: The api of the data widget is sent as the payload of
  1176. * this event.
  1177. *
  1178. * @param {object} renderInfo - information about rendering
  1179. */
  1180. renderComplete: function renderComplete() {
  1181. var renderInfo = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  1182. var resizeFlag = !!(renderInfo && renderInfo.visView && renderInfo.visView.resizing);
  1183. var renderingNewData = !!(renderInfo && renderInfo.visView && renderInfo.visView.getRenderingNewData());
  1184. //isResizing is only true if this render is ONLY resizing....
  1185. //ie: subsequent tasks may not need to do any work if only resizing but would if data changes and we're resizing as well.
  1186. var isResizing = resizeFlag && !renderingNewData;
  1187. this._isCurrentlyRendering = false;
  1188. this._renderCount++;
  1189. var stateApi = this.content.getFeature('state.internal');
  1190. stateApi.setStatus(stateApi.STATUS.RENDERED);
  1191. this.$el.attr('renderCount', this._renderCount);
  1192. this.$el.attr('appcues-data-id', 'widget-content');
  1193. this.dashboardApi.triggerDashboardEvent('rendered', this.api);
  1194. this.trigger('renderComplete', {
  1195. isResizing: isResizing,
  1196. renderCount: this._renderCount
  1197. });
  1198. // For widgets that have truthy expandOnRender property, open the dataset panel
  1199. if (this.get('expandOnRender')) {
  1200. // Remove the flag so we no longer open the dataset panel on the next time
  1201. this.set({
  1202. expandOnRender: undefined
  1203. }, {
  1204. merge: true,
  1205. remove: true,
  1206. silent: true
  1207. });
  1208. this._invokeLifeCycleHandlers('pre:widget.maximize', {
  1209. id: this.id
  1210. });
  1211. }
  1212. this.renderCompleteDeferred.resolve({
  1213. renderCount: this._renderCount,
  1214. isResizing: isResizing
  1215. });
  1216. this.updateDescription(this._getVisualizationDefaultLabel());
  1217. if (this.layoutAPI && this.layoutAPI.updateTitle) {
  1218. //Title may have changed silently during render - send update event to ensure it's in sync
  1219. var smartTitleEnabledConfig = this.getDashboardApi().getAppConfig('smartTitle');
  1220. var smartTitleFeature = this.content.getFeature('SmartTitle');
  1221. var smartTitleEnabled = smartTitleEnabledConfig || smartTitleFeature && smartTitleFeature.shouldUseGeneratedTitle();
  1222. if (smartTitleEnabled) {
  1223. this.layoutAPI.updateTitle();
  1224. }
  1225. }
  1226. },
  1227. _callVisFunction: function _callVisFunction(functionName, args, defaultReturn) {
  1228. if (this._currVis && typeof this._currVis[functionName] === 'function') {
  1229. return this._currVis[functionName].apply(this._currVis, Array.isArray(args) ? args : [args]);
  1230. }
  1231. return defaultReturn;
  1232. },
  1233. _getVisualizationDefaultLabel: function _getVisualizationDefaultLabel() {
  1234. var description = this._callVisFunction('getDescription', null, resources.get('visualizationLabel'));
  1235. description = resources.get('WidgetLabelWithDescripion', {
  1236. label: description,
  1237. description: resources.get('f10KeyDescription')
  1238. });
  1239. return description;
  1240. },
  1241. _triggerTitlePreparation: function _triggerTitlePreparation() {
  1242. if (!this._titlePrepDone) {
  1243. if (this.widgetChromeEventRouter) {
  1244. this.widgetChromeEventRouter.trigger('title:containerReady', this);
  1245. this._titlePrepDone = true;
  1246. }
  1247. }
  1248. },
  1249. addTranslationIcon: function addTranslationIcon(multilingualAttributesInfo) {
  1250. var _this14 = this;
  1251. var num = 0;
  1252. var widgetTitleTranslationIconAdded = false;
  1253. var decoratorAPI = this.getDecoratorAPI();
  1254. var isVizDecarationUpdated = false;
  1255. var translationService = this.dashboardApi.getDashboardCoreSvc('TranslationService');
  1256. multilingualAttributesInfo.forEach(function (_ref2) {
  1257. var propertyParentModel = _ref2.propertyParentModel,
  1258. propertyName = _ref2.propertyName;
  1259. // Handle the widget title
  1260. if ((propertyName === 'titleHtml' || propertyName === 'name') && !widgetTitleTranslationIconAdded) {
  1261. var header = _this14.$el.parent().find('.widgetHeader');
  1262. if (!translationService.hasTranslationIcon(header)) {
  1263. translationService.appendTranslationIcon(header);
  1264. widgetTitleTranslationIconAdded = true;
  1265. num++;
  1266. }
  1267. } else {
  1268. var icons = _this14._decorateWithTranslationIcon(decoratorAPI, propertyParentModel, '!');
  1269. isVizDecarationUpdated = icons > 0;
  1270. num += icons;
  1271. }
  1272. });
  1273. if (isVizDecarationUpdated) {
  1274. if (this.isVisible()) {
  1275. if (decoratorAPI) {
  1276. decoratorAPI.updateDecorations();
  1277. }
  1278. } else {
  1279. this.updateDecorationsOnShow = true;
  1280. }
  1281. }
  1282. return num;
  1283. },
  1284. removeTranslationIcon: function removeTranslationIcon(multilingualAttributesInfo) {
  1285. var _this15 = this;
  1286. var num = 0;
  1287. var isTitleDecorationCleared = false;
  1288. var decoratorAPI = this.getDecoratorAPI();
  1289. var isVizDecarationUpdated = false;
  1290. var translationService = this.dashboardApi.getDashboardCoreSvc('TranslationService');
  1291. multilingualAttributesInfo.forEach(function (_ref3) {
  1292. var propertyParentModel = _ref3.propertyParentModel,
  1293. propertyName = _ref3.propertyName;
  1294. if ((propertyName === 'titleHtml' || propertyName === 'name') && !isTitleDecorationCleared) {
  1295. var header = _this15.$el.parent().find('.widgetHeader');
  1296. if (translationService.hasTranslationIcon(header)) {
  1297. translationService.removeTranslationIcon(header);
  1298. isTitleDecorationCleared = true;
  1299. num++;
  1300. }
  1301. } else {
  1302. var icons = _this15._decorateWithTranslationIcon(decoratorAPI, propertyParentModel, '');
  1303. isVizDecarationUpdated = icons > 0;
  1304. num += icons;
  1305. }
  1306. });
  1307. if (isVizDecarationUpdated) {
  1308. if (this.isVisible()) {
  1309. if (decoratorAPI) {
  1310. decoratorAPI.updateDecorations();
  1311. }
  1312. } else {
  1313. this.updateDecorationsOnShow = true;
  1314. }
  1315. }
  1316. return num;
  1317. },
  1318. _decorateWithTranslationIcon: function _decorateWithTranslationIcon(decoratorAPI, propertyParentModel, value) {
  1319. var num = 0;
  1320. var slots = this.content.getFeature('Visualization').getSlots().getSlotList();
  1321. var mappedSlots = this.content.getFeature('Visualization').getSlots().getMappedSlotList();
  1322. var mappedSlotsNameSet = mappedSlots.reduce(function (map, slot) {
  1323. map[slot.getId()] = true;
  1324. return map;
  1325. }, {});
  1326. var slotIndex = -1;
  1327. slots.forEach(function (slot) {
  1328. var slotDef = slot.getDefinition();
  1329. var isMapped = mappedSlotsNameSet[slot.getId()];
  1330. if (isMapped || slotDef.getProperty('mldIndicatorIfUnmapped')) {
  1331. slotIndex++;
  1332. if (slotDef.getProperty('mldTitleProperty') !== propertyParentModel.id || !propertyParentModel.multilingual) {
  1333. return;
  1334. }
  1335. if (isMapped) {
  1336. var dataItems = slot.getDataItemList();
  1337. dataItems.forEach(function (dataItem) {
  1338. var id = dataItem.getColumnId();
  1339. // Skip the virtual multi measure series data item
  1340. if (id !== '_multiMeasuresSeries') {
  1341. if (decoratorAPI) {
  1342. decoratorAPI.decorateItem(slotIndex, 'attentionBadge', value);
  1343. }
  1344. num++;
  1345. }
  1346. });
  1347. } else if (slotDef.getProperty('mldIndicatorIfUnmapped')) {
  1348. if (decoratorAPI) {
  1349. decoratorAPI.decorateItem(slotIndex, 'attentionBadge', value);
  1350. }
  1351. num++;
  1352. }
  1353. }
  1354. });
  1355. return num;
  1356. },
  1357. onAuthoringMode: function onAuthoringMode() {
  1358. LiveWidget.inherited('onAuthoringMode', this, arguments);
  1359. // Update the description when we switch modes (e.g. remove F10 instruction to launch the focus view from the widget
  1360. // description
  1361. this.updateDescription(this._getVisualizationDefaultLabel());
  1362. },
  1363. onConsumeMode: function onConsumeMode() {
  1364. LiveWidget.inherited('onConsumeMode', this, arguments);
  1365. // Update the description when we switch modes
  1366. this.updateDescription(this._getVisualizationDefaultLabel());
  1367. },
  1368. isInFocusMode: function isInFocusMode() {
  1369. return this._isMaximized;
  1370. },
  1371. /**
  1372. * @returns true when the widget has completed its first render. NOTE: Currently, the change is restricted to the needs of
  1373. * createLiveWidget (first render only). An additional change should be made to allow a caller to identify when
  1374. * renders are complete for subsequent interactive operations.
  1375. */
  1376. isFirstRenderComplete: function isFirstRenderComplete() {
  1377. return this._renderCount > 0;
  1378. },
  1379. /**
  1380. * Return a promise that will be resolved when the widget starts rendering
  1381. * @returns {Promise} resolved when widget starts rendering
  1382. */
  1383. whenRenderStart: function whenRenderStart() {
  1384. if (!this.renderStartDeferred) {
  1385. this.renderStartDeferred = new Deferred();
  1386. }
  1387. return this.renderStartDeferred.promise;
  1388. },
  1389. /**
  1390. * Return a promise that will be resolved when the widget is rendered.
  1391. *
  1392. * @returns
  1393. */
  1394. whenRenderComplete: function whenRenderComplete() {
  1395. return this.renderCompleteDeferred.promise;
  1396. },
  1397. /**
  1398. * Process changes on the pageContext.
  1399. * @param event - the event payload (which includes the pageContextResult API object)
  1400. */
  1401. onPageContextChanged: function onPageContextChanged(event) {
  1402. // Make sure the event matches the assetId which is unique accross all sources.
  1403. if (!this.visualization || !this.visualization.getDataSource()) {
  1404. return;
  1405. }
  1406. if (event.ignorePageContextChanged && event.ignorePageContextChanged()) {
  1407. return;
  1408. }
  1409. if (event.noSelfRefresh && event.noSelfRefresh() && event.senderMatches && event.senderMatches(this.id)) {
  1410. return;
  1411. }
  1412. if (event.isInScope(this.getScope())) {
  1413. this.visAPI.clearModelInvalid();
  1414. if (this.isVisible()) {
  1415. this._callVisFunction('onChangePageContext', {
  1416. eventName: 'change:pagecontext',
  1417. changeEvent: event
  1418. });
  1419. } else {
  1420. this.rerenderOnShow = { refresh: { data: true, annotation: true } };
  1421. }
  1422. }
  1423. },
  1424. onAmbiguousConnectionResolved: function onAmbiguousConnectionResolved(payload) {
  1425. var state = this.visAPI.getInvalidReason();
  1426. if (payload && payload.sourceId === this.visAPI.getModule().getSourceId() && state && (state.code === 'CQE-801' || state.code === 'CQE-802')) {
  1427. // Listen and refresh widget only after an ambiguous connection is resolved and widget is still
  1428. // in ambiguous connection error state.
  1429. this.reRender({
  1430. refresh: true
  1431. });
  1432. }
  1433. },
  1434. _sourceIdsMatch: function _sourceIdsMatch(payload, module) {
  1435. payload = payload || {};
  1436. var sourceIds = payload.getSourceIds ? payload.getSourceIds() : [];
  1437. return module ? _.contains(sourceIds, module.getSourceId()) : false;
  1438. },
  1439. _assetIdsMatch: function _assetIdsMatch(payload, module) {
  1440. payload = payload || {};
  1441. var assetIds = payload.getAssetIds ? payload.getAssetIds() : [];
  1442. return module ? _.contains(assetIds, module.getAssetId()) : false;
  1443. },
  1444. idsNotInModule: function idsNotInModule(sourceIds, assetIds) {
  1445. var module = this.visAPI.getModule();
  1446. var payload = {
  1447. getSourceIds: function getSourceIds() {
  1448. return sourceIds;
  1449. },
  1450. getAssetIds: function getAssetIds() {
  1451. return assetIds;
  1452. }
  1453. };
  1454. if (_.isArray(sourceIds) && !!sourceIds.length && !this._sourceIdsMatch(payload, module)) {
  1455. return true;
  1456. }
  1457. return _.isArray(assetIds) && !!assetIds.length && !this._assetIdsMatch(payload, module);
  1458. },
  1459. changeAggregation: function changeAggregation(mapping, newAggregationType, options) {
  1460. this.visModelManager.changeAggregationType(mapping, newAggregationType, options);
  1461. },
  1462. /**
  1463. * check if the widget supports contextual grid
  1464. * @return {Promise} - promise resolves with true if contextualGrid is supported
  1465. */
  1466. isContextualGridEnabled: function isContextualGridEnabled() {
  1467. var _this16 = this;
  1468. return this.visualizationDfd.promise.then(this._prepareAsyncCallback(function () {
  1469. return _this16.doesWidgetSupportContextualGrid();
  1470. }));
  1471. },
  1472. /**
  1473. * @override
  1474. * Notify Properties pane on chrome getting selected as well
  1475. */
  1476. onChromeSelected: function onChromeSelected(isGroupSelect) {
  1477. LiveWidget.inherited('onChromeSelected', this, arguments);
  1478. this.onFocus();
  1479. if (this.isAuthoringMode && this.widgetChromeEventRouter && !isGroupSelect) {
  1480. this.widgetChromeEventRouter.trigger('title:chromeSelected');
  1481. }
  1482. },
  1483. /**
  1484. * Handler for Widget chrome deselect event
  1485. */
  1486. onChromeDeselected: function onChromeDeselected() {
  1487. LiveWidget.inherited('onChromeDeselected', this, arguments);
  1488. this.focusOn = false;
  1489. if (this.isAuthoringMode && this.widgetChromeEventRouter) {
  1490. this.widgetChromeEventRouter.trigger('title:chromeDeselected');
  1491. }
  1492. this.toggleLassoMode(false);
  1493. },
  1494. /**
  1495. * Handler for Focus event Gets list of available visualizations for the widget and notifies Property pane so that it can
  1496. * show properties to change
  1497. */
  1498. onFocus: function onFocus() {
  1499. var _this17 = this;
  1500. return this.readyDfd.promise.then(this._prepareAsyncCallback(function () {
  1501. _this17.refreshPropertiesPane();
  1502. _this17.focusOn = true;
  1503. }));
  1504. },
  1505. /**
  1506. * Event handler: Process a change to the UI for a property
  1507. *
  1508. * @param info = A structure from the UI including: 1) the category of the property (the property name or class), 2) the new
  1509. * value
  1510. */
  1511. onPropertyUpdate: function onPropertyUpdate(info) {
  1512. if (this.isAnOverridableVisProperty(info.category)) {
  1513. var prop = {
  1514. id: info.category,
  1515. value: info.item
  1516. };
  1517. /* since changing any of auto binning properties make changes in data, the local filters will be removed. so,
  1518. need to create new transaction to keeps track of local filters for undo changes in auto binning properties.
  1519. */
  1520. this.updateVisProperties(prop, {
  1521. payloadData: {
  1522. undoRedoTransactionId: info.transactionId || info.undoRedoTransactionId,
  1523. transactionToken: info.transactionToken
  1524. },
  1525. silent: info.silent
  1526. });
  1527. } else if (info.category === 'saveTitle') {
  1528. if (this.widgetChromeEventRouter) {
  1529. this.widgetChromeEventRouter.trigger('title:updateModel', info);
  1530. }
  1531. } else {
  1532. // if it wasn't a data widget-specific property, pass it down to the baseWidget class to see if wants to handle it
  1533. LiveWidget.inherited('onPropertyUpdate', this, [info]);
  1534. }
  1535. },
  1536. /**
  1537. * @returns the DOM element for this widget.
  1538. */
  1539. getDOM: function getDOM() {
  1540. return this.el;
  1541. },
  1542. //avoid the term expandView as this denotes the view part but not the general mode.
  1543. getExpandViewContent: function getExpandViewContent(data, $container) {
  1544. var _this18 = this;
  1545. $container.addClass('liveWidget');
  1546. return this.visExpandMode.getExpandModeContent($container).catch(function (err) {
  1547. _this18.renderError(err);
  1548. return Promise.reject(err);
  1549. });
  1550. },
  1551. /**
  1552. * Triggers event in VisModelManager for exiting the widget
  1553. */
  1554. onExitContainer: function onExitContainer() {
  1555. this._callVisFunction('onExitContainer');
  1556. },
  1557. /**
  1558. * Triggers event in VisModelManager for entering the widget
  1559. */
  1560. onEnterContainer: function onEnterContainer() {
  1561. this._callVisFunction('onEnterContainer');
  1562. },
  1563. /**
  1564. * Called on restoring widget from focus view to original state (from ExpandedView) after the animation/transition from
  1565. * maximize to original state is done
  1566. *
  1567. * This will then ensure widget is moved back to the previous parent (canvas layout) & do cleanup
  1568. */
  1569. onRestore: function onRestore() {
  1570. if (this._$focusView) {
  1571. this._$focusView.find('.focusSidebar').hide();
  1572. }
  1573. var visExpandMode = this.dashboardApi.getCanvas().getContent(this.getId()).getFeature('VisExpandMode');
  1574. visExpandMode.restore();
  1575. this._$focusView = null;
  1576. if (this.$placeholder) {
  1577. this.$placeholder.remove();
  1578. }
  1579. LiveWidget.inherited('onRestore', this, arguments);
  1580. },
  1581. reveal: function reveal() {
  1582. return this._callVisFunction('reveal', null, Promise.resolve());
  1583. },
  1584. getEdgeSelection: function getEdgeSelection() {
  1585. return this._currVis && this._currVis.eventHandler ? this._currVis.eventHandler.getEdgeSelection() : false;
  1586. },
  1587. isLassoSelectActive: function isLassoSelectActive() {
  1588. return this._currVis && this._currVis.eventHandler ? this._currVis.eventHandler.isLassoSelectActive() : false;
  1589. },
  1590. /**
  1591. * Toggle the state of lasso mode
  1592. *
  1593. * @param {boolean} [state] - The final state of lasso mode for the Vis. True means
  1594. * switch on, false means switch off. When not provided, the current state is inverted.
  1595. */
  1596. toggleLassoMode: function toggleLassoMode(state) {
  1597. return this._currVis && this._currVis.eventHandler ? this._currVis.eventHandler.toggleLassoSelect(state) : false;
  1598. },
  1599. mapVizRenderForPrint: function mapVizRenderForPrint() {
  1600. return this.visAPI.mapVizRenderForPrint();
  1601. },
  1602. _setSynchronizePageContextFilters: function _setSynchronizePageContextFilters() {
  1603. var payload = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  1604. this.synchDataFilterEntries = payload.synchDataFilterEntries;
  1605. },
  1606. getSynchDataFilterEntries: function getSynchDataFilterEntries() {
  1607. return this.synchDataFilterEntries;
  1608. },
  1609. getTranslationIconEl: function getTranslationIconEl() {
  1610. return this.$el.parent().find('.widgetHeader');
  1611. },
  1612. onHide: function onHide() {
  1613. if (this._isCurrentlyRendering) {
  1614. this.setReRenderOnShow({
  1615. resizing: true
  1616. });
  1617. }
  1618. },
  1619. resize: function resize() {
  1620. var _this19 = this,
  1621. _arguments = arguments;
  1622. var content = this._getContent();
  1623. if (content) {
  1624. return VisUtil.validateVisDefinition(content, this.dashboardApi, { visId: this.model.visId }).then(function (isValid) {
  1625. if (_this19._previousDefinitionState === false && isValid === true) {
  1626. content.getFeature('Visualization').getDefinition().refresh();
  1627. }
  1628. _this19._previousDefinitionState = isValid;
  1629. return isValid;
  1630. }).then(this._prepareAsyncCallback(function () {
  1631. return LiveWidget.inherited('resize', _this19, _arguments);
  1632. }));
  1633. } else {
  1634. LiveWidget.inherited('resize', this, arguments);
  1635. }
  1636. },
  1637. _getContent: function _getContent() {
  1638. if (!this.content) {
  1639. this.content = this.dashboardApi.getCanvas() && this.dashboardApi.getCanvas().getContent(this.id);
  1640. }
  1641. return this.content;
  1642. }
  1643. });
  1644. /**
  1645. * Gets the default spec for a DataWidet. This takes a few options options.archetype: The archetype of the visualiztion
  1646. * options.visId: The visId of the visualization options.avatarIcon: The icon to show as an avatar when dragging the incomplete
  1647. * LiveWidget into it's location
  1648. *
  1649. * Also sets the expandOnRender option to force the widget to expand as soon as it's placed so that the user can fill out the
  1650. * appropriate slots
  1651. */
  1652. LiveWidget.getDefaultSpec = function (name, options) {
  1653. return Promise.resolve({
  1654. model: {
  1655. mapping: [],
  1656. type: 'live',
  1657. visId: options.visId,
  1658. expandOnRender: true,
  1659. avatarHtml: '<div class="staticContent"><img src="' + options.avatarIcon + '" style="width:200px; height:200px"></img></div>'
  1660. },
  1661. ignoreMe: name
  1662. });
  1663. };
  1664. return LiveWidget;
  1665. });
  1666. //# sourceMappingURL=LiveWidget.js.map