init commit
This commit is contained in:
362
ui/src/helpers/player-helper.js
Normal file
362
ui/src/helpers/player-helper.js
Normal file
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - https://xibosignage.com
|
||||
*
|
||||
* This file is part of Xibo.
|
||||
*
|
||||
* Xibo is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* Xibo is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const PlayerHelper = function() {
|
||||
// Check the query params to see if we're in editor mode
|
||||
const self = this;
|
||||
|
||||
this.getPinnedSlots = function(dataSlots) {
|
||||
return Object.keys(dataSlots)
|
||||
.reduce(function(a, b) {
|
||||
const dataSlot = dataSlots[b];
|
||||
if (dataSlot.hasPinnedSlot) return [...a, dataSlot.slot];
|
||||
return a;
|
||||
}, []);
|
||||
};
|
||||
|
||||
this.getPinnedItems = function(dataSlotItems) {
|
||||
if (Object.values(dataSlotItems).length === 0) {
|
||||
return dataSlotItems;
|
||||
}
|
||||
|
||||
return Object.keys(dataSlotItems).reduce(function(items, itemKey) {
|
||||
const item = dataSlotItems[itemKey];
|
||||
|
||||
if (item.pinSlot) {
|
||||
items[itemKey] = item;
|
||||
}
|
||||
|
||||
return items;
|
||||
}, {});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get items by Key
|
||||
* @param {Object} items
|
||||
* @param {String} itemsKey
|
||||
* @param {Boolean} isStandalone
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
this.getItemsByKey = (items, itemsKey, isStandalone) => {
|
||||
if (isStandalone && items.hasOwnProperty(itemsKey) &&
|
||||
Object.keys(items[itemsKey]).length > 0
|
||||
) {
|
||||
return Object.keys(items[itemsKey]).reduce(function(a, itemKey) {
|
||||
return [...a, items[itemsKey][itemKey]];
|
||||
}, []);
|
||||
}
|
||||
|
||||
if (items.hasOwnProperty(itemsKey)) {
|
||||
return items[itemsKey];
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets minimum and maximum slot
|
||||
* If minSlot is zero, it means it's not a data slot
|
||||
* @param {Array} collection
|
||||
* @return {{minSlot: (number|number), maxSlot: (number|number)}}
|
||||
*/
|
||||
this.getMinAndMaxSlot = function(collection) {
|
||||
const minValue = 1;
|
||||
const getSlots = (items) => items.map(function(elem) {
|
||||
return elem?.slot + 1 || 0;
|
||||
});
|
||||
const minSlot = collection === null ?
|
||||
minValue :
|
||||
Math.min(...getSlots(collection));
|
||||
const maxSlot = collection === null ?
|
||||
minValue :
|
||||
Math.max(...getSlots(collection));
|
||||
|
||||
return {
|
||||
minSlot,
|
||||
maxSlot,
|
||||
};
|
||||
};
|
||||
|
||||
this.getMaxMinSlot = (objectsArray, itemsKey, isStandalone) => {
|
||||
const minValue = 1;
|
||||
const groupItems = objectsArray?.length > 0 ?
|
||||
objectsArray.reduce(
|
||||
(a, b) => {
|
||||
return [...a, ...self.getItemsByKey(b, itemsKey, isStandalone)];
|
||||
}, []) : null;
|
||||
const getSlots = (items) => items.map(function(elem) {
|
||||
return elem?.slot || 0;
|
||||
});
|
||||
const minSlot = groupItems === null ?
|
||||
minValue :
|
||||
Math.min(...getSlots(groupItems)) + 1;
|
||||
const maxSlot = groupItems === null ?
|
||||
minValue :
|
||||
Math.max(...getSlots(groupItems)) + 1;
|
||||
|
||||
return {
|
||||
minSlot,
|
||||
maxSlot,
|
||||
};
|
||||
};
|
||||
|
||||
this.isGroup = function(element) {
|
||||
return element.hasOwnProperty('groupId');
|
||||
};
|
||||
|
||||
this.isMarquee = function(effect) {
|
||||
return effect === 'marqueeLeft' ||
|
||||
effect === 'marqueeRight' ||
|
||||
effect === 'marqueeUp' ||
|
||||
effect === 'marqueeDown';
|
||||
};
|
||||
|
||||
this.renderElement = function(hbs, props, isStatic) {
|
||||
const hbsTemplate = hbs(Object.assign(props, globalOptions));
|
||||
let topPos = props.top;
|
||||
let leftPos = props.left;
|
||||
const hasGroup = Boolean(props.groupId);
|
||||
const hasGroupProps = Boolean(props.groupProperties);
|
||||
|
||||
// @NOTE: I think this is deprecated but needs more checking
|
||||
if (props.group) {
|
||||
if (props.group.isMarquee) {
|
||||
topPos = (props.top - props.group.top);
|
||||
leftPos = (props.left - props.group.left);
|
||||
} else {
|
||||
if (props.top >= props.group.top) {
|
||||
topPos = (props.top - props.group.top);
|
||||
}
|
||||
if (props.left >= props.group.left) {
|
||||
leftPos = (props.left - props.group.left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let cssStyles = {
|
||||
height: props.height,
|
||||
width: props.width,
|
||||
position: 'absolute',
|
||||
top: topPos,
|
||||
left: leftPos,
|
||||
zIndex: props.layer,
|
||||
transform: `rotate(${props?.rotation || 0}deg)`,
|
||||
};
|
||||
|
||||
if (isStatic) {
|
||||
cssStyles = {
|
||||
...cssStyles,
|
||||
top: props.top,
|
||||
left: props.left,
|
||||
zIndex: props.layer,
|
||||
};
|
||||
|
||||
if (hasGroup && hasGroupProps) {
|
||||
cssStyles.top = (props.top >= props.groupProperties.top) ?
|
||||
(props.top - props.groupProperties.top) : 0;
|
||||
cssStyles.left = (props.left >= props.groupProperties.left) ?
|
||||
(props.left - props.groupProperties.left) : 0;
|
||||
cssStyles.zIndex = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
if (!props.isGroup && props.dataOverride === 'text' &&
|
||||
(props.group && props.group.isMarquee) &&
|
||||
(props.effect === 'marqueeLeft' || props.effect === 'marqueeRight')
|
||||
) {
|
||||
cssStyles = {
|
||||
...cssStyles,
|
||||
position: 'static',
|
||||
top: 'unset',
|
||||
left: 'unset',
|
||||
width: props?.textWrap ? props.width : 'initial',
|
||||
display: 'flex',
|
||||
flexShrink: '0',
|
||||
wordWrap: 'break-word',
|
||||
};
|
||||
}
|
||||
|
||||
const $renderedElem = $(hbsTemplate).first()
|
||||
.attr('id', props.elementId)
|
||||
.addClass(`${props.uniqueID}--item`)
|
||||
.css(cssStyles);
|
||||
|
||||
if (!props.isGroup && props.dataOverride === 'text' &&
|
||||
(props.group && props.group.isMarquee) &&
|
||||
(props.effect === 'marqueeLeft' || props.effect === 'marqueeRight')
|
||||
) {
|
||||
$renderedElem.get(0).style.removeProperty('white-space');
|
||||
$renderedElem.get(0).style.setProperty(
|
||||
'white-space',
|
||||
props?.textWrap ? 'unset' : 'nowrap',
|
||||
'important',
|
||||
);
|
||||
}
|
||||
|
||||
return $renderedElem.prop('outerHTML');
|
||||
};
|
||||
|
||||
this.renderDataItem = function(
|
||||
isGroup,
|
||||
dataItemKey,
|
||||
dataItem,
|
||||
item,
|
||||
slot,
|
||||
maxSlot,
|
||||
isPinSlot,
|
||||
pinnedSlots,
|
||||
groupId,
|
||||
$groupContent,
|
||||
groupObj,
|
||||
meta,
|
||||
$content,
|
||||
) {
|
||||
const $groupContentItem = $(`<div class="${groupId}--item"
|
||||
data-group-key="${dataItemKey}"></div>`);
|
||||
const groupKey = '.' + groupId + '--item[data-group-key=%key%]';
|
||||
|
||||
// For each data item, parse it and add it to the content;
|
||||
if (item.hasOwnProperty('hbs') &&
|
||||
typeof item.hbs === 'function' && dataItemKey !== 'empty'
|
||||
) {
|
||||
let groupItemStyles = {
|
||||
width: groupObj.width,
|
||||
height: groupObj.height,
|
||||
};
|
||||
|
||||
if (groupObj && groupObj.isMarquee) {
|
||||
groupItemStyles = {
|
||||
...groupItemStyles,
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
flexShrink: '0',
|
||||
};
|
||||
}
|
||||
|
||||
$groupContentItem.css(groupItemStyles);
|
||||
|
||||
if ($groupContent &&
|
||||
$groupContent.find(
|
||||
groupKey.replace('%key%', dataItemKey),
|
||||
).length === 0
|
||||
) {
|
||||
$groupContent.append($groupContentItem);
|
||||
}
|
||||
|
||||
let isSingleElement = false;
|
||||
|
||||
if (!isGroup && item.dataOverride === 'text' && groupObj.isMarquee) {
|
||||
if (item.effect === 'marqueeLeft' || item.effect === 'marqueeRight') {
|
||||
if ($groupContent.find(
|
||||
groupKey.replace('%key%', dataItemKey)).length === 1
|
||||
) {
|
||||
$groupContent.find(
|
||||
groupKey.replace('%key%', dataItemKey),
|
||||
).remove();
|
||||
}
|
||||
isSingleElement = true;
|
||||
} else if (item.effect === 'marqueeDown' ||
|
||||
item.effect === 'marqueeUp') {
|
||||
isSingleElement = false;
|
||||
}
|
||||
}
|
||||
|
||||
const $itemContainer = isSingleElement ?
|
||||
$groupContent : $groupContent.find(
|
||||
groupKey.replace('%key%', dataItemKey),
|
||||
);
|
||||
const props = Object.assign(
|
||||
item.templateData,
|
||||
{isGroup},
|
||||
(String(item.dataOverride).length > 0 &&
|
||||
String(item.dataOverrideWith).length > 0) ?
|
||||
dataItem : {data: dataItem},
|
||||
{group: groupObj},
|
||||
);
|
||||
|
||||
// Handle special cases where data field name for override
|
||||
// that's the same as template variable
|
||||
// E.g. When a dataset column is "text" and the element is using
|
||||
// text element, extended or not
|
||||
if (props.isExtended) {
|
||||
if (props.type === 'dataset' &&
|
||||
props.hasOwnProperty('datasetField') &&
|
||||
dataItem.hasOwnProperty(props.datasetField)
|
||||
) {
|
||||
props[props.dataOverride] = dataItem[props.datasetField];
|
||||
} else {
|
||||
const extendWith =
|
||||
transformer.getExtendedDataKey(props.dataOverrideWith);
|
||||
if (props.dataOverride === extendWith &&
|
||||
dataItem.hasOwnProperty(extendWith)
|
||||
) {
|
||||
props[props.dataOverride] = dataItem[extendWith];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const $elementContent = $(self.renderElement(
|
||||
item.hbs,
|
||||
props,
|
||||
));
|
||||
|
||||
// Add style scope to container
|
||||
const $elementContentContainer = $('<div>');
|
||||
$elementContentContainer.append($elementContent).attr(
|
||||
'data-style-scope',
|
||||
'element_' +
|
||||
props.type + '__' +
|
||||
props.id,
|
||||
);
|
||||
|
||||
$itemContainer.append(
|
||||
$elementContentContainer,
|
||||
);
|
||||
|
||||
const itemID = item.uniqueID || item.templateData?.uniqueID;
|
||||
// Handle the rendering of the template
|
||||
(item.onTemplateRender() !== undefined) && item.onTemplateRender()(
|
||||
item.elementId,
|
||||
$itemContainer.find(`.${itemID}--item`).parent(),
|
||||
dataItem,
|
||||
{item, ...item.templateData, data: dataItem},
|
||||
meta,
|
||||
);
|
||||
} else {
|
||||
if ($groupContent &&
|
||||
$groupContent.find(
|
||||
groupKey.replace('%key%', dataItemKey)).length === 0
|
||||
) {
|
||||
$groupContent.append($groupContentItem);
|
||||
}
|
||||
|
||||
const $itemContainer = $groupContent.find(
|
||||
groupKey.replace('%key%', dataItemKey),
|
||||
);
|
||||
|
||||
$itemContainer.append('');
|
||||
}
|
||||
};
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
module.exports = new PlayerHelper();
|
||||
Reference in New Issue
Block a user