Server IP : 80.87.202.40 / Your IP : 216.73.216.169 Web Server : Apache System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64 User : bitrix ( 600) PHP Version : 8.2.27 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/ui/viewer/ |
Upload File : |
;(function () { 'use strict'; BX.namespace('BX.UI.Viewer'); BX.UI.Viewer.Controller = function(options) { /** * @type {BX.UI.Viewer.Item[]} */ this.items = null; this.currentIndex = null; this.handlers = { handleCarouselItemError: this.handleCarouselItemError.bind(this), }; this.baseContainer = options.baseContainer || document.body; this.setItems(options.items || []); this.isBodyPaddingAdded = null; this.cycleMode = options.hasOwnProperty('cycleMode') ? options.cycleMode : true; this.stretch = options.hasOwnProperty('stretch') ? options.stretch : false; this.cachedData = {}; this.optionsByGroup = {}; this.layout = { container: null, content: null, inner: null, title: null, itemContainer: null, next: null, prev: null, close: null, downloadBtn: null, moreBtn: null, error: null, loader: null, loaderContainer: null, loaderText: null, defaultActions: null, extraActions: null, carouselItems: null, carouselContainer: null, carouselScrollable: null, }; // Compatibility for BX.Disk.Viewer.Actions.runActionEdit this.actionPanel = { getItemById: () => {}, }; this.maxParallelLoads = 3; this.loadingItems = new Set(); this.moreMenu = null; this.eventsAlreadyBinded = false; this.init(); }; BX.UI.Viewer.Controller.prototype = { /** * @param {HTMLElement} node * @return {BX.Promise} */ buildItemListByNode: function (node) { var promise = new BX.Promise(); var nodes = []; if (this.isSeparateItem(node)) { nodes = [node]; } else if(node.dataset.viewerGroupBy) { nodes = [].slice.call(node.ownerDocument.querySelectorAll("[data-viewer][data-viewer-group-by='" + node.dataset.viewerGroupBy + "']")); } else { nodes = [node]; } this.loadExtensions(this.collectExtensionsForItems(nodes)).then(function (){ var items = nodes.map(function(node) { return BX.UI.Viewer.buildItemByNode(node); }); promise.fulfill(items); }.bind(this)); return promise; }, /** * @param {HTMLElement} node * @return {boolean} */ isSeparateItem: function (node) { return node.dataset.viewerSeparateItem; }, shouldProcessSeparateMode: function (items) { return items.length === 1 && items[0].isSeparateItem(); }, shouldRunViewer: function (node) { if (!BX.type.isDomNode(node) || !node.dataset) { return false; } if (!node.dataset.hasOwnProperty('viewer')) { return false; } return true; }, /** * * @param {HTMLElement[]} nodes * @return {Array} */ collectExtensionsForItems: function (nodes) { var extensionSet = new Set(); nodes.forEach(function (node) { if (BX.type.isString(node.dataset.viewerExtension)) { extensionSet.add(node.dataset.viewerExtension); } }); var extensions = []; extensionSet.forEach(function (ext) { if (shouldLoadExtensions(ext)) { extensions.push(ext); } }); return extensions; }, /** * @param {MouseEvent} event * @return {HTMLElement|null} */ extractTargetFromEvent: function (event) { var target = BX.getEventTarget(event); var shouldRunViewer = false; var maxDepth = 8; do { if (this.shouldRunViewer(target)) { shouldRunViewer = true; break; } target = target.parentNode; maxDepth--; } while (maxDepth > 0 && target); return shouldRunViewer? target : null; }, handleDocumentClick: function (event) { var target = this.extractTargetFromEvent(event); if (!target) { return; } if (target.tagName !== 'A' && target.closest('a[target="_blank"]')) { return false; } event.preventDefault(); this.buildItemListByNode(target).then(function(items){ if (items.length === 0) { return; } if (this.shouldProcessSeparateMode(items)) { this.setItems(items).then(function(){ this.openSeparate(0); }.bind(this)); return; } // shortcut for download if ((BX.browser.IsMac() && event.metaKey) || event.ctrlKey) { this.runActionByNode(target, 'download'); } else { this.setItems(items).then(function(){ this.open(this.getIndexByNode(target)); }.bind(this)); } }.bind(this)); }, bindEvents: function () { if (this.eventsAlreadyBinded) { return; } this.eventsAlreadyBinded = true; this.handlers.keyPress = this.handleKeyPress.bind(this); this.handlers.touchStart = this.handleTouchStart.bind(this); this.handlers.touchEnd = this.handleTouchEnd.bind(this); this.handlers.resize = this.handleWindowResize.bind(this); this.handlers.showNext = this.showNext.bind(this); this.handlers.showPrev = this.showPrev.bind(this); this.handlers.close = this.close.bind(this); this.handlers.handleClickOnItemContainer = this.handleClickOnItemContainer.bind(this); this.handlers.handleSliderCloseByEsc = this.handleSliderCloseByEsc.bind(this); BX.bind(document, 'keydown', this.handlers.keyPress); BX.bind(window, 'resize', this.handlers.resize); BX.bind(this.getItemContainer(), 'touchstart', this.handlers.touchStart); BX.bind(this.getItemContainer(), 'touchend', this.handlers.touchEnd); BX.bind(this.getItemContainer(), 'click', this.handlers.handleClickOnItemContainer); BX.bind(this.getNextButton(), 'click', this.handlers.showNext); BX.bind(this.getPrevButton(), 'click', this.handlers.showPrev); BX.bind(this.getCloseButton(), 'click', this.handlers.close); BX.addCustomEvent('SidePanel.Slider:onCloseByEsc', this.handlers.handleSliderCloseByEsc); }, handleVisibleControls: function(ev) { if (BX.browser.IsMobile() || BX.hasClass(document.documentElement, 'bx-touch')) { return; } if (this._timerIdReadingMode) { clearTimeout(this._timerIdReadingMode); } if (!this.cursorInPerimeter(ev) || BX.findParent(ev.target, {className: 'ui-viewer-next'}) || BX.findParent(ev.target, {className: 'ui-viewer-prev'})) { this.disableReadingMode(); } else { this._timerIdReadingMode = setTimeout(function () { this.enableReadingMode(); }.bind(this), 2800); } }, handleMoreBtnClick() { if (this.moreMenu === null) { const item = this.getCurrentItem(); const menuItems = [ ...item.getMoreMenuItems(), ...this.createMoreMenuItems(item), ]; if (menuItems.length === 0) { return; } this.moreMenu = new BX.Main.Menu({ angle: true, minWidth: 150, bindElement: this.getMoreButton(), offsetLeft: 30, offsetTop: -20, items: menuItems, cacheable: false, events: { onDestroy: () => { this.moreMenu = null; }, }, }); } this.moreMenu.show(); }, enableReadingMode: function(withTimer) { if (BX.browser.IsMobile() || !this.isOnTop()) { return; } if(withTimer) { this._timerIdReadingMode = setTimeout(function() { this.layout.container.classList.add('ui-viewer-reading-mode'); }.bind(this), 5000); return; } this.layout.container.classList.add('ui-viewer-reading-mode'); }, disableReadingMode: function() { if(this._timerIdReadingMode) { clearTimeout(this._timerIdReadingMode); } this.layout.container.classList.remove('ui-viewer-reading-mode'); }, cursorInPerimeter: function(ev) { var offsetVertical = document.body.clientHeight / 100 * 30; var offsetHorizontal = document.body.clientWidth / 100 * 30; offsetHorizontal < 300 ? offsetHorizontal = 300 : null; offsetVertical < 300 ? offsetVertical = 300 : null; if( ev.y < offsetVertical || ev.y > document.body.clientHeight - offsetVertical || ev.x < offsetHorizontal || ev.x > document.body.clientWidth - offsetHorizontal) { return false } return true; }, /** * @param {BX.SidePanel.Event} event */ handleSliderCloseByEsc: function(event) { if (this.isOpen() && (this.getZindex() > event.getSlider().getZindex())) { event.denyAction(); } }, adjustViewport: function () { var viewportNode = document.querySelector('[name="viewport"]'); if (!viewportNode) { return; } this._viewportContent = viewportNode.getAttribute('content'); viewportNode.setAttribute('content', 'width=device-width, user-scalable=no'); }, restoreViewport: function () { var viewportNode = document.querySelector('[name="viewport"]'); if (!this._viewportContent || !viewportNode) { return; } viewportNode.setAttribute('content', this._viewportContent); }, unbindEvents: function() { this.eventsAlreadyBinded = false; BX.unbind(document, 'keydown', this.handlers.keyPress); BX.unbind(window, 'resize', this.handlers.resize); BX.unbind(this.getItemContainer(), 'touchstart', this.handlers.touchStart); BX.unbind(this.getItemContainer(), 'touchend', this.handlers.touchEnd); BX.unbind(this.getItemContainer(), 'click', this.handlers.handleClickOnItemContainer); BX.unbind(this.getNextButton(), 'click', this.handlers.showNext); BX.unbind(this.getPrevButton(), 'click', this.handlers.showPrev); BX.unbind(this.getCloseButton(), 'click', this.handlers.close); }, init: function () {}, openByNode: function (node) { this.buildItemListByNode(node).then(function (items) { if (items.length === 0) { return; } if (this.shouldProcessSeparateMode(items)) { this.setItems(items).then(function(){ this.openSeparate(0); }.bind(this)); return; } this.setItems(items).then(function(){ this.open(this.getIndexByNode(node)); }.bind(this)); }.bind(this)); }, runActionByNode: function (node, actionId, additionalParams) { this.buildItemListByNode(node).then(function (items) { if (items.length === 0) { return; } this.setItems(items).then(function(){ this.runAction(this.getIndexByNode(node), actionId, additionalParams); }.bind(this)); }.bind(this)); }, runAction(index, actionId, additionalParams) { const item = this.getItemByIndex(index); if (actionId === 'download') { // Compatibility: a download action doesn't exist anymore window.open(item.getDownloadUrl(), this.isExternalLink(item.getDownloadUrl()) ? '_blank' : '_self'); return; } var actionToRun = item.getActions().find(function (action) { return action.id === actionId; }); console.log('actionToRun', actionId, actionToRun); if (!BX.type.isFunction(actionToRun.action)) { console.log('action is not a function'); return; } actionToRun.action.call(this, item, additionalParams); }, /** * @returns {number} */ getZindex: function () { var container = this.getViewerContainer(); if (!container.parentNode) { return 0; } var component = BX.ZIndexManager.getComponent(container); return component.getZIndex(); }, /** * @param items * @return {Promise<extensionsCollection>} */ setItems: function (items) { if (!BX.type.isArray(items)) { throw new Error("BX.UI.Viewer.Controller.setItems: 'items' has to be Array."); } BX.onCustomEvent('BX.UI.Viewer.Controller:onSetItems', [this, items]); if (this.items !== null) { this.items.forEach((item) => { item.destroy(); }); } this.items = items; this.items.forEach(function (item) { item.setController(this); }, this); return this.loadExtensions(this.collectExtensionsForAction(items)); }, /** * * @param {String|String[]} extensions - Extension name * @return {Promise<extensionsCollection>} */ loadExtensions: function (extensions) { return BX.loadExt(extensions); }, /** * * @param {BX.UI.Viewer.Item[]} items * @return {Array} */ collectExtensionsForAction: function (items) { var extensionSet = new Set(); items.forEach(function (item) { var actions = item.getActions() || []; actions.forEach(function (action) { if (!action.extension) { return; } if (!BX.type.isArray(action.extension)) { action.extension = [action.extension]; } action.extension.forEach(function (ext) { extensionSet.add(ext); }); }); }); var extensions = []; extensionSet.forEach(function (ext) { if (shouldLoadExtensions(ext)) { extensions.push(ext); } }); return extensions; }, appendItem: function (item) { if (!(item instanceof BX.UI.Viewer.Item)) { throw new Error("BX.UI.Viewer.Controller.appendItem: 'item' has to be instance of BX.UI.Viewer.Item."); } item.setController(this); this.items.push(item); }, /** * Reloads item in collection items. It means that we replace old item by the one new * which is the copy. * @param {BX.UI.Viewer.Item} item * @param {Object} options */ reloadItem: function (item, options) { options = options || {}; if (!(item instanceof BX.UI.Viewer.Item)) { throw new Error("BX.UI.Viewer.Controller.reloadItem: 'item' has to be instance of BX.UI.Viewer.Item."); } var index = this.items.indexOf(item); if (index === -1) { throw new Error("BX.UI.Viewer.Controller.reloadItem: there is no 'item' in items to reload."); } var newItem = null; if (item.sourceNode) { newItem = BX.UI.Viewer.buildItemByNode(item.sourceNode); } else { newItem = new item.constructor(item.options); } item.destroy(); newItem.setController(this); newItem.applyReloadOptions(options); this.items[index] = newItem; }, show: function (index, options) { options = options || {}; if (typeof index === 'undefined') { index = 0; } BX.onCustomEvent('BX.UI.Viewer.Controller:onBeforeShow', [this, index]); var item = this.getItemByIndex(index); if (!item) { return; } this.hideErrorBlock(); this.hideCurrentItem(); this.disableReadingMode(); this.showLoading(); const moveToStart = index === 0 && this.currentIndex === this.items.length - 1; const moveToEnd = this.currentIndex === 0 && index === this.items.length - 1; const direction = (index < this.currentIndex && !moveToStart) || moveToEnd ? 'backward' : 'forward'; this.currentIndex = index; this.updateActions(this.getCurrentItem()); this.observeItemLoading(item); const title = item.getTitle(); this.setTitle(BX.Type.isStringFilled(title) ? title : ''); item.load() .then((loadedItem) => { this.unobserveItemLoading(item); if (this.getCurrentItem() === loadedItem) { console.log('show item'); this.processShowItem(loadedItem, options); if (options.asFirstToShow) { loadedItem.asFirstToShow(); } } this.processPreload(index, direction); }) .catch((reason) => { this.unobserveItemLoading(item); var loadedItem = reason.item; console.log('catch viewer'); BX.onCustomEvent('BX.UI.Viewer.Controller:onItemError', [this, reason, loadedItem]); if (this.getCurrentItem() === loadedItem) { this.processError(reason, loadedItem); } BX.onCustomEvent('BX.UI.Viewer.Controller:onAfterProcessItemError', [this, reason, loadedItem]); }) ; this.updateControls(); this.lockScroll(); this.adjustViewerHeight(); const cycleMove = this.items.length > 20 && (moveToStart || moveToEnd); this.selectCarouselItem(this.currentIndex, options.asFirstToShow !== true && !cycleMove); }, processPreload(fromIndex, direction) { if (this.maxParallelLoads < 1) { return; } const maxPreloads = this.maxParallelLoads - 1; let preloadIndex = direction === 'backward' ? fromIndex - 1 : fromIndex + 1; const shouldPreload = () => { if (direction === 'backward') { return preloadIndex > (fromIndex - 1 - maxPreloads); } return preloadIndex < (maxPreloads + fromIndex + 1); }; while (shouldPreload()) { const itemByIndex = this.getItemByIndex(preloadIndex); if (!itemByIndex) { break; } console.log('Trying to preload', preloadIndex); this.observeItemLoading(itemByIndex); itemByIndex.load() .then(() => { this.unobserveItemLoading(itemByIndex); }) .catch(() => { this.unobserveItemLoading(itemByIndex); }) ; if (direction === 'backward') { preloadIndex--; } else { preloadIndex++; } } }, /** * Reloads item and rerun show if item was as current item. * @param {BX.UI.Viewer.Item} item * @param {Object} options */ reload: function (item, options) { var isCurrentVisibleItem = this.getCurrentItem() === item; this.reloadItem(item, options); if (isCurrentVisibleItem) { console.log('reload'); this.show(this.currentIndex); } }, /** * Reloads and show current item. * @param {?Object} options */ reloadCurrentItem: function (options) { this.reload(this.getCurrentItem(), options || {}); }, /** * @param {BX.UI.Viewer.Item} item */ processShowItem: function(item, options) { this.hideCurrentItem(); this.hideLoading(); this.unlockExtraActions(); var contentWrapper = BX.create('div', { props: { className: 'ui-viewer-inner-content-wrapper' }, }); const title = item.getTitle(); this.setTitle(BX.Type.isStringFilled(title) ? title : ''); var fragment = document.createDocumentFragment(); fragment.appendChild(item.render(options)); contentWrapper.appendChild(fragment); var classList = this.layout.container.classList; var containerModifiers = item.listContainerModifiers(); if (containerModifiers.length) { classList.add.apply(classList, containerModifiers); } this.layout.itemContainer.appendChild(contentWrapper); item.afterRender(); this.adjustControlsSize(item.getContentWidth()); BX.onCustomEvent('BX.UI.Viewer.Controller:onAfterShow', [this, item]); }, adjustControlsSize: function(contentWidth) { this.getNextButton().style.width = null; this.getPrevButton().style.width = null; this.getNextButton().style.maxWidth = null; this.getPrevButton().style.maxWidth = null; if (contentWidth && BX.Type.isFunction(contentWidth.then)) { contentWidth.then((width) => { let controlWidth = Math.floor((document.body.offsetWidth - Math.ceil(width)) / 2); controlWidth = Math.max(controlWidth, 70); this.getNextButton().style.width = controlWidth + 'px'; this.getPrevButton().style.width = controlWidth + 'px'; this.getNextButton().style.maxWidth = 'none'; this.getPrevButton().style.maxWidth = 'none'; }); } }, /** * @param {Object} reason * @param {BX.UI.Viewer.Item} item */ processError: function(reason, item) { reason = reason || {}; var message = reason.message || null; if (BX.type.isArray(reason.errors) && reason.errors.length) { if (reason.errors[0].code === 1000 && !reason.message) { message = BX.message("JS_UI_VIEWER_ITEM_TRANSFORMATION_ERROR_1").replace('#DOWNLOAD_LINK#', item.getSrc()); } } this.hideCurrentItem(); this.hideLoading(); this.cleanExtraActions(); var contentWrapper = BX.create('div', { props: { className: 'ui-viewer-inner-content-wrapper' } }); var title = item.getTitle(); this.setTitle(BX.Type.isStringFilled(title) ? title : ''); var options = {}; if (message) { options.title = message; } if (reason.description) { options.description = reason.description; } contentWrapper.appendChild(this.getErrorBlock(options, item)); this.layout.itemContainer.appendChild(contentWrapper); }, hideErrorBlock: function() { if (this.layout.error) { BX.remove(this.layout.error); } }, /** * @param {Object} options * @param {?string} [options.viewType='info'] * @param {?string} [options.title] * @param {?string} [options.description] * @param {BX.UI.Viewer.Item} item * @return {null} */ getErrorBlock: function(options, item) { this.hideErrorBlock(); var viewType = BX.prop.getString(options, 'viewType', 'info'); var title = BX.prop.getString(options, 'title', BX.message("JS_UI_VIEWER_ITEM_TRANSFORMATION_ERROR_1").replace('#DOWNLOAD_LINK#', item.getSrc())); var description = BX.prop.getString(options, 'description', BX.message("JS_UI_VIEWER_ITEM_TRANSFORMATION_HINT")); this.layout.error = BX.create('div', { props: { className: 'ui-viewer-error' }, style: { maxWidth: description? '440px' : null }, children: [ BX.create('div', { props: { className: 'ui-viewer-' + viewType + '-title' }, html: title }), BX.create('div', { props: { className: 'ui-viewer-' + viewType + '-text' }, html: description }) ] }); return this.layout.error; }, /** * @param {String} link * @return {boolean} */ isExternalLink: function (link) { var isAbsoluteLink = new RegExp('^([a-z]+://|//)', 'i'); if (!isAbsoluteLink.test(link)) { return false; } if (!BX.getClass('URL')) { return link.indexOf(location.hostname) === -1; } try { return (new URL(link)).hostname !== location.hostname; } catch(e) {} return true; }, /** * @param {BX.UI.Viewer.Item} item */ createMoreMenuItems(item) { return item.getActions().map((action) => { const menuItem = { ...action }; if (!menuItem.href && BX.type.isFunction(menuItem.action)) { const fn = menuItem.action; menuItem.onclick = (event, menuItem) => { fn.call(this, item, { event, menuItem }); this.moreMenu?.close(); }; } menuItem.disabled = menuItem.disableBeforeLoaded === true && item.isLoaded === false; if (BX.Type.isArrayFilled(menuItem.items)) { menuItem.items = menuItem.items.map((subMenuItem) => { const newMenuItem = { ...subMenuItem }; if (BX.type.isString(newMenuItem.onclick)) { const onclick = new Function('event', 'popupItem', newMenuItem.onclick); newMenuItem.onclick = (event, popupItem) => { onclick(event, popupItem); this.moreMenu?.close(); }; } return newMenuItem; }); } return menuItem; }); }, /** * @param {BX.UI.Viewer.Item} item */ refineItemActions(item) { const defaultActions = { download: { id: 'download', type: 'download', text: BX.Loc.getMessage('JS_UI_VIEWER_ITEM_ACTION_DOWNLOAD'), href: item.src, buttonIconClass: 'ui-btn-icon-download', }, edit: { id: 'edit', type: 'edit', text: BX.Loc.getMessage('JS_UI_VIEWER_ITEM_ACTION_EDIT'), buttonIconClass: 'ui-btn-icon-edit', }, share: { id: 'share', type: 'share', text: BX.Loc.getMessage('JS_UI_VIEWER_ITEM_ACTION_SHARE'), buttonIconClass: 'ui-btn-icon-share', }, print: { id: 'print', type: 'print', text: BX.Loc.getMessage('JS_UI_VIEWER_ITEM_ACTION_PRINT'), buttonIconClass: 'ui-btn-icon-print ui-btn-disabled', disableBeforeLoaded: true, }, info: { id: 'info', type: 'info', text: BX.Loc.getMessage('JS_UI_VIEWER_ITEM_ACTION_INFO'), buttonIconClass: 'ui-btn-icon-info ui-action-panel-item-last', }, delete: { id: 'delete', type: 'delete', text: BX.Loc.getMessage('JS_UI_VIEWER_ITEM_ACTION_DELETE'), buttonIconClass: 'ui-btn-icon-remove', }, }; const actions = []; for (const [, nakedAction] of item.getNakedActions().entries()) { const action = ( defaultActions[nakedAction.type] ? BX.mergeEx(defaultActions[nakedAction.type], nakedAction) : nakedAction ); if (!action.id) { action.id = action.type; } if (action.id === 'download' && BX.Type.isStringFilled(action.href)) { item.setDownloadUrl(action.href); continue; } if (!action.action && action.href) { action.action = () => { window.open(action.href, this.isExternalLink(action.href) ? '_blank' : '_self'); }; } if (BX.type.isString(action.action)) { const params = action.params || {}; const actionString = action.action; action.action = (item, additionalParams) => { try { const fn = eval(actionString); fn.call(this, item, params, additionalParams); } catch (e) { console.log(e); } }; } actions.push(action); } return actions; }, getLoader: function(options) { if (!this.layout.loader) { this.layout.loader = BX.create('div', { props: { className: 'ui-viewer-loader' }, style: { zIndex: -1 }, children: [ this.layout.loaderContainer = BX.create('div', { props: { className: 'ui-viewer-loader-container' } }), this.layout.loaderText = BX.create('div', { props: { className: 'ui-viewer-loader-text' }, text: '' }) ] }); var loader = new BX.Loader({size: 130}); loader.show(this.layout.loaderContainer); } return this.layout.loader; }, getPrevButton: function() { if (!this.layout.prev) { this.layout.prev = BX.create('div', { props: { className: 'ui-viewer-prev' }, events: { mousewheel: function(event) { this.handleMouseWheelOnControlButton(this.layout.prev, event); }.bind(this) } }) } return this.layout.prev; }, getNextButton: function() { if (!this.layout.next) { this.layout.next = BX.create('div', { props: { className: 'ui-viewer-next' }, events: { mousewheel: function(event) { this.handleMouseWheelOnControlButton(this.layout.next, event); }.bind(this) } }); } return this.layout.next; }, handleMouseWheelOnControlButton: function(controlNode, event) { if (this._timeoutIdMouseWheel) { clearTimeout(this._timeoutIdMouseWheel); } controlNode.style.pointerEvents = 'none'; this._timeoutIdMouseWheel = setTimeout(function() { controlNode.style.pointerEvents = null; }, 50); }, getCloseButton: function() { if (!this.layout.close) { this.layout.close = BX.create('div', { props: { className: 'ui-viewer-close', }, html: '<div class="ui-viewer-close-icon">' + '<div class="ui-icon-set --cross-40"></div>' + '</div>' }); } return this.layout.close; }, getDownloadButton: function() { if (!this.layout.downloadBtn) { this.layout.downloadBtn = BX.Tag.render` <a class="ui-viewer-download-btn" target="_blank" title="${BX.Text.encode(BX.Loc.getMessage('JS_UI_VIEWER_ITEM_ACTION_DOWNLOAD'))}" href="" download > <div class="ui-icon-set --download ui-viewer-download-btn-icon"></div> </a> `; } return this.layout.downloadBtn; }, getMoreButton: function() { if (!this.layout.moreBtn) { this.layout.moreBtn = BX.Tag.render` <div class="ui-viewer-more-btn" onclick="${this.handleMoreBtnClick.bind(this)}"> <div class="ui-icon-set --more ui-viewer-more-btn-icon"></div> </div> `; } return this.layout.moreBtn; }, isOpen: function () { return this._isOpen; }, addBodyPadding: function() { var padding = window.innerWidth - document.documentElement.clientWidth; if (BX.getClass('BXIM.messenger.popupMessenger') || padding === 0) { return; } document.body.style.paddingRight = padding + 'px'; var imBar = document.getElementById('bx-im-bar'); if(imBar) { var borderColor = 'rgb(238, 242, 244)'; if(document.body.classList.contains('bitrix24-light-theme')) { borderColor = 'rgba(255, 255, 255, .1)'; } if(document.body.classList.contains('bitrix24-dark-theme')) { borderColor = 'rgba(82, 92, 105, .1)'; } imBar.style.borderRight = padding + 'px solid ' + borderColor; } this.isBodyPaddingAdded = true; }, removeBodyPadding: function() { document.body.style.removeProperty('padding-right'); var imBar = document.getElementById('bx-im-bar'); if (imBar) { imBar.style.removeProperty('border-right'); } this.isBodyPaddingAdded = false; }, openSeparate: function(index) { var item = this.getItemByIndex(index); if (!item) { return; } item.load() .then(function (loadedItem) {}.bind(this)) .catch(function (reason) { var loadedItem = reason.item; console.log('catch viewer'); BX.onCustomEvent('BX.UI.Viewer.Controller:onItemError', [this, reason, loadedItem]); if (this.getCurrentItem() === loadedItem) { this.processError(reason, loadedItem); } BX.onCustomEvent('BX.UI.Viewer.Controller:onAfterProcessItemError', [this, reason, loadedItem]); }.bind(this)); }, open: function(index) { this.adjustViewport(); this.addBodyPadding(); var container = this.getViewerContainer(); const baseContainer = this.baseContainer || document.body; baseContainer.appendChild(container); BX.focus(container); var component = BX.ZIndexManager.getComponent(container); if (!component) { BX.ZIndexManager.register(container); } BX.ZIndexManager.bringToFront(container); this.createCarouselItems(index); this.show(index, { asFirstToShow: true, }); this.bindEvents(); this._isOpen = true; }, setTitle: function(title) { if (BX.Type.isStringFilled(title)) { this.getTitleContainer().textContent = title; } }, getTitleContainer: function() { if (!this.layout.title) { this.layout.title = BX.Tag.render` <span class="ui-viewer-title-text"></span> `; } return this.layout.title; }, getExtraActions: function() { if (!this.layout.extraActions) { this.layout.extraActions = BX.Tag.render` <div class="ui-viewer-extra-actions"></div> `; } return this.layout.extraActions; }, getDefaultActions: function() { if (!this.layout.defaultActions) { this.layout.defaultActions = BX.Tag.render` <div class="ui-viewer-default-actions"> ${this.getDownloadButton()} ${this.getMoreButton()} </div> `; } return this.layout.defaultActions; }, observeItemLoading(item) { const supportAbort = Object.hasOwn(Object.getPrototypeOf(item), 'abort'); if (item.isLoaded === true || !supportAbort) { return; } this.loadingItems.add(item); if (this.loadingItems.size > this.maxParallelLoads) { for (const queueItem of this.loadingItems) { if (queueItem === item || queueItem === this.getCurrentItem()) { continue; } const success = queueItem.abort(); if (success === true) { this.loadingItems.delete(queueItem); break; } } } }, unobserveItemLoading(item) { this.loadingItems.delete(item); }, updateActions(item) { this.getDownloadButton().setAttribute('href', item.getDownloadUrl()); if (item.getActions().length > 0) { BX.Dom.removeClass(this.getMoreButton(), '--hidden'); } else { BX.Dom.addClass(this.getMoreButton(), '--hidden'); } this.renderExtraActions(item); if (!item.isLoaded) { this.lockExtraActions(); } }, getCarouselContainer: function() { if (!this.layout.carouselContainer) { this.layout.carouselContainer = BX.Tag.render` <div class="ui-viewer-carousel"> ${this.getCarouselScrollable()} <div> `; } return this.layout.carouselContainer; }, getCarouselScrollable: function() { if (!this.layout.carouselScrollable) { this.layout.carouselScrollable = BX.Tag.render` <div class="ui-viewer-carousel-scrollable" onscroll="${BX.Runtime.debounce(this.handleCarouselScroll, 200, this)}" > ${this.getCarouselItems()} </div> `; } return this.layout.carouselScrollable; }, getCarouselItems: function() { if (!this.layout.carouselItems) { this.layout.carouselItems = BX.Tag.render` <div class="ui-viewer-carousel-items" onclick="${this.handleCarouselClick.bind(this)}"></div> `; } return this.layout.carouselItems; }, getCarouselItemWidth: function(selected = false) { const margin = 4 * 2; return selected ? 72 + margin : 46 + margin; }, createCarouselItems: function(selectedIndex) { const itemsContainer = this.getCarouselItems(); BX.Dom.clean(itemsContainer); BX.Dom.style(itemsContainer, { paddingLeft: null, paddingRight: null }); BX.Dom.style(this.getCarouselContainer(), { width: null }); BX.Dom.remove(this.getCarouselContainer()); if (!this.isCarouselEnabled()) { return; } for (const [index, item] of this.items.entries()) { const container = BX.Tag.render` <div class="ui-viewer-carousel-item" data-index="${index}"></div> `; if (index === selectedIndex) { BX.Dom.addClass(container, '--selected'); } if (item.getPreviewUrl() === null) { const icon = this.createItemIcon(item); icon.renderTo(container); } else { const img = document.createElement('img'); img.className = 'ui-viewer-carousel-item-preview-img'; img.onerror = this.handlers.handleCarouselItemError; // Preload first 10 items if (index <= 10 || item.getPreviewUrl().startsWith('blob:')) { img.src = item.getPreviewUrl(); } else { img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; container.dataset.lazyloadSrc = item.getPreviewUrl(); } container.appendChild(img); } BX.Dom.append(container, this.getCarouselItems()); } BX.Dom.append(this.getCarouselContainer(), this.getViewerContainer()); const maxViewportWidth = this.getCarouselItemWidth() * 6 + this.getCarouselItemWidth(true); // 7 items const minViewportWidth = ( (this.getItemsCount() - 1) * this.getCarouselItemWidth() + this.getCarouselItemWidth(true) / 2 ) * 2; const viewportWidth = Math.max( this.getCarouselContainer().offsetWidth, Math.min(minViewportWidth, maxViewportWidth), ); const offset = viewportWidth / 2 - this.getCarouselItemWidth(true) / 2; BX.Dom.style(this.getCarouselContainer(), { width: `${viewportWidth}px`, }); BX.Dom.style(itemsContainer, { paddingLeft: `${offset}px`, paddingRight: `${offset}px`, }); }, loadCarouselPreviews() { const itemWidth = this.getCarouselItemWidth(); const scrollContainer = this.getCarouselItems().parentNode; const windowWidth = scrollContainer.parentNode.offsetWidth; const itemsInWindow = Math.round(windowWidth / itemWidth); const scrollLeft = scrollContainer.scrollLeft; const middleItemIndex = Math.ceil(scrollLeft / itemWidth); const leftItemIndex = Math.max(middleItemIndex - itemsInWindow, 0); const rightItemIndex = Math.min(middleItemIndex + itemsInWindow * 1.5, this.items.length - 1); for (let index = leftItemIndex; index <= rightItemIndex; index++) { const carouselItem = this.getCarouselItems().children[index]; if (carouselItem.dataset.lazyloadSrc) { const img = carouselItem.firstElementChild; img.src = carouselItem.dataset.lazyloadSrc; BX.Dom.attr(carouselItem, 'data-lazyload-src', null); } } }, getFileExtension: function(filename) { const position = BX.Type.isStringFilled(filename) ? filename.lastIndexOf('.') : -1; return position > 0 ? filename.slice(Math.max(0, position + 1)) : ''; }, handleCarouselClick: function(event) { const container = event.target.closest('.ui-viewer-carousel-item'); if (!container) { return; } const index = Number(container.dataset.index); this.show(index); }, handleCarouselScroll(event) { this.loadCarouselPreviews(); }, handleCarouselItemError(event) { const container = event.target.closest('.ui-viewer-carousel-item'); const index = container.dataset.index; const item = this.getItemByIndex(index); BX.Dom.clean(container); const icon = this.createItemIcon(item); icon.renderTo(container); }, createItemIcon(item) { return new BX.UI.Icons.Generator.FileIcon({ name: this.getFileExtension(item.getTitle()) || '...', size: 18, }); }, isCarouselEnabled: function() { if (this.items.length < 2) { return false; } return this.items.some((item) => { return item.getPreviewUrl() !== null; }); }, selectCarouselItem: function(index, smooth = true) { if (!this.isCarouselEnabled()) { return; } const currentSelected = this.getCarouselItems().querySelector('.ui-viewer-carousel-item.--selected'); if (currentSelected) { BX.Dom.removeClass(currentSelected, '--selected'); } const newSelected = this.getCarouselItems().querySelector(`.ui-viewer-carousel-item[data-index="${index}"]`); BX.Dom.addClass(newSelected, '--selected'); this.adjustCarouselPosition(this.currentIndex, smooth); this.loadCarouselPreviews(); }, adjustCarouselPosition: function(index, smooth = true) { if (!this.isCarouselEnabled()) { return false; } const scrollContainer = this.getCarouselItems().parentNode; const carouselItem = this.getCarouselItems().querySelector(`.ui-viewer-carousel-item[data-index="${index}"]`); if (carouselItem === null) { return false; } const scrollLeft = this.getCarouselItemWidth() * index; if (scrollContainer.scrollLeft !== scrollLeft) { scrollContainer.scrollTo({ left: scrollLeft, behavior: smooth ? 'smooth' : 'instant', }); return true; } return false; }, hideCurrentItem: function() { if (this.getCurrentItem()) { var classList = this.layout.container.classList; var containerModifiers = this.getCurrentItem().listContainerModifiers(); if (containerModifiers.length) { classList.remove.apply(classList, containerModifiers); } this.getCurrentItem().beforeHide(); } this.moreMenu?.close(); BX.cleanNode(this.layout.itemContainer); }, updateControls: function() { if (!this.allowToUseCycleMode() && this.currentIndex + 1 >= this.items.length) { BX.addClass(this.getNextButton(), 'ui-viewer-navigation-hide'); } else { BX.removeClass(this.getNextButton(), 'ui-viewer-navigation-hide'); } if (!this.allowToUseCycleMode() && this.currentIndex === 0) { BX.addClass(this.getPrevButton(), 'ui-viewer-navigation-hide'); } else { BX.removeClass(this.getPrevButton(), 'ui-viewer-navigation-hide'); } }, /** * @return {BX.UI.Viewer.Item} */ getCurrentItem: function () { return this.getItemByIndex(this.currentIndex); }, /** * @param {HTMLElement} node * @return {int} */ getIndexByNode: function (node) { var nodeIndex = null; this.items.forEach(function (item, index) { if (item.sourceNode === node) { nodeIndex = index; } }); return nodeIndex; }, /** * * @param index * @returns BX.UI.Viewer.Item */ getItemByIndex: function (index) { index = parseInt(index, 10); BX.onCustomEvent('BX.UI.Viewer.Controller:onGetItemByIndex', [this, index]); if (index < 0 || (index - 1) > this.items.length) { return null; } return this.items[index]; }, getItemsCount: function() { return this.items.length; }, handleClickOnItemContainer: function (event) { this.getCurrentItem().handleClickOnItemContainer(event); }, allowToUseCycleMode: function () { var cycleMode = this.cycleMode; var groupBy = this.getCurrentItem().getGroupBy(); if (this.optionsByGroup[groupBy] && this.optionsByGroup[groupBy].hasOwnProperty('cycleMode')) { cycleMode = this.optionsByGroup[groupBy].cycleMode; } return this.items.length > 1 && cycleMode; }, showNext: function () { var index = this.currentIndex + 1; if (this.allowToUseCycleMode() && index >= this.items.length) { index = 0; } this.show(index); }, showPrev: function () { var index = this.currentIndex - 1; if (this.allowToUseCycleMode() && index === -1) { index = this.items.length - 1; } this.show(index); }, close: function () { this._isOpen = false; BX.onCustomEvent('BX.UI.Viewer.Controller:onClose', [this]); BX.addClass(this.layout.container, 'ui-viewer-hide'); this.restoreViewport(); this.hideCurrentItem(); BX.bind(this.layout.container, 'transitionend', function() { BX.ZIndexManager.unregister(this.layout.container); BX.remove(this.layout.container); BX.removeClass(this.layout.container, 'ui-viewer-hide'); BX.unbindAll(this.layout.container); this.unLockScroll(); this.unbindEvents(); this.disableReadingMode(); if(this.isBodyPaddingAdded) { this.removeBodyPadding(); } }.bind(this)); // this.items = null; // this.currentIndex = null; // this.layout.container = null; // this.layout.close = null; }, showLoading: function (options) { options = options || {}; options.zIndex = BX.type.isNumber(options.zIndex)? options.zIndex : -1; this.layout.inner.appendChild(this.getLoader()); this.setTextOnLoading(options.text || ''); this.layout.loader.style.zIndex = options.zIndex; }, setTextOnLoading: function (text) { this.layout.loaderText.textContent = text; }, hideLoading: function () { BX.remove(this.layout.loader); }, lockScroll: function() { BX.addClass(document.body, 'ui-viewer-lock-body'); }, unLockScroll: function() { BX.removeClass(document.body, 'ui-viewer-lock-body'); }, renderExtraActions(item) { this.cleanExtraActions(); BX.Dom.append(item.renderExtraActions(), this.getExtraActions()); }, cleanExtraActions() { BX.Dom.clean(this.getExtraActions()); }, lockExtraActions: function() { BX.Dom.addClass(this.getExtraActions(), '--locked'); }, unlockExtraActions: function() { BX.Dom.removeClass(this.getExtraActions(), '--locked'); }, handleWindowResize() { this.adjustViewerHeight(); const item = this.getCurrentItem(); if (item) { item.handleResize(); } }, adjustViewerHeight: function() { if(!this.layout.container || BX.browser.IsMobile()) return; this.layout.container.style.height = document.documentElement.clientHeight + 'px'; }, getViewerContainer: function() { if (!this.layout.container) { this.layout.container = BX.create('div', { props: { className: 'ui-viewer', tabIndex: 22081990, }, style: { height: window.clientHeight + 'px' }, children: [ BX.Tag.render` <div class="ui-viewer-header"> <div class="ui-viewer-author"></div> <div class="ui-viewer-title"> ${this.getTitleContainer()} </div> <div class="ui-viewer-actions"> ${this.getExtraActions()} ${this.getDefaultActions()} ${this.getCloseButton()} </div> </div> `, this.layout.inner = BX.create('div', { props: { className: 'ui-viewer-inner', }, children: [ this.getItemContainer(), ], }), this.getPrevButton(), this.getNextButton(), ], }); } return this.layout.container; }, getItemContainer: function() { if (!this.layout.itemContainer) { this.layout.itemContainer = BX.create('div', { props: { className: 'ui-viewer-inner-content' } }); } return this.layout.itemContainer }, handleTouchStart: function(event) { var touchObject = event.changedTouches[0]; this.swipeDirection = null; this.startX = touchObject.pageX; this.startY = touchObject.pageY; this.startTime = (new Date()).getTime(); // event.preventDefault(); }, handleTouchEnd: function(event) { var touchObject = event.changedTouches[0]; var allowedTime = 300; var threshold = 80; var restraint = 100; var distanceX = touchObject.pageX - this.startX; var distanceY = touchObject.pageY - this.startY; var elapsedTime = (new Date()).getTime() - this.startTime; if (elapsedTime <= allowedTime) { if (Math.abs(distanceX) >= threshold && Math.abs(distanceY) <= restraint) { this.swipeDirection = (distanceX < 0) ? 'left' : 'right'; } // else if (Math.abs(distanceY) >= threshold && Math.abs(distanceX) <= restraint) // { // this.swipeDirection = (distanceY < 0) ? 'up' : 'down'; // } } switch (this.swipeDirection) { case 'left': this.showPrev(); break; case 'right': this.showNext(); break; } // event.preventDefault(); }, isOnTop: function () { if (!this.isOpen()) { return false; } if (BX.getClass('BXIM.messenger') && BXIM.messenger.popupMessenger) { return true; } if (!BX.getClass('BX.SidePanel.Instance') || !BX.SidePanel.Instance.getTopSlider()) { return true; } return this.getZindex() > BX.SidePanel.Instance.getTopSlider().getZindex(); }, handleKeyPress: function (event) { if (!this.isOnTop()) { return; } if (event.metaKey) { return; } const handled = this.getCurrentItem().handleKeyPress(event); if (handled === true) { return; } switch (event.code) { case 'Space': case 'ArrowRight': this.showNext(); event.preventDefault(); event.stopPropagation(); break; case 'ArrowLeft': this.showPrev(); event.preventDefault(); event.stopPropagation(); break; case 'Escape': this.close(); event.preventDefault(); event.stopPropagation(); break; } }, setOptionsByGroup: function (groupBy, options) { this.optionsByGroup[groupBy] = options; return this; }, getCachedData: function(id) { return this.cachedData[id]; }, setCachedData: function(id, data) { this.cachedData[id] = data; }, unsetCachedData: function(id) { this.cachedData[id] = null; }, /** * @param {String} type * @param {String} className */ addType: function (type, className) { return BX.UI.Viewer.addType(type, className); } }; /** * @extends {BX.UI.Viewer.Controller} * @param options * @constructor */ BX.UI.Viewer.InlineController = function (options) { options = options || {}; BX.UI.Viewer.Controller.apply(this, arguments); }; BX.UI.Viewer.InlineController.prototype = { __proto__: BX.UI.Viewer.Controller.prototype, constructor: BX.UI.Viewer.Controller, adjustViewport: function(){}, addBodyPadding: function(){}, adjustZindex: function(){}, adjustViewerHeight: function(){}, // showLoading: function(){}, /** * @param {HTMLElement} node */ renderItemByNode: function (node) { if (!node) { return; } this.buildItemListByNode(node).then(function(items){ if (items.length === 0) { return; } this.setItems(items).then(function(){ this.open(0); }.bind(this)); }.bind(this)); }, getViewerContainer: function() { if (!this.layout.container) { //this.layout.inner? for showLoading this.layout.container = this.layout.inner = BX.create('div', { props: { className: 'ui-viewer-inner' }, children: [ this.getItemContainer() ] }); } return this.layout.container; }, handleClickOnItemContainer: function(){}, handleKeyPress: function(){}, temp() { // timestamp update 207392 }, }; /** * @param type * @param {HTMLElement} node * @return {BX.UI.Viewer.Item} */ BX.UI.Viewer.buildItemByTypeAndNode = function (type, node) { var item = new type(); if (!(item instanceof BX.UI.Viewer.Item)) { throw new Error("BX.UI.Viewer.buildItemByTypeAndNode: 'item' has to be instance of BX.UI.Viewer.Item."); } item.bindSourceNode(node); item.setPropertiesByNode(node); item.setActions(BX.UI.Viewer.Instance.refineItemActions(item)); return item; }; /** * @param {HTMLElement} node * @returns {BX.UI.Viewer.Item} */ BX.UI.Viewer.buildItemByNode = function (node) { if (!BX.type.isDomNode(node)) { throw new Error("BX.UI.Viewer.buildItemByNode: 'node' has to be DomNode."); } var typeCode = node.dataset.viewerType; if (!typeCode && node.tagName.toLowerCase() === 'img') { typeCode = 'image'; } BX.UI.Viewer.triggerEventToFindTypeClass(typeCode); var className = types[typeCode]; if (className) { return BX.UI.Viewer.buildItemByTypeAndNode(BX.getClass(className), node); } if (node.dataset.viewerTypeClass) { if (!BX.getClass(node.dataset.viewerTypeClass)) { throw new Error("BX.UI.Viewer.buildItemByNode: could not find class " + node.dataset.viewerTypeClass); } return BX.UI.Viewer.buildItemByTypeAndNode(BX.getClass(node.dataset.viewerTypeClass), node); } console.warn("BX.UI.Viewer.buildItemByNode: could not find class to build type {" + typeCode + "}"); return BX.UI.Viewer.buildItemByTypeAndNode(BX.getClass(types.unknown), node); }; var types = { image: 'BX.UI.Viewer.Image', plainText: 'BX.UI.Viewer.PlainText', unknown: 'BX.UI.Viewer.Unknown', video: 'BX.UI.Viewer.Video', audio: 'BX.UI.Viewer.Audio', document: 'BX.UI.Viewer.Document', code: 'BX.UI.Viewer.HightlightCode' }; /** * @param {String} type * @param {String} className */ BX.UI.Viewer.addType = function (type, className) { types[type] = className; }; BX.UI.Viewer.triggerEventToFindTypeClass = function (type) { BX.onCustomEvent('BX.UI.Viewer.Controller:onFindType', [BX.UI.Viewer.Instance, type]); }; /** * @param {HTMLElement} container * @param filter * @returns {BX.Promise} */ BX.UI.Viewer.bind = function (container, filter) { if (!BX.type.isDomNode(container)) { throw new Error("BX.UI.Viewer.bind: 'container' has to be DomNode."); } if (!BX.type.isPlainObject(filter) && !BX.type.isFunction(filter)) { filter = function(node) { return BX.type.isElementNode(node) && node.dataset.hasOwnProperty('viewer'); }; } BX.bindDelegate(container, 'click', filter, function(event) { var nodes = BX.findChildren(container, filter, true); var indexToShow = 0; var targetNode = BX.getEventTarget(event); if (targetNode.tagName !== 'A' && targetNode.closest('a[target="_blank"]')) { return false; } const openViewer = () => { const items = nodes.map((node, index) => { if (node === targetNode) { indexToShow = index; } return top.BX.UI.Viewer.buildItemByNode(node); }); top.BX.UI.Viewer.Instance.setItems(items) .then(() => { top.BX.UI.Viewer.Instance.open(indexToShow); }) .catch(() => { // Fail silently }); }; if (window.top !== window && !BX.getClass('window.top.BX.UI.Viewer.Instance')) { top.BX.loadExt('ui.viewer') .then(() => { openViewer(); }) .catch(() => { // Fail Silently }); } else { openViewer(); } event.preventDefault(); }); }; var shouldLoadExtensions = function(extension) { if (extension === 'disk.viewer.actions' && BX.getClass('BX.Disk.Viewer.Actions')) { return false; } if (extension === 'disk.viewer.document-item' && BX.getClass('BX.Disk.Viewer.DocumentItem')) { return false; } return true; } var instance = null; /** * @memberOf BX.UI.Viewer * @name BX.UI.Viewer#Instance * @type BX.UI.Viewer.Controller * @static * @readonly */ Object.defineProperty(BX.UI.Viewer, 'Instance', { enumerable: false, get: function() { if (window.top !== window && BX.getClass('window.top.BX.UI.Viewer.Instance')) { return window.top.BX.UI.Viewer.Instance; } if (instance === null) { instance = new BX.UI.Viewer.Controller({}); } return instance; } }); window.document.addEventListener('click', function(event) { if (event.button !== 0) { return; } if (window.top !== window && !BX.getClass('window.top.BX.UI.Viewer.Instance')) { top.BX.loadExt('ui.viewer').then(function () { top.BX.UI.Viewer.Instance.handleDocumentClick(event); }); } else { top.BX.UI.Viewer.Instance.handleDocumentClick(event); } }, true); //We have to show viewer only in top window not in iframe. //So we try to load ui.viewer to the top window if there is no viewer. if (window.top !== window && !BX.getClass('window.top.BX.UI.Viewer.Instance')) { top.BX.loadExt('ui.viewer'); } })();