/* * (c) Copyright Ascensio System SIA 2010-2024 * * This program is a free software product. You can redistribute it and/or * modify it under the terms of the GNU Affero General Public License (AGPL) * version 3 as published by the Free Software Foundation. In accordance with * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect * that Ascensio System SIA expressly excludes the warranty of non-infringement * of any third-party rights. * * This program is distributed WITHOUT ANY WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html * * You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish * street, Riga, Latvia, EU, LV-1050. * * The interactive user interfaces in modified source and object code versions * of the Program must display Appropriate Legal Notices, as required under * Section 5 of the GNU AGPL version 3. * * Pursuant to Section 7(b) of the License you must retain the original Product * logo when distributing the program. Pursuant to Section 7(e) we decline to * grant you any rights under trademark law for use of our trademarks. * * All the Product's GUI elements, including illustrations and icon sets, as * well as technical writing content are licensed under the terms of the * Creative Commons Attribution-ShareAlike 4.0 International. See the License * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode * */ "use strict"; (function(window, document){ // Import var g_fontApplication = AscFonts.g_fontApplication; var ImageLoadStatus = AscFonts.ImageLoadStatus; var CImage = AscFonts.CImage; function CGlobalFontLoader() { // сначала хотел писать "вытеснение" из этого мапа. // но тогда нужно хранить base64 строки. Это не круто. По памяти - даже // выигрыш будет. Не особо то шрифты жмутся lzw или deflate // поэтому лучше из памяти будем удалять base64 строки this.fonts_streams = []; // теперь вся информация о всех возможных шрифтах. Они во всех редакторах должны быть одни и те же this.fontFilesPath = "../../../../fonts/"; this.fontFiles = AscFonts.g_font_files; this.fontInfos = AscFonts.g_font_infos; this.map_font_index = AscFonts.g_map_font_index; // динамическая подгрузка шрифтов this.ThemeLoader = null; this.Api = null; this.fonts_loading = []; this.bIsLoadDocumentFirst = false; // информация для загрузки по одному шрифту this.currentInfoLoaded = null; this.loadFontCallBack = null; this.loadFontCallBackArgs = null; // при переоткрытиях файла - заменить на LoadDocumentFonts2 this.IsLoadDocumentFonts2 = false; this.check_loaded_timer_id = -1; this.endLoadingCallback = null; // Счетчик загрузки шрифтов через метод LoadFonts this.loadFontsCounter = 0; this.perfStart = 0; this.put_Api = function(api) { this.Api = api; }; // добавляем шрифт в список для загрузки this.AddLoadFonts = function(name, need_styles) { var fontinfo = g_fontApplication.GetFontInfo(name); this.fonts_loading[this.fonts_loading.length] = fontinfo; this.fonts_loading[this.fonts_loading.length - 1].NeedStyles = (need_styles === undefined) ? 0x0F : need_styles; return fontinfo; }; this.AddLoadFontsNotPick = function(info, need_styles) { this.fonts_loading[this.fonts_loading.length] = info; this.fonts_loading[this.fonts_loading.length - 1].NeedStyles = (need_styles === undefined) ? 0x0F : need_styles; }; // проверить все fontinfo из fonts_loading на нужность загрузки, и вернуть есть ли хоть один заново запущенный this.CheckFontsNeedLoadingLoad = function() { let fonts = this.fonts_loading; let isNeed = false; for (let i = 0, len = fonts.length; i < len; i++) { if (true === fonts[i].CheckFontLoadStyles(this)) isNeed = true; } return isNeed; }; // нужно ли грузить хоть один из списка (без запуска загрузки) this.CheckFontsNeedLoading = function(fonts) { for (let i in fonts) { let info = g_fontApplication.GetFontInfo(fonts[i].name); if (true === info.CheckFontLoadStylesNoLoad(this)) return true; } return false; }; this.isWorking = function() { return (this.check_loaded_timer_id !== -1) ? true : false; }; this.LoadDocumentFonts = function(fonts) { if (this.IsLoadDocumentFonts2) return this.LoadDocumentFonts2(fonts); let gui_fonts = []; let gui_count = 0; for (let i = 0; i < this.fontInfos.length; i++) { let info = this.fontInfos[i]; if (info.Name !== "ASCW3") gui_fonts[gui_count++] = new AscFonts.CFont(info.Name, "", info.Thumbnail); } // сначала заполняем массив this.fonts_loading объекстами fontinfo for (let i in fonts) { this.AddLoadFonts(fonts[i].name, fonts[i].NeedStyles); } this.Api.sync_InitEditorFonts(gui_fonts); // но только если редактор!!! if (this.Api.IsNeedDefaultFonts()) { // теперь добавим шрифты, без которых редактор как без рук (спецсимволы + дефолтовые стили документа) this.AddLoadFonts("Arial", 0x0F); this.AddLoadFonts("Symbol", 0x0F); this.AddLoadFonts("Wingdings", 0x0F); this.AddLoadFonts("Courier New", 0x0F); this.AddLoadFonts("Times New Roman", 0x0F); } this.Api.asyncFontsDocumentStartLoaded(); this.bIsLoadDocumentFirst = true; this.CheckFontsNeedLoadingLoad(); this._LoadFonts(); }; this.LoadDocumentFonts2 = function(fonts, blockType, callback) { if (this.isWorking()) { // такого быть не должно return; } this.endLoadingCallback = (undefined !== callback) ? callback : null; this.BlockOperationType = blockType; // сначала заполняем массив this.fonts_loading объекстами fontinfo for (var i in fonts) this.AddLoadFonts(fonts[i].name, 0x0F); if (null == this.ThemeLoader) this.Api.asyncFontsDocumentStartLoaded(this.BlockOperationType); else this.ThemeLoader.asyncFontsStartLoaded(); this.CheckFontsNeedLoadingLoad(); this._LoadFonts(); }; this._LoadFonts = function() { if (this.bIsLoadDocumentFirst === true && 0 === this.perfStart && this.fonts_loading.length > 0) this.perfStart = performance.now(); if (0 === this.fonts_loading.length) { if (this.perfStart > 0) { let perfEnd = performance.now(); AscCommon.sendClientLog("debug", AscCommon.getClientInfoString("onLoadFonts", perfEnd - this.perfStart), this.Api); this.perfStart = 0; } if (null != this.endLoadingCallback) { this.endLoadingCallback.call(this.Api); this.endLoadingCallback = null; } else if (null == this.ThemeLoader) this.Api.asyncFontsDocumentEndLoaded(this.BlockOperationType); else this.ThemeLoader.asyncFontsEndLoaded(); this.BlockOperationType = undefined; this.bIsLoadDocumentFirst = false; return; } if (this.fonts_loading[0].CheckFontLoadStyles(this)) { let _t = this; this.check_loaded_timer_id = setTimeout(function(){ _t.check_loaded_list(); }, 50); } else { if (this.bIsLoadDocumentFirst === true) { this.Api.OpenDocumentProgress.CurrentFont++; this.Api.SendOpenProgress(); } this.fonts_loading.shift(); this._LoadFonts(); } }; this.check_loaded_list = function() { this.check_loaded_timer_id = -1; if (0 === this.fonts_loading.length) { // значит асинхронно удалилось this._LoadFonts(); return; } let current = this.fonts_loading[0]; let isNeed = current.CheckFontLoadStyles(this); if (true === isNeed) { let _t = this; this.check_loaded_timer_id = setTimeout(function(){ _t.check_loaded_list(); }, 50); } else { if (this.bIsLoadDocumentFirst === true) { this.Api.OpenDocumentProgress.CurrentFont++; this.Api.SendOpenProgress(); } this.fonts_loading.shift(); this._LoadFonts(); } }; // одиночная загрузка шрифта this.LoadFont = function(fontinfo, loadFontCallBack, loadFontCallBackArgs) { this.currentInfoLoaded = fontinfo; this.currentInfoLoaded.NeedStyles = 15; // все стили let isNeed = this.currentInfoLoaded.CheckFontLoadStyles(this); if ( undefined === loadFontCallBack ) { this.loadFontCallBack = this.Api.asyncFontEndLoaded; this.loadFontCallBackArgs = this.currentInfoLoaded; } else { this.loadFontCallBack = loadFontCallBack; this.loadFontCallBackArgs = loadFontCallBackArgs; } if (isNeed) { this.Api.asyncFontStartLoaded(); let _t = this; setTimeout(function() { _t.check_loaded(); }, 20); return true; } else { this.currentInfoLoaded = null; return false; } }; this.check_loaded = function() { if (!this.currentInfoLoaded) return; let isNeed = this.currentInfoLoaded.CheckFontLoadStyles(this); if (isNeed) { let _t = this; setTimeout(function() { _t.check_loaded(); }, 50); } else { this.loadFontCallBack.call( this.Api, this.loadFontCallBackArgs ); this.currentInfoLoaded = null; } }; // используется только в тестовом примере (предзагрузка в кэш браузера) this.LoadFontsFromServer = function(fonts) { let count = fonts.length; for (let i = 0; i < count; i++) { let info = g_fontApplication.GetFontInfo(fonts[i]); info && info.LoadFontsFromServer(this); } }; this.isFontLoadInProgress = function() { return (this.isWorking() || this.loadFontsCounter > 0); }; this.LoadFonts = function(fonts, callback) { ++this.loadFontsCounter; let fontMap = {} if (fonts && Array.isArray(fonts)) { for (let i = 0; i < fonts.length; ++i) { let name = fonts[i]; fontMap[name] = AscFonts.g_fontApplication.GetFontInfo(name); fontMap[name].NeedStyles = 15; } } else { for (let name in fonts) { fontMap[name] = AscFonts.g_fontApplication.GetFontInfo(name); fontMap[name].NeedStyles = 15; } } let globalLoader = this; let checkLoaded = function() { let needLoad = 0; for (let name in fontMap) { if (!fontMap[name].CheckFontLoadStyles(globalLoader)) delete fontMap[name]; else ++needLoad; } if (needLoad) return setTimeout(checkLoaded, 50); if (callback) callback(); --globalLoader.loadFontsCounter; }; checkLoaded(); } } function CGlobalImageLoader() { this.map_image_index = {}; // loading this.Api = null; this.ThemeLoader = null; this.bIsLoadDocumentFirst = false; this.bIsAsyncLoadDocumentImages = false; this.isBlockchainSupport = false; var oThis = this; if (window["AscDesktopEditor"] && window["AscDesktopEditor"]["IsLocalFile"] && window["AscDesktopEditor"]["isBlockchainSupport"]) { this.isBlockchainSupport = (window["AscDesktopEditor"]["isBlockchainSupport"]() && !window["AscDesktopEditor"]["IsLocalFile"]()); if (this.isBlockchainSupport) { Image.prototype.preload_crypto = function(_url) { window["crypto_images_map"] = window["crypto_images_map"] || {}; if (!window["crypto_images_map"][_url]) window["crypto_images_map"][_url] = []; window["crypto_images_map"][_url].push(this); window["AscDesktopEditor"]["PreloadCryptoImage"](_url, AscCommon.g_oDocumentUrls.getLocal(_url)); oThis.Api.sync_StartAction(Asc.c_oAscAsyncActionType.BlockInteraction, Asc.c_oAscAsyncAction.LoadImage); }; Image.prototype["onload_crypto"] = function(_src, _crypto_data) { if (_crypto_data && AscCommon.EncryptionWorker && AscCommon.EncryptionWorker.isCryptoImages()) { AscCommon.EncryptionWorker.decryptImage(_src, this, _crypto_data); return; } this.crossOrigin = ""; this.src = _src; oThis.Api.sync_EndAction(Asc.c_oAscAsyncActionType.BlockInteraction, Asc.c_oAscAsyncAction.LoadImage); }; } } this.put_Api = function(api) { this.Api = api; if (this.Api.IsAsyncOpenDocumentImages !== undefined) { this.bIsAsyncLoadDocumentImages = this.Api.IsAsyncOpenDocumentImages(); if (this.bIsAsyncLoadDocumentImages) { if (undefined === this.Api.asyncImageEndLoadedBackground) this.bIsAsyncLoadDocumentImages = false; } } }; this.loadImageByUrl = function(image, url, isDisableCrypto) { if (this.isBlockchainSupport && (true !== isDisableCrypto)) image.preload_crypto(url); else image.src = url; }; this.LoadDocumentImages = function (images, isCheckExists, syncImages) { if (isCheckExists) { for (let i = images.length - 1; i >= 0; i--) { let id = AscCommon.getFullImageSrc2(images[i]); if (this.map_image_index[id] && (this.map_image_index[id].Status === ImageLoadStatus.Complete)) { images.splice(i, 1); } } if (0 === images.length) return; } // сначала заполним массив const oRequiredSyncImages = {}; const arrImagesLoading = []; for (let id in images) { const sFullImageSrc = AscCommon.getFullImageSrc2(images[id]); arrImagesLoading.push(sFullImageSrc); if (syncImages && syncImages[images[id]]) { oRequiredSyncImages[sFullImageSrc] = true; } } if (syncImages) { for (let id in syncImages) { const sFullImageSrc = AscCommon.getFullImageSrc2(id); if (!oRequiredSyncImages[sFullImageSrc]) { arrImagesLoading.push(sFullImageSrc); oRequiredSyncImages[sFullImageSrc] = true; } } } if (this.ThemeLoader == null) this.Api.asyncImagesDocumentStartLoaded(arrImagesLoading); else this.ThemeLoader.asyncImagesStartLoaded(arrImagesLoading); if (!this.bIsAsyncLoadDocumentImages) { this._LoadImages(arrImagesLoading); } else { this._LoadImagesAsync(arrImagesLoading, oRequiredSyncImages); } }; this._LoadImages = function (arrImages) { let fOnEachImageLoadCallback; if (oThis.bIsLoadDocumentFirst === true) { fOnEachImageLoadCallback = function () { oThis.Api.OpenDocumentProgress.CurrentImage++; oThis.Api.SendOpenProgress(); }; } this.LoadImagesWithCallback(arrImages, function () { if (oThis.ThemeLoader == null) oThis.Api.asyncImagesDocumentEndLoaded(); else oThis.ThemeLoader.asyncImagesEndLoaded(); }, [], false, fOnEachImageLoadCallback); }; this._LoadImagesAsync = function (arrImages, oRequiredSyncImages) { const arrAsyncImages = []; const arrSyncImages = []; for (let i = 0; i < arrImages.length; i += 1) { if (oRequiredSyncImages[arrImages[i]]) { arrSyncImages.push(arrImages[i]); } else { arrAsyncImages.push(arrImages[i]); } } let fOnEachImageLoadCallback; if (oThis.bIsLoadDocumentFirst === true) { fOnEachImageLoadCallback = function () { oThis.Api.OpenDocumentProgress.CurrentImage++; oThis.Api.SendOpenProgress(); }; } this.LoadImagesWithCallback(arrSyncImages, function () { for (let i = 0; i < arrAsyncImages.length; i += 1) { oThis.LoadImageAsync(arrAsyncImages[i]); } if (oThis.ThemeLoader == null) oThis.Api.asyncImagesDocumentEndLoaded(); else oThis.ThemeLoader.asyncImagesEndLoaded(); }, [], false, fOnEachImageLoadCallback); }; this.LoadImage = function(src, type) { var image = this.map_image_index[src]; if (undefined != image) return image; this.Api.asyncImageStartLoaded(); var oImage = new CImage(src); // просто прокидываем параметр oImage.Type = type; oImage.Image = new Image(); oImage.Status = ImageLoadStatus.Loading; oThis.map_image_index[oImage.src] = oImage; oImage.Image.onload = function() { oImage.Status = ImageLoadStatus.Complete; oThis.Api.asyncImageEndLoaded(oImage); }; oImage.Image.onerror = function() { oImage.Image = null; oImage.Status = ImageLoadStatus.Complete; oThis.Api.asyncImageEndLoaded(oImage); }; AscCommon.backoffOnErrorImg(oImage.Image, function(img) { // Remove crossOrigin on retry to maximize display success img.crossOrigin = null; oThis.loadImageByUrl(img, img.src); }); // Enable CORS for cross-origin images to allow canvas manipulation oImage.Image.crossOrigin = 'anonymous'; this.loadImageByUrl(oImage.Image, oImage.src); return null; }; this.LoadImageAsync = function(imgSrc) { var oImage = new CImage(imgSrc); oImage.Status = ImageLoadStatus.Loading; oImage.Image = new Image(); oThis.map_image_index[oImage.src] = oImage; oImage.Image.onload = function() { oImage.Status = ImageLoadStatus.Complete; oThis.Api.asyncImageEndLoadedBackground(oImage); }; oImage.Image.onerror = function() { oImage.Status = ImageLoadStatus.Complete; oImage.Image = null; oThis.Api.asyncImageEndLoadedBackground(oImage); }; AscCommon.backoffOnErrorImg(oImage.Image, function(img) { // Remove crossOrigin on retry to maximize display success img.crossOrigin = null; oThis.loadImageByUrl(img, img.src); }); // Enable CORS for cross-origin images to allow canvas manipulation oImage.Image.crossOrigin = 'anonymous'; oThis.loadImageByUrl(oImage.Image, oImage.src); }; this.LoadImagesWithCallback = function(arr, loadImageCallBack, loadImageCallBackArgs, isDisableCrypto, onEachImageLoadCallback) { let arrAsync = []; for (let i = 0; i < arr.length; i++) { if (this.map_image_index[arr[i]] && (this.map_image_index[arr[i]].Status === ImageLoadStatus.Complete)) continue; arrAsync.push(arr[i]); } if (arrAsync.length == 0) { loadImageCallBack.call(this.Api, loadImageCallBackArgs); return; } let asyncImageCounter = arrAsync.length; const callback = loadImageCallBack.bind(this.Api, loadImageCallBackArgs); for (let i = 0; i < arrAsync.length; i++) { var oImage = new CImage(arrAsync[i]); oImage.Image = new Image(); oImage.Image.parentImage = oImage; oImage.Status = ImageLoadStatus.Loading; this.map_image_index[oImage.src] = oImage; oImage.Image.onload = function () { this.parentImage.Status = ImageLoadStatus.Complete; asyncImageCounter--; onEachImageLoadCallback && onEachImageLoadCallback(); if (asyncImageCounter === 0) callback(); }; oImage.Image.onerror = function () { this.parentImage.Image = null; this.parentImage.Status = ImageLoadStatus.Complete; asyncImageCounter--; onEachImageLoadCallback && onEachImageLoadCallback(); if (asyncImageCounter === 0) callback(); }; AscCommon.backoffOnErrorImg(oImage.Image, function(img) { // Remove crossOrigin on retry to maximize display success img.crossOrigin = null; oThis.loadImageByUrl(img, img.src); }); // Enable CORS for cross-origin images to allow canvas manipulation oImage.Image.crossOrigin = 'anonymous'; this.loadImageByUrl(oImage.Image, oImage.src, isDisableCrypto); } }; } //---------------------------------------------------------export--------------------------------------------------- window['AscCommon'] = window['AscCommon'] || {}; window['AscCommon'].CGlobalFontLoader = CGlobalFontLoader; window['AscCommon'].g_font_loader = new CGlobalFontLoader(); window['AscCommon'].g_image_loader = new CGlobalImageLoader(); })(window, window.document);