WebpageWidget.js 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. *
  5. * IBM Cognos Products: Dashboard
  6. *
  7. * (C) Copyright IBM Corp. 2014, 2019
  8. *
  9. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  10. */
  11. define(['underscore', './UrlWidget', '../../../app/nls/StringResources', '../../../lib/@waca/core-client/js/core-client/utils/Utils', 'jquery', 'doT', 'text!./Webpage.html', 'react-dom', '../../../lib/@waca/dashboard-common/dist/ui/CenterLoadingSpinner', '../../../lib/@waca/core-client/js/core-client/utils/BrowserUtils'], function (_, UrlWidget, resources, Utils, $, dot, Template, ReactDOM, CenterLoadingSpinner, BrowserUtils) {
  12. var renderTemplate = dot.template(Template);
  13. var maxHiddenWebpageWidgets = BrowserUtils.isIE() ? 4 : 8;
  14. var hiddenWebpageWidgets = [];
  15. var pruneTimer = void 0;
  16. var removeWidgetFromHiddenList = function removeWidgetFromHiddenList(id) {
  17. hiddenWebpageWidgets = hiddenWebpageWidgets.filter(function (obj) {
  18. return obj.id !== id;
  19. });
  20. };
  21. /**
  22. * This function will unload iframes of webpage widgets that are hidden
  23. * if we have too many of them.
  24. */
  25. var pruneHiddenWebpageWidgets = function pruneHiddenWebpageWidgets() {
  26. if (!pruneTimer) {
  27. pruneTimer = setTimeout(function () {
  28. var widgetsToPrune = hiddenWebpageWidgets.length - maxHiddenWebpageWidgets;
  29. if (widgetsToPrune > 0) {
  30. console.info('Unloading ' + widgetsToPrune + ' webpage widgets');
  31. for (var i = 0; i < widgetsToPrune; i++) {
  32. var hiddenWidget = hiddenWebpageWidgets.shift();
  33. hiddenWidget.widget.destroyIframe();
  34. }
  35. }
  36. pruneTimer = undefined;
  37. }, 50);
  38. }
  39. };
  40. var WebpageWidget = UrlWidget.extend({
  41. init: function init(options) {
  42. WebpageWidget.inherited('init', this, arguments);
  43. this._firstRendering = true;
  44. this.api = {
  45. getId: this.getId.bind(this),
  46. setUrl: this.setUrl.bind(this)
  47. };
  48. this.model = this.model || {};
  49. var initialConfigJson = options ? options.initialConfigJSON : null;
  50. if (initialConfigJson) {
  51. this.model.url = initialConfigJson.url;
  52. this.model.title = initialConfigJson.title ? initialConfigJson.title.translationTable : null;
  53. }
  54. this.propertyCallbacks = this.dashboardApi.getFeature('PropertyCallbacks');
  55. },
  56. destroy: function destroy() {
  57. this.destroyIframe();
  58. this._removeLoadingIndicator();
  59. WebpageWidget.inherited('destroy', this, arguments);
  60. },
  61. destroyIframe: function destroyIframe() {
  62. removeWidgetFromHiddenList(this.model.id);
  63. var $iframe = this._getIFrameElement();
  64. if ($iframe) {
  65. $iframe.off();
  66. }
  67. this.$el.empty();
  68. },
  69. onShow: function onShow() {
  70. WebpageWidget.inherited('onShow', this, arguments);
  71. removeWidgetFromHiddenList(this.model.id);
  72. this.render();
  73. },
  74. onHide: function onHide() {
  75. hiddenWebpageWidgets.push({
  76. id: this.model.id,
  77. widget: this
  78. });
  79. pruneHiddenWebpageWidgets();
  80. WebpageWidget.inherited('onHide', this, arguments);
  81. },
  82. onContainerReady: function onContainerReady() /*o*/{
  83. WebpageWidget.inherited('onContainerReady', this, arguments);
  84. if (this.model) {
  85. this.model.on('change:url', this._onUrlChange, this);
  86. this.model.on('change:title', this._onTitleChange, this);
  87. this.addWhiteListAttrs('url', 'title');
  88. }
  89. this.eventRouter.trigger('webpage:ready', this.api);
  90. },
  91. render: function render() {
  92. var messageInfo = this.propertyCallbacks.validateWebpageLink(null, this.model.url);
  93. if (messageInfo.isValid === false) {
  94. if (this._firstRendering === true) {
  95. this._firstRendering = false;
  96. if (this.isAuthoringMode) {
  97. var message = messageInfo.message;
  98. this.$el.append(UrlWidget.getDefaultConsumeMarkup(message));
  99. this.onAuthoringMode();
  100. } else {
  101. var _message = !this.model.url ? resources.get('webpageMissingUrl') : resources.get('webpageUrlValidationError');
  102. this.$el.append(UrlWidget.getDefaultConsumeMarkup(_message));
  103. this.onConsumeMode();
  104. }
  105. }
  106. } else {
  107. if (this.$el.parent().is(':visible') && this._getIFrameElement().length === 0) {
  108. this._updateHtmlContent(this._getUpdatedHtmlContent(), true);
  109. this._newIframe();
  110. if (this.isAuthoringMode) {
  111. this.onAuthoringMode();
  112. } else {
  113. this.onConsumeMode();
  114. }
  115. }
  116. }
  117. },
  118. setUrl: function setUrl(url) {
  119. var errorData = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  120. if (url) {
  121. url = url.trim();
  122. }
  123. var ret = this.propertyCallbacks.validateWebpageLink(null, url);
  124. errorData.message = ret.message;
  125. if (ret.isValid) {
  126. var transactionId = _.uniqueId('_addWebpage_');
  127. this.set({
  128. url: url
  129. }, {
  130. sender: this,
  131. payloadData: {
  132. undoRedoTransactionId: transactionId
  133. }
  134. });
  135. return true;
  136. }
  137. return false;
  138. },
  139. _getTitle: function _getTitle() {
  140. return this.model && this.model.get ? this.model.get('title') : null;
  141. },
  142. _getHtmlRender: function _getHtmlRender(props) {
  143. props = props || {};
  144. var title = this._getTitle();
  145. if (title) {
  146. props.title = title;
  147. }
  148. props = _.extend({}, { url: this.model.url }, props);
  149. if (Utils.isIpad()) {
  150. props.isIpad = true;
  151. }
  152. return renderTemplate(props);
  153. },
  154. updateMarkup: function updateMarkup() {
  155. WebpageWidget.inherited('updateMarkup', this, arguments);
  156. this._newIframe();
  157. },
  158. _newIframe: function _newIframe() {
  159. this._getIFrameElement().on('load', this._onIframeLoaded.bind(this));
  160. },
  161. onEnterContainer: function onEnterContainer() {
  162. var mediaInput = this.$el.find('.inlineEditInput');
  163. mediaInput.focus();
  164. },
  165. _onUrlChange: function _onUrlChange() {
  166. var updatedHtml = this._getUpdatedHtmlContent();
  167. if (this.model.url) {
  168. this._updateHtmlContent(updatedHtml, false);
  169. this._addLoadingIndicator();
  170. } else {
  171. this.renderInlineEditUi();
  172. }
  173. this.updateMarkup();
  174. this.updateModelContent(updatedHtml);
  175. // This update is necessary to cover undo/redo cases
  176. this._lastSetURL = this.model.url || null;
  177. },
  178. _onTitleChange: function _onTitleChange(options) {
  179. var $iframe = this._getIFrameElement();
  180. if ($iframe) {
  181. $iframe.attr('aria-label', options.value);
  182. }
  183. },
  184. _getUpdatedHtmlContent: function _getUpdatedHtmlContent() {
  185. if (this.propertyCallbacks.validateWebpageLink(null, this.model.url).isValid) {
  186. this.model.url = this.encodeURL(this.model.url);
  187. return this._getHtmlRender();
  188. } else {
  189. return this._getMissingUrlMarkup();
  190. }
  191. },
  192. _updateHtmlContent: function _updateHtmlContent(updatedHtml, bFirstTime) {
  193. this._removeLoadingIndicator();
  194. if (bFirstTime) {
  195. this.$el.append(updatedHtml);
  196. } else {
  197. this._getStaticWidgetElement().replaceWith(updatedHtml);
  198. }
  199. },
  200. onStartMove: function onStartMove(o) {
  201. var _this = this;
  202. if (this.model.url && o.selectedNodes) {
  203. var applyToThisWidget = _.find(o.selectedNodes, function (n) {
  204. return _this.$el.is($(n));
  205. });
  206. if (applyToThisWidget) {
  207. this._showLoadingIndicator();
  208. }
  209. }
  210. },
  211. _onIframeLoaded: function _onIframeLoaded() {
  212. this._hideLoadingIndicator();
  213. },
  214. isInlineEditMode: function isInlineEditMode() {
  215. return !this.model.url;
  216. },
  217. getInlineEditCaption: function getInlineEditCaption() {
  218. return resources.get('webpagePasteLink');
  219. },
  220. getMissingUrlText: function getMissingUrlText() {
  221. return resources.get('webpageMissingUrl');
  222. },
  223. getLabel: function getLabel() {
  224. return this._getTitle() || resources.get('webpageWidgetTitle');
  225. },
  226. getContentClass: function getContentClass() {
  227. return 'webpageWidgetContent';
  228. },
  229. getHtmlRender: function getHtmlRender() {
  230. return this._getHtmlRender();
  231. },
  232. _getStaticWidgetElement: function _getStaticWidgetElement() {
  233. return this.$el.find('.staticContent');
  234. },
  235. _getIFrameElement: function _getIFrameElement() {
  236. return this.$el.find('iframe');
  237. },
  238. _getMissingUrlMarkup: function _getMissingUrlMarkup() {
  239. return UrlWidget.getDefaultConsumeMarkup(this.getMissingUrlText());
  240. },
  241. _addLoadingIndicator: function _addLoadingIndicator() {
  242. if (!this._$loadingIndicatorBlocker) {
  243. this._$loadingIndicatorBlocker = $('<div></div>');
  244. }
  245. this._getStaticWidgetElement().prepend(this._$loadingIndicatorBlocker);
  246. ReactDOM.render(CenterLoadingSpinner({
  247. size: 'normal',
  248. 'variant': 'circle'
  249. }), this._$loadingIndicatorBlocker[0]);
  250. },
  251. _hideLoadingIndicator: function _hideLoadingIndicator() {
  252. if (this._$loadingIndicatorBlocker) {
  253. this._$loadingIndicatorBlocker[0].style.setProperty('display', 'none');
  254. }
  255. },
  256. _showLoadingIndicator: function _showLoadingIndicator() {
  257. if (this._$loadingIndicatorBlocker) {
  258. this._$loadingIndicatorBlocker[0].style.setProperty('display', 'flex');
  259. }
  260. },
  261. _removeLoadingIndicator: function _removeLoadingIndicator() {
  262. if (this._$loadingIndicatorBlocker) {
  263. ReactDOM.unmountComponentAtNode(this._$loadingIndicatorBlocker[0]);
  264. this._$loadingIndicatorBlocker.remove();
  265. this._$loadingIndicatorBlocker = null;
  266. }
  267. }
  268. });
  269. WebpageWidget.getDefaultSpec = function () {
  270. return Promise.resolve({
  271. model: {
  272. type: 'webpage',
  273. avatarHtml: UrlWidget.getDefaultConsumeMarkup(),
  274. title: ''
  275. },
  276. layoutProperties: {
  277. style: {
  278. width: '480px',
  279. height: '360px'
  280. }
  281. }
  282. });
  283. };
  284. return WebpageWidget;
  285. });
  286. //# sourceMappingURL=WebpageWidget.js.map