sense-navigation
Version:
Sense Sheet Navigation + Actions visualization extension for Qlik Sense.
401 lines (362 loc) • 13.6 kB
JavaScript
/* global define,window */
define(
[
'./lib/external/lodash/lodash.min',
'qlik',
'angular',
'./lib/external/sense-extension-utils/index',
'./properties',
'text!./template.ng.html',
'css!./lib/css/main.min.css',
'css!./lib/external/font-awesome/css/font-awesome.min.css'
],
function (__, qlik, angular, extUtils, props, ngTemplate) { // eslint-disable-line max-params
'use strict';
const DEBUG = true;
// Helper function to split numbers.
function splitToStringNum(str, sep) {
let a = str.split(sep);
for (let i = 0; i < a.length; i++) {
if (!isNaN(a[i])) {
a[i] = Number(a[i]);
}
}
return a;
}
function fixUrl(url) {
if (url.startsWith('http://') || url.startsWith('https://') || (url.startsWith('mailto://'))) {
return url;
}
return 'http://' + url;
}
/**
* Check if running in an iframe.
*
* Catching the error is necessary as browsers could block access to window.top due to the same-origin-policy.
* (see same origin policy)
*
* @link https://stackoverflow.com/questions/326069/how-to-identify-if-a-webpage-is-being-loaded-inside-an-iframe-or-directly-into-t
*
* @returns {boolean}
*/
function inIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
return {
definition: props,
support: {
export: false,
exportData: false,
snapshot: false
},
initialProperties: {},
snapshot: {canTakeSnapshot: false},
template: ngTemplate,
controller: [
'$scope', '$element', function ($scope, $element) { // eslint-disable-line no-unused-vars
// Note: getPreferredSize is an undocumented method and not supported right now.
// (Therefore it is not used in this extension)
// this.getPreferredSize = function () {
// var $btn = this.$element.find('.btn');
// var size = {
// w: $btn.width(),
// h: $btn.height() + 7
// };
// var df = Deferred();
// df.resolve( size );
// return df.promise;
// };
$scope.doNavigate = function () {
if (DEBUG) {
window.console.group('DEBUG');
window.console.log('navigationAction', $scope.layout.props.navigationAction);
window.console.groupEnd();
}
switch ($scope.layout.props.navigationAction) {
case 'gotoSheet':
$scope.gotoSheet($scope.layout.props.selectedSheet);
break;
case 'gotoSheetById':
$scope.gotoSheet($scope.layout.props.sheetId);
break;
case 'gotoStory':
$scope.gotoStory($scope.layout.props.selectedStory);
break;
case 'firstSheet':
$scope.firstSheet();
break;
case 'nextSheet':
$scope.nextSheet();
break;
case 'openWebsite': // eslint-disable-line no-case-declarations
let url = $scope.layout.props.websiteUrl;
const same = $scope.layout.props.sameWindow;
if (!__.isEmpty(url)) {
const isIframe = inIframe();
let target = '';
if (same && isIframe) {
target = '_parent';
} else if (same) {
target = '_self';
}
window.open(fixUrl(url), target);
}
break;
case 'prevSheet':
$scope.prevSheet();
break;
case 'lastSheet':
$scope.lastSheet();
break;
// eslint-disable capitalized-comments
// case "openApp":
// console.log('Open', $scope.layout.props.selectedApp);
// qlik.openApp( $scope.layout.props.selectedApp );
// break;
// eslint-enable capitalized-comments
case 'switchToEdit': // eslint-disable-line no-case-declarations
const result = qlik.navigation.setMode(qlik.navigation.EDIT);
if (!result.success) {
window.console.error(result.errorMsg);
}
break;
default:
break;
}
};
$scope.isEditMode = function () {
return qlik.navigation.getMode() === qlik.navigation.EDIT;
};
$scope.doAction = function () { // eslint-disable-line complexity
const app = qlik.currApp(); // ARGHH: Why is this still sync instead of async
let fld;
let val;
let actionType;
let softLock;
let bookmark;
let variable;
if ($scope.layout.props && $scope.layout.props.actionItems) {
for (let i = 0; i < $scope.layout.props.actionItems.length; i++) {
actionType = $scope.layout.props.actionItems[i].actionType;
fld = __.isEmpty($scope.layout.props.actionItems[i].selectedField) ? $scope.layout.props.actionItems[i].field : $scope.layout.props.actionItems[i].selectedField;
val = $scope.layout.props.actionItems[i].value;
softLock = $scope.layout.props.actionItems[i].softLock;
bookmark = $scope.layout.props.actionItems[i].selectedBookmark;
variable = $scope.layout.props.actionItems[i].variable;
if (DEBUG) {
window.console.group('DEBUG');
window.console.log('actionItems', $scope.layout.props.actionItems);
window.console.log('actionType: ', actionType);
window.console.log('fld: ', fld);
window.console.groupEnd();
}
switch (actionType) {
case 'applyBookmark':
if (!__.isEmpty(bookmark)) {
app.bookmark.apply(bookmark);
}
break;
case 'back':
app.back().catch(function (err) {
window.console.error(err);
});
break;
case 'clearAll':
app.clearAll();
break;
case 'clearField':
if (!__.isEmpty(fld)) {
app.field(fld).clear();
}
break;
case 'clearOther':
app.field(fld).clearOther(softLock);
break;
case 'forward':
app.forward()
.catch(function (err) {
window.console.error(err);
});
break;
case 'lockAll':
app.lockAll();
break;
case 'lockField':
if (!__.isEmpty(fld)) {
app.field(fld).lock();
}
break;
case 'replaceBookmark':
if (!__.isEmpty(bookmark)) {
app.bookmark.apply(bookmark);
}
break;
case 'selectAll':
if (!__.isEmpty(fld)) {
app.field(fld).selectAll(softLock);
}
break;
case 'selectAlternative':
if (!__.isEmpty(fld)) {
app.field(fld).selectAlternative(softLock);
}
break;
case 'selectAndLockField':
if (!__.isEmpty(fld) && (!__.isEmpty(val))) {
app.field(fld).selectMatch(val, true);
app.field(fld).lock();
}
break;
case 'selectExcluded':
if (!__.isEmpty(fld)) {
app.field(fld).selectExcluded(softLock);
}
break;
case 'selectField':
if (!__.isEmpty(fld) && (!__.isEmpty(val))) {
app.field(fld).selectMatch(val, false);
}
break;
case 'selectValues':
if (!__.isEmpty(fld) && (!__.isEmpty(val))) {
let vals = splitToStringNum(val, ';');
app.field(fld).selectValues(vals, false);
}
break;
case 'selectPossible':
if (!__.isEmpty(fld)) {
app.field(fld).selectPossible(softLock);
}
break;
case 'setVariable':
if (!__.isEmpty(variable)) {
$scope.setVariableContent(variable, val);
}
break;
case 'toggleSelect':
if (!__.isEmpty(fld) && (!__.isEmpty(val))) {
app.field(fld).toggleSelect(val, softLock);
}
break;
case 'unlockAll':
app.unlockAll();
break;
case 'unlockAllAndClearAll':
$scope.unlockAllAndClearAll();
break;
case 'unlockField':
if (!__.isEmpty(fld)) {
app.field(fld).unlock();
}
break;
default:
break;
}
}
}
};
// Helper function to be used in the template, defining the button class.
$scope.getButtonClasses = function (props) {
let classes = [];
// Main style
if (props.buttonStyle === 'by-expression') {
classes.push('btn-' + props.buttonStyleExpression);
}
if (props.buttonStyle) {
classes.push('btn-' + props.buttonStyle);
} else {
classes.push('btn-default');
}
// Width
if (props.fullWidth) {
classes.push('full-width');
} else {
classes.push('auto-width');
}
// Multiline
if (props.isButtonMultiLine) {
classes.push('btn-multiline');
}
return classes.join(' ');
};
$scope.getButtonCustomCss = function (props) {
if (props.buttonStyle === 'by-css') {
window.console.log(props.buttonStyleCss);
return props.buttonStyleCss;
}
return '';
};
$scope.go = function () {
if (!$scope.isEditMode()) {
$scope.doAction();
$scope.doNavigate();
}
};
$scope.firstSheet = function () {
if ($scope.checkQlikNavigation()) {
extUtils.getFirstSheet().then(function (result) {
qlik.navigation.gotoSheet(result.id);
});
}
};
$scope.nextSheet = function () {
if ($scope.checkQlikNavigation()) {
qlik.navigation.nextSheet();
}
};
$scope.prevSheet = function () {
if ($scope.checkQlikNavigation()) {
qlik.navigation.prevSheet();
}
};
$scope.lastSheet = function () {
if ($scope.checkQlikNavigation()) {
extUtils.getLastSheet().then(function (result) {
qlik.navigation.gotoSheet(result.id);
});
}
};
$scope.gotoSheet = function (sheetId) {
if ($scope.checkQlikNavigation() && !__.isEmpty(sheetId)) {
let r = qlik.navigation.gotoSheet(sheetId);
if (!r.success) {
window.console.error(r.errorMsg);
}
}
};
$scope.gotoStory = function (storyId) {
if ($scope.checkQlikNavigation() && !__.isEmpty(storyId)) {
qlik.navigation.gotoStory(storyId);
}
};
// Todo: Use method from sense-extension-utils/variable-utils.js
$scope.setVariableContent = function (variableName, variableValue) {
const app = qlik.currApp();
app.variable.setContent(variableName, variableValue)
.then(function (/* reply */) {
angular.noop();
})
.catch(function (err) {
window.console.error(err);
});
};
$scope.checkQlikNavigation = function () {
if (!qlik.navigation) {
window.console.error('Capability API qlik.navigation is not supported in the current version of Qlik Sense.');
return false;
}
return true;
};
$scope.unlockAllAndClearAll = function () {
const app = qlik.currApp();
app.unlockAll();
app.clearAll();
};
}
]
};
});