/**
* Licensed Materials - Property of IBM
* IBM Cognos Products: Collaboration
* (C) Copyright IBM Corp. 2017, 2019
*
* US Government Users Restricted Rights - Use, duplication or disclosure
* restricted by GSA ADP Schedule Contract with IBM Corp.
*/
define([
'jquery',
'../../lib/@waca/core-client/js/core-client/ui/core/Class',
'../../lib/@waca/baglass/js/baglass/utils/Utils',
'../../lib/@waca/baglass/js/baglass/api/Url',
'../../nls/StringResources',
'./GenerateImage'
], function ($, Class, Utils, Url, StringResources, GenerateImage) {
'use strict';
const HTML_2_CANVAS_PROXY = 'v1/collaboration/proxy/html2canvas';
const SHARE_CONTEXTUAL_ACTION_KEY = 'com.ibm.ca.collaboration.shareContextual';
const SVG_NO_IMAGE = '<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">' +
'<rect x="0" y="0" width="100%" height="100%" fill="#eaeaea"/>' +
'<g transform="translate(-64, -64)">' +
'<svg x="50%" y="50%" width="128" height="128">' +
'<g fill="#c0bfc0">' +
'<path d="M64,12L4,116h120L64,12z M64,20.004L117.074,112H10.926L64,20.004z"/>' +
'<polygon points="60,56 60,64 62,84 66,84 68,64 68,56"/>' +
'<circle cx="64" cy="92" r="4"/>' +
'</g>' +
'</svg>' +
'</g>' +
'</svg>';
const ShareableItems = Class.extend( /** @lends ShareableItems */ {
/**
* @desc Constructor for ShareableItems.
* @constructs ShareableItems
* @extends Class
* @public
* @param {object} options
* @param {object} options.logger
*/
init: function (options) {
this._url = new Url();
this._proxy = options && options.html2canvasProxy || HTML_2_CANVAS_PROXY;
this.setLogger(options);
},
setLogger: function (options) {
if (options && options.glassContext) {
this._logger = options.glassContext.getCoreSvc('.Logger');
} else {
this._logger = null;
}
},
/**
* Returns an object containing a link representing the current context.
*
* @instance
* @param {object} glassContext
* @param {object} target
* @returns {Promise} resolved as an object that contains shareUrl and embedUrl
*/
getLink: function (glassContext, target) {
return this._isValid(glassContext, target)
.then(function (isValid) {
//if target exist, means share accessed from context menu so no need to validate
if (!target && !isValid) {
return '';
}
return this._getPublicContext(glassContext, target)
.then(function (publicContext) {
return this._getActionController(glassContext, publicContext, target)
.then(function (actionController) {
return this._url.getUrlMap(actionController, glassContext, publicContext)
.then(function (urlMap) {
if (urlMap.link) {
// special case: take the link as-is
return { shareUrl: urlMap.link };
}
return {
shareUrl: this._url.getUrl({ urlMap: urlMap }, glassContext),
embedUrl: this._url.getUrl({ urlMap: urlMap, isEmbed: true }, glassContext)
};
}.bind(this));
}.bind(this));
}.bind(this));
}.bind(this));
},
_isValid: function (glassContext, target) {
return Promise.resolve(!!this._getAssetId(glassContext, target));
},
_getResourceToShare: function (glassContext, target) {
return this._getPublicContext(glassContext, target)
.then(function (publicContext) {
return this._getActionController(glassContext, publicContext, target)
.then(function (actionController) {
return this._getShareableItems(actionController, publicContext)
.then(function (items) {
const item = items[0];
return {
el: item.el && item.el.length ? item.el[0] : item.el,
label: item.label,
type: publicContext.urlMap.type
};
}.bind(this));
}.bind(this));
}.bind(this))
.catch(function (err) {
if (this._logger) {
this._logger.error(err);
}
throw err;
}.bind(this));
},
/**
* Returns an object containing an image representing the current context.
*
* @instance
* @param {object} glassContext
* @returns {Promise} with image (as text).
*/
getScreenshot: function (glassContext, target) {
return this._getResourceToShare(glassContext, target)
.then(resource => {
return this._buildItem(resource.el, resource.label, resource.type);
});
},
/**
* Check whether it's possible to screenshot the current context.
*
* @param {*} glassContext
* @param {*} target
* @returns {Promise} that resolves to true or false.
*/
canCaptureImage: function (glassContext, target) {
const isPdfReport = (resource) => {
const reportTypes = [ 'report', 'reportView', 'output' ];
if (reportTypes.indexOf(resource.type) > -1) {
return $(resource.el).find('div#idPdfViewer').is(':visible');
}
return false;
};
return this._getResourceToShare(glassContext, target)
.then(resource => {
if (isPdfReport(resource)) {
return false;
}
// Add more checks here to detect resources that can't be captured.
return true;
});
},
/**
* Gets share action controller
*
* @instance
* @param {object} glassContext
* @returns {Promise}
*/
getActionController: function (glassContext, target) {
return this._getPublicContext(glassContext, target)
.then(this._getActionController.bind(this, glassContext));
},
/**
* Notifies share action controller after share slideout shows.
* @param {object} options
* @param {object} options.glassContext
* @param {object} options.slideout
* @instance
* @returns {Promise}
*/
enterShareState: function (options) {
return this.getActionController(options.glassContext)
.then(function (actionController) {
if (actionController && actionController.enterShareState) {
return actionController.enterShareState(options);
}
}.bind(this));
},
/**
* Notifies share action controller after share slideout hides.
* @param {object} options.glassContext
* @param {object} options.slideout
* @instance
* @returns {Promise}
*/
leaveShareState: function (options) {
return this.getActionController(options.glassContext)
.then(function (actionController) {
if (actionController && actionController.leaveShareState) {
return actionController.leaveShareState(options);
}
}.bind(this));
},
_getActionController: function (glassContext, context, target) {
const type = context.urlMap.type;
const selection = target && target.plugin && target.plugin.activeObject && target.plugin.activeObject.aSelectedContext;
return Utils.getSharedResourceActionController(glassContext, type, selection);
},
_getPublicContext: function (glassContext, target) {
const isDefaultAction = !!target;
target = target || {};
return new Promise(function (resolve) {
const publicContext = {
urlMap: {
objRef: this._getResourceObjRef(glassContext, target),
type: this._getResourceType(glassContext, target)
},
mode: this._getMode(glassContext, target),
isDefaultAction: isDefaultAction,
target: target,
glassContext: glassContext // required by getUrlMap
};
const mode = this._getResourceMode(glassContext, target);
if (mode) {
publicContext.urlMap.mode = mode;
}
resolve(publicContext);
}.bind(this));
},
_getShareableItems: function (actionController, publicContext) {
return new Promise(function (resolve, reject) {
if (actionController && typeof actionController.getShareableItems === 'function') {
resolve(actionController.getShareableItems(publicContext));
} else {
reject(new Error(StringResources.get('error_not_implemented')));
}
});
},
_getGenerator: function () {
return new GenerateImage(this._getGenerateImageOptions());
},
_getGenerateImageOptions: function () {
return {
logger: this._logger,
proxy: this._proxy,
elementMap: {
'mediaWidget': SVG_NO_IMAGE,
'webpageWidget': SVG_NO_IMAGE
}
};
},
_buildItem: function (el, label, resourceType) {
const item = {};
item.label = label;
// TEMP FIX FOR DEMO: delay the image generation by 1sec to help with animation
// See 223989: [Collaboration]: screenshot panel is rolled into share panel during screenshot capture
return new Promise(function (resolve, reject) {
const generator = this._getGenerator();
window.setTimeout(function () {
generator
.generateImage(el, null, resourceType)
.then(function (image) {
item.image = image;
return item;
})
.then(resolve)
.catch(reject)
.finally(function () {
if (generator) {
generator.destroy();
}
});
}, 1000);
}.bind(this));
},
_getResourceType: function (glassContext, target) {
let type;
try {
type = target.plugin.options[0].type;
} catch (e) {
type = glassContext.currentAppView.getType();
}
return type;
},
_getResourceObjRef: function (glassContext, target) {
let objRef;
try {
objRef = target.plugin.options[0].id;
} catch (e) {
objRef = this._getAssetId(glassContext, target);
}
return objRef;
},
_getAssetId: function (glassContext, target) {
const content = glassContext.currentAppView.getContent();
//Dashboard, Exploration, Storytelling & Data Modules updates content.objRef when an asset is saved
//Report and Datasets updates content.application.storeID when it is saved
//Notebooks update content.id
const type = this._getResourceType(glassContext, target);
const id = type === 'jupyterNotebook' ? content.id : content.objRef;
return id || (content.application && content.application.storeID);
},
_getResourceMode: function (glassContext, target) {
let mode;
try {
const obj = target.activeObject.aSelectedContext[0];
if (obj.defaultScreenTip === 'story') {
mode = 'story';
}
} catch (e) {
const content = glassContext.currentAppView.getContent();
mode = content.mode;
if (mode === undefined && content.isStoryMode) {
// When we open a story then reload the page
mode = 'story';
}
}
return mode;
},
_getMode: function (glassContext, target) {
try {
const isDynamic = target.itemId === SHARE_CONTEXTUAL_ACTION_KEY;
return isDynamic ? Url.MODES.DYNAMIC : Url.MODES.CURRENT;
} catch (e) {
return Url.MODES.CURRENT;
}
}
});
return ShareableItems;
});