123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- 'use strict';
- /*
- *+------------------------------------------------------------------------+
- *| Licensed Materials - Property of IBM
- *| IBM Cognos Products: Dashboard
- *| (C) Copyright IBM Corp. 2015, 2020
- *|
- *| US Government Users Restricted Rights - Use, duplication or disclosure
- *| restricted by GSA ADP Schedule Contract with IBM Corp.
- *+------------------------------------------------------------------------+
- */
- define(['jquery', './StaticWidget', '../../../lib/@waca/core-client/js/core-client/utils/Utils', '../../../lib/@waca/core-client/js/core-client/utils/BidiUtil', '../../../dashboard/util/TextEditor', '../../../app/nls/StringResources', 'underscore', '../../../app/util/EventChainLocal', './TextPlaceholderView', './IconPlaceholderView', '../../../lib/@waca/dashboard-common/dist/api/PropertiesProviderAPI', '../../../lib/@waca/dashboard-common/dist/core/APIFactory'], function ($, StaticWidget, Utils, BidiUtil, TextEditor, resources, _, EventChainLocal, TextPlaceholderView, IconPlaceholderView, PropertiesProviderAPI, APIFactory) {
- var getDefaultSpec = function getDefaultSpec(name, options) {
- if (!options) {
- options = {
- style: 'responsive',
- placeholder: {
- showIcon: false,
- text: resources.get('textPlaceHolder')
- }
- };
- }
- var widgetContent = '<div class="staticContent">' + TextEditor.getTextHTML(options) + '</div>';
- var spec = {
- model: {
- type: 'text',
- name: resources.get('textPlaceHolder'),
- content: widgetContent
- },
- layoutProperties: {
- style: {
- width: '200px',
- height: '50px'
- }
- }
- };
- if (options.style === 'responsive') {
- spec.model.isResponsive = true;
- }
- if (options.placeholder) {
- spec.model.placeholder = options.placeholder;
- // build avatarHtml from placeholder text so avatar won't be blank
- if (!options.text) {
- spec.model.avatarHtml = '<div class="staticContent">' + TextEditor.getTextHTML(_.extend({ text: options.placeholder.text }, options)) + '</div>';
- }
- }
- return Promise.resolve(spec);
- };
- /**
- * The text widget object is a widget for displaying text strings on the canvas. In authoring mode, the text can be edited inline,
- * but in consumption mode becomes a read-only element of the canvas.
- */
- var TextWidget = null;
- TextWidget = StaticWidget.extend({
- _didCleanseContent: false,
- init: function init(options) {
- TextWidget.inherited('init', this, arguments);
- if (options && options.initialConfigJSON && options.initialConfigJSON.placeholder) {
- var placeholder = options.initialConfigJSON.placeholder;
- var PlaceholderView = placeholder.iconType ? IconPlaceholderView : TextPlaceholderView;
- this.placeholder = new PlaceholderView(_.extend({ el: this.$el }, placeholder));
- this.$el.on('primaryaction', this.onSelect.bind(this));
- }
- },
- destroy: function destroy() {
- this.textEditor.toggleEditing(false);
- this.model.off('change:content', this.textEditor.applyContent, this.textEditor);
- this.content.off('change:property', this._onPropertyChange, this);
- this.$el.off('primaryaction', this.onSelect.bind(this));
- this.textEditor.selectionUnbindNodeEvents();
- this.textEditor.destroy();
- TextWidget.inherited('destroy', this, arguments);
- },
- _onPropertyChange: function _onPropertyChange() {
- this.updateModelContent();
- },
- onContainerReady: function onContainerReady() {
- var _this = this;
- TextWidget.inherited('onContainerReady', this, arguments);
- this.$el.append(this._cleanContent(this.model.get('content')));
- var colorDefault = {
- 'color': 'responsiveColor'
- };
- var defaults = this.initialConfigJSON && this.initialConfigJSON.defaults ? _.extend(this.initialConfigJSON.defaults, colorDefault) : colorDefault;
- this.textEditor = new TextEditor({
- 'node': this.getWidgetStyleNode(),
- 'container': this.$widgetContainer,
- 'widget': this,
- 'toolbarNode': this.$widgetContainer,
- 'isResponsive': this.model.isResponsive,
- 'supportsLists': true,
- 'initialState': defaults
- });
- BidiUtil.initElementForBidi(this.getWidgetStyleNode().get(0));
- this.model.on('change:content', this.textEditor.applyContent, this.textEditor);
- this.content.on('change:property', this._onPropertyChange, this);
- this.addColorProperties(['content']);
- // The model is ready; if we cleansed the content on load, do a forced/silent save.
- if (this._didCleanseContent) {
- this._didCleanseContent = false;
- this._silentUpdate();
- }
- this.textEditor.initContentEditable('p > span');
- this.textEditor.selectionBindNodeEvents();
- if (this.placeholder) {
- this.placeholder.render();
- this.updatePlaceholder();
- }
- this._updateAriaInfo();
- //Setting the model to the current HTML so we properly deal with upgrading from old dashboards
- this.model.set({ content: this.getHtmlRender() }, { silent: true, payloadData: { skipUndoRedo: true } });
- // todo lifecycle_cleanup -- this should be turned into a feature.
- var propertiesAPI = APIFactory.createAPI(this, [PropertiesProviderAPI]);
- this.content.getFeature('Properties').registerProvider(propertiesAPI);
- // TODO: replace this with a better fix after release
- // if custom fonts are in the save TextWidget, they will not resize properly on first load
- // need the setTimeout to allow the fonts to download
- // 900ms was the lowest timing that still worked on a slow internet connection
- return new Promise(function (resolve) {
- setTimeout(function () {
- _this.fillText();
- resolve();
- }, 900);
- });
- },
- _updateAriaInfo: function _updateAriaInfo() {
- this.updateDescription(this.getLabel());
- var editableNodeId = this.model.id + 'Editable';
- var editableNode = this.$el.find('.note-editable').get(0);
- if (editableNode) {
- editableNode.setAttribute('id', editableNodeId);
- this.$widgetContainer.attr('aria-labelledby', function () {
- return $(this).attr('aria-labelledby') + ' ' + editableNodeId;
- });
- }
- },
- onShow: function onShow() {
- this.fillText();
- },
- _shouldShowPlaceholder: function _shouldShowPlaceholder() {
- if (this.initialConfigJSON.placeholder.iconType === 'list') {
- return !this.textEditor.hasText();
- } else {
- return !this.textEditor.hasContent();
- }
- },
- updatePlaceholder: function updatePlaceholder() {
- if (this._shouldShowPlaceholder()) {
- this._showPlaceholder();
- } else {
- this._hidePlaceholder();
- }
- },
- _showPlaceholder: function _showPlaceholder() {
- this.$widgetContainer.addClass('placeholder');
- this.textEditor.hide();
- this.placeholder.show();
- },
- _hidePlaceholder: function _hidePlaceholder() {
- var immediateEditing = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
- this.$widgetContainer.removeClass('placeholder');
- this.placeholder.hide();
- this.textEditor.show(immediateEditing);
- },
- onSelect: function onSelect(event) {
- var isDescendant = function isDescendant(node, className) {
- while (node) {
- if (node.classList && _.contains(node.classList, className)) {
- return true;
- }
- node = node.parentNode;
- }
- return false;
- };
- if (this.isAuthoringMode) {
- if (this.placeholder && this.placeholder.isShowing()) {
- // Prevent widget ODT from displaying/updating
- new EventChainLocal(event).setProperty('preventDefaultContextBar', true);
- this._hidePlaceholder(true);
- } else if (this.placeholder && this._shouldShowPlaceholder() && isDescendant(event.target, 'moveHandle')) {
- this._showPlaceholder();
- } else if (this.textEditor.isEditing()) {
- new EventChainLocal(event).setProperty('preventDefaultContextBar', true);
- }
- }
- },
- onChromeSelected: function onChromeSelected(isGroupSelect) {
- TextWidget.inherited('onChromeSelected', this, arguments);
- if (this.isAuthoringMode && !isGroupSelect) {
- this.textEditor.attachEnterEditEvents();
- }
- },
- onChromeDeselected: function onChromeDeselected() {
- TextWidget.inherited('onChromeDeselected', this, arguments);
- if (this.placeholder && !this.placeholder.isShowing() && this._shouldShowPlaceholder()) {
- this._showPlaceholder();
- }
- if (this.isAuthoringMode) {
- this.textEditor.toggleEditing(false);
- this.textEditor.detachEnterEditEvents();
- }
- },
- onAuthoringMode: function onAuthoringMode() {
- TextWidget.inherited('onAuthoringMode', this, arguments);
- },
- onConsumeMode: function onConsumeMode() {
- TextWidget.inherited('onConsumeMode', this, arguments);
- this.textEditor.toggleEditing(false);
- },
- onEnterContainer: function onEnterContainer() {
- this.textEditor.toggleEditing(true);
- if (this.placeholder && this.placeholder.isShowing()) {
- this._hidePlaceholder(true);
- }
- },
- _cleanContent: function _cleanContent(content) {
- var cleansedContent = TextEditor.cleanContentElements(content);
- if (!cleansedContent || !this.isValidHtmlContent(cleansedContent)) {
- // TODO: Re-evaluate this, as our placeholders are now externalized. What is truly needed here?
- // Provide a fallback to default spec when content is unusable
- var textOptions = {
- 'text': resources.get('textPlaceHolder'),
- 'style': 'responsive'
- };
- cleansedContent = '<div class="staticContent">' + TextEditor.getTextHTML(textOptions) + '</div>';
- }
- if (cleansedContent !== content) {
- this._didCleanseContent = true;
- }
- return cleansedContent;
- },
- _silentUpdate: function _silentUpdate() {
- this.onPropertyUpdate({
- transactionId: null // Want a silent save (no undo)
- });
- },
- /**
- * Implementation of {@link PropertiesProviderAPI#getPropertyLayoutList}
- * TODO: need to add unit test
- */
- getPropertyLayoutList: function getPropertyLayoutList() {
- var propertyLayoutList = TextWidget.inherited('getPropertyLayoutList', this, arguments) || [];
- var textEditorPropertyLayoutList = this.textEditor.getPropertyLayoutList();
- if (textEditorPropertyLayoutList) {
- propertyLayoutList.push.apply(propertyLayoutList, textEditorPropertyLayoutList);
- }
- return propertyLayoutList;
- },
- getPropertyList: function getPropertyList() {
- var propertyList = [];
- var textEditorPropertyList = this.textEditor.getPropertyList();
- if (textEditorPropertyList) {
- propertyList.push.apply(propertyList, textEditorPropertyList);
- }
- return propertyList;
- },
- selectAll: function selectAll() {
- document.getSelection().selectAllChildren(this.getWidgetStyleNode().get(0));
- },
- /**
- * Based on properties, generate the HTML for the static content
- */
- getHtmlRender: function getHtmlRender() {
- return this.getWidgetStyleNode().get(0) ? this.getWidgetStyleNode().get(0).outerHTML : '';
- },
- fillText: function fillText() {
- if (!this.placeholder || !this.placeholder.isShowing()) {
- this.textEditor.fillText();
- } else if (this.placeholder.fillText && this.placeholder.isShowing()) {
- this.placeholder.fillText();
- }
- },
- /*
- * Helpers.
- */
- resize: function resize() {
- this.fillText();
- if (this.placeholder && this.placeholder.resize) {
- this.placeholder.resize();
- }
- },
- registerEventGroup: function registerEventGroup() {
- // Text widgets are not added to event groups
- },
- /**
- * Updates the model version of the markup of this widget
- * @param updatedHtml An optional parameter of the markup.
- * This is to avoid regenerating the markup if it is already known.
- */
- updateModelContent: function updateModelContent(updatedHtml, transactionId) {
- // regenerate the HTML if not passed
- if (!updatedHtml) {
- updatedHtml = TextEditor.cleanContentElements(this.getHtmlRender());
- } else {
- updatedHtml = TextEditor.cleanContentElements(updatedHtml);
- }
- // update the model to persist the changes. Use transactionId to combine undos
- var data = {};
- if (transactionId) {
- _.extend(data, {
- payloadData: {
- undoRedoTransactionId: transactionId
- }
- });
- } else {
- _.extend(data, {
- silent: true
- });
- }
- var widgetTitle = this.textEditor.getTitleFromHtml(this.getHtmlRender());
- this.set({
- content: updatedHtml,
- name: widgetTitle,
- isResponsive: this.textEditor.isResponsive
- }, data);
- },
- getLabel: function getLabel() {
- return resources.get('textWidgetTitle');
- }
- });
- TextWidget.getDefaultSpec = getDefaultSpec;
- return TextWidget;
- });
- //# sourceMappingURL=TextWidget.js.map
|