'use strict'; /** * Licensed Materials - Property of IBM * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2014, 2019 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. */ define(['jquery', 'doT', './StaticWidget', 'text!./NewWidget.html'], function ($, dot, StaticWidget, NewWidget) { var NewWidgetTemplate = dot.template(NewWidget); /** * The UrlWidget is a generic class for widgets which load some markup based on a URL resource */ var F12 = 123; var ESCAPE = 27; var UrlWidget = null; UrlWidget = StaticWidget.extend({ init: function init() { UrlWidget.inherited('init', this, arguments); }, initialize: function initialize() { return Promise.resolve(); }, getId: function getId() { return this.id; }, onContainerReady: function onContainerReady() { UrlWidget.inherited('onContainerReady', this, arguments); if (this.isInlineEditMode()) { this.renderInlineEditUi(); } this.updateModeMarkup(); }, /** * Virtual method * @return boolean true iff inline edit mode should be shown (i.e. not the actual widget payload rendering) */ isInlineEditMode: function isInlineEditMode() { return false; }, onConsumeMode: function onConsumeMode() { UrlWidget.inherited('onConsumeMode', this, arguments); var $container = this.$el.find('.staticContent'); $container.removeClass('authoring'); $container.addClass('consume'); }, onAuthoringMode: function onAuthoringMode() { UrlWidget.inherited('onAuthoringMode', this, arguments); var $container = this.$el.find('.staticContent'); $container.removeClass('consume'); $container.addClass('authoring'); }, /** * Virtual method * @return The caption for the inline edit textbox */ getInlineEditCaption: function getInlineEditCaption() { return null; }, /** * Virtual method * @return The text to show in consume mode when no URL has been specified */ getMissingUrlText: function getMissingUrlText() { return null; }, /** * Virtual method * @return The text for aria label */ getAriaLabelText: function getAriaLabelText() { return null; }, /** * Virtual method * @return The content class, used to hook into default images for pinning */ getContentClass: function getContentClass() { return null; }, renderInlineEditUi: function renderInlineEditUi() { if (!this.$el.find('.inlineEditInput').length) { this.$el.find('.staticContent').replaceWith(NewWidgetTemplate({ title: this.getInlineEditCaption(), missingUrlText: this.getMissingUrlText(), mediaAriaLabel: this.getAriaLabelText(), contentClass: this.getContentClass(), includeAuthoringMode: true })); var $input = this.$el.find('.staticContent input'); $input.on('click touchend', function () { // give focus to the text input $input.focus(); }.bind(this)); $(this.el).on('keydown', '.inlineEditInput', this._inlineKeyDown.bind(this)); $(this.el).on('keyup', '.inlineEditInput', this._inlineKeyUp.bind(this)); $(this.el).on('input', '.inlineEditInput', this._inlineInput.bind(this)); //Might be called multiple time so we need to cleanup $(this.el).off('blur.urlWidget'); // re-register $(this.el).on('blur.urlWidget', '.inlineEditInput', this._onBlur.bind(this)); } }, _onBlur: function _onBlur() { if (this.$el.find('.inlineEditInput').val()) { this._uiSetUrl(); } }, getLabel: function getLabel() { // To be implemented by concrete class }, _inlineKeyDown: function _inlineKeyDown(evt) { if (evt.keyCode === 13) { this._uiSetUrl(); } evt.stopPropagation(); }, _inlineKeyUp: function _inlineKeyUp(evt) { evt.stopPropagation(); if (evt.keyCode === ESCAPE || evt.keyCode === F12 && evt.shiftKey) { // esc this.$el.focus(); } }, _inlineInput: function _inlineInput(evt) { var $inlineEdit = this.$el.find('.inlineEdit'); if ($(evt.target).val() === '' && $inlineEdit.hasClass('inputError')) { $inlineEdit.removeClass('inputError'); } }, _uiSetUrl: function _uiSetUrl() { var url = this.$el.find('.inlineEditInput').val(); if (url !== this._lastSetURL) { //Suppress double executions (Enter setting the url, triggering a blur) this._lastSetURL = url; var errorData = {}; var urlSetSuccessfully = this.setUrl(url, errorData); var $inlineEdit = this.$el.find('.inlineEdit'); if (urlSetSuccessfully) { $inlineEdit.removeClass('inputError'); } else { $inlineEdit.addClass('inputError'); $inlineEdit.find('.errorMessage').html(errorData.message); } this.refreshPropertiesPane(); } }, /** * Virtual method * @param The URL entered by the user * @return true iff the URL is successfully set */ setUrl: function setUrl() /*url, error data object*/{ return false; }, /** * Applies any mode-specific changes to the markup * (e.g. authoring vs consume mode). */ updateModeMarkup: function updateModeMarkup() { if (this.isAuthoringMode) { this.onAuthoringMode(); } else { this.onConsumeMode(); } this.updateDescription(this.getLabel()); }, /** * Updates markup, derived class may do more work */ updateMarkup: function updateMarkup() { this.updateModeMarkup(); }, onChromeSelected: function onChromeSelected() { UrlWidget.inherited('onChromeSelected', this, arguments); this.onFocus(); setTimeout(function () { $(this.el).find('.inlineEditInput').focus(); }.bind(this), 10); }, registerEventGroup: function registerEventGroup() { // URL widgets are not added to event groups }, encodeURL: function encodeURL(URL) { //Run a decode/encode to block XSS attacks while not double-encoding already encoded URLs //As different parts of the URI encode different characters, we need to parse the URI to encode different parts using different functions. var URLComponents = URL.split('?'); var urlPath = encodeURI(decodeURI(URLComponents[0])), searchPath = URLComponents[1]; URLComponents = []; URLComponents.push(urlPath); if (searchPath) { var searchPathComponents = searchPath.split('&'), encodedSearchPathComponents = []; for (var i = 0; i < searchPathComponents.length; i++) { var paramComponents = searchPathComponents[i].split('='), encodedParamComponents = []; for (var j = 0; j < paramComponents.length; j++) { encodedParamComponents.push(encodeURIComponent(decodeURIComponent(paramComponents[j].replace(/\+/g, ' ')))); } encodedSearchPathComponents.push(encodedParamComponents.join('=')); } URLComponents.push(encodedSearchPathComponents.join('&')); } return URLComponents.join('?'); } }); /** * Checks whether the browser will render this URL as an embedded object * (e.g. iframe, video, audio, embed, object, etc.) */ UrlWidget._isAllowedProtocol = function (url) { if (!url) { return false; } url = url.toLowerCase(); // https:// is always allowed - i.e. both in pages served from HTTP or // HTTPS // // is always allowed as it will copy whichever protocol the // containing page is using // http:// is only allowed if the containing page is using HTTP as well if (url.indexOf('https://') === 0 || url.indexOf('//') === 0) { return true; } return false; }; /** * Checks whether the browser will render this URL as an embedded object * (e.g. iframe, video, audio, embed, object, etc.) */ UrlWidget.isAllowedProtocol = function (url) { return UrlWidget._isAllowedProtocol(url); }; UrlWidget.getDefaultConsumeMarkup = function (missingText) { return NewWidgetTemplate({ missingUrlText: missingText }); }; return UrlWidget; }); //# sourceMappingURL=UrlWidget.js.map