From bf8c793c502c7f8f79a17b704b0159f3339838f2 Mon Sep 17 00:00:00 2001 From: ignaciogros Date: Sat, 17 Jan 2026 10:02:29 +0100 Subject: [PATCH 01/41] Fix #1023 - Integrate v3.0.2 TinyMCE fixes and remove the image optimization tool from the `exeimage` plugin. --- public/app/editor/tinymce_5_settings.js | 2 +- public/libs/README | 6 - public/libs/tinymce_5/js/tinymce/langs/all.js | 5 +- .../tinymce/plugins/codemagic/codemagic.html | 10 +- .../tinymce/plugins/exeimage/css/content.css | 2 +- .../plugins/exeimage/image-compressor/LICENSE | 21 - .../exeimage/image-compressor/README.md | 10 - .../exeimage/image-compressor/css/main.css | 28 - .../image-compressor/image-compressor.test.js | 213 - .../image-compressor/images/exelearning.png | Bin 1996 -> 0 bytes .../exeimage/image-compressor/index.html | 73 - .../image-compressor/js/compressor.min.js | 16 - .../exeimage/image-compressor/js/langs/all.js | 41 - .../exeimage/image-compressor/js/main.js | 507 -- .../exeimage/image-compressor/js/vue.min.js | 6 - .../js/tinymce/plugins/exeimage/plugin.min.js | 5798 +++++++++-------- .../tinymce/plugins/exemedia/css/content.css | 13 +- .../js/tinymce/plugins/exemedia/plugin.min.js | 965 ++- .../tinymce/plugins/exemermaid/plugin.min.js | 11 +- .../tinymce/plugins/template/css/content.css | 10 +- 20 files changed, 4152 insertions(+), 3585 deletions(-) delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/LICENSE delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/README.md delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/css/main.css delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/image-compressor.test.js delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/images/exelearning.png delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/index.html delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/compressor.min.js delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/langs/all.js delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/main.js delete mode 100644 public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/vue.min.js diff --git a/public/app/editor/tinymce_5_settings.js b/public/app/editor/tinymce_5_settings.js index 976b5a03c..234adb821 100644 --- a/public/app/editor/tinymce_5_settings.js +++ b/public/app/editor/tinymce_5_settings.js @@ -23,7 +23,7 @@ var $exeTinyMCE = { }, insert: { title: 'Insert', - items: 'template | hr charmap anchor clearfloat addcontent | abbr insertdatetime', + items: 'template | hr charmap anchor clearfloat addcontent exemermaid | abbr insertdatetime', }, // ' | exegames_hangman' removed format: { title: 'Format', diff --git a/public/libs/README b/public/libs/README index 8f1a6c963..b96d52be5 100644 --- a/public/libs/README +++ b/public/libs/README @@ -586,12 +586,6 @@ * Files: /public/libs/tinymce\_5/\* * Copyright: Tiny Technologies Inc. * License: LGPL-2.1 -* Files: /public/libs/tinymce\_5/js/tinymce/plugins/exeimage/image-compressor/js/compressor.min.js - * Copyright: Fengyuan Chen - * License: MIT -* Files: /public/libs/tinymce\_5/js/tinymce/plugins/exeimage/image-compressor/js/vue.min.js - * Copyright: Evan You - * License: MIT * Files: /public/libs/tinymce\_5/js/tinymce/plugins/exemindmap/editor (cropper.js, jquery-cropper.js and CSS) * Copyright: Chen Fengyuan * License: MIT diff --git a/public/libs/tinymce_5/js/tinymce/langs/all.js b/public/libs/tinymce_5/js/tinymce/langs/all.js index 532352540..cf79d9c7d 100644 --- a/public/libs/tinymce_5/js/tinymce/langs/all.js +++ b/public/libs/tinymce_5/js/tinymce/langs/all.js @@ -435,5 +435,8 @@ tinymce.addI18n('all',{ "Extended Latin": _("Extended Latin"), "Symbols": _("Symbols"), "Arrows": _("Arrows"), - +// Mermaid diagram +"Mermaid diagram": _("Mermaid diagram"), +"Mermaid code": _("Mermaid code"), +"Need help? Visit %s": _("Need help? Visit %s"), }); \ No newline at end of file diff --git a/public/libs/tinymce_5/js/tinymce/plugins/codemagic/codemagic.html b/public/libs/tinymce_5/js/tinymce/plugins/codemagic/codemagic.html index 1da10fdfb..650fba73c 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/codemagic/codemagic.html +++ b/public/libs/tinymce_5/js/tinymce/plugins/codemagic/codemagic.html @@ -8,15 +8,7 @@ */ --> - + diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css index fcb122d41..bcdcb1a01 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css +++ b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css @@ -4,6 +4,6 @@ .position-right{margin:1.5em 0 1.5em auto} .float-left{float:left;margin:.5em 1.5em 1em 0} .float-right{float:right;margin:.5em 0 1em 1.5em} -.figcaption{padding-top:.2em} +.figcaption{padding-top:.2em;line-height:1em} .figcaption.header{padding-top:0;padding-bottom:.2em} /* /The New eXeLearning */ \ No newline at end of file diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/LICENSE b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/LICENSE deleted file mode 100644 index 4ca99accb..000000000 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright 2015-present Chen Fengyuan - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/README.md b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/README.md deleted file mode 100644 index 6aad49460..000000000 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Compressor.js -JavaScript image compressor. It uses the browser's native canvas.toBlob API to do the compression work, which means it is lossy compression. -[compressorjs](https://github.com/fengyuanchen/compressorjs), by [Fengyuan Chen](https://chenfengyuan.com/en/), released under the MIT license. -See the LICENSE file for more information. - -# Vue.js -It includes [Vue.js v2.6.11](https://github.com/vuejs/vue/releases/tag/v2.6.11), by [Evan You](https://evanyou.me/), released under the MIT license. - -# Compressor for eXeLearning -Adapted for eXeLearning by [Ignacio Gros](http://www.gros.es/), released under the MIT license. \ No newline at end of file diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/css/main.css b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/css/main.css deleted file mode 100644 index 77b55b775..000000000 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/css/main.css +++ /dev/null @@ -1,28 +0,0 @@ -/* eXeLearning */ -body{background:#FFF;padding:20px;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-size:14px;color:#333741;margin:0} -input, select{font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;font-size:14px} -#imageEditorOptions{margin:20px 0} -label[for='inputMaxWidth'],#inputMaxWidth, -label[for='inputMaxHeight'],#inputMaxHeight, -label[for='inputWidth'],#inputWidth, -label[for='inputHeight'],#inputHeight, -label[for='inputQuality'],#inputQuality{display:none} - - -#imageEditorOptions p{margin:0} -#imageEditorOptions input,#imageEditorOptions select{width:70px;margin-right:20px} -#imageEditorData .imageEditorData{margin-right:20px} -#imageEditorPreviewer{max-width:100%;height:300px;line-height:300px;max-height:300px;margin-bottom:20px;text-align:center; -/* background: url("../images/exelearning.png"); */ -} -#imageEditorOutputImg{max-width:100%;max-height:300px;vertical-align:middle} -.sr-only{position:absolute!important;clip:rect(1px, 1px, 1px, 1px)} -#app{width:900px;margin:0 auto} -#imageEditorUploader{background:#F8F9FA;padding:20px;border:1px solid #D9DADB;text-align:center} -#imageEditorUploader p{margin:0} -a,#imageEditorUploader label{color:rgb(0, 123, 255);text-decoration:none;cursor:pointer} -a:hover,a:focus{text-decoration:underline} -#imageEditorBackupMessage{float:right;margin-right:20px} -#imageEditorSaveImg{background:#2276D2;color:#FFF;padding:7px 10px;text-decoration:none;float:right; - /* display:none */ -} \ No newline at end of file diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/image-compressor.test.js b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/image-compressor.test.js deleted file mode 100644 index 591cdadb8..000000000 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/image-compressor.test.js +++ /dev/null @@ -1,213 +0,0 @@ -/** - * Image Compressor Bun Tests - * - * Unit tests for the image-compressor URL detection and dimension calculation logic. - * These test the key functions modified to support blob/asset URLs from IndexedDB. - * - * Run with: bun test public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/__tests__/ - */ - -/* eslint-disable no-undef */ - -describe('Image Compressor URL Detection', () => { - describe('URL type detection', () => { - it('should detect blob: URLs', () => { - const url = 'blob:http://localhost:3001/12345-abcde'; - expect(url.startsWith('blob:')).toBe(true); - expect(url.startsWith('asset://')).toBe(false); - expect(url.startsWith('data:')).toBe(false); - }); - - it('should detect asset:// URLs', () => { - const url = 'asset://91bbffad-6b30-56d5-1b62-0db6704559e8/image.jpg'; - expect(url.startsWith('blob:')).toBe(false); - expect(url.startsWith('asset://')).toBe(true); - expect(url.startsWith('data:')).toBe(false); - }); - - it('should detect data: URLs', () => { - const url = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=='; - expect(url.startsWith('blob:')).toBe(false); - expect(url.startsWith('asset://')).toBe(false); - expect(url.startsWith('data:')).toBe(true); - }); - - it('should detect regular file URLs', () => { - const url = 'files/tmp/session123/image.png'; - expect(url.startsWith('blob:')).toBe(false); - expect(url.startsWith('asset://')).toBe(false); - expect(url.startsWith('data:')).toBe(false); - }); - - it('should detect resources/ URLs', () => { - const url = 'resources/image.png'; - expect(url.indexOf('resources/') === 0).toBe(true); - }); - }); - - describe('Dimension calculation', () => { - function calculateDimensions(originalWidth, originalHeight, newsize) { - const aspectRatio = originalWidth / originalHeight || 1; - let newWidth, newHeight; - - if (aspectRatio > 1) { - newWidth = newsize; - newHeight = Math.round(newsize / aspectRatio); - } else if (aspectRatio < 1) { - newHeight = newsize; - newWidth = Math.round(newsize * aspectRatio); - } else { - newWidth = newHeight = newsize; - } - - return { newWidth, newHeight }; - } - - it('should calculate dimensions for landscape image (width > height)', () => { - const result = calculateDimensions(800, 600, 400); - expect(result.newWidth).toBe(400); - expect(result.newHeight).toBe(300); - }); - - it('should calculate dimensions for portrait image (height > width)', () => { - const result = calculateDimensions(600, 800, 400); - expect(result.newWidth).toBe(300); - expect(result.newHeight).toBe(400); - }); - - it('should calculate dimensions for square image', () => { - const result = calculateDimensions(500, 500, 300); - expect(result.newWidth).toBe(300); - expect(result.newHeight).toBe(300); - }); - - it('should handle zero dimensions with fallback', () => { - const result = calculateDimensions(0, 0, 300); - // aspectRatio would be NaN, || 1 makes it 1 - expect(result.newWidth).toBe(300); - expect(result.newHeight).toBe(300); - }); - }); - - describe('Asset ID extraction', () => { - function extractAssetId(assetUrl) { - return assetUrl.replace('asset://', '').split('/')[0]; - } - - it('should extract asset ID from asset:// URL with filename', () => { - const url = 'asset://91bbffad-6b30-56d5-1b62-0db6704559e8/image.jpg'; - expect(extractAssetId(url)).toBe('91bbffad-6b30-56d5-1b62-0db6704559e8'); - }); - - it('should extract asset ID from asset:// URL without filename', () => { - const url = 'asset://91bbffad-6b30-56d5-1b62-0db6704559e8'; - expect(extractAssetId(url)).toBe('91bbffad-6b30-56d5-1b62-0db6704559e8'); - }); - }); - - describe('Filename extraction', () => { - function extractFilename(url) { - if (url.startsWith('blob:')) { - return 'optimized_image.jpg'; - } - return url.split('/').pop().split('_').pop(); - } - - it('should return default filename for blob URLs', () => { - const url = 'blob:http://localhost:3001/12345-abcde'; - expect(extractFilename(url)).toBe('optimized_image.jpg'); - }); - - it('should extract filename from regular path', () => { - const url = 'files/tmp/session/prefix_myimage.png'; - expect(extractFilename(url)).toBe('myimage.png'); - }); - }); -}); - -describe('ExeImage Plugin skipSubmit Flag', () => { - describe('skipSubmit behavior', () => { - let mockImgCompressor; - - beforeEach(() => { - mockImgCompressor = { - originalSrc: 'blob:http://localhost:3001/test', - isBlob: true, - isAsset: false, - skipSubmit: false, - }; - }); - - it('should check skipSubmit flag and reset it', () => { - // Simulate mySubmit check - mockImgCompressor.skipSubmit = true; - - if (mockImgCompressor && mockImgCompressor.skipSubmit) { - mockImgCompressor.skipSubmit = false; - // api.close() would be called here - } - - expect(mockImgCompressor.skipSubmit).toBe(false); - }); - - it('should not skip when skipSubmit is false', () => { - mockImgCompressor.skipSubmit = false; - let skipped = false; - - if (mockImgCompressor && mockImgCompressor.skipSubmit) { - skipped = true; - } - - expect(skipped).toBe(false); - }); - - it('should detect blob image type correctly', () => { - mockImgCompressor.originalSrc = 'blob:http://localhost:3001/test'; - mockImgCompressor.isBlob = mockImgCompressor.originalSrc.startsWith('blob:'); - mockImgCompressor.isAsset = mockImgCompressor.originalSrc.startsWith('asset://'); - - expect(mockImgCompressor.isBlob).toBe(true); - expect(mockImgCompressor.isAsset).toBe(false); - }); - - it('should detect asset image type correctly', () => { - mockImgCompressor.originalSrc = 'asset://uuid-here/filename.jpg'; - mockImgCompressor.isBlob = mockImgCompressor.originalSrc.startsWith('blob:'); - mockImgCompressor.isAsset = mockImgCompressor.originalSrc.startsWith('asset://'); - - expect(mockImgCompressor.isBlob).toBe(false); - expect(mockImgCompressor.isAsset).toBe(true); - }); - }); -}); - -describe('Image extension detection', () => { - function getExtensionFromDataUrl(src) { - if (src.indexOf('data:image/') !== 0) return null; - - let ext = src.replace('data:image/', ''); - ext = ext.split(';')[0].toLowerCase(); - if (ext === 'jpeg') ext = 'jpg'; - return ext; - } - - it('should extract png extension', () => { - const src = 'data:image/png;base64,iVBORw0KGgo...'; - expect(getExtensionFromDataUrl(src)).toBe('png'); - }); - - it('should extract and normalize jpeg to jpg', () => { - const src = 'data:image/jpeg;base64,/9j/4AAQSkZJRg...'; - expect(getExtensionFromDataUrl(src)).toBe('jpg'); - }); - - it('should extract gif extension', () => { - const src = 'data:image/gif;base64,R0lGODlhAQAB...'; - expect(getExtensionFromDataUrl(src)).toBe('gif'); - }); - - it('should return null for non-data URLs', () => { - const src = 'blob:http://localhost:3001/test'; - expect(getExtensionFromDataUrl(src)).toBe(null); - }); -}); diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/images/exelearning.png b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/images/exelearning.png deleted file mode 100644 index 581371e5e7cda42130582b54c2030c4c57b2bba3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1996 zcmV;-2Q&DIP)>i@T@l=ZSYd1brsQx>k2Mim8sO0~Ge-q^ooGP(Gz7Uc;5#?-3 zJ>CN!1o|3`EOdU>0i4+|9oHS@m1mq z2{@@AfDvma_5t;HG8~`FX-6f$QIDfbiB9=UD)rxy>|)DLS#g2{06EW2!-r6F4b-5V zUb~fp9SV*#m3(5wW0!m=fJUYGukny1eRo4WX$cb#MRVc2k0|$l0e%r20{1w9jDPMG z!RAIoLX>j@Og3z_>c>Ba(@(Xx6}>IIt09>g@c2_y@-2*nq%qsF5>dPxOf(vxu;1)7 zPgLQC`2XN&!p(3E$L~@p)-(65{dXpZI_5N+r7t8u zH474t7rcHC`$M<-^WWlfC+q$9H}* zm3&CV#tdLzWfCOgU{ZA;0Oia4BXaA$;vP8KMilH1OoC)|gmu3-&qTN64dfE7D6~pfrOz!1JR_$tY~EfB(S%mL8~t+ zo5nQc*xH8Nxbhp(sMQx7%i$zu$lHxL0*xBP>Bjxn%B22p$adqbOC%qJhOO>VUB4l{ zY*ai6mHZVnY?US1#YRB_&+_?ko~Vj7s@4HWbruATB7XC0Mg~F?CAO(*pv!L>qJ9WCin~uPtJ1J&=c)}#x55S(GT8hj8F4y%(U!9JyCj23Th_YImISn zTu=B`Gn{p0GXkF&e)(wMsYjgBRb}K?0M8JXwUGz-dN0)3+GVZNJ!7I)VV> z<)tU!3IdVwskh=@4hujz8k{nOVyTi^o5vm!5F~RiUU2}$CgvrNAtX>*fqLR8if}9t zQO>PceQ_~CSqLim=Lm>^Th&kC3kM-tiXb@(a$mIK3`&nBj=KoSlAsXD{)i_akelpm zhb20T5P0-jshB}BgG%Q_EYVpqC@j?~CXhf4S}f67CMXc_3;DRF2N7AB)PE6@ilXhC zrknfx?{tA$uUPE2OxbC+Lw;Vap&CaNKMf%%7m(Pz>p)U{)4>P{0#Zl=;lUFjK|l(r z3)J67ND2Vb3IK_B9P3YZTC9eUEJaqvX+!4&f_f4mSxk_PwrJI86x6>(Nb;H!wDMRp zg0X~V5ak?&kZ4iKKSdyxB{pcYfxLaJAxUIAKDqn~hbX9FZ)m@p!fMEMz7e9qK?p-Z z-5NVcHXGREXri3yC9EPC?<(0ov4I5IfIx5!0#Z&Mu)Cr0kU-R#2E-}1kUvViXFMdE z4ea?S^*H(=6^JPJJYyjt%Gt9TaUlqaGu+CgsBw@$7i}xk@+s?g$7P0v@*F5 z4H{?%t-Hd91Ydb0EuwfDDl*pjO2Qm8X5c5C6Y3li3`sPh0C3Qdj738R?w5RtEnjP| z@%2{I1Xq$83TmTRnSZvm)8g-XYHd>QkPNJ&$1@i?WX|vSMWypytNM7c9#iK%_*_vJ zx2)c{xnkT^74IdCbvoZV6eg;IQiB?4S)(<&k1JbHhL%!^xyp839!C-)OD28YONwDWxJmdv8$iz6TV~WrG_tsVhV1xbgpa zTTmC13f+WImz7Tlwk4VSaYL51DL9_s+uL5L)vMaKt2){K(in8}WQS;F0|3OKX?{+W zDg;r<*CC2uqQCzae25B9GGA)=c8pEw3CmA)pwg+*4JMOt$wfWBN~lm2*oL%rpBMA% zYE3G#_stNkH>hNX#;!g|-UHfsL^?f7g@sB1N9rH+_m>CIA13gOM)1!duUG47$jGxL zbLWbGO_g{@>vkxIrL`#**3)Xpv9-Zhfjp9V{en}}<0BwoDNnuTLct#eu#(Dc8|t_N e00$J+@y!MjT;^h6KXOn20000 - - - - - - - - -Compressor.js - - - -
-
- -
-

- - - - - - - - - - - - - - - - - -

-
-
-
- -
-

- Name: {{ output.name || 'N/A' }} - Before: {{ input.size | prettySize }} - After: {{ output.size | prettySize }} (-{{ ((1 - (output.size / input.size)) * 100).toFixed(0) }}%) - Finish - - -

-
-
-
- - - - - - - diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/compressor.min.js b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/compressor.min.js deleted file mode 100644 index 62e34a0ce..000000000 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/compressor.min.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - eXeLearning changes (i18n only): - invalid data URI → Please enter a valid URL. - The first argument must be an image File or Blob object. → Please select an image. - The first argument must be a File or Blob object. → Please select an image. -*/ -/*! - * Compressor.js v1.0.6 - * https://fengyuanchen.github.io/compressorjs - * - * Copyright 2018-present Chen Fengyuan - * Released under the MIT license - * - * Date: 2019-11-23T04:43:12.442Z - */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Compressor=t()}(this,function(){"use strict";function a(e,t){for(var r=0;rh.convertSize&&"image/png"===h.mimeType&&(D="#fff",h.mimeType="image/jpeg"),b.fillStyle=D,b.fillRect(0,0,O,U),h.beforeDraw&&h.beforeDraw.call(this,b,d),!this.aborted&&(b.save(),b.translate(O/2,U/2),b.rotate(n*Math.PI/180),b.scale(l,c),b.drawImage(f,x,A,M,T),b.restore(),h.drew&&h.drew.call(this,b,d),!this.aborted)){var E=function(e){t.aborted||t.done({naturalWidth:r,naturalHeight:a,result:e})};d.toBlob?d.toBlob(E,h.mimeType,h.quality):E(L(d.toDataURL(h.mimeType,h.quality)))}}},{key:"done",value:function(e){var t=e.naturalWidth,r=e.naturalHeight,a=e.result,i=this.file,n=this.image,o=this.options;if(m&&!o.checkOrientation&&m.revokeObjectURL(n.src),a)if(o.strict&&a.size>i.size&&o.mimeType===i.type&&!(o.width>t||o.height>r||o.minWidth>t||o.minHeight>r))a=i;else{var l=new Date;a.lastModified=l.getTime(),a.lastModifiedDate=l,a.name=i.name,a.name&&a.type!==i.type&&(a.name=a.name.replace(p,function(e){var t=P(e)?e.substr(6):"";return"jpeg"===t&&(t="jpg"),".".concat(t)}(a.type)))}else a=i;this.result=a,o.success&&o.success.call(this,a)}},{key:"fail",value:function(e){var t=this.options;if(!t.error)throw e;t.error.call(this,e)}},{key:"abort",value:function(){this.aborted||(this.aborted=!0,this.reader?this.reader.abort():this.image.complete?this.fail(new Error("The compression process has been aborted.")):(this.image.onload=null,this.image.onabort()))}}],[{key:"noConflict",value:function(){return window.Compressor=g,r}},{key:"setDefaults",value:function(e){s(n,e)}}]),r}()}); \ No newline at end of file diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/langs/all.js b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/langs/all.js deleted file mode 100644 index 150ba8760..000000000 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/langs/all.js +++ /dev/null @@ -1,41 +0,0 @@ -// Image compressor translation file -// Uses IIFE to ensure proper initialization order -(function(global) { - 'use strict'; - - // Define translation function with proper fallback - var translateFn; - try { - if (typeof top !== 'undefined' && top !== null && typeof top._ === 'function') { - translateFn = top._; - } else if (typeof global._ === 'function') { - translateFn = global._; - } else { - translateFn = function(str) { return str; }; - } - } catch (e) { - // Cross-origin or security error - translateFn = function(str) { return str; }; - } - - // Export as global _ for this context - global._ = translateFn; - - // Create i18n object - global.$i18n = { - imageOptimizer: translateFn("Image optimizer"), - uploadInstructions: translateFn("Drop image here or $browse...$"), - size: translateFn('Size'), - maxWidth: translateFn("Max width"), - maxHeight: translateFn("Max height"), - width: translateFn("Width"), - height: translateFn("Height"), - quality: translateFn("Quality"), - name: translateFn("Name"), - originalSize: translateFn("Before"), - resultSize: translateFn("After"), - finish: translateFn("Finish"), - newImageWarning: translateFn("You just added that image. Save the iDevice or select the image again to update it."), - backupWarning: translateFn("$Save the current image$ before overwriting it.") - }; -})(typeof window !== 'undefined' ? window : this); diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/main.js b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/main.js deleted file mode 100644 index 216622dd7..000000000 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/main.js +++ /dev/null @@ -1,507 +0,0 @@ -window.addEventListener('DOMContentLoaded', function () { - var Vue = window.Vue; - var URL = window.URL || window.webkitURL; - var XMLHttpRequest = window.XMLHttpRequest; - var Compressor = window.Compressor; - - new Vue({ - el: '#app', - - data: function () { - var vm = this; - - return { - options: { - strict: true, - checkOrientation: true, - // maxWidth: eXeImageCompressor.sizeLimit, - // maxHeight: eXeImageCompressor.sizeLimit, - maxWidth: eXeImageCompressor.imgMaxSize, - maxHeight: eXeImageCompressor.imgMaxSize, - minWidth: 0, - minHeight: 0, - width: undefined, - height: undefined, - quality: 0.95, - mimeType: '', - convertSize: 5000000, - success: function (result) { - var reader = new FileReader(); - reader.readAsDataURL(result); - reader.onloadend = function () { - var base64data = reader.result; - vm.outputURL = base64data; - } - // console.log('Output: ', result); - - if (URL) { - vm.outputURL = URL.createObjectURL(result); - } - - vm.output = result; - // See #487 vm.$refs.input.value = ''; - }, - error: function (err) { - window.alert(err.message); - }, - }, - inputURL: '', - outputURL: '', - input: {}, - output: {}, - }; - }, - - filters: { - prettySize: function (size) { - var kilobyte = 1024; - var megabyte = kilobyte * kilobyte; - - if (size > megabyte) { - return (size / megabyte).toFixed(2) + ' MB'; - } else if (size > kilobyte) { - return (size / kilobyte).toFixed(2) + ' KB'; - } else if (size >= 0) { - return size + ' B'; - } - - return 'N/A'; - }, - }, - - methods: { - compress: function (file) { - if (!file) { - return; - } - - // console.log('Input: ', file); - - if (URL) { - this.inputURL = URL.createObjectURL(file); - } - - this.input = file; - new Compressor(file, this.options); - }, - - change: function (e) { - var file = e.target.files ? e.target.files[0] : null; - if (file) { - // Reset firstImageLoaded so loadImage() can update the field - eXeImageCompressor.firstImageLoaded = false; - jQuery("#inputSize").val(""); - jQuery("#inputMaxWidth").val(eXeImageCompressor.maxSize)[0].dispatchEvent(new Event('input')); - jQuery("#inputMaxHeight").val(eXeImageCompressor.maxSize)[0].dispatchEvent(new Event('input')); - var fileUrl = URL.createObjectURL(file); - eXeImageCompressor.loadImage(fileUrl); - } - this.compress(file); - }, - - dragover: function (e) { - e.preventDefault(); - }, - - drop: function (e) { - e.preventDefault(); - // eXeLearning - jQuery("#inputSize").val(""); - jQuery("#inputMaxWidth").val(eXeImageCompressor.maxSize)[0].dispatchEvent(new Event('input')); - jQuery("#inputMaxHeight").val(eXeImageCompressor.maxSize)[0].dispatchEvent(new Event('input')); - // Reset firstImageLoaded so loadImage() can update the field - eXeImageCompressor.firstImageLoaded = false; - // / eXeLearning - var file = e.dataTransfer.files ? e.dataTransfer.files[0] : null; - if (file) { - var fileUrl = URL.createObjectURL(file); - eXeImageCompressor.loadImage(fileUrl); - } - this.compress(file); - }, - }, - - watch: { - options: { - deep: true, - handler: function () { - this.compress(this.input); - }, - }, - }, - - mounted: function () { - if (!XMLHttpRequest) { - return; - } - - // eXeLearning - // var name = "exelearning.png"; - // var url = "images/"+name; - var originalSrc = top.imgCompressor.originalSrc; - var assetManager = top.eXeLearning?.app?.project?._yjsBridge?.assetManager; - - // Detect URL type for blob/asset handling - var isBlob = originalSrc.startsWith('blob:'); - var isAsset = originalSrc.startsWith('asset://'); - var isDataUrl = originalSrc.startsWith('data:'); - - // Store URL type info for save handler - top.imgCompressor.isBlob = isBlob; - top.imgCompressor.isAsset = isAsset; - top.imgCompressor.assetId = null; - - if (isAsset) { - if (assetManager && typeof assetManager.extractAssetId === 'function') { - top.imgCompressor.assetId = assetManager.extractAssetId(originalSrc); - } else { - var rawAssetId = originalSrc.replace('asset://', '').split('/')[0]; - var dotIndex = rawAssetId.indexOf('.'); - if (dotIndex > 0) rawAssetId = rawAssetId.substring(0, dotIndex); - top.imgCompressor.assetId = rawAssetId; - } - } - - if (!top.imgCompressor.assetId && isBlob && assetManager && assetManager.reverseBlobCache) { - var cachedAssetId = assetManager.reverseBlobCache.get(originalSrc); - if (cachedAssetId) { - top.imgCompressor.assetId = cachedAssetId; - } - } - - if (!top.imgCompressor.assetId) { - var selectedImg = top.tinymce?.activeEditor?.selection?.getNode?.(); - var dataAssetId = selectedImg && selectedImg.getAttribute ? selectedImg.getAttribute('data-asset-id') : null; - if (dataAssetId) { - top.imgCompressor.assetId = dataAssetId; - } - } - - var ext = originalSrc.split('.').pop(); - ext = ext.toLowerCase(); - if (ext == "jpg" || ext == "jpeg" || originalSrc.indexOf("data:image/jpeg") == 0) jQuery("#inputQuality,label[for='inputQuality']").show(); - else jQuery("#inputQuality,label[for='inputQuality']").hide(); - - var name = originalSrc.split("/").pop().split("_").pop(); - // For blob URLs, extract a reasonable filename - if (isBlob) { - name = 'optimized_image.jpg'; - } - top.imgCompressor.fileToSave = name; - - var vm = this; - - // Handle blob: URLs (from IndexedDB/FileManager) - if (isBlob) { - fetch(originalSrc) - .then(function(response) { return response.blob(); }) - .then(function(blob) { - var date = new Date(); - blob.lastModified = date.getTime(); - blob.lastModifiedDate = date; - blob.name = name; - vm.compress(blob); - eXeImageCompressor.loadImage(originalSrc); - }) - .catch(function(err) { - console.error('Error loading blob image:', err); - jQuery("label[for='file']").trigger("click"); - }); - return; - } - - // Handle asset:// URLs (from Yjs AssetManager) - if (isAsset) { - if (assetManager) { - // Extract assetId from asset://uuid/filename - var assetId = top.imgCompressor.assetId || originalSrc.replace('asset://', '').split('/')[0]; - assetManager.getAsset(assetId).then(function(asset) { - if (asset && asset.blob) { - var blob = asset.blob; - var date = new Date(); - blob.lastModified = date.getTime(); - blob.lastModifiedDate = date; - blob.name = asset.filename || name; - top.imgCompressor.fileToSave = asset.filename || name; - vm.compress(blob); - // Create blob URL for preview - var blobUrl = URL.createObjectURL(blob); - eXeImageCompressor.loadImage(blobUrl); - } else { - console.error('Asset not found:', assetId); - jQuery("label[for='file']").trigger("click"); - } - }).catch(function(err) { - console.error('Error loading asset image:', err); - jQuery("label[for='file']").trigger("click"); - }); - } else { - console.error('AssetManager not available'); - jQuery("label[for='file']").trigger("click"); - } - return; - } - - // Handle data: URLs - if (isDataUrl) { - fetch(originalSrc) - .then(function(response) { return response.blob(); }) - .then(function(blob) { - var date = new Date(); - blob.lastModified = date.getTime(); - blob.lastModifiedDate = date; - blob.name = name; - vm.compress(blob); - eXeImageCompressor.loadImage(originalSrc); - }) - .catch(function(err) { - console.error('Error loading data URL image:', err); - jQuery("label[for='file']").trigger("click"); - }); - return; - } - - // Original behavior for server URLs - var url = `../../../../../../../${originalSrc}`; - if (originalSrc.indexOf("resources/") == 0) { - var parts = originalSrc.split("/"); - parts = parts[1]; - if (parts != "") { - url = top.window.location + "/" + originalSrc; - name = parts; - var backupWarning = $i18n.backupWarning; - backupWarning = backupWarning.replace("$", ''); - backupWarning = backupWarning.replace("$", ''); - $("#imageEditorBackupMessage").html(backupWarning); - // Get the image size - eXeImageCompressor.loadImage(url); - } - } else if (originalSrc.indexOf("/previews/") == 0) { - top.eXe.app.alert($i18n.newImageWarning); - } else { - // Open the file picker - jQuery("label[for='file']").trigger("click"); - } - - var xhr = new XMLHttpRequest(); - - xhr.onload = function () { - var blob = xhr.response; - var date = new Date(); - - blob.lastModified = date.getTime(); - blob.lastModifiedDate = date; - blob.name = name; - vm.compress(blob); - }; - xhr.open('GET', url); - xhr.responseType = 'blob'; - xhr.send(); - }, - }); -}); -// eXeLearning -var eXeImageCompressor = { - type: "file", // base64 or file - sizeLimit: 1200, // true max size - maxSize: 1200, // default size - setMaxSize: function () { - var v = this.getCookie("eXeImageCompressorMaxSize"); - if (!isNaN(v) && v != "") { - v = Math.round(v); - if (v > 0 && v < this.sizeLimit) this.maxSize = v; - } - }, - firstImageLoaded: false, - closeOptimizer: function () { - // Close only the image optimizer dialog, leaving "Insert/Edit Image" dialog open - // Find TinyMCE from available window contexts - var tinymceRef = null; - var contexts = [top, parent]; - - for (var i = 0; i < contexts.length; i++) { - try { - if (contexts[i]?.tinymce?.activeEditor) { - tinymceRef = contexts[i].tinymce; - break; - } - } catch (e) { - // Cross-origin access blocked - } - } - - // Close only one dialog (the topmost one, which is the optimizer) - var wm = tinymceRef?.activeEditor?.windowManager; - if (wm) { - wm.close(); - } - }, - setCookie: function (cvalue) { - var d = new Date(); - d.setTime(d.getTime() + (30 * 24 * 60 * 60 * 1000)); - var expires = "expires=" + d.toUTCString(); - document.cookie = "eXeImageCompressorMaxSize=" + cvalue + ";" + expires + ";path=/;SameSite=Lax"; - }, - getCookie: function (cname) { - var name = cname + "="; - var decodedCookie = decodeURIComponent(document.cookie); - var ca = decodedCookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i]; - while (c.charAt(0) == ' ') c = c.substring(1); - if (c.indexOf(name) == 0) return c.substring(name.length, c.length); - } - return ""; - }, - loadImage: function (url) { - var img = new Image(); - img.onload = function () { - var w = this.width; - var h = this.height; - if (!isNaN(w) && !isNaN(h)) { - var v = w; - if (h > w) v = h; - // Limit to sizeLimit (absolute maximum) - if (v > eXeImageCompressor.sizeLimit) v = eXeImageCompressor.sizeLimit; - if (eXeImageCompressor.firstImageLoaded == false) { - // If image is larger than maxSize, use maxSize as default - if (v >= eXeImageCompressor.maxSize) { - v = eXeImageCompressor.maxSize; - } - // Always update the field on first load - jQuery("#inputSize").val(v); - jQuery("#inputMaxWidth").val(v)[0].dispatchEvent(new Event('input')); - jQuery("#inputMaxHeight").val(v)[0].dispatchEvent(new Event('input')); - } - eXeImageCompressor.firstImageLoaded = true; - } - } - img.src = url; - var ext = url.split('.').pop(); - ext = ext.toLowerCase(); - if (ext == "jpg" || ext == "jpeg" || url.indexOf("data:image/jpeg") == 0) jQuery("#inputQuality,label[for='inputQuality']").show(); - else jQuery("#inputQuality,label[for='inputQuality']").hide(); - }, - init: function () { - this.i18n(); - this.setMaxSize(); - - // Set imgMaxSize to sizeLimit (will be used by Compressor.js options) - // The actual inputSize value will be set by loadImage() based on the real image dimensions - this.imgMaxSize = this.sizeLimit; - - // Set the max attribute to sizeLimit (absolute maximum allowed) - document.querySelector("#inputSize").max = this.sizeLimit; - document.querySelector("#inputSize").title = `1 - ${this.sizeLimit}px`; - - // Update Vue's options.maxWidth and options.maxHeight (they were undefined when Vue was created) - jQuery("#inputMaxWidth").val(this.sizeLimit)[0].dispatchEvent(new Event('input')); - jQuery("#inputMaxHeight").val(this.sizeLimit)[0].dispatchEvent(new Event('input')); - - // Don't set inputSize here - let loadImage() set it based on actual image dimensions - - setTimeout(function () { - // Note: We don't register a 'load' event on #imageEditorOutputImg here - // because loadImage() is called from mounted() with the ORIGINAL image URL. - // The compressed image may have different dimensions due to Compressor.js options. - jQuery("#imageEditorSaveImg").fadeIn().click(function () { - - // Update the cookie - var v = jQuery("#inputSize").val(); - if (!isNaN(v) && v < eXeImageCompressor.sizeLimit) eXeImageCompressor.setCookie(v); - - var img = jQuery("#imageEditorOutputImg"); - var src = img.attr("src"); - - // Set image data using TinyMCE dialog API and close optimizer - function setSourceAndClose(base64Src, imgWidth, imgHeight) { - // Use the TinyMCE dialog API stored in top.imgCompressor - var api = top.imgCompressor?.api; - if (api) { - // Update Source field - api.setData({ src: { value: base64Src } }); - // Update Width and Height fields - api.setData({ - dimensions: { - width: String(imgWidth), - height: String(imgHeight) - } - }); - } - // Close the image optimizer dialog - eXeImageCompressor.closeOptimizer(); - } - - // Get the dimensions of the optimized image - var imgElement = img[0]; - var imgWidth = imgElement.naturalWidth || imgElement.width; - var imgHeight = imgElement.naturalHeight || imgElement.height; - - // The optimized image is already in base64 format (data:image/...) - if (src && src.indexOf("data:image/") === 0) { - setSourceAndClose(src, imgWidth, imgHeight); - } else if (src && src.indexOf("blob:") === 0) { - // Convert blob URL to base64 - fetch(src) - .then(function(response) { return response.blob(); }) - .then(function(blob) { - var reader = new FileReader(); - reader.onloadend = function() { - setSourceAndClose(reader.result, imgWidth, imgHeight); - }; - reader.readAsDataURL(blob); - }) - .catch(function(err) { - console.error('Error converting blob to base64:', err); - eXeImageCompressor.closeOptimizer(); - }); - } else { - // Fallback: just close - eXeImageCompressor.closeOptimizer(); - } - - return false; - }); - jQuery("#inputSize").on('input', function () { - var v = this.value; - v = v.replace(/\D/g, ''); - // Limit to sizeLimit (absolute maximum) - if (v !== '' && parseInt(v, 10) > eXeImageCompressor.sizeLimit) { - v = eXeImageCompressor.sizeLimit; - } - this.value = v; - if (v !== '') { - jQuery("#inputMaxWidth").val(v)[0].dispatchEvent(new Event('input')); - jQuery("#inputMaxHeight").val(v)[0].dispatchEvent(new Event('input')); - } - }); - }, 1000); - }, - i18n: function () { - - document.title = $i18n.imageOptimizer; - var e = $("#imageEditorUploader p"); - var html = $i18n.uploadInstructions; - html = html.replace("$", ''); - e.html(html) - $("label[for='inputSize']").html($i18n.size + ":"); - $("label[for='inputMaxWidth']").html($i18n.maxWidth + ":"); - $("label[for='inputMaxHeight']").html($i18n.maxHeight + ":"); - $("label[for='inputWidth']").html($i18n.width + ":"); - $("label[for='inputHeight']").html($i18n.height + ":"); - $("label[for='inputQuality']").html($i18n.quality + ":"); - $("#imageEditorLabelName").html($i18n.name + ":"); - $("#imageEditorLabelOriginalSize").html($i18n.originalSize + ":"); - $("#imageEditorLabelResultSize").html($i18n.resultSize + ":"); - $("#imageEditorSaveImg").html($i18n.finish); - - }, - - sendNewImagePath: function () { - - } -} -jQuery(function () { - eXeImageCompressor.init(); -}); diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/vue.min.js b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/vue.min.js deleted file mode 100644 index 05e21102a..000000000 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/js/vue.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Vue.js v2.6.11 - * (c) 2014-2019 Evan You - * Released under the MIT License. - */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).Vue=t()}(this,function(){"use strict";var e=Object.freeze({});function t(e){return null==e}function n(e){return null!=e}function r(e){return!0===e}function i(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function o(e){return null!==e&&"object"==typeof e}var a=Object.prototype.toString;function s(e){return"[object Object]"===a.call(e)}function c(e){var t=parseFloat(String(e));return t>=0&&Math.floor(t)===t&&isFinite(e)}function u(e){return n(e)&&"function"==typeof e.then&&"function"==typeof e.catch}function l(e){return null==e?"":Array.isArray(e)||s(e)&&e.toString===a?JSON.stringify(e,null,2):String(e)}function f(e){var t=parseFloat(e);return isNaN(t)?e:t}function p(e,t){for(var n=Object.create(null),r=e.split(","),i=0;i-1)return e.splice(n,1)}}var m=Object.prototype.hasOwnProperty;function y(e,t){return m.call(e,t)}function g(e){var t=Object.create(null);return function(n){return t[n]||(t[n]=e(n))}}var _=/-(\w)/g,b=g(function(e){return e.replace(_,function(e,t){return t?t.toUpperCase():""})}),$=g(function(e){return e.charAt(0).toUpperCase()+e.slice(1)}),w=/\B([A-Z])/g,C=g(function(e){return e.replace(w,"-$1").toLowerCase()});var x=Function.prototype.bind?function(e,t){return e.bind(t)}:function(e,t){function n(n){var r=arguments.length;return r?r>1?e.apply(t,arguments):e.call(t,n):e.call(t)}return n._length=e.length,n};function k(e,t){t=t||0;for(var n=e.length-t,r=new Array(n);n--;)r[n]=e[n+t];return r}function A(e,t){for(var n in t)e[n]=t[n];return e}function O(e){for(var t={},n=0;n0,Z=J&&J.indexOf("edge/")>0,G=(J&&J.indexOf("android"),J&&/iphone|ipad|ipod|ios/.test(J)||"ios"===K),X=(J&&/chrome\/\d+/.test(J),J&&/phantomjs/.test(J),J&&J.match(/firefox\/(\d+)/)),Y={}.watch,Q=!1;if(z)try{var ee={};Object.defineProperty(ee,"passive",{get:function(){Q=!0}}),window.addEventListener("test-passive",null,ee)}catch(e){}var te=function(){return void 0===B&&(B=!z&&!V&&"undefined"!=typeof global&&(global.process&&"server"===global.process.env.VUE_ENV)),B},ne=z&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function re(e){return"function"==typeof e&&/native code/.test(e.toString())}var ie,oe="undefined"!=typeof Symbol&&re(Symbol)&&"undefined"!=typeof Reflect&&re(Reflect.ownKeys);ie="undefined"!=typeof Set&&re(Set)?Set:function(){function e(){this.set=Object.create(null)}return e.prototype.has=function(e){return!0===this.set[e]},e.prototype.add=function(e){this.set[e]=!0},e.prototype.clear=function(){this.set=Object.create(null)},e}();var ae=S,se=0,ce=function(){this.id=se++,this.subs=[]};ce.prototype.addSub=function(e){this.subs.push(e)},ce.prototype.removeSub=function(e){h(this.subs,e)},ce.prototype.depend=function(){ce.target&&ce.target.addDep(this)},ce.prototype.notify=function(){for(var e=this.subs.slice(),t=0,n=e.length;t-1)if(o&&!y(i,"default"))a=!1;else if(""===a||a===C(e)){var c=Pe(String,i.type);(c<0||s0&&(st((u=e(u,(a||"")+"_"+c))[0])&&st(f)&&(s[l]=he(f.text+u[0].text),u.shift()),s.push.apply(s,u)):i(u)?st(f)?s[l]=he(f.text+u):""!==u&&s.push(he(u)):st(u)&&st(f)?s[l]=he(f.text+u.text):(r(o._isVList)&&n(u.tag)&&t(u.key)&&n(a)&&(u.key="__vlist"+a+"_"+c+"__"),s.push(u)));return s}(e):void 0}function st(e){return n(e)&&n(e.text)&&!1===e.isComment}function ct(e,t){if(e){for(var n=Object.create(null),r=oe?Reflect.ownKeys(e):Object.keys(e),i=0;i0,a=t?!!t.$stable:!o,s=t&&t.$key;if(t){if(t._normalized)return t._normalized;if(a&&r&&r!==e&&s===r.$key&&!o&&!r.$hasNormal)return r;for(var c in i={},t)t[c]&&"$"!==c[0]&&(i[c]=pt(n,c,t[c]))}else i={};for(var u in n)u in i||(i[u]=dt(n,u));return t&&Object.isExtensible(t)&&(t._normalized=i),R(i,"$stable",a),R(i,"$key",s),R(i,"$hasNormal",o),i}function pt(e,t,n){var r=function(){var e=arguments.length?n.apply(null,arguments):n({});return(e=e&&"object"==typeof e&&!Array.isArray(e)?[e]:at(e))&&(0===e.length||1===e.length&&e[0].isComment)?void 0:e};return n.proxy&&Object.defineProperty(e,t,{get:r,enumerable:!0,configurable:!0}),r}function dt(e,t){return function(){return e[t]}}function vt(e,t){var r,i,a,s,c;if(Array.isArray(e)||"string"==typeof e)for(r=new Array(e.length),i=0,a=e.length;idocument.createEvent("Event").timeStamp&&(sn=function(){return cn.now()})}function un(){var e,t;for(an=sn(),rn=!0,Qt.sort(function(e,t){return e.id-t.id}),on=0;onon&&Qt[n].id>e.id;)n--;Qt.splice(n+1,0,e)}else Qt.push(e);nn||(nn=!0,Ye(un))}}(this)},fn.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||o(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Re(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},fn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},fn.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},fn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||h(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var pn={enumerable:!0,configurable:!0,get:S,set:S};function dn(e,t,n){pn.get=function(){return this[t][n]},pn.set=function(e){this[t][n]=e},Object.defineProperty(e,n,pn)}function vn(e){e._watchers=[];var t=e.$options;t.props&&function(e,t){var n=e.$options.propsData||{},r=e._props={},i=e.$options._propKeys=[];e.$parent&&$e(!1);var o=function(o){i.push(o);var a=Me(o,t,n,e);xe(r,o,a),o in e||dn(e,"_props",o)};for(var a in t)o(a);$e(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]="function"!=typeof t[n]?S:x(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;s(t=e._data="function"==typeof t?function(e,t){le();try{return e.call(t,t)}catch(e){return Re(e,t,"data()"),{}}finally{fe()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&y(r,o)||(a=void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&dn(e,"_data",o))}var a;Ce(t,!0)}(e):Ce(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=te();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new fn(e,a||S,S,hn)),i in e||mn(e,i,o)}}(e,t.computed),t.watch&&t.watch!==Y&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;i-1:"string"==typeof e?e.split(",").indexOf(t)>-1:(n=e,"[object RegExp]"===a.call(n)&&e.test(t));var n}function An(e,t){var n=e.cache,r=e.keys,i=e._vnode;for(var o in n){var a=n[o];if(a){var s=xn(a.componentOptions);s&&!t(s)&&On(n,o,r,i)}}}function On(e,t,n,r){var i=e[t];!i||r&&i.tag===r.tag||i.componentInstance.$destroy(),e[t]=null,h(n,t)}!function(t){t.prototype._init=function(t){var n=this;n._uid=bn++,n._isVue=!0,t&&t._isComponent?function(e,t){var n=e.$options=Object.create(e.constructor.options),r=t._parentVnode;n.parent=t.parent,n._parentVnode=r;var i=r.componentOptions;n.propsData=i.propsData,n._parentListeners=i.listeners,n._renderChildren=i.children,n._componentTag=i.tag,t.render&&(n.render=t.render,n.staticRenderFns=t.staticRenderFns)}(n,t):n.$options=De($n(n.constructor),t||{},n),n._renderProxy=n,n._self=n,function(e){var t=e.$options,n=t.parent;if(n&&!t.abstract){for(;n.$options.abstract&&n.$parent;)n=n.$parent;n.$children.push(e)}e.$parent=n,e.$root=n?n.$root:e,e.$children=[],e.$refs={},e._watcher=null,e._inactive=null,e._directInactive=!1,e._isMounted=!1,e._isDestroyed=!1,e._isBeingDestroyed=!1}(n),function(e){e._events=Object.create(null),e._hasHookEvent=!1;var t=e.$options._parentListeners;t&&qt(e,t)}(n),function(t){t._vnode=null,t._staticTrees=null;var n=t.$options,r=t.$vnode=n._parentVnode,i=r&&r.context;t.$slots=ut(n._renderChildren,i),t.$scopedSlots=e,t._c=function(e,n,r,i){return Pt(t,e,n,r,i,!1)},t.$createElement=function(e,n,r,i){return Pt(t,e,n,r,i,!0)};var o=r&&r.data;xe(t,"$attrs",o&&o.attrs||e,null,!0),xe(t,"$listeners",n._parentListeners||e,null,!0)}(n),Yt(n,"beforeCreate"),function(e){var t=ct(e.$options.inject,e);t&&($e(!1),Object.keys(t).forEach(function(n){xe(e,n,t[n])}),$e(!0))}(n),vn(n),function(e){var t=e.$options.provide;t&&(e._provided="function"==typeof t?t.call(e):t)}(n),Yt(n,"created"),n.$options.el&&n.$mount(n.$options.el)}}(wn),function(e){var t={get:function(){return this._data}},n={get:function(){return this._props}};Object.defineProperty(e.prototype,"$data",t),Object.defineProperty(e.prototype,"$props",n),e.prototype.$set=ke,e.prototype.$delete=Ae,e.prototype.$watch=function(e,t,n){if(s(t))return _n(this,e,t,n);(n=n||{}).user=!0;var r=new fn(this,e,t,n);if(n.immediate)try{t.call(this,r.value)}catch(e){Re(e,this,'callback for immediate watcher "'+r.expression+'"')}return function(){r.teardown()}}}(wn),function(e){var t=/^hook:/;e.prototype.$on=function(e,n){var r=this;if(Array.isArray(e))for(var i=0,o=e.length;i1?k(t):t;for(var n=k(arguments,1),r='event handler for "'+e+'"',i=0,o=t.length;iparseInt(this.max)&&On(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};!function(e){var t={get:function(){return F}};Object.defineProperty(e,"config",t),e.util={warn:ae,extend:A,mergeOptions:De,defineReactive:xe},e.set=ke,e.delete=Ae,e.nextTick=Ye,e.observable=function(e){return Ce(e),e},e.options=Object.create(null),M.forEach(function(t){e.options[t+"s"]=Object.create(null)}),e.options._base=e,A(e.options.components,Tn),function(e){e.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(t.indexOf(e)>-1)return this;var n=k(arguments,1);return n.unshift(this),"function"==typeof e.install?e.install.apply(e,n):"function"==typeof e&&e.apply(null,n),t.push(e),this}}(e),function(e){e.mixin=function(e){return this.options=De(this.options,e),this}}(e),Cn(e),function(e){M.forEach(function(t){e[t]=function(e,n){return n?("component"===t&&s(n)&&(n.name=n.name||e,n=this.options._base.extend(n)),"directive"===t&&"function"==typeof n&&(n={bind:n,update:n}),this.options[t+"s"][e]=n,n):this.options[t+"s"][e]}})}(e)}(wn),Object.defineProperty(wn.prototype,"$isServer",{get:te}),Object.defineProperty(wn.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(wn,"FunctionalRenderContext",{value:Tt}),wn.version="2.6.11";var En=p("style,class"),Nn=p("input,textarea,option,select,progress"),jn=function(e,t,n){return"value"===n&&Nn(e)&&"button"!==t||"selected"===n&&"option"===e||"checked"===n&&"input"===e||"muted"===n&&"video"===e},Dn=p("contenteditable,draggable,spellcheck"),Ln=p("events,caret,typing,plaintext-only"),Mn=function(e,t){return Hn(t)||"false"===t?"false":"contenteditable"===e&&Ln(t)?t:"true"},In=p("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,translate,truespeed,typemustmatch,visible"),Fn="http://www.w3.org/1999/xlink",Pn=function(e){return":"===e.charAt(5)&&"xlink"===e.slice(0,5)},Rn=function(e){return Pn(e)?e.slice(6,e.length):""},Hn=function(e){return null==e||!1===e};function Bn(e){for(var t=e.data,r=e,i=e;n(i.componentInstance);)(i=i.componentInstance._vnode)&&i.data&&(t=Un(i.data,t));for(;n(r=r.parent);)r&&r.data&&(t=Un(t,r.data));return function(e,t){if(n(e)||n(t))return zn(e,Vn(t));return""}(t.staticClass,t.class)}function Un(e,t){return{staticClass:zn(e.staticClass,t.staticClass),class:n(e.class)?[e.class,t.class]:t.class}}function zn(e,t){return e?t?e+" "+t:e:t||""}function Vn(e){return Array.isArray(e)?function(e){for(var t,r="",i=0,o=e.length;i-1?hr(e,t,n):In(t)?Hn(n)?e.removeAttribute(t):(n="allowfullscreen"===t&&"EMBED"===e.tagName?"true":t,e.setAttribute(t,n)):Dn(t)?e.setAttribute(t,Mn(t,n)):Pn(t)?Hn(n)?e.removeAttributeNS(Fn,Rn(t)):e.setAttributeNS(Fn,t,n):hr(e,t,n)}function hr(e,t,n){if(Hn(n))e.removeAttribute(t);else{if(q&&!W&&"TEXTAREA"===e.tagName&&"placeholder"===t&&""!==n&&!e.__ieph){var r=function(t){t.stopImmediatePropagation(),e.removeEventListener("input",r)};e.addEventListener("input",r),e.__ieph=!0}e.setAttribute(t,n)}}var mr={create:dr,update:dr};function yr(e,r){var i=r.elm,o=r.data,a=e.data;if(!(t(o.staticClass)&&t(o.class)&&(t(a)||t(a.staticClass)&&t(a.class)))){var s=Bn(r),c=i._transitionClasses;n(c)&&(s=zn(s,Vn(c))),s!==i._prevClass&&(i.setAttribute("class",s),i._prevClass=s)}}var gr,_r,br,$r,wr,Cr,xr={create:yr,update:yr},kr=/[\w).+\-_$\]]/;function Ar(e){var t,n,r,i,o,a=!1,s=!1,c=!1,u=!1,l=0,f=0,p=0,d=0;for(r=0;r=0&&" "===(h=e.charAt(v));v--);h&&kr.test(h)||(u=!0)}}else void 0===i?(d=r+1,i=e.slice(0,r).trim()):m();function m(){(o||(o=[])).push(e.slice(d,r).trim()),d=r+1}if(void 0===i?i=e.slice(0,r).trim():0!==d&&m(),o)for(r=0;r-1?{exp:e.slice(0,$r),key:'"'+e.slice($r+1)+'"'}:{exp:e,key:null};_r=e,$r=wr=Cr=0;for(;!zr();)Vr(br=Ur())?Jr(br):91===br&&Kr(br);return{exp:e.slice(0,wr),key:e.slice(wr+1,Cr)}}(e);return null===n.key?e+"="+t:"$set("+n.exp+", "+n.key+", "+t+")"}function Ur(){return _r.charCodeAt(++$r)}function zr(){return $r>=gr}function Vr(e){return 34===e||39===e}function Kr(e){var t=1;for(wr=$r;!zr();)if(Vr(e=Ur()))Jr(e);else if(91===e&&t++,93===e&&t--,0===t){Cr=$r;break}}function Jr(e){for(var t=e;!zr()&&(e=Ur())!==t;);}var qr,Wr="__r",Zr="__c";function Gr(e,t,n){var r=qr;return function i(){null!==t.apply(null,arguments)&&Qr(e,i,n,r)}}var Xr=Ve&&!(X&&Number(X[1])<=53);function Yr(e,t,n,r){if(Xr){var i=an,o=t;t=o._wrapper=function(e){if(e.target===e.currentTarget||e.timeStamp>=i||e.timeStamp<=0||e.target.ownerDocument!==document)return o.apply(this,arguments)}}qr.addEventListener(e,t,Q?{capture:n,passive:r}:n)}function Qr(e,t,n,r){(r||qr).removeEventListener(e,t._wrapper||t,n)}function ei(e,r){if(!t(e.data.on)||!t(r.data.on)){var i=r.data.on||{},o=e.data.on||{};qr=r.elm,function(e){if(n(e[Wr])){var t=q?"change":"input";e[t]=[].concat(e[Wr],e[t]||[]),delete e[Wr]}n(e[Zr])&&(e.change=[].concat(e[Zr],e.change||[]),delete e[Zr])}(i),rt(i,o,Yr,Qr,Gr,r.context),qr=void 0}}var ti,ni={create:ei,update:ei};function ri(e,r){if(!t(e.data.domProps)||!t(r.data.domProps)){var i,o,a=r.elm,s=e.data.domProps||{},c=r.data.domProps||{};for(i in n(c.__ob__)&&(c=r.data.domProps=A({},c)),s)i in c||(a[i]="");for(i in c){if(o=c[i],"textContent"===i||"innerHTML"===i){if(r.children&&(r.children.length=0),o===s[i])continue;1===a.childNodes.length&&a.removeChild(a.childNodes[0])}if("value"===i&&"PROGRESS"!==a.tagName){a._value=o;var u=t(o)?"":String(o);ii(a,u)&&(a.value=u)}else if("innerHTML"===i&&qn(a.tagName)&&t(a.innerHTML)){(ti=ti||document.createElement("div")).innerHTML=""+o+"";for(var l=ti.firstChild;a.firstChild;)a.removeChild(a.firstChild);for(;l.firstChild;)a.appendChild(l.firstChild)}else if(o!==s[i])try{a[i]=o}catch(e){}}}}function ii(e,t){return!e.composing&&("OPTION"===e.tagName||function(e,t){var n=!0;try{n=document.activeElement!==e}catch(e){}return n&&e.value!==t}(e,t)||function(e,t){var r=e.value,i=e._vModifiers;if(n(i)){if(i.number)return f(r)!==f(t);if(i.trim)return r.trim()!==t.trim()}return r!==t}(e,t))}var oi={create:ri,update:ri},ai=g(function(e){var t={},n=/:(.+)/;return e.split(/;(?![^(]*\))/g).forEach(function(e){if(e){var r=e.split(n);r.length>1&&(t[r[0].trim()]=r[1].trim())}}),t});function si(e){var t=ci(e.style);return e.staticStyle?A(e.staticStyle,t):t}function ci(e){return Array.isArray(e)?O(e):"string"==typeof e?ai(e):e}var ui,li=/^--/,fi=/\s*!important$/,pi=function(e,t,n){if(li.test(t))e.style.setProperty(t,n);else if(fi.test(n))e.style.setProperty(C(t),n.replace(fi,""),"important");else{var r=vi(t);if(Array.isArray(n))for(var i=0,o=n.length;i-1?t.split(yi).forEach(function(t){return e.classList.add(t)}):e.classList.add(t);else{var n=" "+(e.getAttribute("class")||"")+" ";n.indexOf(" "+t+" ")<0&&e.setAttribute("class",(n+t).trim())}}function _i(e,t){if(t&&(t=t.trim()))if(e.classList)t.indexOf(" ")>-1?t.split(yi).forEach(function(t){return e.classList.remove(t)}):e.classList.remove(t),e.classList.length||e.removeAttribute("class");else{for(var n=" "+(e.getAttribute("class")||"")+" ",r=" "+t+" ";n.indexOf(r)>=0;)n=n.replace(r," ");(n=n.trim())?e.setAttribute("class",n):e.removeAttribute("class")}}function bi(e){if(e){if("object"==typeof e){var t={};return!1!==e.css&&A(t,$i(e.name||"v")),A(t,e),t}return"string"==typeof e?$i(e):void 0}}var $i=g(function(e){return{enterClass:e+"-enter",enterToClass:e+"-enter-to",enterActiveClass:e+"-enter-active",leaveClass:e+"-leave",leaveToClass:e+"-leave-to",leaveActiveClass:e+"-leave-active"}}),wi=z&&!W,Ci="transition",xi="animation",ki="transition",Ai="transitionend",Oi="animation",Si="animationend";wi&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(ki="WebkitTransition",Ai="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(Oi="WebkitAnimation",Si="webkitAnimationEnd"));var Ti=z?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(e){return e()};function Ei(e){Ti(function(){Ti(e)})}function Ni(e,t){var n=e._transitionClasses||(e._transitionClasses=[]);n.indexOf(t)<0&&(n.push(t),gi(e,t))}function ji(e,t){e._transitionClasses&&h(e._transitionClasses,t),_i(e,t)}function Di(e,t,n){var r=Mi(e,t),i=r.type,o=r.timeout,a=r.propCount;if(!i)return n();var s=i===Ci?Ai:Si,c=0,u=function(){e.removeEventListener(s,l),n()},l=function(t){t.target===e&&++c>=a&&u()};setTimeout(function(){c0&&(n=Ci,l=a,f=o.length):t===xi?u>0&&(n=xi,l=u,f=c.length):f=(n=(l=Math.max(a,u))>0?a>u?Ci:xi:null)?n===Ci?o.length:c.length:0,{type:n,timeout:l,propCount:f,hasTransform:n===Ci&&Li.test(r[ki+"Property"])}}function Ii(e,t){for(;e.length1}function Ui(e,t){!0!==t.data.show&&Pi(t)}var zi=function(e){var o,a,s={},c=e.modules,u=e.nodeOps;for(o=0;ov?_(e,t(i[y+1])?null:i[y+1].elm,i,d,y,o):d>y&&$(r,p,v)}(p,h,y,o,l):n(y)?(n(e.text)&&u.setTextContent(p,""),_(p,null,y,0,y.length-1,o)):n(h)?$(h,0,h.length-1):n(e.text)&&u.setTextContent(p,""):e.text!==i.text&&u.setTextContent(p,i.text),n(v)&&n(d=v.hook)&&n(d=d.postpatch)&&d(e,i)}}}function k(e,t,i){if(r(i)&&n(e.parent))e.parent.data.pendingInsert=t;else for(var o=0;o-1,a.selected!==o&&(a.selected=o);else if(N(Wi(a),r))return void(e.selectedIndex!==s&&(e.selectedIndex=s));i||(e.selectedIndex=-1)}}function qi(e,t){return t.every(function(t){return!N(t,e)})}function Wi(e){return"_value"in e?e._value:e.value}function Zi(e){e.target.composing=!0}function Gi(e){e.target.composing&&(e.target.composing=!1,Xi(e.target,"input"))}function Xi(e,t){var n=document.createEvent("HTMLEvents");n.initEvent(t,!0,!0),e.dispatchEvent(n)}function Yi(e){return!e.componentInstance||e.data&&e.data.transition?e:Yi(e.componentInstance._vnode)}var Qi={model:Vi,show:{bind:function(e,t,n){var r=t.value,i=(n=Yi(n)).data&&n.data.transition,o=e.__vOriginalDisplay="none"===e.style.display?"":e.style.display;r&&i?(n.data.show=!0,Pi(n,function(){e.style.display=o})):e.style.display=r?o:"none"},update:function(e,t,n){var r=t.value;!r!=!t.oldValue&&((n=Yi(n)).data&&n.data.transition?(n.data.show=!0,r?Pi(n,function(){e.style.display=e.__vOriginalDisplay}):Ri(n,function(){e.style.display="none"})):e.style.display=r?e.__vOriginalDisplay:"none")},unbind:function(e,t,n,r,i){i||(e.style.display=e.__vOriginalDisplay)}}},eo={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function to(e){var t=e&&e.componentOptions;return t&&t.Ctor.options.abstract?to(zt(t.children)):e}function no(e){var t={},n=e.$options;for(var r in n.propsData)t[r]=e[r];var i=n._parentListeners;for(var o in i)t[b(o)]=i[o];return t}function ro(e,t){if(/\d-keep-alive$/.test(t.tag))return e("keep-alive",{props:t.componentOptions.propsData})}var io=function(e){return e.tag||Ut(e)},oo=function(e){return"show"===e.name},ao={name:"transition",props:eo,abstract:!0,render:function(e){var t=this,n=this.$slots.default;if(n&&(n=n.filter(io)).length){var r=this.mode,o=n[0];if(function(e){for(;e=e.parent;)if(e.data.transition)return!0}(this.$vnode))return o;var a=to(o);if(!a)return o;if(this._leaving)return ro(e,o);var s="__transition-"+this._uid+"-";a.key=null==a.key?a.isComment?s+"comment":s+a.tag:i(a.key)?0===String(a.key).indexOf(s)?a.key:s+a.key:a.key;var c=(a.data||(a.data={})).transition=no(this),u=this._vnode,l=to(u);if(a.data.directives&&a.data.directives.some(oo)&&(a.data.show=!0),l&&l.data&&!function(e,t){return t.key===e.key&&t.tag===e.tag}(a,l)&&!Ut(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=A({},c);if("out-in"===r)return this._leaving=!0,it(f,"afterLeave",function(){t._leaving=!1,t.$forceUpdate()}),ro(e,o);if("in-out"===r){if(Ut(a))return u;var p,d=function(){p()};it(c,"afterEnter",d),it(c,"enterCancelled",d),it(f,"delayLeave",function(e){p=e})}}return o}}},so=A({tag:String,moveClass:String},eo);function co(e){e.elm._moveCb&&e.elm._moveCb(),e.elm._enterCb&&e.elm._enterCb()}function uo(e){e.data.newPos=e.elm.getBoundingClientRect()}function lo(e){var t=e.data.pos,n=e.data.newPos,r=t.left-n.left,i=t.top-n.top;if(r||i){e.data.moved=!0;var o=e.elm.style;o.transform=o.WebkitTransform="translate("+r+"px,"+i+"px)",o.transitionDuration="0s"}}delete so.mode;var fo={Transition:ao,TransitionGroup:{props:so,beforeMount:function(){var e=this,t=this._update;this._update=function(n,r){var i=Zt(e);e.__patch__(e._vnode,e.kept,!1,!0),e._vnode=e.kept,i(),t.call(e,n,r)}},render:function(e){for(var t=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,i=this.$slots.default||[],o=this.children=[],a=no(this),s=0;s-1?Gn[e]=t.constructor===window.HTMLUnknownElement||t.constructor===window.HTMLElement:Gn[e]=/HTMLUnknownElement/.test(t.toString())},A(wn.options.directives,Qi),A(wn.options.components,fo),wn.prototype.__patch__=z?zi:S,wn.prototype.$mount=function(e,t){return function(e,t,n){var r;return e.$el=t,e.$options.render||(e.$options.render=ve),Yt(e,"beforeMount"),r=function(){e._update(e._render(),n)},new fn(e,r,S,{before:function(){e._isMounted&&!e._isDestroyed&&Yt(e,"beforeUpdate")}},!0),n=!1,null==e.$vnode&&(e._isMounted=!0,Yt(e,"mounted")),e}(this,e=e&&z?Yn(e):void 0,t)},z&&setTimeout(function(){F.devtools&&ne&&ne.emit("init",wn)},0);var po=/\{\{((?:.|\r?\n)+?)\}\}/g,vo=/[-.*+?^${}()|[\]\/\\]/g,ho=g(function(e){var t=e[0].replace(vo,"\\$&"),n=e[1].replace(vo,"\\$&");return new RegExp(t+"((?:.|\\n)+?)"+n,"g")});var mo={staticKeys:["staticClass"],transformNode:function(e,t){t.warn;var n=Fr(e,"class");n&&(e.staticClass=JSON.stringify(n));var r=Ir(e,"class",!1);r&&(e.classBinding=r)},genData:function(e){var t="";return e.staticClass&&(t+="staticClass:"+e.staticClass+","),e.classBinding&&(t+="class:"+e.classBinding+","),t}};var yo,go={staticKeys:["staticStyle"],transformNode:function(e,t){t.warn;var n=Fr(e,"style");n&&(e.staticStyle=JSON.stringify(ai(n)));var r=Ir(e,"style",!1);r&&(e.styleBinding=r)},genData:function(e){var t="";return e.staticStyle&&(t+="staticStyle:"+e.staticStyle+","),e.styleBinding&&(t+="style:("+e.styleBinding+"),"),t}},_o=function(e){return(yo=yo||document.createElement("div")).innerHTML=e,yo.textContent},bo=p("area,base,br,col,embed,frame,hr,img,input,isindex,keygen,link,meta,param,source,track,wbr"),$o=p("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr,source"),wo=p("address,article,aside,base,blockquote,body,caption,col,colgroup,dd,details,dialog,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,head,header,hgroup,hr,html,legend,li,menuitem,meta,optgroup,option,param,rp,rt,source,style,summary,tbody,td,tfoot,th,thead,title,tr,track"),Co=/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,xo=/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,ko="[a-zA-Z_][\\-\\.0-9_a-zA-Z"+P.source+"]*",Ao="((?:"+ko+"\\:)?"+ko+")",Oo=new RegExp("^<"+Ao),So=/^\s*(\/?)>/,To=new RegExp("^<\\/"+Ao+"[^>]*>"),Eo=/^]+>/i,No=/^",""":'"',"&":"&"," ":"\n"," ":"\t","'":"'"},Io=/&(?:lt|gt|quot|amp|#39);/g,Fo=/&(?:lt|gt|quot|amp|#39|#10|#9);/g,Po=p("pre,textarea",!0),Ro=function(e,t){return e&&Po(e)&&"\n"===t[0]};function Ho(e,t){var n=t?Fo:Io;return e.replace(n,function(e){return Mo[e]})}var Bo,Uo,zo,Vo,Ko,Jo,qo,Wo,Zo=/^@|^v-on:/,Go=/^v-|^@|^:|^#/,Xo=/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/,Yo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Qo=/^\(|\)$/g,ea=/^\[.*\]$/,ta=/:(.*)$/,na=/^:|^\.|^v-bind:/,ra=/\.[^.\]]+(?=[^\]]*$)/g,ia=/^v-slot(:|$)|^#/,oa=/[\r\n]/,aa=/\s+/g,sa=g(_o),ca="_empty_";function ua(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:ma(t),rawAttrsMap:{},parent:n,children:[]}}function la(e,t){Bo=t.warn||Sr,Jo=t.isPreTag||T,qo=t.mustUseProp||T,Wo=t.getTagNamespace||T;t.isReservedTag;zo=Tr(t.modules,"transformNode"),Vo=Tr(t.modules,"preTransformNode"),Ko=Tr(t.modules,"postTransformNode"),Uo=t.delimiters;var n,r,i=[],o=!1!==t.preserveWhitespace,a=t.whitespace,s=!1,c=!1;function u(e){if(l(e),s||e.processed||(e=fa(e,t)),i.length||e===n||n.if&&(e.elseif||e.else)&&da(n,{exp:e.elseif,block:e}),r&&!e.forbidden)if(e.elseif||e.else)a=e,(u=function(e){var t=e.length;for(;t--;){if(1===e[t].type)return e[t];e.pop()}}(r.children))&&u.if&&da(u,{exp:a.elseif,block:a});else{if(e.slotScope){var o=e.slotTarget||'"default"';(r.scopedSlots||(r.scopedSlots={}))[o]=e}r.children.push(e),e.parent=r}var a,u;e.children=e.children.filter(function(e){return!e.slotScope}),l(e),e.pre&&(s=!1),Jo(e.tag)&&(c=!1);for(var f=0;f]*>)","i")),p=e.replace(f,function(e,n,r){return u=r.length,Do(l)||"noscript"===l||(n=n.replace(//g,"$1").replace(//g,"$1")),Ro(l,n)&&(n=n.slice(1)),t.chars&&t.chars(n),""});c+=e.length-p.length,e=p,A(l,c-u,c)}else{var d=e.indexOf("<");if(0===d){if(No.test(e)){var v=e.indexOf("--\x3e");if(v>=0){t.shouldKeepComment&&t.comment(e.substring(4,v),c,c+v+3),C(v+3);continue}}if(jo.test(e)){var h=e.indexOf("]>");if(h>=0){C(h+2);continue}}var m=e.match(Eo);if(m){C(m[0].length);continue}var y=e.match(To);if(y){var g=c;C(y[0].length),A(y[1],g,c);continue}var _=x();if(_){k(_),Ro(_.tagName,e)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(d>=0){for($=e.slice(d);!(To.test($)||Oo.test($)||No.test($)||jo.test($)||(w=$.indexOf("<",1))<0);)d+=w,$=e.slice(d);b=e.substring(0,d)}d<0&&(b=e),b&&C(b.length),t.chars&&b&&t.chars(b,c-b.length,c)}if(e===n){t.chars&&t.chars(e);break}}function C(t){c+=t,e=e.substring(t)}function x(){var t=e.match(Oo);if(t){var n,r,i={tagName:t[1],attrs:[],start:c};for(C(t[0].length);!(n=e.match(So))&&(r=e.match(xo)||e.match(Co));)r.start=c,C(r[0].length),r.end=c,i.attrs.push(r);if(n)return i.unarySlash=n[1],C(n[0].length),i.end=c,i}}function k(e){var n=e.tagName,c=e.unarySlash;o&&("p"===r&&wo(n)&&A(r),s(n)&&r===n&&A(n));for(var u=a(n)||!!c,l=e.attrs.length,f=new Array(l),p=0;p=0&&i[a].lowerCasedTag!==s;a--);else a=0;if(a>=0){for(var u=i.length-1;u>=a;u--)t.end&&t.end(i[u].tag,n,o);i.length=a,r=a&&i[a-1].tag}else"br"===s?t.start&&t.start(e,[],!0,n,o):"p"===s&&(t.start&&t.start(e,[],!1,n,o),t.end&&t.end(e,n,o))}A()}(e,{warn:Bo,expectHTML:t.expectHTML,isUnaryTag:t.isUnaryTag,canBeLeftOpenTag:t.canBeLeftOpenTag,shouldDecodeNewlines:t.shouldDecodeNewlines,shouldDecodeNewlinesForHref:t.shouldDecodeNewlinesForHref,shouldKeepComment:t.comments,outputSourceRange:t.outputSourceRange,start:function(e,o,a,l,f){var p=r&&r.ns||Wo(e);q&&"svg"===p&&(o=function(e){for(var t=[],n=0;nc&&(s.push(o=e.slice(c,i)),a.push(JSON.stringify(o)));var u=Ar(r[1].trim());a.push("_s("+u+")"),s.push({"@binding":u}),c=i+r[0].length}return c-1"+("true"===o?":("+t+")":":_q("+t+","+o+")")),Mr(e,"change","var $$a="+t+",$$el=$event.target,$$c=$$el.checked?("+o+"):("+a+");if(Array.isArray($$a)){var $$v="+(r?"_n("+i+")":i)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Br(t,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Br(t,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Br(t,"$$c")+"}",null,!0)}(e,r,i);else if("input"===o&&"radio"===a)!function(e,t,n){var r=n&&n.number,i=Ir(e,"value")||"null";Er(e,"checked","_q("+t+","+(i=r?"_n("+i+")":i)+")"),Mr(e,"change",Br(t,i),null,!0)}(e,r,i);else if("input"===o||"textarea"===o)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,u=o?"change":"range"===r?Wr:"input",l="$event.target.value";s&&(l="$event.target.value.trim()"),a&&(l="_n("+l+")");var f=Br(t,l);c&&(f="if($event.target.composing)return;"+f),Er(e,"value","("+t+")"),Mr(e,u,f,null,!0),(s||a)&&Mr(e,"blur","$forceUpdate()")}(e,r,i);else if(!F.isReservedTag(o))return Hr(e,r,i),!1;return!0},text:function(e,t){t.value&&Er(e,"textContent","_s("+t.value+")",t)},html:function(e,t){t.value&&Er(e,"innerHTML","_s("+t.value+")",t)}},isPreTag:function(e){return"pre"===e},isUnaryTag:bo,mustUseProp:jn,canBeLeftOpenTag:$o,isReservedTag:Wn,getTagNamespace:Zn,staticKeys:function(e){return e.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(",")}(ba)},xa=g(function(e){return p("type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap"+(e?","+e:""))});function ka(e,t){e&&($a=xa(t.staticKeys||""),wa=t.isReservedTag||T,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||d(e.tag)||!wa(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every($a)))}(t);if(1===t.type){if(!wa(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function(?:\s+[\w$]+)?\s*\(/,Oa=/\([^)]*?\);*$/,Sa=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,Ta={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},Ea={esc:["Esc","Escape"],tab:"Tab",enter:"Enter",space:[" ","Spacebar"],up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete","Del"]},Na=function(e){return"if("+e+")return null;"},ja={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:Na("$event.target !== $event.currentTarget"),ctrl:Na("!$event.ctrlKey"),shift:Na("!$event.shiftKey"),alt:Na("!$event.altKey"),meta:Na("!$event.metaKey"),left:Na("'button' in $event && $event.button !== 0"),middle:Na("'button' in $event && $event.button !== 1"),right:Na("'button' in $event && $event.button !== 2")};function Da(e,t){var n=t?"nativeOn:":"on:",r="",i="";for(var o in e){var a=La(e[o]);e[o]&&e[o].dynamic?i+=o+","+a+",":r+='"'+o+'":'+a+","}return r="{"+r.slice(0,-1)+"}",i?n+"_d("+r+",["+i.slice(0,-1)+"])":n+r}function La(e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return La(e)}).join(",")+"]";var t=Sa.test(e.value),n=Aa.test(e.value),r=Sa.test(e.value.replace(Oa,""));if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(ja[s])o+=ja[s],Ta[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=Na(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+=function(e){return"if(!$event.type.indexOf('key')&&"+e.map(Ma).join("&&")+")return null;"}(a)),o&&(i+=o),"function($event){"+i+(t?"return "+e.value+"($event)":n?"return ("+e.value+")($event)":r?"return "+e.value:e.value)+"}"}return t||n?e.value:"function($event){"+(r?"return "+e.value:e.value)+"}"}function Ma(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=Ta[e],r=Ea[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var Ia={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(e,t){e.wrapData=function(n){return"_b("+n+",'"+e.tag+"',"+t.value+","+(t.modifiers&&t.modifiers.prop?"true":"false")+(t.modifiers&&t.modifiers.sync?",true":"")+")"}},cloak:S},Fa=function(e){this.options=e,this.warn=e.warn||Sr,this.transforms=Tr(e.modules,"transformCode"),this.dataGenFns=Tr(e.modules,"genData"),this.directives=A(A({},Ia),e.directives);var t=e.isReservedTag||T;this.maybeComponent=function(e){return!!e.component||!t(e.tag)},this.onceId=0,this.staticRenderFns=[],this.pre=!1};function Pa(e,t){var n=new Fa(t);return{render:"with(this){return "+(e?Ra(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function Ra(e,t){if(e.parent&&(e.pre=e.pre||e.parent.pre),e.staticRoot&&!e.staticProcessed)return Ha(e,t);if(e.once&&!e.onceProcessed)return Ba(e,t);if(e.for&&!e.forProcessed)return za(e,t);if(e.if&&!e.ifProcessed)return Ua(e,t);if("template"!==e.tag||e.slotTarget||t.pre){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=qa(e,t),i="_t("+n+(r?","+r:""),o=e.attrs||e.dynamicAttrs?Ga((e.attrs||[]).concat(e.dynamicAttrs||[]).map(function(e){return{name:b(e.name),value:e.value,dynamic:e.dynamic}})):null,a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)n=function(e,t,n){var r=t.inlineTemplate?null:qa(t,n,!0);return"_c("+e+","+Va(t,n)+(r?","+r:"")+")"}(e.component,e,t);else{var r;(!e.plain||e.pre&&t.maybeComponent(e))&&(r=Va(e,t));var i=e.inlineTemplate?null:qa(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o>>0}(a):"")+")"}(e,e.scopedSlots,t)+","),e.model&&(n+="model:{value:"+e.model.value+",callback:"+e.model.callback+",expression:"+e.model.expression+"},"),e.inlineTemplate){var o=function(e,t){var n=e.children[0];if(n&&1===n.type){var r=Pa(n,t.options);return"inlineTemplate:{render:function(){"+r.render+"},staticRenderFns:["+r.staticRenderFns.map(function(e){return"function(){"+e+"}"}).join(",")+"]}"}}(e,t);o&&(n+=o+",")}return n=n.replace(/,$/,"")+"}",e.dynamicAttrs&&(n="_b("+n+',"'+e.tag+'",'+Ga(e.dynamicAttrs)+")"),e.wrapData&&(n=e.wrapData(n)),e.wrapListeners&&(n=e.wrapListeners(n)),n}function Ka(e){return 1===e.type&&("slot"===e.tag||e.children.some(Ka))}function Ja(e,t){var n=e.attrsMap["slot-scope"];if(e.if&&!e.ifProcessed&&!n)return Ua(e,t,Ja,"null");if(e.for&&!e.forProcessed)return za(e,t,Ja);var r=e.slotScope===ca?"":String(e.slotScope),i="function("+r+"){return "+("template"===e.tag?e.if&&n?"("+e.if+")?"+(qa(e,t)||"undefined")+":undefined":qa(e,t)||"undefined":Ra(e,t))+"}",o=r?"":",proxy:true";return"{key:"+(e.slotTarget||'"default"')+",fn:"+i+o+"}"}function qa(e,t,n,r,i){var o=e.children;if(o.length){var a=o[0];if(1===o.length&&a.for&&"template"!==a.tag&&"slot"!==a.tag){var s=n?t.maybeComponent(a)?",1":",0":"";return""+(r||Ra)(a,t)+s}var c=n?function(e,t){for(var n=0,r=0;r':'
',ts.innerHTML.indexOf(" ")>0}var os=!!z&&is(!1),as=!!z&&is(!0),ss=g(function(e){var t=Yn(e);return t&&t.innerHTML}),cs=wn.prototype.$mount;return wn.prototype.$mount=function(e,t){if((e=e&&Yn(e))===document.body||e===document.documentElement)return this;var n=this.$options;if(!n.render){var r=n.template;if(r)if("string"==typeof r)"#"===r.charAt(0)&&(r=ss(r));else{if(!r.nodeType)return this;r=r.innerHTML}else e&&(r=function(e){if(e.outerHTML)return e.outerHTML;var t=document.createElement("div");return t.appendChild(e.cloneNode(!0)),t.innerHTML}(e));if(r){var i=rs(r,{outputSourceRange:!1,shouldDecodeNewlines:os,shouldDecodeNewlinesForHref:as,delimiters:n.delimiters,comments:n.comments},this),o=i.render,a=i.staticRenderFns;n.render=o,n.staticRenderFns=a}}return cs.call(this,e,t)},wn.compile=rs,wn}); \ No newline at end of file diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/plugin.min.js b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/plugin.min.js index 65a3234e0..68ccf7903 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/plugin.min.js +++ b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/plugin.min.js @@ -6,2671 +6,3377 @@ * * Version: 5.10.2 (2021-11-17) */ + +/** + * "Title and Attribution" tab by Manuel Narváez (https://github.com/mnarvaezm) + */ + (function () { - 'use strict'; + 'use strict'; + + let lang = 'en'; + let idSelectedImage = ''; + let img_v_style = 'none'; + let currentAlignStyle = ''; + let alignImageStyle = ''; + let style_for_div = ''; + let attibutionTab = false; + let actualAlign = 'position-center'; + + let bm = ''; + + // replace var Dialog return {open:open} + async function doit(editor) { + + var helpers = { + onSubmit: submitHandler(editor), + imageSize: imageSize(editor), + addToBlobCache: addToBlobCache(editor), + createBlobCache: createBlobCache(editor), + alertErr: alertErr(editor), + normalizeCss: normalizeCss(editor), + parseStyle: parseStyle(editor), + serializeStyle: serializeStyle(editor), + uploadImage: uploadImage(editor) + }; + + await collect(editor).then(makeDialog(helpers)).then(editor.windowManager.open); - let lang = 'en'; - let idSelectedImage = ''; - let numero_imagenes = 0; - let img_v_style = 'none'; - let currentAlignStyle = ''; - let alignImageStyle = ''; - let style_for_div = ''; - let attibutionTab = false; - let actualAlign = 'position-center'; + // bookmark selected image + bm = tinymce.activeEditor.selection.getBookmark(); - let bm = ''; + setGenTabStyle(); + setDialogStyle(); + } - // replace var Dialog return {open:open} - async function doit(editor) { + function setDialogStyle() { + let divDialog = $('div.tox-dialog').eq(0); + tinymce.DOM.setStyles(divDialog, { 'min-width': 600 }); - var helpers = { - onSubmit: submitHandler(editor), - imageSize: imageSize(editor), - addToBlobCache: addToBlobCache(editor), - createBlobCache: createBlobCache(editor), - alertErr: alertErr(editor), - normalizeCss: normalizeCss(editor), - parseStyle: parseStyle(editor), - serializeStyle: serializeStyle(editor), - uploadImage: uploadImage(editor) - }; + let divBody = $('div.tox-dialog__body').eq(0); + tinymce.DOM.setStyles(divBody, { 'display': 'grid', 'grid-template-columns': '1fr' }); - await collect(editor).then(makeDialog(helpers)).then(editor.windowManager.open); + let divNavbar = $('div.tox-dialog__body-nav').eq(0); + tinymce.DOM.removeClass(divNavbar, 'tox-dialog__body-nav'); + tinymce.DOM.setStyle(divNavbar, 'margin-top', 10); - // bookmark selected image - bm = tinymce.activeEditor.selection.getBookmark(); + for (let i = 0; i < divNavbar.children().length; i++) { + tinymce.DOM.setStyles(divNavbar.children().eq(i), { 'margin-left': 20 }); + } - setGenTabStyle(); - setDialogStyle(); - } + let divBodyContent = $('div.tox-dialog__body-content').eq(0); + tinymce.DOM.setStyles(divBodyContent, { 'max-height': 400 }); + } - function setDialogStyle() { - let divDialog = $('div.tox-dialog').eq(0); - tinymce.DOM.setStyles(divDialog, { 'min-width': 600 }); + function setGenTabStyle() { + let editor = tinymce.activeEditor; - let divBody = $('div.tox-dialog__body').eq(0); - tinymce.DOM.setStyles(divBody, { 'display': 'grid', 'grid-template-columns': '1fr' }); + let divsInput = $('div.tox-form__group'); - let divNavbar = $('div.tox-dialog__body-nav').eq(0); - tinymce.DOM.removeClass(divNavbar, 'tox-dialog__body-nav'); - tinymce.DOM.setStyle(divNavbar, 'margin-top', 10); + for (let i = 0; i < 3; i++) { + editor.dom.setStyles(divsInput.eq(i), { 'display': 'flex', 'margin-top': 5 }); + editor.dom.setStyles(divsInput.eq(i).children().eq(0), { 'min-width': 180, 'padding': '8px 0' }); + editor.dom.setStyles(divsInput.eq(i).children().eq(1), { 'display': 'flex', 'width': '100%' }); + } - for (let i = 0; i < divNavbar.children().length; i++) { - tinymce.DOM.setStyles(divNavbar.children().eq(i), { 'margin-left': 20 }); - } + let divBodyForm = $('div.tox-form').eq(0); - let divBodyContent = $('div.tox-dialog__body-content').eq(0); - tinymce.DOM.setStyles(divBodyContent, { 'max-height': 400 }); - } + let divDimensions = divBodyForm.children().eq(3); + editor.dom.setStyles(divDimensions, { 'margin-top': 5 }); - function setGenTabStyle() { - let editor = tinymce.activeEditor; + // hide label width and hieght and block + let divWidth = divDimensions.children().eq(0).children().eq(0); + let labelWidth = divWidth.children().eq(0); + labelWidth.hide(); + let inputWidth = divWidth.children().eq(1); + editor.dom.setAttribs(inputWidth, { id: "width-dimension"}); + + let divHeight = divDimensions.children().eq(0).children().eq(1); + let labelHeight = divHeight.children().eq(0); + labelHeight.hide(); + let inputHeight = divHeight.children().eq(1); + editor.dom.setAttribs(inputHeight, { id: "height-dimension"}); - let divsInput = $('div.tox-form__group'); + let divBlock = divDimensions.children().eq(0).children().eq(2); + let labelBlock = divBlock.children().eq(0); + labelBlock.hide(); - for (let i = 0; i < 4; i++) { - if (i != 1) { - editor.dom.setStyles(divsInput.eq(i), { 'display': 'flex', 'margin-top': 5 }); - editor.dom.setStyles(divsInput.eq(i).children().eq(0), { 'min-width': 180, 'padding': '8px 0' }); - editor.dom.setStyles(divsInput.eq(i).children().eq(1), { 'display': 'flex', 'width': '100%' }); - } + //Add label block dimensions + let labelBlockDimensions = editor.dom.create('label', { 'class': 'tox-label' }, _('Constrain proportions')); + let divBlockDimensions = editor.dom.create('div', { 'class': 'tox-form__group' }, labelBlockDimensions); + divBlock.after(divBlockDimensions); + + //Add label X + let labelX = editor.dom.create('label', { 'class': 'tox-label' }, 'x'); + let divLabelX = editor.dom.create('div', { 'class': 'tox-form__group', 'style': 'min-width: 8px; padding: 8px 0; margin-left: 5px' }, labelX); + divWidth.after(divLabelX); + + //Add label dimensions + let labelDimensions = editor.dom.create('label', { 'class': 'tox-label' }, _('Dimensions')); + let divLabelDimensions = editor.dom.create('div', { 'class': 'tox-form__group', 'style': 'min-width: 176px' }, labelDimensions); + divWidth.before(divLabelDimensions); } - let divBodyForm = $('div.tox-form').eq(0); + function setAdvTabStyle() { + let divsInput = $('div.tox-form__group'); + for (let i = 0; i < 2; i++) { + tinymce.DOM.setStyles(divsInput.eq(i), { 'display': 'flex', 'margin-top': 5 }); + tinymce.DOM.setStyles(divsInput.eq(i).children().eq(0), { 'min-width': 130, 'padding': '8px 0' }); + tinymce.DOM.setStyles(divsInput.eq(i).children().eq(1), { 'width': '100%' }); + } - let divOptimizer = divBodyForm.children().eq(1); - editor.dom.setStyles(divOptimizer, { 'display': 'flex', 'margin-top': 5 }); - editor.dom.setStyles(divOptimizer.children().eq(0), { 'margin-left': 180, 'width': '100%' }); - let buttonOptimizer = divOptimizer.children().eq(0); - editor.dom.setAttribs(buttonOptimizer, { id: 'openOptimizer' }); + let divSpace = $('div.tox-form__grid').eq(0); + tinymce.DOM.setStyles(divSpace, { 'margin-top': 5 }); - // Enable optimizer button if source already has an optimizable value - let srcInput = divBodyForm[0]?.querySelector('.tox-form__group input.tox-textfield'); - let srcValue = srcInput?.value || ''; - let isOptimizable = srcValue.startsWith('blob:') || - srcValue.startsWith('asset://') || - srcValue.startsWith('data:') || - srcValue.startsWith('resources/') || - srcValue.startsWith('/previews/'); - if (srcValue && isOptimizable) { - tinymce.dom.DomQuery("#openOptimizer").removeAttr("disabled"); - } + for (let i = 0; i < 4; i++) { + tinymce.DOM.setStyles(divSpace.children().eq(i), { 'display': 'flex' }); + tinymce.DOM.setStyles(divSpace.children().eq(i).children().eq(0), { 'min-width': 130, 'padding': '8px 0' }); + tinymce.DOM.setStyles(divSpace.children().eq(i).children().eq(1), { 'width': '100%' }); + if (i == 2 || i == 3) { + tinymce.DOM.setStyles(divSpace.children().eq(i), { 'margin-top': 5 }); + } + } - let divDimensions = divBodyForm.children().eq(3); - editor.dom.setStyles(divDimensions, { 'margin-top': 5 }); - - // hide label width and hieght and block - let divWidth = divDimensions.children().eq(0).children().eq(0); - let labelWidth = divWidth.children().eq(0); - labelWidth.hide(); - let inputWidth = divWidth.children().eq(1); - editor.dom.setAttribs(inputWidth, { id: "width-dimension"}); - - let divHeight = divDimensions.children().eq(0).children().eq(1); - let labelHeight = divHeight.children().eq(0); - labelHeight.hide(); - let inputHeight = divHeight.children().eq(1); - editor.dom.setAttribs(inputHeight, { id: "height-dimension"}); - - let divBlock = divDimensions.children().eq(0).children().eq(2); - let labelBlock = divBlock.children().eq(0); - labelBlock.hide(); - - //Add label block dimensions - let labelBlockDimensions = editor.dom.create('label', { 'class': 'tox-label' }, _('Constrain proportions')); - let divBlockDimensions = editor.dom.create('div', { 'class': 'tox-form__group' }, labelBlockDimensions); - divBlock.after(divBlockDimensions); - - //Add label X - let labelX = editor.dom.create('label', { 'class': 'tox-label' }, 'x'); - let divLabelX = editor.dom.create('div', { 'class': 'tox-form__group', 'style': 'min-width: 8px; padding: 8px 0; margin-left: 5px' }, labelX); - divWidth.after(divLabelX); - - //Add label dimensions - let labelDimensions = editor.dom.create('label', { 'class': 'tox-label' }, _('Dimensions')); - let divLabelDimensions = editor.dom.create('div', { 'class': 'tox-form__group', 'style': 'min-width: 176px' }, labelDimensions); - divWidth.before(divLabelDimensions); - - } - - function setAdvTabStyle() { - - let divsInput = $('div.tox-form__group'); - - for (let i = 0; i < 2; i++) { - tinymce.DOM.setStyles(divsInput.eq(i), { 'display': 'flex', 'margin-top': 5 }); - tinymce.DOM.setStyles(divsInput.eq(i).children().eq(0), { 'min-width': 130, 'padding': '8px 0' }); - tinymce.DOM.setStyles(divsInput.eq(i).children().eq(1), { 'width': '100%' }); } - let divSpace = $('div.tox-form__grid').eq(0); - tinymce.DOM.setStyles(divSpace, { 'margin-top': 5 }); + function setAttributionTabStyle() { - for (let i = 0; i < 4; i++) { - tinymce.DOM.setStyles(divSpace.children().eq(i), { 'display': 'flex' }); - tinymce.DOM.setStyles(divSpace.children().eq(i).children().eq(0), { 'min-width': 130, 'padding': '8px 0' }); - tinymce.DOM.setStyles(divSpace.children().eq(i).children().eq(1), { 'width': '100%' }); - if (i == 2 || i == 3) { - tinymce.DOM.setStyles(divSpace.children().eq(i), { 'margin-top': 5 }); - } - } + let divHeader = $('div.tox-form__group').eq(0); - } + tinymce.DOM.setStyles(divHeader, { 'display': 'flex', 'margin-top': 5 }); + tinymce.DOM.setStyles(divHeader.children().eq(0), { 'min-width': 100, 'padding': '8px 0' }); + tinymce.DOM.setStyles(divHeader.children().eq(1), { 'width': '100%' }); - function setAttributionTabStyle() { + let divFooter = $('div.tox-form__group').eq(1); + tinymce.DOM.setStyles(divFooter, { 'border': '1px solid #666', 'padding': '0px 10px 10px 10px', 'margin-top': 10 }); - let divHeader = $('div.tox-form__group').eq(0); + // If we use fieldset, problem to select listbox - tinymce.DOM.setStyles(divHeader, { 'display': 'flex', 'margin-top': 5 }); - tinymce.DOM.setStyles(divHeader.children().eq(0), { 'min-width': 100, 'padding': '8px 0' }); - tinymce.DOM.setStyles(divHeader.children().eq(1), { 'width': '100%' }); + // let fieldsetHtml = tinymce.DOM.getOuterHTML(divFooter); + // fieldsetHtml = fieldsetHtml.replace("([^div>]*)$/, "fieldset>" + '$1'); + // tinymce.DOM.setOuterHTML(divFooter, fieldsetHtml); + // tinymce.DOM.setStyles(divFooter, { 'margin-top': 5 }); - let divFooter = $('div.tox-form__group').eq(1); - tinymce.DOM.setStyles(divFooter, { 'border': '1px solid #666', 'padding': '0px 10px 10px 10px', 'margin-top': 10 }); + // let divLegend = $('fieldset.tox-form__group').children().eq(0); + // tinymce.DOM.setStyles(divLegend, {'margin-left': 10, 'padding-left': 10}); - // If we use fieldset, problem to select listbox + // let legendHtml = tinymce.DOM.getOuterHTML(divLegend); + // legendHtml = legendHtml.replace('', 'legend>'); + // tinymce.DOM.setOuterHTML(divLegend, legendHtml); - // let fieldsetHtml = tinymce.DOM.getOuterHTML(divFooter); - // fieldsetHtml = fieldsetHtml.replace("([^div>]*)$/, "fieldset>" + '$1'); - // tinymce.DOM.setOuterHTML(divFooter, fieldsetHtml); - // tinymce.DOM.setStyles(divFooter, { 'margin-top': 5 }); + let divLegend = divFooter.children().eq(0); + tinymce.DOM.setStyles(divLegend, { 'width': 'fit-content', 'padding-left': 10, 'position': 'relative', 'top': '-10px', 'left': '0px', 'background-color': 'white' }); - // let divLegend = $('fieldset.tox-form__group').children().eq(0); - // tinymce.DOM.setStyles(divLegend, {'margin-left': 10, 'padding-left': 10}); + let newFooter = $('div.tox-form__group'); + for (let i = 1; i < newFooter.children().length; i++) { + tinymce.DOM.setStyles(newFooter.children().eq(i), { 'display': 'flex' }); + tinymce.DOM.setStyles(newFooter.children().eq(i).children().eq(0), { 'min-width': 200, 'padding': '8px 0' }); + tinymce.DOM.setStyles(newFooter.children().eq(i).children().eq(1), { 'width': '100%' }); + } - // let legendHtml = tinymce.DOM.getOuterHTML(divLegend); - // legendHtml = legendHtml.replace('', 'legend>'); - // tinymce.DOM.setOuterHTML(divLegend, legendHtml); + /** + Footer caption + */ - let divLegend = divFooter.children().eq(0); - tinymce.DOM.setStyles(divLegend, { 'width': 'fit-content', 'padding-left': 10, 'position': 'relative', 'top': '-10px', 'left': '0px', 'background-color': 'white' }); + // for (let i = 0; i < 2; i++) { + // tinymce.DOM.setStyles(divsInput.eq(i), { 'display': 'flex', 'margin-top': 5 }); + // tinymce.DOM.setStyles(divsInput.eq(i).children().eq(0), { 'min-width': 130, 'padding': '8px 0' }); + // tinymce.DOM.setStyles(divsInput.eq(i).children().eq(1), { 'width': '100%' }); + // } - let newFooter = $('div.tox-form__group'); - for (let i = 1; i < newFooter.children().length; i++) { - tinymce.DOM.setStyles(newFooter.children().eq(i), { 'display': 'flex' }); - tinymce.DOM.setStyles(newFooter.children().eq(i).children().eq(0), { 'min-width': 200, 'padding': '8px 0' }); - tinymce.DOM.setStyles(newFooter.children().eq(i).children().eq(1), { 'width': '100%' }); - } + // let divSpace = $('div.tox-form__grid').eq(0); + // tinymce.DOM.setStyles(divSpace, { 'margin-top': 5 }); - /** - Footer caption - */ - - // for (let i = 0; i < 2; i++) { - // tinymce.DOM.setStyles(divsInput.eq(i), { 'display': 'flex', 'margin-top': 5 }); - // tinymce.DOM.setStyles(divsInput.eq(i).children().eq(0), { 'min-width': 130, 'padding': '8px 0' }); - // tinymce.DOM.setStyles(divsInput.eq(i).children().eq(1), { 'width': '100%' }); - // } - - // let divSpace = $('div.tox-form__grid').eq(0); - // tinymce.DOM.setStyles(divSpace, { 'margin-top': 5 }); - - // for (let i = 0; i < 4; i++) { - // tinymce.DOM.setStyles(divSpace.children().eq(i), { 'display': 'flex' }); - // tinymce.DOM.setStyles(divSpace.children().eq(i).children().eq(0), { 'min-width': 130, 'padding': '8px 0' }); - // tinymce.DOM.setStyles(divSpace.children().eq(i).children().eq(1), { 'width': '100%' }); - // } - - - } - - var global$6 = tinymce.util.Tools.resolve('tinymce.PluginManager'); - - var __assign = function () { - __assign = Object.assign || function __assign(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) - if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); - }; - - var typeOf = function (x) { - var t = typeof x; - if (x === null) { - return 'null'; - } else if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) { - return 'array'; - } else if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) { - return 'string'; - } else { - return t; - } - }; - var isType = function (type) { - return function (value) { - return typeOf(value) === type; - }; - }; - var isSimpleType = function (type) { - return function (value) { - return typeof value === type; - }; - }; - var eq = function (t) { - return function (a) { - return t === a; - }; - }; - var isString = isType('string'); - var isObject = isType('object'); - var isArray = isType('array'); - var isNull = eq(null); - var isBoolean = isSimpleType('boolean'); - var isNullable = function (a) { - return a === null || a === undefined; - }; - var isNonNullable = function (a) { - return !isNullable(a); - }; - var isFunction = isSimpleType('function'); - var isNumber = isSimpleType('number'); - - var noop = function () { - }; - var constant = function (value) { - return function () { - return value; - }; - }; - var identity = function (x) { - return x; - }; - var never = constant(false); - var always = constant(true); - - var none = function () { - return NONE; - }; - var NONE = function () { - var call = function (thunk) { - return thunk(); - }; - var id = identity; - var me = { - fold: function (n, _s) { - return n(); - }, - isSome: never, - isNone: always, - getOr: id, - getOrThunk: call, - getOrDie: function (msg) { - throw new Error(msg || 'error: getOrDie called on none.'); - }, - getOrNull: constant(null), - getOrUndefined: constant(undefined), - or: id, - orThunk: call, - map: none, - each: noop, - bind: none, - exists: never, - forall: always, - filter: function () { - return none(); - }, - toArray: function () { - return []; - }, - toString: constant('none()') - }; - return me; - }(); - var some = function (a) { - var constant_a = constant(a); - var self = function () { - return me; - }; - var bind = function (f) { - return f(a); - }; - var me = { - fold: function (n, s) { - return s(a); - }, - isSome: always, - isNone: never, - getOr: constant_a, - getOrThunk: constant_a, - getOrDie: constant_a, - getOrNull: constant_a, - getOrUndefined: constant_a, - or: self, - orThunk: self, - map: function (f) { - return some(f(a)); - }, - each: function (f) { - f(a); - }, - bind: bind, - exists: bind, - forall: bind, - filter: function (f) { - return f(a) ? me : NONE; - }, - toArray: function () { - return [a]; - }, - toString: function () { - return 'some(' + a + ')'; - } - }; - return me; - }; - var from = function (value) { - return value === null || value === undefined ? NONE : some(value); - }; - var Optional = { - some: some, - none: none, - from: from - }; - - var keys = Object.keys; - var hasOwnProperty = Object.hasOwnProperty; - var each = function (obj, f) { - var props = keys(obj); - for (var k = 0, len = props.length; k < len; k++) { - var i = props[k]; - var x = obj[i]; - f(x, i); - } - }; - var objAcc = function (r) { - return function (x, i) { - r[i] = x; - }; - }; - var internalFilter = function (obj, pred, onTrue, onFalse) { - var r = {}; - each(obj, function (x, i) { - (pred(x, i) ? onTrue : onFalse)(x, i); - }); - return r; - }; - var filter = function (obj, pred) { - var t = {}; - internalFilter(obj, pred, objAcc(t), noop); - return t; - }; - var has = function (obj, key) { - return hasOwnProperty.call(obj, key); - }; - var hasNonNullableKey = function (obj, key) { - return has(obj, key) && obj[key] !== undefined && obj[key] !== null; - }; - - var nativePush = Array.prototype.push; - var flatten = function (xs) { - var r = []; - for (var i = 0, len = xs.length; i < len; ++i) { - if (!isArray(xs[i])) { - throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs); - } - nativePush.apply(r, xs[i]); - } - return r; - }; - var get = function (xs, i) { - return i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none(); - }; - var head = function (xs) { - return get(xs, 0); - }; - var findMap = function (arr, f) { - for (var i = 0; i < arr.length; i++) { - var r = f(arr[i], i); - if (r.isSome()) { - return r; - } - } - return Optional.none(); - }; + // for (let i = 0; i < 4; i++) { + // tinymce.DOM.setStyles(divSpace.children().eq(i), { 'display': 'flex' }); + // tinymce.DOM.setStyles(divSpace.children().eq(i).children().eq(0), { 'min-width': 130, 'padding': '8px 0' }); + // tinymce.DOM.setStyles(divSpace.children().eq(i).children().eq(1), { 'width': '100%' }); + // } - typeof window !== 'undefined' ? window : Function('return this;')(); - var rawSet = function (dom, key, value) { - if (isString(value) || isBoolean(value) || isNumber(value)) { - dom.setAttribute(key, value + ''); - } else { - console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom); - throw new Error('Attribute value was not simple'); - } - }; - var set = function (element, key, value) { - rawSet(element.dom, key, value); - }; - var remove = function (element, key) { - element.dom.removeAttribute(key); - }; - - var fromHtml = function (html, scope) { - var doc = scope || document; - var div = doc.createElement('div'); - div.innerHTML = html; - if (!div.hasChildNodes() || div.childNodes.length > 1) { - console.error('HTML does not have a single root node', html); - throw new Error('HTML must have a single root node'); - } - return fromDom(div.childNodes[0]); - }; - var fromTag = function (tag, scope) { - var doc = scope || document; - var node = doc.createElement(tag); - return fromDom(node); - }; - var fromText = function (text, scope) { - var doc = scope || document; - var node = doc.createTextNode(text); - return fromDom(node); - }; - var fromDom = function (node) { - if (node === null || node === undefined) { - throw new Error('Node cannot be null or undefined'); } - return { dom: node }; - }; - var fromPoint = function (docElm, x, y) { - return Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom); - }; - var SugarElement = { - fromHtml: fromHtml, - fromTag: fromTag, - fromText: fromText, - fromDom: fromDom, - fromPoint: fromPoint - }; - - var global$5 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils'); - - var global$4 = tinymce.util.Tools.resolve('tinymce.util.Promise'); - - var global$3 = tinymce.util.Tools.resolve('tinymce.util.URI'); - - var global$2 = tinymce.util.Tools.resolve('tinymce.util.XHR'); - - var hasDimensions = function (editor) { - return editor.getParam('image_dimensions', true, 'boolean'); - }; - var hasAdvTab = function (editor) { - return editor.getParam('image_advtab', false, 'boolean'); - }; - var hasUploadTab = function (editor) { - return editor.getParam('image_uploadtab', true, 'boolean'); - }; - // attribution tab - var hasAttributionTab = function (editor) { - return editor.getParam('exeimage_attributiontab', true, 'boolean'); - }; - var getPrependUrl = function (editor) { - return editor.getParam('image_prepend_url', '', 'string'); - }; - var getClassList = function (editor) { - return editor.getParam('image_class_list'); - }; - var hasDescription = function (editor) { - return editor.getParam('image_description', true, 'boolean'); - }; - var hasImageTitle = function (editor) { - return editor.getParam('image_title', false, 'boolean'); - }; - var hasImageCaption = function (editor) { - return editor.getParam('image_caption', false, 'boolean'); - }; - var getImageList = function (editor) { - return editor.getParam('image_list', false); - }; - var hasUploadUrl = function (editor) { - return isNonNullable(editor.getParam('images_upload_url')); - }; - var hasUploadHandler = function (editor) { - return isNonNullable(editor.getParam('images_upload_handler')); - }; - var showAccessibilityOptions = function (editor) { - return editor.getParam('a11y_advanced_options', false, 'boolean'); - }; - var isAutomaticUploadsEnabled = function (editor) { - return editor.getParam('automatic_uploads', true, 'boolean'); - }; - - var parseIntAndGetMax = function (val1, val2) { - return Math.max(parseInt(val1, 10), parseInt(val2, 10)); - }; - var getImageSize = function (url) { - return new global$4(function (callback) { - var img = document.createElement('img'); - var done = function (dimensions) { - img.onload = img.onerror = null; - if (img.parentNode) { - img.parentNode.removeChild(img); - } - callback(dimensions); - }; - img.onload = function () { - var width = parseIntAndGetMax(img.width, img.clientWidth); - var height = parseIntAndGetMax(img.height, img.clientHeight); - var dimensions = { - width: width, - height: height + + var global$6 = tinymce.util.Tools.resolve('tinymce.PluginManager'); + + var __assign = function () { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) + if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; }; - done(global$4.resolve(dimensions)); - }; - img.onerror = function () { - done(global$4.reject('Failed to get image dimensions for: ' + url)); - }; - var style = img.style; - style.visibility = 'hidden'; - style.position = 'fixed'; - style.bottom = style.left = '0px'; - style.width = style.height = 'auto'; - document.body.appendChild(img); - img.src = url; - }); - }; - var removePixelSuffix = function (value) { - if (value) { - value = value.replace(/px$/, ''); - } - return value; - }; - var addPixelSuffix = function (value) { - if (value.length > 0 && /^[0-9]+$/.test(value)) { - value += 'px'; - } - return value; - }; - var mergeMargins = function (css) { - if (css.margin) { - var splitMargin = String(css.margin).split(' '); - switch (splitMargin.length) { - case 1: - css['margin-top'] = css['margin-top'] || splitMargin[0]; - css['margin-right'] = css['margin-right'] || splitMargin[0]; - css['margin-bottom'] = css['margin-bottom'] || splitMargin[0]; - css['margin-left'] = css['margin-left'] || splitMargin[0]; - break; - case 2: - css['margin-top'] = css['margin-top'] || splitMargin[0]; - css['margin-right'] = css['margin-right'] || splitMargin[1]; - css['margin-bottom'] = css['margin-bottom'] || splitMargin[0]; - css['margin-left'] = css['margin-left'] || splitMargin[1]; - break; - case 3: - css['margin-top'] = css['margin-top'] || splitMargin[0]; - css['margin-right'] = css['margin-right'] || splitMargin[1]; - css['margin-bottom'] = css['margin-bottom'] || splitMargin[2]; - css['margin-left'] = css['margin-left'] || splitMargin[1]; - break; - case 4: - css['margin-top'] = css['margin-top'] || splitMargin[0]; - css['margin-right'] = css['margin-right'] || splitMargin[1]; - css['margin-bottom'] = css['margin-bottom'] || splitMargin[2]; - css['margin-left'] = css['margin-left'] || splitMargin[3]; - } - delete css.margin; - } - return css; - }; - var createImageList = function (editor, callback) { - var imageList = getImageList(editor); - if (isString(imageList)) { - global$2.send({ - url: imageList, - success: function (text) { - callback(JSON.parse(text)); - } - }); - } else if (isFunction(imageList)) { - imageList(callback); - } else { - callback(imageList); - } - }; - var waitLoadImage = function (editor, data, imgElm) { - var selectImage = function () { - imgElm.onload = imgElm.onerror = null; - if (editor.selection) { - editor.selection.select(imgElm); - editor.nodeChanged(); - } - }; - imgElm.onload = function () { - if (!data.width && !data.height && hasDimensions(editor)) { - editor.dom.setAttribs(imgElm, { - width: String(imgElm.clientWidth), - height: String(imgElm.clientHeight) - }); - } - selectImage(); - }; - imgElm.onerror = selectImage; - }; - var blobToDataUri = function (blob) { - return new global$4(function (resolve, reject) { - var reader = new FileReader(); - reader.onload = function () { - resolve(reader.result); - }; - reader.onerror = function () { - reject(reader.error.message); - }; - reader.readAsDataURL(blob); - }); - }; - var isPlaceholderImage = function (imgElm) { - return imgElm.nodeName === 'IMG' && (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder')); - }; - var isSafeImageUrl = function (editor, src) { - return global$3.isDomSafe(src, 'img', editor.settings); - }; - - var DOM = global$5.DOM; - var getHspace = function (image) { - if (image.style.marginLeft && image.style.marginRight && image.style.marginLeft === image.style.marginRight) { - return removePixelSuffix(image.style.marginLeft); - } else { - return ''; - } - }; - var getVspace = function (image) { - if (image.style.marginTop && image.style.marginBottom && image.style.marginTop === image.style.marginBottom) { - return removePixelSuffix(image.style.marginTop); - } else { - return ''; - } - }; - var getBorder = function (image) { - if (image.style.borderWidth) { - return removePixelSuffix(image.style.borderWidth); - } else { - return ''; - } - }; - var getAttrib = function (image, name) { - if (image.hasAttribute(name)) { - return image.getAttribute(name); - } else { - return ''; - } - }; - var getStyle = function (image, name) { - return image.style[name] ? image.style[name] : ''; - }; - var hasCaption = function (image) { - return image.parentNode !== null && image.parentNode.nodeName === 'FIGURE'; - }; - var updateAttrib = function (image, name, value) { - if (value === '') { - image.removeAttribute(name); - } else { - image.setAttribute(name, value); - } - }; - var wrapInFigure = function (image) { - var figureElm = DOM.create('figure', { class: 'image' }); - DOM.insertAfter(figureElm, image); - figureElm.appendChild(image); - figureElm.appendChild(DOM.create('figcaption', { contentEditable: 'true' }, 'Caption')); - figureElm.contentEditable = 'false'; - }; - var removeFigure = function (image) { - var figureElm = image.parentNode; - DOM.insertAfter(image, figureElm); - DOM.remove(figureElm); - }; - var toggleCaption = function (image) { - if (hasCaption(image)) { - removeFigure(image); - } else { - wrapInFigure(image); - } - }; - var normalizeStyle = function (image, normalizeCss) { - var attrValue = image.getAttribute('style'); - var value = normalizeCss(attrValue !== null ? attrValue : ''); - if (value.length > 0) { - image.setAttribute('style', value); - image.setAttribute('data-mce-style', value); - } else { - image.removeAttribute('style'); - } - }; - var setSize = function (name, normalizeCss) { - return function (image, name, value) { - if (image.style[name]) { - image.style[name] = addPixelSuffix(value); - normalizeStyle(image, normalizeCss); - } else { - updateAttrib(image, name, value); - } - }; - }; - var getSize = function (image, name) { - if (image.style[name]) { - return removePixelSuffix(image.style[name]); - } else { - return getAttrib(image, name); - } - }; - var setHspace = function (image, value) { - var pxValue = addPixelSuffix(value); - image.style.marginLeft = pxValue; - image.style.marginRight = pxValue; - }; - var setVspace = function (image, value) { - var pxValue = addPixelSuffix(value); - image.style.marginTop = pxValue; - image.style.marginBottom = pxValue; - }; - var setBorder = function (image, value) { - var pxValue = addPixelSuffix(value); - image.style.borderWidth = pxValue; - }; - var setBorderStyle = function (image, value) { - image.style.borderStyle = value; - }; - var getBorderStyle = function (image) { - return getStyle(image, 'borderStyle'); - }; - var isFigure = function (elm) { - return elm.nodeName === 'FIGURE'; - }; - var isImage = function (elm) { - return elm.nodeName === 'IMG'; - }; - var getIsDecorative = function (image) { - return DOM.getAttrib(image, 'alt').length === 0 && DOM.getAttrib(image, 'role') === 'presentation'; - }; - var getAlt = function (image) { - if (getIsDecorative(image)) { - return ''; - } else { - return getAttrib(image, 'alt'); - } - }; - var defaultData = function () { - return { - src: '', - alt: '', - title: '', - width: '', - height: '', - class: '', - style: '', - caption: false, - hspace: '', - vspace: '', - border: '', - borderStyle: '', - isDecorative: false - }; - }; - var getStyleValue = function (normalizeCss, data) { - var image = document.createElement('img'); - updateAttrib(image, 'style', data.style); - if (getHspace(image) || data.hspace !== '') { - setHspace(image, data.hspace); - } - if (getVspace(image) || data.vspace !== '') { - setVspace(image, data.vspace); - } - if (getBorder(image) || data.border !== '') { - setBorder(image, data.border); - } - if (getBorderStyle(image) || data.borderStyle !== '') { - setBorderStyle(image, data.borderStyle); - } - return normalizeCss(image.getAttribute('style')); - }; - var create = function (normalizeCss, data) { - var image = document.createElement('img'); - write(normalizeCss, __assign(__assign({}, data), { caption: false }), image); - setAlt(image, data.alt, data.isDecorative); - if (data.caption) { - var figure = DOM.create('figure', { class: 'image' }); - figure.appendChild(image); - figure.appendChild(DOM.create('figcaption', { contentEditable: 'true' }, 'Caption')); - figure.contentEditable = 'false'; - return figure; - } else { - return image; - } - }; - var read = function (normalizeCss, image) { - return { - src: getAttrib(image, 'src'), - alt: getAlt(image), - title: getAttrib(image, 'title'), - width: getSize(image, 'width'), - height: getSize(image, 'height'), - class: getAttrib(image, 'class'), - style: normalizeCss(getAttrib(image, 'style')), - caption: hasCaption(image), - hspace: getHspace(image), - vspace: getVspace(image), - border: getBorder(image), - borderStyle: getStyle(image, 'borderStyle'), - isDecorative: getIsDecorative(image) - }; - }; - var updateProp = function (image, oldData, newData, name, set) { - if (newData[name] !== oldData[name]) { - set(image, name, newData[name]); - } - }; - var setAlt = function (image, alt, isDecorative) { - if (isDecorative) { - DOM.setAttrib(image, 'role', 'presentation'); - var sugarImage = SugarElement.fromDom(image); - set(sugarImage, 'alt', ''); - } else { - if (isNull(alt)) { - var sugarImage = SugarElement.fromDom(image); - remove(sugarImage, 'alt'); - } else { - var sugarImage = SugarElement.fromDom(image); - set(sugarImage, 'alt', alt); - } - if (DOM.getAttrib(image, 'role') === 'presentation') { - DOM.setAttrib(image, 'role', ''); - } - } - }; - var updateAlt = function (image, oldData, newData) { - if (newData.alt !== oldData.alt || newData.isDecorative !== oldData.isDecorative) { - setAlt(image, newData.alt, newData.isDecorative); - } - }; - var normalized = function (set, normalizeCss) { - return function (image, name, value) { - set(image, value); - normalizeStyle(image, normalizeCss); - }; - }; - var write = function (normalizeCss, newData, image) { - var oldData = read(normalizeCss, image); - updateProp(image, oldData, newData, 'caption', function (image, _name, _value) { - return toggleCaption(image); - }); - updateProp(image, oldData, newData, 'src', updateAttrib); - updateProp(image, oldData, newData, 'title', updateAttrib); - updateProp(image, oldData, newData, 'width', setSize('width', normalizeCss)); - updateProp(image, oldData, newData, 'height', setSize('height', normalizeCss)); - updateProp(image, oldData, newData, 'class', updateAttrib); - updateProp(image, oldData, newData, 'style', normalized(function (image, value) { - return updateAttrib(image, 'style', value); - }, normalizeCss)); - updateProp(image, oldData, newData, 'hspace', normalized(setHspace, normalizeCss)); - updateProp(image, oldData, newData, 'vspace', normalized(setVspace, normalizeCss)); - updateProp(image, oldData, newData, 'border', normalized(setBorder, normalizeCss)); - updateProp(image, oldData, newData, 'borderStyle', normalized(setBorderStyle, normalizeCss)); - updateAlt(image, oldData, newData); - }; - - var normalizeCss$1 = function (editor, cssText) { - var css = editor.dom.styles.parse(cssText); - var mergedCss = mergeMargins(css); - var compressed = editor.dom.styles.parse(editor.dom.styles.serialize(mergedCss)); - return editor.dom.styles.serialize(compressed); - }; - var getSelectedImage = function (editor) { - var imgElm = editor.selection.getNode(); - var figureElm = editor.dom.getParent(imgElm, 'figure.image'); - if (figureElm) { - return editor.dom.select('img', figureElm)[0]; - } - if (imgElm && (imgElm.nodeName !== 'IMG' || isPlaceholderImage(imgElm))) { - return null; - } - return imgElm; - }; - var splitTextBlock = function (editor, figure) { - var dom = editor.dom; - var textBlockElements = filter(editor.schema.getTextBlockElements(), function (_, parentElm) { - return !editor.schema.isValidChild(parentElm, 'figure'); - }); - var textBlock = dom.getParent(figure.parentNode, function (node) { - return hasNonNullableKey(textBlockElements, node.nodeName); - }, editor.getBody()); - if (textBlock) { - return dom.split(textBlock, figure); - } else { - return figure; - } - }; - var readImageDataFromSelection = function (editor) { - var image = getSelectedImage(editor); - return image ? read(function (css) { - return normalizeCss$1(editor, css); - }, image) : defaultData(); - }; - var insertImageAtCaret = function (editor, data) { - var elm = create(function (css) { - return normalizeCss$1(editor, css); - }, data); - editor.dom.setAttrib(elm, 'data-mce-id', '__mcenew'); - editor.focus(); - editor.selection.setContent(elm.outerHTML); - var insertedElm = editor.dom.select('*[data-mce-id="__mcenew"]')[0]; - editor.dom.setAttrib(insertedElm, 'data-mce-id', null); - if (isFigure(insertedElm)) { - var figure = splitTextBlock(editor, insertedElm); - editor.selection.select(figure); - } else { - editor.selection.select(insertedElm); - } - }; - var syncSrcAttr = function (editor, image) { - editor.dom.setAttrib(image, 'src', image.getAttribute('src')); - }; - var deleteImage = function (editor, image) { - if (image) { - var elm = editor.dom.is(image.parentNode, 'figure.image') ? image.parentNode : image; - editor.dom.remove(elm); - editor.focus(); - editor.nodeChanged(); - if (editor.dom.isEmpty(editor.getBody())) { - editor.setContent(''); - editor.selection.setCursorLocation(); - } - } - }; - var writeImageDataToSelection = function (editor, data) { - var image = getSelectedImage(editor); - write(function (css) { - return normalizeCss$1(editor, css); - }, data, image); - syncSrcAttr(editor, image); - if (isFigure(image.parentNode)) { - var figure = image.parentNode; - splitTextBlock(editor, figure); - editor.selection.select(image.parentNode); - } else { - editor.selection.select(image); - waitLoadImage(editor, data, image); - } - }; - var sanitizeImageData = function (editor, data) { - var src = data.src; - return __assign(__assign({}, data), { src: isSafeImageUrl(editor, src) ? src : '' }); - }; - var insertOrUpdateImage = function (editor, partialData) { - var image = getSelectedImage(editor); - if (image) { - var selectedImageData = read(function (css) { - return normalizeCss$1(editor, css); - }, image); - var data = __assign(__assign({}, selectedImageData), partialData); - var sanitizedData = sanitizeImageData(editor, data); - if (data.src) { - writeImageDataToSelection(editor, sanitizedData); - } else { - deleteImage(editor, image); - } - } else if (partialData.src) { - insertImageAtCaret(editor, __assign(__assign({}, defaultData()), partialData)); - } - }; - - var deep = function (old, nu) { - var bothObjects = isObject(old) && isObject(nu); - return bothObjects ? deepMerge(old, nu) : nu; - }; - var baseMerge = function (merger) { - return function () { - var objects = []; - for (var _i = 0; _i < arguments.length; _i++) { - objects[_i] = arguments[_i]; - } - if (objects.length === 0) { - throw new Error('Can\'t merge zero objects'); - } - var ret = {}; - for (var j = 0; j < objects.length; j++) { - var curObject = objects[j]; - for (var key in curObject) { - if (has(curObject, key)) { - ret[key] = merger(ret[key], curObject[key]); - } - } - } - return ret; - }; - }; - var deepMerge = baseMerge(deep); - - var isNotEmpty = function (s) { - return s.length > 0; - }; - - var global$1 = tinymce.util.Tools.resolve('tinymce.util.ImageUploader'); - - var global = tinymce.util.Tools.resolve('tinymce.util.Tools'); - - var getValue = function (item) { - return isString(item.value) ? item.value : ''; - }; - var getText = function (item) { - if (isString(item.text)) { - return item.text; - } else if (isString(item.title)) { - return item.title; - } else { - return ''; - } - }; - var sanitizeList = function (list, extractValue) { - var out = []; - global.each(list, function (item) { - var text = getText(item); - if (item.menu !== undefined) { - var items = sanitizeList(item.menu, extractValue); - out.push({ - text: text, - items: items - }); - } else { - var value = extractValue(item); - out.push({ - text: text, - value: value - }); - } - }); - return out; - }; - var sanitizer = function (extractor) { - if (extractor === void 0) { - extractor = getValue; - } - return function (list) { - if (list) { - return Optional.from(list).map(function (list) { - return sanitizeList(list, extractor); - }); - } else { - return Optional.none(); - } - }; - }; - var sanitize = function (list) { - return sanitizer(getValue)(list); - }; - var isGroup = function (item) { - return has(item, 'items'); - }; - var findEntryDelegate = function (list, value) { - return findMap(list, function (item) { - if (isGroup(item)) { - return findEntryDelegate(item.items, value); - } else if (item.value === value) { - return Optional.some(item); - } else { - return Optional.none(); - } - }); - }; - var findEntry = function (optList, value) { - return optList.bind(function (list) { - return findEntryDelegate(list, value); - }); - }; - var ListUtils = { - sanitizer: sanitizer, - sanitize: sanitize, - findEntry: findEntry - }; - - var makeTab$2 = function (_info) { - return { - title: 'Advanced', - name: 'advanced', - items: [ - { - type: 'input', - label: 'Style', - name: 'style' - }, - { - type: 'listbox', - name: 'alignstyle', - label: _('Alignment'), - items: [ - { - text: _('-- Not Set --'), - value: '' - }, - { - text: _('Baseline'), - value: 'baseline' - }, - { - text: _('Top'), - value: 'top' + return __assign.apply(this, arguments); + }; + + var typeOf = function (x) { + var t = typeof x; + if (x === null) { + return 'null'; + } else if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) { + return 'array'; + } else if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) { + return 'string'; + } else { + return t; + } + }; + var isType = function (type) { + return function (value) { + return typeOf(value) === type; + }; + }; + var isSimpleType = function (type) { + return function (value) { + return typeof value === type; + }; + }; + var eq = function (t) { + return function (a) { + return t === a; + }; + }; + var isString = isType('string'); + var isObject = isType('object'); + var isArray = isType('array'); + var isNull = eq(null); + var isBoolean = isSimpleType('boolean'); + var isNullable = function (a) { + return a === null || a === undefined; + }; + var isNonNullable = function (a) { + return !isNullable(a); + }; + var isFunction = isSimpleType('function'); + var isNumber = isSimpleType('number'); + + var noop = function () { + }; + var constant = function (value) { + return function () { + return value; + }; + }; + var identity = function (x) { + return x; + }; + var never = constant(false); + var always = constant(true); + + var none = function () { + return NONE; + }; + var NONE = function () { + var call = function (thunk) { + return thunk(); + }; + var id = identity; + var me = { + fold: function (n, _s) { + return n(); }, - { - text: _('Middle'), - value: 'middle' + isSome: never, + isNone: always, + getOr: id, + getOrThunk: call, + getOrDie: function (msg) { + throw new Error(msg || 'error: getOrDie called on none.'); }, - { - text: _('Bottom'), - value: 'bottom' + getOrNull: constant(null), + getOrUndefined: constant(undefined), + or: id, + orThunk: call, + map: none, + each: noop, + bind: none, + exists: never, + forall: always, + filter: function () { + return none(); }, - { - text: _('Text top'), - value: 'text-top' + toArray: function () { + return []; }, - { - text: _('Text Bottom'), - value: 'text-bottom' + toString: constant('none()') + }; + return me; + }(); + var some = function (a) { + var constant_a = constant(a); + var self = function () { + return me; + }; + var bind = function (f) { + return f(a); + }; + var me = { + fold: function (n, s) { + return s(a); }, - { - text: _('Left'), - value: 'left' + isSome: always, + isNone: never, + getOr: constant_a, + getOrThunk: constant_a, + getOrDie: constant_a, + getOrNull: constant_a, + getOrUndefined: constant_a, + or: self, + orThunk: self, + map: function (f) { + return some(f(a)); }, - { - text: _('Right'), - value: 'right' - } - - ] - }, - { - type: 'grid', - columns: 2, - items: [ - { - type: 'input', - label: 'Vertical space', - name: 'vspace', - inputMode: 'numeric' + each: function (f) { + f(a); }, - { - type: 'input', - label: 'Horizontal space', - name: 'hspace', - inputMode: 'numeric' + bind: bind, + exists: bind, + forall: bind, + filter: function (f) { + return f(a) ? me : NONE; }, - { - type: 'input', - label: 'Border width', - name: 'border', - inputMode: 'numeric' + toArray: function () { + return [a]; }, - { - type: 'listbox', - name: 'borderstyle', - label: 'Border style', - items: [ - { - text: _('Select...'), - value: '' - }, - { - text: _('Solid'), - value: 'solid' - }, - { - text: _('Dotted'), - value: 'dotted' - }, - { - text: _('Dashed'), - value: 'dashed' - }, - { - text: _('Double'), - value: 'double' - }, - { - text: _('Groove'), - value: 'groove' - }, - { - text: _('Ridge'), - value: 'ridge' - }, - { - text: _('Inset'), - value: 'inset' - }, - { - text: _('Outset'), - value: 'outset' - }, - { - text: _('None'), - value: 'none' - }, - { - text: _('Hidden'), - value: 'hidden' - } - ] - } - ] - } - ] - }; - }; - var AdvTab = { makeTab: makeTab$2 }; - - var collect = function (editor) { - var urlListSanitizer = ListUtils.sanitizer(function (item) { - return editor.convertURL(item.value || item.url, 'src'); - }); - var futureImageList = new global$4(function (completer) { - createImageList(editor, function (imageList) { - completer(urlListSanitizer(imageList).map(function (items) { - return flatten([ - [{ - text: 'None', - value: '' - }], - items - ]); - })); - }); - }); - var classList = ListUtils.sanitize(getClassList(editor)); - var hasAdvTab$1 = hasAdvTab(editor); - var hasUploadTab$1 = hasUploadTab(editor); - // attribution tab - var hasAttributionTab$1 = hasAttributionTab(editor); - var hasUploadUrl$1 = hasUploadUrl(editor); - var hasUploadHandler$1 = hasUploadHandler(editor); - var image = readImageDataFromSelection(editor); - var hasDescription$1 = hasDescription(editor); - var hasImageTitle$1 = hasImageTitle(editor); - var hasDimensions$1 = hasDimensions(editor); - var hasImageCaption$1 = hasImageCaption(editor); - var hasAccessibilityOptions = showAccessibilityOptions(editor); - var automaticUploads = isAutomaticUploadsEnabled(editor); - var prependURL = Optional.some(getPrependUrl(editor)).filter(function (preUrl) { - return isString(preUrl) && preUrl.length > 0; - }); - return futureImageList.then(function (imageList) { - return { - image: image, - imageList: imageList, - classList: classList, - hasAdvTab: hasAdvTab$1, - hasUploadTab: hasUploadTab$1, - hasUploadUrl: hasUploadUrl$1, - hasUploadHandler: hasUploadHandler$1, - // attribution tab - hasAttributionTab: hasAttributionTab$1, - hasDescription: hasDescription$1, - hasImageTitle: hasImageTitle$1, - hasDimensions: hasDimensions$1, - hasImageCaption: hasImageCaption$1, - prependURL: prependURL, - hasAccessibilityOptions: hasAccessibilityOptions, - automaticUploads: automaticUploads - }; - }); - }; - - var makeItems = function (info) { - var imageUrl = { - name: 'src', - type: 'urlinput', - filetype: 'image', - label: _('Source') - }; - - - - // var imageOptimizer = { - // name: 'imageOptimizer', - // type: 'checkbox', - // label: _('Image optimizer') - // }; - - var imageOptimizer = { - name: 'imageOptimizer', - type: 'button', - text: _('Image optimizer'), - disabled: true - }; - - var imageList = info.imageList.map(function (items) { - return { - name: 'images', - type: 'listbox', - label: _('Image list'), - items: items - }; - }); - var imageDescription = { - name: 'alt', - type: 'input', - label: _('Alternative description'), - disabled: info.hasAccessibilityOptions && info.image.isDecorative - }; - var imageTitle = { - name: 'title', - type: 'input', - label: _('Image title') - }; - var imageDimensions = { - name: 'dimensions', - type: 'sizeinput' - }; - var isDecorative = { - type: 'label', - label: _('Accessibility'), - items: [{ - name: 'isDecorative', - type: 'checkbox', - label: _('Image is decorative') - }] - }; - var classList = info.classList.map(function (items) { - return { - name: 'classes', - type: 'listbox', - label: _('Class'), - items: items - }; - }); - var caption = { - type: 'label', - label: _('Caption'), - items: [{ - type: 'checkbox', - name: 'caption', - label: _('Show caption') - }] - }; - var getDialogContainerType = function (useColumns) { - return useColumns ? { - type: 'grid', - columns: 2 - } : { type: 'panel' }; - }; - return flatten([ - [imageUrl], - - [imageOptimizer], - imageList.toArray(), - info.hasAccessibilityOptions && info.hasDescription ? [isDecorative] : [], - info.hasDescription ? [imageDescription] : [], - info.hasImageTitle ? [imageTitle] : [], - info.hasDimensions ? [imageDimensions] : [], - [__assign(__assign({}, getDialogContainerType(info.classList.isSome() && info.hasImageCaption)), { - items: flatten([ - classList.toArray(), - info.hasImageCaption ? [caption] : [] - ]) - })] - ]); - }; - var makeTab$1 = function (info) { - return { - title: 'General', - name: 'general', - items: makeItems(info) - }; - }; - var MainTab = { - makeTab: makeTab$1, - makeItems: makeItems - }; - - var makeTab$3 = function (_info) { - var items = [{ - type: 'dropzone', - name: 'fileinput' - }]; - return { - title: 'Upload', - name: 'upload', - items: items - }; - }; - var UploadTab = { makeTab: makeTab$3 }; - - - // attribution tab - - function getTabContent(info) { - return [ - { - type: 'input', - label: _("Header"), - name: 'attr_imageheader' - }, - { - type: 'label', - label: _('Footer caption'), - items: [ - { - type: 'input', - label: _("Image Title"), - name: 'attr_imagetitle' - }, - { - type: 'input', - label: _("Title Link"), - name: 'attr_imagetitlelink' - }, - { - type: 'input', - label: _("Source/Author"), - name: 'authorname' - }, - { - type: 'input', - label: _("Source/Author Link"), - name: 'authornamelink' - }, - { - type: 'listbox', - name: 'captionlicense', - label: _("License"), - items: [ - { text: _("Choose a license..."), value: '' }, - { text: _("Public Domain"), value: 'pd' }, - { text: "GNU/GPL", value: 'gnu-gpl' }, - { text: "Creative Commons (" + _("Public Domain") + ")", value: 'CC0' }, - { text: "Creative Commons BY", value: 'CC-BY' }, - { text: "Creative Commons BY-SA", value: 'CC-BY-SA' }, - { text: "Creative Commons BY-ND", value: 'CC-BY-ND' }, - { text: "Creative Commons BY-NC", value: 'CC-BY-NC' }, - { text: "Creative Commons BY-NC-SA", value: 'CC-BY-NC-SA' }, - { text: "Creative Commons BY-NC-ND", value: 'CC-BY-NC-ND' }, - { text: "Copyright (" + _("All Rights Reserved") + ")", value: 'copyright' }, - { text: _("Custom license"), value: 'custom' } - ] - }, - { - type: 'input', - name: 'customcaptionlicense', - label: _("Custom license"), - disabled: true - } - ] - } - ] - } - - var makeTab$4 = function (info) { - return { - title: _("Title and Attribution"), - name: 'attribution', - items: getTabContent(info) - } - } - - var AttributionTab = { makeTab: makeTab$4 }; - - - var createState = function (info) { - - return { - prevImage: ListUtils.findEntry(info.imageList, info.image.src), - prevAlt: info.image.alt, - open: true + toString: function () { + return 'some(' + a + ')'; + } + }; + return me; + }; + var from = function (value) { + return value === null || value === undefined ? NONE : some(value); + }; + var Optional = { + some: some, + none: none, + from: from }; - }; - var fromImageData = function (image) { - if (image.style != "") { - let style_string_arr = image.style.replaceAll(': ', ';').split(";"); - let index_style; - for (let i = 0; i < style_string_arr.length; i++) { - if (style_string_arr[i].includes('vertical-align') || style_string_arr[i].includes('float')) { - index_style = i + 1; + var keys = Object.keys; + var hasOwnProperty = Object.hasOwnProperty; + var each = function (obj, f) { + var props = keys(obj); + for (var k = 0, len = props.length; k < len; k++) { + var i = props[k]; + var x = obj[i]; + f(x, i); } - } - img_v_style = style_string_arr[index_style]; - } - - idSelectedImage = image.class; - - let figure = tinymce.activeEditor.dom.get('figure_' + idSelectedImage); - - if (figure != null) { - if (tinymce.activeEditor.dom.hasClass(figure, 'position-center')) { - actualAlign = 'position-center'; - img_v_style = ''; - } - else if (tinymce.activeEditor.dom.hasClass(figure, 'position-right')) { - actualAlign = 'position-right'; - img_v_style = 'right'; - } - else if (tinymce.activeEditor.dom.hasClass(figure, 'position-left')) { - actualAlign = 'position-left'; - img_v_style = 'left'; - } - else if (tinymce.activeEditor.dom.hasClass(figure, 'float-right')) { - actualAlign = 'float-right'; - img_v_style = 'right'; - } - else if (tinymce.activeEditor.dom.hasClass(figure, 'float-left')) { - actualAlign = 'float-left'; - img_v_style = 'left'; - } - else { - actualAlign = ''; - } - } + }; + var objAcc = function (r) { + return function (x, i) { + r[i] = x; + }; + }; + var internalFilter = function (obj, pred, onTrue, onFalse) { + var r = {}; + each(obj, function (x, i) { + (pred(x, i) ? onTrue : onFalse)(x, i); + }); + return r; + }; + var filter = function (obj, pred) { + var t = {}; + internalFilter(obj, pred, objAcc(t), noop); + return t; + }; + var has = function (obj, key) { + return hasOwnProperty.call(obj, key); + }; + var hasNonNullableKey = function (obj, key) { + return has(obj, key) && obj[key] !== undefined && obj[key] !== null; + }; - return { - src: { - value: image.src, - meta: {} - }, - images: image.src, - alt: image.alt, - title: image.title, - dimensions: { - width: image.width, - height: image.height - }, - classes: image.class, - caption: image.caption, - style: image.style, - vspace: image.vspace, - border: image.border, - hspace: image.hspace, - borderstyle: image.borderStyle, - fileinput: [], - isDecorative: image.isDecorative - }; - }; - var toImageData = function (data, removeEmptyAlt) { - return { - src: data.src.value, - alt: data.alt.length === 0 && removeEmptyAlt ? null : data.alt, - title: data.title, - width: data.dimensions.width, - height: data.dimensions.height, - class: data.classes, - style: data.style, - caption: data.caption, - hspace: data.hspace, - vspace: data.vspace, - border: data.border, - borderStyle: data.borderstyle, - isDecorative: data.isDecorative - }; - }; - var addPrependUrl2 = function (info, srcURL) { - if (!/^(?:[a-zA-Z]+:)?\/\//.test(srcURL)) { - return info.prependURL.bind(function (prependUrl) { - if (srcURL.substring(0, prependUrl.length) !== prependUrl) { - return Optional.some(prependUrl + srcURL); + var nativePush = Array.prototype.push; + var flatten = function (xs) { + var r = []; + for (var i = 0, len = xs.length; i < len; ++i) { + if (!isArray(xs[i])) { + throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs); + } + nativePush.apply(r, xs[i]); } - return Optional.none(); - }); - } - return Optional.none(); - }; - var addPrependUrl = function (info, api) { - var data = api.getData(); - addPrependUrl2(info, data.src.value).each(function (srcURL) { - api.setData({ - src: { - value: srcURL, - meta: data.src.meta - } - }); - }); - }; - var formFillFromMeta2 = function (info, data, meta) { - if (info.hasDescription && isString(meta.alt)) { - data.alt = meta.alt; - } - if (info.hasAccessibilityOptions) { - data.isDecorative = meta.isDecorative || data.isDecorative || false; - } - // SDWEB - - // Put the title of the file in the image title field - // if (info.hasImageTitle && isString(meta.title)) { - // data.title = meta.title; - // } - - // SDWEB FIN - - if (info.hasDimensions) { - if (isString(meta.width)) { - data.dimensions.width = meta.width; - } - if (isString(meta.height)) { - data.dimensions.height = meta.height; - } - } - if (isString(meta.class)) { - ListUtils.findEntry(info.classList, meta.class).each(function (entry) { - data.classes = entry.value; - }); - } - if (info.hasImageCaption) { - if (isBoolean(meta.caption)) { - data.caption = meta.caption; - } - } - if (info.hasAdvTab) { - if (isString(meta.style)) { - data.style = meta.style; - } - if (isString(meta.vspace)) { - data.vspace = meta.vspace; - } - if (isString(meta.border)) { - data.border = meta.border; - } - if (isString(meta.hspace)) { - data.hspace = meta.hspace; - } - if (isString(meta.borderstyle)) { - data.borderstyle = meta.borderstyle; - } - } - }; - var formFillFromMeta = function (info, api) { - var data = api.getData(); - var meta = data.src.meta; - if (meta !== undefined) { - var newData = deepMerge({}, data); - formFillFromMeta2(info, newData, meta); - api.setData(newData); - } - }; - var calculateImageSize = function (helpers, info, state, api) { - var data = api.getData(); - var url = data.src.value; - var meta = data.src.meta || {}; - if (!meta.width && !meta.height && info.hasDimensions) { - if (isNotEmpty(url)) { - helpers.imageSize(url).then(function (size) { - if (state.open) { - api.setData({ dimensions: size }); - } - }).catch(function (e) { - return console.error(e); - }); - } else { - api.setData({ - dimensions: { - width: '', - height: '' - } - }); - } - } - }; - var updateImagesDropdown = function (info, state, api) { - var data = api.getData(); - var image = ListUtils.findEntry(info.imageList, data.src.value); - state.prevImage = image; - api.setData({ - images: image.map(function (entry) { - return entry.value; - }).getOr('') - }); - }; - var changeSrc = function (helpers, info, state, api) { - addPrependUrl(info, api); - formFillFromMeta(info, api); - calculateImageSize(helpers, info, state, api); - updateImagesDropdown(info, state, api); - }; - var changeImages = function (helpers, info, state, api) { - var data = api.getData(); - var image = ListUtils.findEntry(info.imageList, data.images); - image.each(function (img) { - var updateAlt = data.alt === '' || state.prevImage.map(function (image) { - return image.text === data.alt; - }).getOr(false); - if (updateAlt) { - if (img.value === '') { - api.setData({ - src: img, - alt: state.prevAlt - }); - } else { - api.setData({ - src: img, - alt: img.text - }); - } - } else { - api.setData({ src: img }); - } - }); - state.prevImage = image; - changeSrc(helpers, info, state, api); - }; - var calcVSpace = function (css) { - var matchingTopBottom = css['margin-top'] === css['margin-bottom']; - return matchingTopBottom ? removePixelSuffix(String(css['margin-top'])) : ''; - }; - var calcHSpace = function (css) { - var matchingLeftRight = css['margin-right'] === css['margin-left']; - return matchingLeftRight ? removePixelSuffix(String(css['margin-right'])) : ''; - }; - var calcBorderWidth = function (css) { - return css['border-width'] ? removePixelSuffix(String(css['border-width'])) : ''; - }; - var calcBorderStyle = function (css) { - return css['border-style'] ? String(css['border-style']) : ''; - }; - var calcStyle = function (parseStyle, serializeStyle, css) { - return serializeStyle(parseStyle(serializeStyle(css))); - }; - var changeStyle2 = function (parseStyle, serializeStyle, data) { - var css = mergeMargins(parseStyle(data.style)); - var dataCopy = deepMerge({}, data); - dataCopy.vspace = calcVSpace(css); - dataCopy.hspace = calcHSpace(css); - dataCopy.border = calcBorderWidth(css); - dataCopy.borderstyle = calcBorderStyle(css); - dataCopy.style = calcStyle(parseStyle, serializeStyle, css); - return dataCopy; - }; - var changeStyle = function (helpers, api) { - var data = api.getData(); - var newData = changeStyle2(helpers.parseStyle, helpers.serializeStyle, data); - api.setData(newData); - }; - var changeAStyle = function (helpers, info, api) { - var data = deepMerge(fromImageData(info.image), api.getData()); - var style = getStyleValue(helpers.normalizeCss, toImageData(data, false)); - api.setData({ style: style }); - }; - var changeFileInput = function (helpers, info, state, api) { - var data = api.getData(); - api.block('Uploading image'); - head(data.fileinput).fold(function () { - api.unblock(); - }, function (file) { - var blobUri = URL.createObjectURL(file); - var finalize = function () { - api.unblock(); - URL.revokeObjectURL(blobUri); - }; - var updateSrcAndSwitchTab = function (url) { - api.setData({ - src: { - value: url, - meta: {} - } - }); - api.showTab('general'); - changeSrc(helpers, info, state, api); - }; - blobToDataUri(file).then(function (dataUrl) { - var blobInfo = helpers.createBlobCache(file, blobUri, dataUrl); - if (info.automaticUploads) { - helpers.uploadImage(blobInfo).then(function (result) { - updateSrcAndSwitchTab(result.url); - finalize(); - }).catch(function (err) { - finalize(); - helpers.alertErr(err); - }); - } else { - helpers.addToBlobCache(blobInfo); - updateSrcAndSwitchTab(blobInfo.blobUri()); - api.unblock(); + return r; + }; + var get = function (xs, i) { + return i >= 0 && i < xs.length ? Optional.some(xs[i]) : Optional.none(); + }; + var head = function (xs) { + return get(xs, 0); + }; + var findMap = function (arr, f) { + for (var i = 0; i < arr.length; i++) { + var r = f(arr[i], i); + if (r.isSome()) { + return r; + } } - }); - }); - }; - - - // +++++ TO DO +++++ - - // // To use in image optimizer - // function setCookie(cname, cvalue, exdays) { - // var d = new Date(); - // d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); - // var expires = "expires=" + d.toUTCString(); - // document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; - // } - - // +++++ TO DO END ++++ + return Optional.none(); + }; + typeof window !== 'undefined' ? window : Function('return this;')(); - var changeHandler = function (helpers, info, state) { - return function (api, evt) { - if (evt.name === 'src') { - changeSrc(helpers, info, state, api); - // SDWEB - // Enable or disable optimization - let btnOpbimizer = tinymce.activeEditor.dom.get("openOptimizer"); - let srcData = api.getData("src").src; - let srcValue = srcData && srcData.value ? srcData.value : ""; - let hasMetaTitle = srcData && srcData.meta && srcData.meta.hasOwnProperty("title"); - let isOptimizableSrc = srcValue.indexOf("blob:") === 0 || - srcValue.indexOf("asset://") === 0 || - srcValue.indexOf("data:") === 0 || - srcValue.indexOf("resources/") === 0 || - srcValue.indexOf("/previews/") === 0; - - if (hasMetaTitle || isOptimizableSrc) { - tinymce.dom.DomQuery("#openOptimizer").removeAttr("disabled"); - } - else { - tinymce.activeEditor.dom.setAttrib(btnOpbimizer, "disabled", "disabled"); - } - - - } else if (evt.name === 'images') { - changeImages(helpers, info, state, api); - } else if (evt.name === 'alt') { - state.prevAlt = api.getData().alt; - } else if (evt.name === 'style') { - changeStyle(helpers, api); - } else if (evt.name === 'vspace' || evt.name === 'hspace' || evt.name === 'border' || evt.name === 'borderstyle') { - changeAStyle(helpers, info, api); - } - - - // +++++ TO DO +++++ - // image optimizer with checkbox - - // else if (evt.name === 'imageOptimizer') { - // var val = "0"; - // // var optimizerOpt = win.find("#imageOptimizer"); - // // if (optimizerOpt.length==1 && optimizerOpt.checked()) val = "1"; - // if (api.getData().imageOptimizer) val = "1"; - - // // You can't enable it right after selecting an image - // if (val == "1") { - // // var src = win.find('#src').value(); - // var src = api.getData().src; - // // if (src.indexOf("/previews/")==0) { - // if (src.value != '') { - // val = "0"; - // tinymce.activeEditor.windowManager.confirm( - // "1. " + _("Clear Image") + " — 2. " + _("Select image") + " (" + _("Source") + ")", - // function (s) { - // if (s) { - // // win.find("#src").value(""); - // // win.find("#imageOptimizer").checked(true); - // api.setData({ src: { meta: src.meta, value: '' } }); - // api.setData({ imageOptimizer: true }); - // } - // } - // ); - // setTimeout(function () { - // // optimizerOpt.checked(false); - // api.setData({ imageOptimizer: false }); - // }, 250); - // } - // } - - // setCookie("exeimageImageOptimizer", val, 30); - // } - - // +++++ TO DO END ++++ - - // align style - else if (evt.name === 'alignstyle') { - alignImageStyle = api.getData().alignstyle; - - if (alignImageStyle !== '') { - if (alignImageStyle === 'right' || alignImageStyle === 'left') { - // Image is not figure and not will be figure - if (idSelectedImage == '' && api.getData().attr_imageheader == '' - && api.getData().attr_imagetitle == '' && api.getData().attr_imagetitlelink == '' - && api.getData().authorname == '' && api.getData().authornamelink == '' && api.getData().captionlicense == '') { - style_for_div = 'float: ' + alignImageStyle; - } else { - style_for_div = ''; - } - } else { - style_for_div = 'vertical-align: ' + alignImageStyle; - } - - } - - changeAStyle(helpers, info, api); - currentAlignStyle = api.getData().alignstyle; - var styleText = api.getData().style; - var styleArray = styleText.split(";"); - for (var i = 0; i < styleArray.length; i++) { - if (styleArray[i].includes("float") || styleArray[i].includes("vertical-align")) { - styleArray.splice(i, 1); - } - } - styleText = styleArray.toString(); - styleText = styleText.replaceAll(",", ";"); - - if (api.getData().alignstyle === 'left' || api.getData().alignstyle === 'right') { - // Image is not figure and not will be figure - if (idSelectedImage == '' && api.getData().attr_imageheader == '' - && api.getData().attr_imagetitle == '' && api.getData().attr_imagetitlelink == '' - && api.getData().authorname == '' && api.getData().authornamelink == '' && api.getData().captionlicense == '') { - api.setData({ style: styleText + "float: " + api.getData().alignstyle + ";" }); - } else { - api.setData({ style: styleText }); - } + var rawSet = function (dom, key, value) { + if (isString(value) || isBoolean(value) || isNumber(value)) { + dom.setAttribute(key, value + ''); } else { - api.setData({ style: styleText + "vertical-align: " + api.getData().alignstyle + ";" }); + console.error('Invalid call to Attribute.set. Key ', key, ':: Value ', value, ':: Element ', dom); + throw new Error('Attribute value was not simple'); } - } - // / align style + }; + var set = function (element, key, value) { + rawSet(element.dom, key, value); + }; + var remove = function (element, key) { + element.dom.removeAttribute(key); + }; - else if (evt.name === 'fileinput') { - changeFileInput(helpers, info, state, api); - } else if (evt.name === 'isDecorative') { - if (api.getData().isDecorative) { - api.disable('alt'); - } else { - api.enable('alt'); + var fromHtml = function (html, scope) { + var doc = scope || document; + var div = doc.createElement('div'); + div.innerHTML = html; + if (!div.hasChildNodes() || div.childNodes.length > 1) { + console.error('HTML does not have a single root node', html); + throw new Error('HTML must have a single root node'); } - } - - // attribution tab - - // attribution, enable custom license - else if (evt.name === 'captionlicense') { + return fromDom(div.childNodes[0]); + }; + var fromTag = function (tag, scope) { + var doc = scope || document; + var node = doc.createElement(tag); + return fromDom(node); + }; + var fromText = function (text, scope) { + var doc = scope || document; + var node = doc.createTextNode(text); + return fromDom(node); + }; + var fromDom = function (node) { + if (node === null || node === undefined) { + throw new Error('Node cannot be null or undefined'); + } + return { dom: node }; + }; + var fromPoint = function (docElm, x, y) { + return Optional.from(docElm.dom.elementFromPoint(x, y)).map(fromDom); + }; + var SugarElement = { + fromHtml: fromHtml, + fromTag: fromTag, + fromText: fromText, + fromDom: fromDom, + fromPoint: fromPoint + }; - if (api.getData().captionlicense === 'custom') { - api.enable('customcaptionlicense'); - } else { - api.disable('customcaptionlicense'); - api.setData({ customcaptionlicense: '' }); - } - } - - else if (evt.name === 'captionlicense' || evt.name === 'authornamelink' || evt.name === 'authorname' || - evt.name === 'attr_imagetitlelink' || evt.name === 'attr_imagetitle' || evt.name === 'attr_imageheader') { - if (api.setData({ style: api.getData().style.replace('float: right;', '') })) { - api.setData({ alignstyle: 'right' }); - } else if (api.setData({ style: api.getData().style.replace('float: left;', '') })) { - api.setData({ alignstyle: 'left' }); - } - - } - - // / attribution tab - - }; - }; - var closeHandler = function (state) { - return function () { - state.open = false; - }; - }; - var makeDialogBody = function (info) { - if (info.hasAdvTab || info.hasUploadUrl || info.hasUploadHandler) { - var tabPanel = { - type: 'tabpanel', - tabs: flatten([ - [MainTab.makeTab(info)], - info.hasAdvTab ? [AdvTab.makeTab(info)] : [], - info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : [], - // attibution tab - info.hasAttributionTab ? [AttributionTab.makeTab(info)] : [] - ]) - }; - return tabPanel; - } else { - var panel = { - type: 'panel', - items: MainTab.makeItems(info) - }; - return panel; - } - }; - var makeDialog = function (helpers) { - return function (info) { - var state = createState(info); - return { - title: _('Insert/Edit Image'), - size: 'normal', - body: makeDialogBody(info), - buttons: [ - { - type: 'cancel', - name: 'cancel', - text: _('Cancel') - }, - { - type: 'submit', - name: 'save', - text: _('Save'), - primary: true - } - ], - initialData: fromImageData(info.image), - onSubmit: helpers.onSubmit(info), - onAction: function (api, details) { - if (details.name !== 'imageOptimizer') return; - - var srcValue = api.getData("src").src.value || ''; - var isBlob = srcValue.startsWith('blob:'); - var isAsset = srcValue.startsWith('asset://'); - var assetManager = top.eXeLearning?.app?.project?._yjsBridge?.assetManager; - var assetId = null; - - // Find the asset ID from the blob URL or asset URL - if (assetManager) { - if (isBlob && assetManager.reverseBlobCache) { - assetId = assetManager.reverseBlobCache.get(srcValue); - } else if (isAsset) { - var match = srcValue.match(/asset:\/\/([^/]+)/); - if (match) assetId = match[1]; - } - // Fallback: check selected image for data-asset-id attribute - if (!assetId) { - var selectedImg = tinymce.activeEditor?.selection?.getNode(); - if (selectedImg?.tagName === 'IMG') { - assetId = selectedImg.getAttribute('data-asset-id'); - } - } - } - - top.imgCompressor = { - originalSrc: srcValue, - isBlob: isBlob, - isAsset: isAsset, - assetId: assetId, - fileToSave: assetManager?.getAssetMetadata?.(assetId)?.filename || 'optimized.jpg', - api: api // Reference to dialog API for setData/getData - }; - - var basePath = (top.eXeLearning?.config?.basePath || '').replace(/\/+$/, ''); - var version = top.eXeLearning?.version || ''; - var optimizerUrl = basePath + (version ? '/' + version : '') + - "/libs/tinymce_5/js/tinymce/plugins/exeimage/image-compressor/index.html"; - - tinymce.activeEditor.windowManager.openUrl({ - title: _('Editor'), - url: optimizerUrl, - }); - }, - onChange: changeHandler(helpers, info, state), - onTabChange: function (api, details) { - // Save data from the tab we are leaving (if it's the “attribution” tab) - if (details.oldTabName === 'attribution') { - var d = api.getData(); - // Store the attribution fields for later restoration - this._savedAttributionData = { - attr_imageheader: d.attr_imageheader, - attr_imagetitle: d.attr_imagetitle, - attr_imagetitlelink: d.attr_imagetitlelink, - authorname: d.authorname, - authornamelink: d.authornamelink, - captionlicense: d.captionlicense, - customcaptionlicense: d.customcaptionlicense - }; - } - if (details.newTabName === 'general') { - setGenTabStyle(); - } - else if (details.newTabName === 'advanced') { - setAdvTabStyle(); - if (img_v_style === '' || img_v_style === 'baseline' || img_v_style === 'top' - || img_v_style === 'middle' || img_v_style === 'bottom' || img_v_style === 'text-top' - || img_v_style === 'text-bottom' || img_v_style === 'left' || img_v_style === 'right') { - api.setData({ alignstyle: img_v_style }); - } - } else if (details.newTabName === 'attribution') { - setAttributionTabStyle(); - attibutionTab = true; - let editor = tinymce.activeEditor; - - let actHeader = editor.dom.get('header_' + idSelectedImage); - let actHeaderHTML = ''; - if (actHeader != null) { - actHeaderHTML = editor.selection.select(actHeader).innerHTML; - actHeaderHTML = actHeaderHTML.replace('', ''); - actHeaderHTML = actHeaderHTML.replace('', ''); - } - - let actTitle = editor.dom.get('title_' + idSelectedImage); - let actTitleHTML = ''; - let actLinkTitleHTML = ''; - if (actTitle != null) { - actTitleHTML = editor.selection.select(actTitle).innerHTML; - actTitleHTML = actTitleHTML.replace('', ''); - actTitleHTML = actTitleHTML.replace('', ''); - - actLinkTitleHTML = editor.dom.getAttrib(actTitle, 'href'); - } - - let actAuthor = editor.dom.get('author_' + idSelectedImage); - let actAuthorHTML = ''; - let actLinkAuthorHTML = ''; - if (actAuthor != null) { - actAuthorHTML = editor.selection.select(actAuthor).innerHTML; - - actLinkAuthorHTML = editor.dom.getAttrib(actAuthor, 'href'); - } - - let actLicense = editor.dom.get('license_' + idSelectedImage); - let actLicenseHTML = ''; - let selectedLicense = ''; - let customLicense = ''; - if (actLicense != null) { - actLicenseHTML = editor.selection.select(actLicense).innerHTML; - if (actLicenseHTML === _("Public Domain")) { - selectedLicense = 'pd'; - } - else if (actLicenseHTML === 'GNU-GPL') { - selectedLicense = 'gnu-gpl'; - } else if (actLicenseHTML === _("All Rights Reserved")) { - selectedLicense = 'copyright'; - } else if (actLicenseHTML === 'CC BY' || actLicenseHTML === 'CC BY-SA' || actLicenseHTML === 'CC BY-ND' - || actLicenseHTML === 'CC BY-NC' || actLicenseHTML === 'CC BY-NC-SA' || actLicenseHTML === 'CC BY-NC-ND') { - selectedLicense = actLicenseHTML.replace('CC ', 'CC-'); - } else if (actLicenseHTML === 'CC0') { - selectedLicense = actLicenseHTML; - } - else { - selectedLicense = 'custom'; - customLicense = actLicenseHTML; - } - } - - // If we have saved attribution data, restore it if needed - if (this._savedAttributionData) { - const _savedAttrs = this._savedAttributionData; - const _actHeaderHTML = savedAttrs.attr_imageheader; - const _actTitleHTML = savedAttrs.attr_imagetitle; - const _actLinkTitleHTML = savedAttrs.attr_imagetitlelink; - const _actAuthorHTML = savedAttrs.authorname; - const _actLinkAuthorHTML = savedAttrs.authornamelink; - const _selectedLicense = savedAttrs.captionlicense; - const _customLicense = savedAttrs.customcaptionlicense; - if (actHeaderHTML = '') actHeaderHTML = _savedAttrs; - if (actTitleHTML = '') actTitleHTML = _actHeaderHTML; - if (actLinkTitleHTML = '') actLinkTitleHTML = _actTitleHTML; - if (actAuthorHTML = '') actAuthorHTML = _actLinkTitleHTML; - if (actLinkAuthorHTML = '') actLinkAuthorHTML = _actAuthorHTML; - if (selectedLicense = '') selectedLicense = _selectedLicense; - if (customLicense = '') customLicense = _customLicense; - } - - api.setData({ attr_imageheader: actHeaderHTML }); - api.setData({ attr_imagetitle: actTitleHTML }); - api.setData({ attr_imagetitlelink: actLinkTitleHTML }); - api.setData({ authorname: actAuthorHTML }); - api.setData({ authornamelink: actLinkAuthorHTML }); - api.setData({ captionlicense: selectedLicense }); - api.setData({ customcaptionlicense: customLicense }); - - if (api.getData().captionlicense === 'custom') { - api.enable('customcaptionlicense'); - } - - } - }, - onClose: closeHandler(state) - }; - }; - }; - - function GetLinkLicense(attrLicense) { - - let linkLicense = ''; - if (attrLicense === 'pd' || attrLicense === 'copyright' || attrLicense === '') { - linkLicense = ''; - } - else if (attrLicense === 'gnu-gpl') { - linkLicense = 'http://www.gnu.org/licenses/gpl.html'; - } - else if (attrLicense === 'CC0') { - linkLicense = 'http://creativecommons.org/publicdomain/zero/1.0/deed.' + lang; + var global$5 = tinymce.util.Tools.resolve('tinymce.dom.DOMUtils'); - } else { - linkLicense = 'http://creativecommons.org/licenses/'; - } + var global$4 = tinymce.util.Tools.resolve('tinymce.util.Promise'); - return linkLicense; - } - - function FormatLicenseName(attrLicense) { - let newName = ''; - if (attrLicense === 'pd') { - newName = _('Public Domain'); - } else if (attrLicense === 'gnu-gpl') { - newName = 'GNU-GPL'; - } else if (attrLicense === 'copyright') { - newName = _("All Rights Reserved"); - } else { - newName = attrLicense.replace('CC-', 'CC '); - } + var global$3 = tinymce.util.Tools.resolve('tinymce.util.URI'); - return newName; - } + var global$2 = tinymce.util.Tools.resolve('tinymce.util.XHR'); - var submitHandler = function (editor) { + var hasDimensions = function (editor) { + return editor.getParam('image_dimensions', true, 'boolean'); + }; + var hasAdvTab = function (editor) { + return editor.getParam('image_advtab', false, 'boolean'); + }; + var hasUploadTab = function (editor) { + return editor.getParam('image_uploadtab', true, 'boolean'); + }; + // attribution tab + var hasAttributionTab = function (editor) { + return editor.getParam('exeimage_attributiontab', true, 'boolean'); + }; + var getPrependUrl = function (editor) { + return editor.getParam('image_prepend_url', '', 'string'); + }; + var getClassList = function (editor) { + return editor.getParam('image_class_list'); + }; + var hasDescription = function (editor) { + return editor.getParam('image_description', true, 'boolean'); + }; + var hasImageTitle = function (editor) { + return editor.getParam('image_title', false, 'boolean'); + }; + var hasImageCaption = function (editor) { + return editor.getParam('image_caption', false, 'boolean'); + }; + var getImageList = function (editor) { + return editor.getParam('image_list', false); + }; + var hasUploadUrl = function (editor) { + return isNonNullable(editor.getParam('images_upload_url')); + }; + var hasUploadHandler = function (editor) { + return isNonNullable(editor.getParam('images_upload_handler')); + }; + var showAccessibilityOptions = function (editor) { + return editor.getParam('a11y_advanced_options', false, 'boolean'); + }; + var isAutomaticUploadsEnabled = function (editor) { + return editor.getParam('automatic_uploads', true, 'boolean'); + }; - return function (info) { - return function (api) { - if (api.getData().alt == "") { - editor.windowManager.confirm( - _("Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off."), - function (s) { - if (s) { - mySubmit(editor, info, api); - } + var parseIntAndGetMax = function (val1, val2) { + return Math.max(parseInt(val1, 10), parseInt(val2, 10)); + }; + var getImageSize = function (url) { + return new global$4(function (callback) { + var img = document.createElement('img'); + var done = function (dimensions) { + img.onload = img.onerror = null; + if (img.parentNode) { + img.parentNode.removeChild(img); + } + callback(dimensions); + }; + img.onload = function () { + var width = parseIntAndGetMax(img.width, img.clientWidth); + var height = parseIntAndGetMax(img.height, img.clientHeight); + var dimensions = { + width: width, + height: height + }; + done(global$4.resolve(dimensions)); + }; + img.onerror = function () { + done(global$4.reject('Failed to get image dimensions for: ' + url)); + }; + var style = img.style; + style.visibility = 'hidden'; + style.position = 'fixed'; + style.bottom = style.left = '0px'; + style.width = style.height = 'auto'; + document.body.appendChild(img); + img.src = url; + }); + }; + var removePixelSuffix = function (value) { + if (value) { + value = value.replace(/px$/, ''); + } + return value; + }; + var addPixelSuffix = function (value) { + if (value.length > 0 && /^[0-9]+$/.test(value)) { + value += 'px'; + } + return value; + }; + var mergeMargins = function (css) { + if (css.margin) { + var splitMargin = String(css.margin).split(' '); + switch (splitMargin.length) { + case 1: + css['margin-top'] = css['margin-top'] || splitMargin[0]; + css['margin-right'] = css['margin-right'] || splitMargin[0]; + css['margin-bottom'] = css['margin-bottom'] || splitMargin[0]; + css['margin-left'] = css['margin-left'] || splitMargin[0]; + break; + case 2: + css['margin-top'] = css['margin-top'] || splitMargin[0]; + css['margin-right'] = css['margin-right'] || splitMargin[1]; + css['margin-bottom'] = css['margin-bottom'] || splitMargin[0]; + css['margin-left'] = css['margin-left'] || splitMargin[1]; + break; + case 3: + css['margin-top'] = css['margin-top'] || splitMargin[0]; + css['margin-right'] = css['margin-right'] || splitMargin[1]; + css['margin-bottom'] = css['margin-bottom'] || splitMargin[2]; + css['margin-left'] = css['margin-left'] || splitMargin[1]; + break; + case 4: + css['margin-top'] = css['margin-top'] || splitMargin[0]; + css['margin-right'] = css['margin-right'] || splitMargin[1]; + css['margin-bottom'] = css['margin-bottom'] || splitMargin[2]; + css['margin-left'] = css['margin-left'] || splitMargin[3]; } - ); + delete css.margin; + } + return css; + }; + var createImageList = function (editor, callback) { + var imageList = getImageList(editor); + if (isString(imageList)) { + global$2.send({ + url: imageList, + success: function (text) { + callback(JSON.parse(text)); + } + }); + } else if (isFunction(imageList)) { + imageList(callback); } else { - mySubmit(editor, info, api); + callback(imageList); } - }; }; - }; + var waitLoadImage = function (editor, data, imgElm) { + var selectImage = function () { + imgElm.onload = imgElm.onerror = null; + if (editor.selection) { + editor.selection.select(imgElm); + editor.nodeChanged(); + } + }; + imgElm.onload = function () { + if (!data.width && !data.height && hasDimensions(editor)) { + editor.dom.setAttribs(imgElm, { + width: String(imgElm.clientWidth), + height: String(imgElm.clientHeight) + }); + } + selectImage(); + }; + imgElm.onerror = selectImage; + }; + var blobToDataUri = function (blob) { + return new global$4(function (resolve, reject) { + var reader = new FileReader(); + reader.onload = function () { + resolve(reader.result); + }; + reader.onerror = function () { + reject(reader.error.message); + }; + reader.readAsDataURL(blob); + }); + }; + var isPlaceholderImage = function (imgElm) { + return imgElm.nodeName === 'IMG' && (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder')); + }; + var isSafeImageUrl = function (editor, src) { + return global$3.isDomSafe(src, 'img', editor.settings); + }; - function mySubmit(editor, info, api) { - // Skip submit if image was already updated by image-compressor (blob/asset mode) - if (top.imgCompressor && top.imgCompressor.skipSubmit) { - top.imgCompressor.skipSubmit = false; - api.close(); - return; - } + var DOM = global$5.DOM; + var getHspace = function (image) { + if (image.style.marginLeft && image.style.marginRight && image.style.marginLeft === image.style.marginRight) { + return removePixelSuffix(image.style.marginLeft); + } else { + return ''; + } + }; + var getVspace = function (image) { + if (image.style.marginTop && image.style.marginBottom && image.style.marginTop === image.style.marginBottom) { + return removePixelSuffix(image.style.marginTop); + } else { + return ''; + } + }; + var getBorder = function (image) { + if (image.style.borderWidth) { + return removePixelSuffix(image.style.borderWidth); + } else { + return ''; + } + }; + var getAttrib = function (image, name) { + if (image.hasAttribute(name)) { + return image.getAttribute(name); + } else { + return ''; + } + }; + var getStyle = function (image, name) { + return image.style[name] ? image.style[name] : ''; + }; + var hasCaption = function (image) { + return image.parentNode !== null && image.parentNode.nodeName === 'FIGURE'; + }; + var updateAttrib = function (image, name, value) { + if (value === '') { + image.removeAttribute(name); + } else { + image.setAttribute(name, value); + } + }; + var wrapInFigure = function (image) { + var figureElm = DOM.create('figure', { class: 'image' }); + DOM.insertAfter(figureElm, image); + figureElm.appendChild(image); + figureElm.appendChild(DOM.create('figcaption', { contentEditable: 'true' }, 'Caption')); + figureElm.contentEditable = 'false'; + }; + var removeFigure = function (image) { + var figureElm = image.parentNode; + DOM.insertAfter(image, figureElm); + DOM.remove(figureElm); + }; + var toggleCaption = function (image) { + if (hasCaption(image)) { + removeFigure(image); + } else { + wrapInFigure(image); + } + }; + var normalizeStyle = function (image, normalizeCss) { + var attrValue = image.getAttribute('style'); + var value = normalizeCss(attrValue !== null ? attrValue : ''); + if (value.length > 0) { + image.setAttribute('style', value); + image.setAttribute('data-mce-style', value); + } else { + image.removeAttribute('style'); + } + }; + var setSize = function (name, normalizeCss) { + return function (image, name, value) { + if (image.style[name]) { + image.style[name] = addPixelSuffix(value); + normalizeStyle(image, normalizeCss); + } else { + updateAttrib(image, name, value); + } + }; + }; + var getSize = function (image, name) { + if (image.style[name]) { + return removePixelSuffix(image.style[name]); + } else { + return getAttrib(image, name); + } + }; + var setHspace = function (image, value) { + var pxValue = addPixelSuffix(value); + image.style.marginLeft = pxValue; + image.style.marginRight = pxValue; + }; + var setVspace = function (image, value) { + var pxValue = addPixelSuffix(value); + image.style.marginTop = pxValue; + image.style.marginBottom = pxValue; + }; + var setBorder = function (image, value) { + var pxValue = addPixelSuffix(value); + image.style.borderWidth = pxValue; + }; + var setBorderStyle = function (image, value) { + image.style.borderStyle = value; + }; + var getBorderStyle = function (image) { + return getStyle(image, 'borderStyle'); + }; + var isFigure = function (elm) { + return elm.nodeName === 'FIGURE'; + }; + var isImage = function (elm) { + return elm.nodeName === 'IMG'; + }; + var getIsDecorative = function (image) { + return DOM.getAttrib(image, 'alt').length === 0 && DOM.getAttrib(image, 'role') === 'presentation'; + }; + var getAlt = function (image) { + if (getIsDecorative(image)) { + return ''; + } else { + return getAttrib(image, 'alt'); + } + }; + var defaultData = function () { + return { + src: '', + alt: '', + title: '', + width: '', + height: '', + class: '', + style: '', + caption: false, + hspace: '', + vspace: '', + border: '', + borderStyle: '', + isDecorative: false, + // eXeLearning specific fields + attr_imageheader: '', + attr_imagetitle: '', + attr_imagetitlelink: '', + authorname: '', + authornamelink: '', + captionlicense: '', + customcaptionlicense: '', + alignstyle: 'center' + }; + }; + var getStyleValue = function (normalizeCss, data) { + var image = document.createElement('img'); + updateAttrib(image, 'style', data.style); + if (getHspace(image) || data.hspace !== '') { + setHspace(image, data.hspace); + } + if (getVspace(image) || data.vspace !== '') { + setVspace(image, data.vspace); + } + if (getBorder(image) || data.border !== '') { + setBorder(image, data.border); + } + if (getBorderStyle(image) || data.borderStyle !== '') { + setBorderStyle(image, data.borderStyle); + } + return normalizeCss(image.getAttribute('style')); + }; + var create = function (normalizeCss, data) { + var image = document.createElement('img'); + write(normalizeCss, __assign(__assign({}, data), { caption: false }), image); + setAlt(image, data.alt, data.isDecorative); + if (data.caption) { + var figure = DOM.create('figure', { class: 'image' }); + figure.appendChild(image); + figure.appendChild(DOM.create('figcaption', { contentEditable: 'true' }, 'Caption')); + figure.contentEditable = 'false'; + return figure; + } else { + return image; + } + }; + var read = function (normalizeCss, image) { + return { + src: getAttrib(image, 'src'), + alt: getAlt(image), + title: getAttrib(image, 'title'), + width: getSize(image, 'width'), + height: getSize(image, 'height'), + class: getAttrib(image, 'class'), + style: normalizeCss(getAttrib(image, 'style')), + caption: hasCaption(image), + hspace: getHspace(image), + vspace: getVspace(image), + border: getBorder(image), + borderStyle: getStyle(image, 'borderStyle'), + isDecorative: getIsDecorative(image) + }; + }; + var updateProp = function (image, oldData, newData, name, set) { + if (newData[name] !== oldData[name]) { + set(image, name, newData[name]); + } + }; + var setAlt = function (image, alt, isDecorative) { + if (isDecorative) { + DOM.setAttrib(image, 'role', 'presentation'); + var sugarImage = SugarElement.fromDom(image); + set(sugarImage, 'alt', ''); + } else { + if (isNull(alt)) { + var sugarImage = SugarElement.fromDom(image); + remove(sugarImage, 'alt'); + } else { + var sugarImage = SugarElement.fromDom(image); + set(sugarImage, 'alt', alt); + } + if (DOM.getAttrib(image, 'role') === 'presentation') { + DOM.setAttrib(image, 'role', ''); + } + } + }; + var updateAlt = function (image, oldData, newData) { + if (newData.alt !== oldData.alt || newData.isDecorative !== oldData.isDecorative) { + setAlt(image, newData.alt, newData.isDecorative); + } + }; + var normalized = function (set, normalizeCss) { + return function (image, name, value) { + set(image, value); + normalizeStyle(image, normalizeCss); + }; + }; + var write = function (normalizeCss, newData, image) { + var oldData = read(normalizeCss, image); + updateProp(image, oldData, newData, 'caption', function (image, _name, _value) { + return toggleCaption(image); + }); + updateProp(image, oldData, newData, 'src', updateAttrib); + updateProp(image, oldData, newData, 'title', updateAttrib); + updateProp(image, oldData, newData, 'width', setSize('width', normalizeCss)); + updateProp(image, oldData, newData, 'height', setSize('height', normalizeCss)); + updateProp(image, oldData, newData, 'class', updateAttrib); + updateProp(image, oldData, newData, 'style', normalized(function (image, value) { + return updateAttrib(image, 'style', value); + }, normalizeCss)); + updateProp(image, oldData, newData, 'hspace', normalized(setHspace, normalizeCss)); + updateProp(image, oldData, newData, 'vspace', normalized(setVspace, normalizeCss)); + updateProp(image, oldData, newData, 'border', normalized(setBorder, normalizeCss)); + updateProp(image, oldData, newData, 'borderStyle', normalized(setBorderStyle, normalizeCss)); + updateAlt(image, oldData, newData); + }; + + var normalizeCss$1 = function (editor, cssText) { + var css = editor.dom.styles.parse(cssText); + var mergedCss = mergeMargins(css); + var compressed = editor.dom.styles.parse(editor.dom.styles.serialize(mergedCss)); + return editor.dom.styles.serialize(compressed); + }; + var getSelectedImage = function (editor) { + var imgElm = editor.selection.getNode(); + + // If the selected node is already an IMG, check if it's valid and return it + if (imgElm && imgElm.nodeName === 'IMG' && !isPlaceholderImage(imgElm)) { + // Make sure it's not an exemedia placeholder + if (imgElm.getAttribute('data-mce-object')) { + var objType = imgElm.getAttribute('data-mce-object'); + if (objType === 'embed' || objType === 'video' || objType === 'audio') { + return null; + } + } + return imgElm; + } + + if (imgElm && imgElm.nodeName === 'FIGURE' && imgElm.classList.contains('exe-figure') && !imgElm.classList.contains('exe-media')) { + var img = editor.dom.select('img', imgElm)[0]; + // Exclude exemedia IMG placeholders + if (img && img.getAttribute('data-mce-object')) { + var objType = img.getAttribute('data-mce-object'); + if (objType === 'embed' || objType === 'video' || objType === 'audio') { + return null; + } + } + return img; + } + + var figureElm = editor.dom.getParent(imgElm, 'figure.exe-figure'); + if (figureElm && !figureElm.classList.contains('exe-media')) { + var img = editor.dom.select('img', figureElm)[0]; + if (img) { + // Exclude exemedia IMG placeholders + if (img.getAttribute('data-mce-object')) { + var objType = img.getAttribute('data-mce-object'); + if (objType === 'embed' || objType === 'video' || objType === 'audio') { + return null; + } + } + return img; + } + } + + var figureElm = editor.dom.getParent(imgElm, 'figure.image'); + if (figureElm) { + return editor.dom.select('img', figureElm)[0]; + } + if (imgElm && (imgElm.nodeName !== 'IMG' || isPlaceholderImage(imgElm))) { + return null; + } + return imgElm; + }; + var splitTextBlock = function (editor, figure) { + var dom = editor.dom; + var textBlockElements = filter(editor.schema.getTextBlockElements(), function (_, parentElm) { + return !editor.schema.isValidChild(parentElm, 'figure'); + }); + var textBlock = dom.getParent(figure.parentNode, function (node) { + return hasNonNullableKey(textBlockElements, node.nodeName); + }, editor.getBody()); + if (textBlock) { + return dom.split(textBlock, figure); + } else { + return figure; + } + }; + var readImageDataFromSelection = function (editor) { + var image = getSelectedImage(editor); + if (!image) return defaultData(); + + var imageData = read(function (css) { + return normalizeCss$1(editor, css); + }, image); + + var figureElement = editor.dom.getParent(image, 'figure.exe-figure'); + if (figureElement) { + // Read header + var headerElement = editor.dom.select('.figcaption.header', figureElement)[0]; + if (headerElement) { + var headerHTML = headerElement.innerHTML || ''; + headerHTML = headerHTML.replace(/<\/?strong>/gi, '').trim(); + imageData.attr_imageheader = headerHTML; + } + + // Read main figcaption + var figcaption = editor.dom.select('figcaption.figcaption', figureElement)[0]; + if (figcaption) { + // Read author + var authorElement = editor.dom.select('a.author', figcaption)[0]; + if (!authorElement) { + authorElement = editor.dom.select('span.author', figcaption)[0]; + } + if (authorElement) { + imageData.authorname = authorElement.textContent.trim(); + imageData.authornamelink = editor.dom.getAttrib(authorElement, 'href') || ''; + } + + // Read title + var titleElement = editor.dom.select('a.title', figcaption)[0]; + if (!titleElement) { + titleElement = editor.dom.select('span.title', figcaption)[0]; + } + if (titleElement) { + var titleHTML = titleElement.innerHTML || titleElement.textContent || ''; + titleHTML = titleHTML.replace(/<\/?em>/gi, '').trim(); + imageData.attr_imagetitle = titleHTML; + imageData.attr_imagetitlelink = editor.dom.getAttrib(titleElement, 'href') || ''; + } + + var licenseElement = editor.dom.select('.custom-license', figcaption)[0]; + if (licenseElement) { + var licenseText = licenseElement.textContent.trim(); + imageData.customcaptionlicense = licenseText; + imageData.captionlicense = 'custom'; + } else { + // Search in .license container + var licenseContainer = editor.dom.select('.license', figcaption)[0]; + if (licenseContainer) { + var licenseLink = editor.dom.select('a', licenseContainer)[0]; + var licenseSpan = editor.dom.select('span', licenseContainer)[0]; + var licenseText = ''; + + if (licenseLink) { + licenseText = licenseLink.textContent.trim(); + } else if (licenseSpan) { + licenseText = licenseSpan.textContent.trim(); + } else { + licenseText = licenseContainer.textContent.replace(/[()]/g, '').trim(); + } + + // Map text to license code + if (licenseText === 'Public Domain' || licenseText === _("Public Domain")) { + imageData.captionlicense = 'pd'; + } else if (licenseText === 'GNU-GPL' || licenseText === 'GNU/GPL') { + imageData.captionlicense = 'gnu-gpl'; + } else if (licenseText === 'All Rights Reserved' || licenseText === _("All Rights Reserved")) { + imageData.captionlicense = 'copyright'; + } else if (licenseText === 'CC 0' || licenseText === 'CC0') { + imageData.captionlicense = 'CC0'; + } else if (licenseText.indexOf('CC ') === 0 || licenseText.indexOf('CC-') === 0) { + // CC BY, CC BY-SA, etc. + imageData.captionlicense = licenseText.replace('CC ', 'CC-'); + } else if (licenseText && licenseText !== '') { + // Custom license + imageData.customcaptionlicense = licenseText; + imageData.captionlicense = 'custom'; + } + } + } + } + + // Read alignment styles from figure or image + var styleToCheck = figureElement ? figureElement.getAttribute('style') : image.getAttribute('style'); + styleToCheck = styleToCheck || ''; + + if (styleToCheck.indexOf('float: left') !== -1 || styleToCheck.indexOf('float:left') !== -1) { + imageData.alignstyle = 'left'; + } else if (styleToCheck.indexOf('float: right') !== -1 || styleToCheck.indexOf('float:right') !== -1) { + imageData.alignstyle = 'right'; + } else if ((styleToCheck.indexOf('margin-left: auto') !== -1 || styleToCheck.indexOf('margin-left:auto') !== -1) && + (styleToCheck.indexOf('margin-right: auto') !== -1 || styleToCheck.indexOf('margin-right:auto') !== -1)) { + imageData.alignstyle = 'center'; + } else if (styleToCheck.indexOf('vertical-align') !== -1) { + var verticalAlignMatch = styleToCheck.match(/vertical-align\s*:\s*([^;]+)/); + if (verticalAlignMatch) { + imageData.alignstyle = verticalAlignMatch[1].trim(); + } + } + } + + return imageData; + }; + var insertImageAtCaret = function (editor, data) { + var elm = create(function (css) { + return normalizeCss$1(editor, css); + }, data); + editor.dom.setAttrib(elm, 'data-mce-id', '__mcenew'); + editor.focus(); + editor.selection.setContent(elm.outerHTML); + var insertedElm = editor.dom.select('*[data-mce-id="__mcenew"]')[0]; + editor.dom.setAttrib(insertedElm, 'data-mce-id', null); + if (isFigure(insertedElm)) { + var figure = splitTextBlock(editor, insertedElm); + // Select the image inside the figure, not the figure itself + var img = editor.dom.select('img', figure)[0]; + if (img) { + editor.selection.select(img); + } else { + editor.selection.select(figure); + } + } else { + editor.selection.select(insertedElm); + } + }; + var syncSrcAttr = function (editor, image) { + editor.dom.setAttrib(image, 'src', image.getAttribute('src')); + }; + var deleteImage = function (editor, image) { + if (image) { + var figure = editor.dom.getParent(image, 'figure'); + var elm = figure ? figure : image; + editor.dom.remove(elm); + editor.focus(); + editor.nodeChanged(); + if (editor.dom.isEmpty(editor.getBody())) { + editor.setContent(''); + editor.selection.setCursorLocation(); + } + } + }; + var writeImageDataToSelection = function (editor, data) { + var image = getSelectedImage(editor); + write(function (css) { + return normalizeCss$1(editor, css); + }, data, image); + syncSrcAttr(editor, image); + if (isFigure(image.parentNode)) { + var figure = image.parentNode; + splitTextBlock(editor, figure); + editor.selection.select(image.parentNode); + } else { + editor.selection.select(image); + waitLoadImage(editor, data, image); + } + }; + var sanitizeImageData = function (editor, data) { + var src = data.src; + return __assign(__assign({}, data), { src: isSafeImageUrl(editor, src) ? src : '' }); + }; + var insertOrUpdateImage = function (editor, partialData) { + var image = getSelectedImage(editor); + if (image) { + var selectedImageData = read(function (css) { + return normalizeCss$1(editor, css); + }, image); + var data = __assign(__assign({}, selectedImageData), partialData); + var sanitizedData = sanitizeImageData(editor, data); + if (data.src) { + writeImageDataToSelection(editor, sanitizedData); + } else { + deleteImage(editor, image); + } + } else if (partialData.src) { + insertImageAtCaret(editor, __assign(__assign({}, defaultData()), partialData)); + } + }; - //set selection to bookmark - tinymce.activeEditor.selection.moveToBookmark(bm); + var deep = function (old, nu) { + var bothObjects = isObject(old) && isObject(nu); + return bothObjects ? deepMerge(old, nu) : nu; + }; + var baseMerge = function (merger) { + return function () { + var objects = []; + for (var _i = 0; _i < arguments.length; _i++) { + objects[_i] = arguments[_i]; + } + if (objects.length === 0) { + throw new Error('Can\'t merge zero objects'); + } + var ret = {}; + for (var j = 0; j < objects.length; j++) { + var curObject = objects[j]; + for (var key in curObject) { + if (has(curObject, key)) { + ret[key] = merger(ret[key], curObject[key]); + } + } + } + return ret; + }; + }; + var deepMerge = baseMerge(deep); - var data = deepMerge(fromImageData(info.image), api.getData()); - editor.execCommand('mceUpdateImage', false, toImageData(data, info.hasAccessibilityOptions)); - editor.editorUpload.uploadImagesAuto(); + var isNotEmpty = function (s) { + return s.length > 0; + }; - let attrEncabezado = api.getData().attr_imageheader; - let attrTitulo = api.getData().attr_imagetitle; - let attrLinkTitulo = api.getData().attr_imagetitlelink; - let attrAutor = api.getData().authorname; - let attrLinkAutor = api.getData().authornamelink; - let attrLicense = ''; - let attrLinkLicense = ''; + var global$1 = tinymce.util.Tools.resolve('tinymce.util.ImageUploader'); - let advAlignStyle = api.getData().alignstyle; + var global = tinymce.util.Tools.resolve('tinymce.util.Tools'); - if (advAlignStyle == 'left') { - actualAlign = 'position-left'; - } else if (advAlignStyle == 'right') { - actualAlign = 'position-right'; - } + var getValue = function (item) { + return isString(item.value) ? item.value : ''; + }; + var getText = function (item) { + if (isString(item.text)) { + return item.text; + } else if (isString(item.title)) { + return item.title; + } else { + return ''; + } + }; + var sanitizeList = function (list, extractValue) { + var out = []; + global.each(list, function (item) { + var text = getText(item); + if (item.menu !== undefined) { + var items = sanitizeList(item.menu, extractValue); + out.push({ + text: text, + items: items + }); + } else { + var value = extractValue(item); + out.push({ + text: text, + value: value + }); + } + }); + return out; + }; + var sanitizer = function (extractor) { + if (extractor === void 0) { + extractor = getValue; + } + return function (list) { + if (list) { + return Optional.from(list).map(function (list) { + return sanitizeList(list, extractor); + }); + } else { + return Optional.none(); + } + }; + }; + var sanitize = function (list) { + return sanitizer(getValue)(list); + }; + var isGroup = function (item) { + return has(item, 'items'); + }; + var findEntryDelegate = function (list, value) { + return findMap(list, function (item) { + if (isGroup(item)) { + return findEntryDelegate(item.items, value); + } else if (item.value === value) { + return Optional.some(item); + } else { + return Optional.none(); + } + }); + }; + var findEntry = function (optList, value) { + return optList.bind(function (list) { + return findEntryDelegate(list, value); + }); + }; + var ListUtils = { + sanitizer: sanitizer, + sanitize: sanitize, + findEntry: findEntry + }; + + var getAlignmentOptions = function (hasAttribution) { + var baseOptions = [ + { + text: _('-- Not Set --'), + value: '' + } + ]; + + if (!hasAttribution) { + baseOptions.push( + { + text: _('Baseline'), + value: 'baseline' + }, + { + text: _('Top'), + value: 'top' + }, + { + text: _('Middle'), + value: 'middle' + }, + { + text: _('Bottom'), + value: 'bottom' + }, + { + text: _('Text top'), + value: 'text-top' + }, + { + text: _('Text Bottom'), + value: 'text-bottom' + } + ); + } + + baseOptions.push( + { + text: _('Left'), + value: 'left' + }, + { + text: _('Right'), + value: 'right' + }, + { + text: _('Center'), + value: 'center' + } + ); + + return baseOptions; + }; + + var makeTab$2 = function (_info) { + var hasAttribution = _info.image && ( + _info.image.attr_imageheader || + _info.image.attr_imagetitle || + _info.image.attr_imagetitlelink || + _info.image.authorname || + _info.image.authornamelink || + _info.image.captionlicense + ); + + return { + title: 'Advanced', + name: 'advanced', + items: [ + { + type: 'input', + label: 'Style', + name: 'style' + }, + { + type: 'listbox', + name: 'alignstyle', + label: _('Alignment'), + items: getAlignmentOptions(hasAttribution) + }, + { + type: 'grid', + columns: 2, + items: [ + { + type: 'input', + label: 'Vertical space', + name: 'vspace', + inputMode: 'numeric' + }, + { + type: 'input', + label: 'Horizontal space', + name: 'hspace', + inputMode: 'numeric' + }, + { + type: 'input', + label: 'Border width', + name: 'border', + inputMode: 'numeric' + }, + { + type: 'listbox', + name: 'borderstyle', + label: 'Border style', + items: [ + { + text: _('Select...'), + value: '' + }, + { + text: _('Solid'), + value: 'solid' + }, + { + text: _('Dotted'), + value: 'dotted' + }, + { + text: _('Dashed'), + value: 'dashed' + }, + { + text: _('Double'), + value: 'double' + }, + { + text: _('Groove'), + value: 'groove' + }, + { + text: _('Ridge'), + value: 'ridge' + }, + { + text: _('Inset'), + value: 'inset' + }, + { + text: _('Outset'), + value: 'outset' + }, + { + text: _('None'), + value: 'none' + }, + { + text: _('Hidden'), + value: 'hidden' + } + ] + } + ] + } + ] + }; + }; + var AdvTab = { makeTab: makeTab$2 }; + + var collect = function (editor) { + var urlListSanitizer = ListUtils.sanitizer(function (item) { + return editor.convertURL(item.value || item.url, 'src'); + }); + var futureImageList = new global$4(function (completer) { + createImageList(editor, function (imageList) { + completer(urlListSanitizer(imageList).map(function (items) { + return flatten([ + [{ + text: 'None', + value: '' + }], + items + ]); + })); + }); + }); + var classList = ListUtils.sanitize(getClassList(editor)); + var hasAdvTab$1 = hasAdvTab(editor); + var hasUploadTab$1 = hasUploadTab(editor); + // attribution tab + var hasAttributionTab$1 = hasAttributionTab(editor); + var hasUploadUrl$1 = hasUploadUrl(editor); + var hasUploadHandler$1 = hasUploadHandler(editor); + var image = readImageDataFromSelection(editor); + var hasDescription$1 = hasDescription(editor); + var hasImageTitle$1 = hasImageTitle(editor); + var hasDimensions$1 = hasDimensions(editor); + var hasImageCaption$1 = hasImageCaption(editor); + var hasAccessibilityOptions = showAccessibilityOptions(editor); + var automaticUploads = isAutomaticUploadsEnabled(editor); + var prependURL = Optional.some(getPrependUrl(editor)).filter(function (preUrl) { + return isString(preUrl) && preUrl.length > 0; + }); + return futureImageList.then(function (imageList) { + return { + image: image, + imageList: imageList, + classList: classList, + hasAdvTab: hasAdvTab$1, + hasUploadTab: hasUploadTab$1, + hasUploadUrl: hasUploadUrl$1, + hasUploadHandler: hasUploadHandler$1, + // attribution tab + hasAttributionTab: hasAttributionTab$1, + hasDescription: hasDescription$1, + hasImageTitle: hasImageTitle$1, + hasDimensions: hasDimensions$1, + hasImageCaption: hasImageCaption$1, + prependURL: prependURL, + hasAccessibilityOptions: hasAccessibilityOptions, + automaticUploads: automaticUploads + }; + }); + }; + + var makeItems = function (info) { + var imageUrl = { + name: 'src', + type: 'urlinput', + filetype: 'image', + label: _('Source') + }; + + var imageList = info.imageList.map(function (items) { + return { + name: 'images', + type: 'listbox', + label: _('Image list'), + items: items + }; + }); + var imageDescription = { + name: 'alt', + type: 'input', + label: _('Alternative description'), + disabled: info.hasAccessibilityOptions && info.image.isDecorative + }; + var imageTitle = { + name: 'title', + type: 'input', + label: _('Image title') + }; + var imageDimensions = { + name: 'dimensions', + type: 'sizeinput' + }; + var isDecorative = { + type: 'label', + label: _('Accessibility'), + items: [{ + name: 'isDecorative', + type: 'checkbox', + label: _('Image is decorative') + }] + }; + var classList = info.classList.map(function (items) { + return { + name: 'classes', + type: 'listbox', + label: _('Class'), + items: items + }; + }); + var caption = { + type: 'label', + label: _('Caption'), + items: [{ + type: 'checkbox', + name: 'caption', + label: _('Show caption') + }] + }; + var getDialogContainerType = function (useColumns) { + return useColumns ? { + type: 'grid', + columns: 2 + } : { type: 'panel' }; + }; + return flatten([ + [imageUrl], + imageList.toArray(), + info.hasAccessibilityOptions && info.hasDescription ? [isDecorative] : [], + info.hasDescription ? [imageDescription] : [], + info.hasImageTitle ? [imageTitle] : [], + info.hasDimensions ? [imageDimensions] : [], + [__assign(__assign({}, getDialogContainerType(info.classList.isSome() && info.hasImageCaption)), { + items: flatten([ + classList.toArray(), + info.hasImageCaption ? [caption] : [] + ]) + })] + ]); + }; + var makeTab$1 = function (info) { + return { + title: 'General', + name: 'general', + items: makeItems(info) + }; + }; + var MainTab = { + makeTab: makeTab$1, + makeItems: makeItems + }; - if (api.getData().captionlicense !== 'custom') { - attrLicense = api.getData().captionlicense; - attrLinkLicense = GetLinkLicense(attrLicense); - attrLicense = FormatLicenseName(attrLicense); + var makeTab$3 = function (_info) { + var items = [{ + type: 'dropzone', + name: 'fileinput' + }]; + return { + title: 'Upload', + name: 'upload', + items: items + }; + }; + var UploadTab = { makeTab: makeTab$3 }; + + + // attribution tab + + function getTabContent(info) { + return [ + { + type: 'input', + label: _("Header"), + name: 'attr_imageheader' + }, + { + type: 'label', + label: _('Footer caption'), + items: [ + { + type: 'input', + label: _("Image Title"), + name: 'attr_imagetitle' + }, + { + type: 'input', + label: _("Title Link"), + name: 'attr_imagetitlelink' + }, + { + type: 'input', + label: _("Source/Author"), + name: 'authorname' + }, + { + type: 'input', + label: _("Source/Author Link"), + name: 'authornamelink' + }, + { + type: 'listbox', + name: 'captionlicense', + label: _("License"), + items: [ + { text: _("Choose a license..."), value: '' }, + { text: _("Public Domain"), value: 'pd' }, + { text: "GNU/GPL", value: 'gnu-gpl' }, + { text: "Creative Commons (" + _("Public Domain") + ")", value: 'CC0' }, + { text: "Creative Commons BY", value: 'CC-BY' }, + { text: "Creative Commons BY-SA", value: 'CC-BY-SA' }, + { text: "Creative Commons BY-ND", value: 'CC-BY-ND' }, + { text: "Creative Commons BY-NC", value: 'CC-BY-NC' }, + { text: "Creative Commons BY-NC-SA", value: 'CC-BY-NC-SA' }, + { text: "Creative Commons BY-NC-ND", value: 'CC-BY-NC-ND' }, + { text: "Copyright (" + _("All Rights Reserved") + ")", value: 'copyright' }, + { text: _("Custom license"), value: 'custom' } + ] + }, + { + type: 'input', + name: 'customcaptionlicense', + label: _("Custom license"), + disabled: true + } + ] + } + ] } - else { - attrLicense = api.getData().customcaptionlicense; + + var makeTab$4 = function (info) { + return { + title: _("Title and Attribution"), + name: 'attribution', + items: getTabContent(info) + } } - if (editor.selection.getNode().tagName !== 'FIGURE') { - if (attrEncabezado != '' || attrTitulo != '' || attrLinkTitulo != '' || attrAutor != '' || attrLinkAutor != '' || attrLicense != '') { - numero_imagenes++; + var AttributionTab = { makeTab: makeTab$4 }; - let container = editor.selection.getSel().anchorNode; - if (container.id === null || container.id === '') { - container.id = "figure_imagen_" + numero_imagenes; - } - let imagenNode = editor.selection.getNode(); + var createState = function (info) { - if (imagenNode.id === null || imagenNode.id === '') { - imagenNode.id = "imagen_" + numero_imagenes; - imagenNode.classList.add("imagen_" + numero_imagenes); + return { + prevImage: ListUtils.findEntry(info.imageList, info.image.src), + prevAlt: info.image.alt, + open: true + }; + }; + var fromImageData = function (image) { + + if (image.style != "") { + let verticalAlignMatch = image.style.match(/vertical-align\s*:\s*([^;]+)/); + let floatMatch = image.style.match(/float\s*:\s*([^;]+)/); + + if (verticalAlignMatch) { + img_v_style = verticalAlignMatch[1].trim(); + } else if (floatMatch) { + img_v_style = floatMatch[1].trim(); + } } - let custom_div_html = ''; + if (image.alignstyle) { + let currentNode = tinymce.activeEditor.selection.getNode(); + let isInColumn = false; + let parentEl = currentNode; + + while (parentEl && parentEl !== tinymce.activeEditor.getBody()) { + if (parentEl.classList && (parentEl.classList.contains('exe-col') || + parentEl.classList.contains('exe-col-1') || + parentEl.classList.contains('exe-col-2') || + parentEl.classList.contains('exe-col-3'))) { + isInColumn = true; + break; + } + parentEl = parentEl.parentNode; + } - if (attrEncabezado !== '') { - let id_header_img = 'header_imagen_' + numero_imagenes; - custom_div_html = '
' + attrEncabezado + '
' + container.innerHTML; + actualAlign = image.alignstyle; + switch (image.alignstyle) { + case 'left': + actualAlign = isInColumn ? 'float-left' : 'position-left'; + img_v_style = 'left'; + break; + case 'right': + actualAlign = isInColumn ? 'float-right' : 'position-right'; + img_v_style = 'right'; + break; + case 'center': + actualAlign = isInColumn ? '' : 'position-center'; + img_v_style = ''; + break; + default: + actualAlign = isInColumn ? '' : 'position-center'; + img_v_style = ''; + } } else { - custom_div_html = container.innerHTML; + idSelectedImage = image.class; + let selectedImage = getSelectedImage(tinymce.activeEditor); + let figure = null; + if (selectedImage) { + figure = tinymce.activeEditor.dom.getParent(selectedImage, 'figure.exe-figure'); + } + let currentNode = tinymce.activeEditor.selection.getNode(); + let isInColumn = false; + let parentEl = currentNode; + + while (parentEl && parentEl !== tinymce.activeEditor.getBody()) { + if (parentEl.classList && (parentEl.classList.contains('exe-col') || + parentEl.classList.contains('exe-col-1') || + parentEl.classList.contains('exe-col-2') || + parentEl.classList.contains('exe-col-3'))) { + isInColumn = true; + break; + } + parentEl = parentEl.parentNode; + } + + if (figure != null) { + if (tinymce.activeEditor.dom.hasClass(figure, 'float-left')) { + actualAlign = 'left'; + img_v_style = 'left'; + } + else if (tinymce.activeEditor.dom.hasClass(figure, 'position-center')) { + actualAlign = 'center'; + img_v_style = ''; + } + else if (tinymce.activeEditor.dom.hasClass(figure, 'float-right')) { + actualAlign = 'right'; + img_v_style = 'right'; + } + else { + actualAlign = ''; + img_v_style = ''; + } + } else { + if (selectedImage) { + if (tinymce.activeEditor.dom.hasClass(selectedImage, 'position-left') || + tinymce.activeEditor.dom.hasClass(selectedImage, 'float-left')) { + actualAlign = 'left'; + img_v_style = 'left'; + } else if (tinymce.activeEditor.dom.hasClass(selectedImage, 'position-center')) { + actualAlign = 'center'; + img_v_style = ''; + } else if (tinymce.activeEditor.dom.hasClass(selectedImage, 'position-right') || + tinymce.activeEditor.dom.hasClass(selectedImage, 'float-right')) { + actualAlign = 'right'; + img_v_style = 'right'; + } + else { + let imgStyle = selectedImage.getAttribute('style') || ''; + let verticalAlignMatch = imgStyle.match(/vertical-align\s*:\s*([^;]+)/); + if (verticalAlignMatch) { + actualAlign = verticalAlignMatch[1].trim(); + img_v_style = actualAlign; + } else { + actualAlign = ''; + img_v_style = ''; + } + } + } else { + actualAlign = ''; + } + } + } + + let cleanedStyle = image.style || ''; + if (actualAlign === 'left' || actualAlign === 'center' || actualAlign === 'right' || + actualAlign === 'baseline' || actualAlign === 'top' || actualAlign === 'middle' || + actualAlign === 'bottom' || actualAlign === 'text-top' || actualAlign === 'text-bottom') { + cleanedStyle = cleanedStyle.replace(/float\s*:\s*[^;]+\s*;?/gi, ''); + cleanedStyle = cleanedStyle.replace(/display\s*:\s*(block|inline-block)\s*;?/gi, ''); + cleanedStyle = cleanedStyle.replace(/vertical-align\s*:\s*[^;]+\s*;?/gi, ''); + cleanedStyle = cleanedStyle.replace(/margin\s*:\s*[^;]+\s*;?/gi, ''); + cleanedStyle = cleanedStyle.replace(/margin-(left|right|top|bottom)\s*:\s*[^;]+\s*;?/gi, ''); + cleanedStyle = cleanedStyle.trim(); + } + + return { + src: { + value: image.src, + meta: {} + }, + images: image.src, + alt: image.alt, + title: image.title, + dimensions: { + width: image.width, + height: image.height + }, + classes: image.class, + caption: image.caption, + style: cleanedStyle, + vspace: image.vspace, + border: image.border, + hspace: image.hspace, + borderstyle: image.borderStyle, + fileinput: [], + isDecorative: image.isDecorative, + // eXeLearning specific fields automatically extracted + attr_imageheader: image.attr_imageheader || '', + attr_imagetitle: image.attr_imagetitle || '', + attr_imagetitlelink: image.attr_imagetitlelink || '', + authorname: image.authorname || '', + authornamelink: image.authornamelink || '', + captionlicense: image.captionlicense || '', + customcaptionlicense: image.customcaptionlicense || '', + alignstyle: image.alignstyle || actualAlign || '' + }; + }; + var toImageData = function (data, removeEmptyAlt) { + return { + src: data.src.value, + alt: data.alt.length === 0 && removeEmptyAlt ? null : data.alt, + title: data.title, + width: data.dimensions.width, + height: data.dimensions.height, + class: data.classes, + style: data.style, + caption: data.caption, + hspace: data.hspace, + vspace: data.vspace, + border: data.border, + borderStyle: data.borderstyle, + isDecorative: data.isDecorative + }; + }; + var addPrependUrl2 = function (info, srcURL) { + if (!/^(?:[a-zA-Z]+:)?\/\//.test(srcURL)) { + return info.prependURL.bind(function (prependUrl) { + if (srcURL.substring(0, prependUrl.length) !== prependUrl) { + return Optional.some(prependUrl + srcURL); + } + return Optional.none(); + }); + } + return Optional.none(); + }; + var addPrependUrl = function (info, api) { + var data = api.getData(); + addPrependUrl2(info, data.src.value).each(function (srcURL) { + api.setData({ + src: { + value: srcURL, + meta: data.src.meta + } + }); + }); + }; + var formFillFromMeta2 = function (info, data, meta) { + if (info.hasDescription && isString(meta.alt)) { + data.alt = meta.alt; + } + if (info.hasAccessibilityOptions) { + data.isDecorative = meta.isDecorative || data.isDecorative || false; + } + + if (info.hasDimensions) { + if (isString(meta.width)) { + data.dimensions.width = meta.width; + } + if (isString(meta.height)) { + data.dimensions.height = meta.height; + } + } + if (isString(meta.class)) { + ListUtils.findEntry(info.classList, meta.class).each(function (entry) { + data.classes = entry.value; + }); + } + if (info.hasImageCaption) { + if (isBoolean(meta.caption)) { + data.caption = meta.caption; + } } + if (info.hasAdvTab) { + if (isString(meta.style)) { + data.style = meta.style; + } + if (isString(meta.vspace)) { + data.vspace = meta.vspace; + } + if (isString(meta.border)) { + data.border = meta.border; + } + if (isString(meta.hspace)) { + data.hspace = meta.hspace; + } + if (isString(meta.borderstyle)) { + data.borderstyle = meta.borderstyle; + } + } + }; + var formFillFromMeta = function (info, api) { + var data = api.getData(); + var meta = data.src.meta; + if (meta !== undefined) { + var newData = deepMerge({}, data); + formFillFromMeta2(info, newData, meta); + api.setData(newData); + } + }; + var calculateImageSize = function (helpers, info, state, api) { + var data = api.getData(); + var url = data.src.value; + var meta = data.src.meta || {}; + if (!meta.width && !meta.height && info.hasDimensions) { + if (isNotEmpty(url)) { + helpers.imageSize(url).then(function (size) { + if (state.open) { + api.setData({ dimensions: size }); + } + }).catch(function (e) { + return console.error(e); + }); + } else { + api.setData({ + dimensions: { + width: '', + height: '' + } + }); + } + } + }; + var updateImagesDropdown = function (info, state, api) { + var data = api.getData(); + var image = ListUtils.findEntry(info.imageList, data.src.value); + state.prevImage = image; + api.setData({ + images: image.map(function (entry) { + return entry.value; + }).getOr('') + }); + }; + var changeSrc = function (helpers, info, state, api) { + addPrependUrl(info, api); + formFillFromMeta(info, api); + calculateImageSize(helpers, info, state, api); + updateImagesDropdown(info, state, api); + }; + var changeImages = function (helpers, info, state, api) { + var data = api.getData(); + var image = ListUtils.findEntry(info.imageList, data.images); + image.each(function (img) { + var updateAlt = data.alt === '' || state.prevImage.map(function (image) { + return image.text === data.alt; + }).getOr(false); + if (updateAlt) { + if (img.value === '') { + api.setData({ + src: img, + alt: state.prevAlt + }); + } else { + api.setData({ + src: img, + alt: img.text + }); + } + } else { + api.setData({ src: img }); + } + }); + state.prevImage = image; + changeSrc(helpers, info, state, api); + }; + var calcVSpace = function (css) { + var matchingTopBottom = css['margin-top'] === css['margin-bottom']; + return matchingTopBottom ? removePixelSuffix(String(css['margin-top'])) : ''; + }; + var calcHSpace = function (css) { + var matchingLeftRight = css['margin-right'] === css['margin-left']; + return matchingLeftRight ? removePixelSuffix(String(css['margin-right'])) : ''; + }; + var calcBorderWidth = function (css) { + return css['border-width'] ? removePixelSuffix(String(css['border-width'])) : ''; + }; + var calcBorderStyle = function (css) { + return css['border-style'] ? String(css['border-style']) : ''; + }; + var calcStyle = function (parseStyle, serializeStyle, css) { + return serializeStyle(parseStyle(serializeStyle(css))); + }; + var changeStyle2 = function (parseStyle, serializeStyle, data) { + var css = mergeMargins(parseStyle(data.style)); + var dataCopy = deepMerge({}, data); + dataCopy.vspace = calcVSpace(css); + dataCopy.hspace = calcHSpace(css); + dataCopy.border = calcBorderWidth(css); + dataCopy.borderstyle = calcBorderStyle(css); + dataCopy.style = calcStyle(parseStyle, serializeStyle, css); + return dataCopy; + }; + var changeStyle = function (helpers, api) { + var data = api.getData(); + var newData = changeStyle2(helpers.parseStyle, helpers.serializeStyle, data); + api.setData(newData); + }; + var changeAStyle = function (helpers, info, api) { + var data = deepMerge(fromImageData(info.image), api.getData()); + var style = getStyleValue(helpers.normalizeCss, toImageData(data, false)); + api.setData({ style: style }); + }; + var changeFileInput = function (helpers, info, state, api) { + var data = api.getData(); + api.block('Uploading image'); + head(data.fileinput).fold(function () { + api.unblock(); + }, function (file) { + var blobUri = URL.createObjectURL(file); + var finalize = function () { + api.unblock(); + URL.revokeObjectURL(blobUri); + }; + var updateSrcAndSwitchTab = function (url) { + api.setData({ + src: { + value: url, + meta: {} + } + }); + api.showTab('general'); + changeSrc(helpers, info, state, api); + }; + blobToDataUri(file).then(function (dataUrl) { + var blobInfo = helpers.createBlobCache(file, blobUri, dataUrl); + if (info.automaticUploads) { + helpers.uploadImage(blobInfo).then(function (result) { + updateSrcAndSwitchTab(result.url); + finalize(); + }).catch(function (err) { + finalize(); + helpers.alertErr(err); + }); + } else { + helpers.addToBlobCache(blobInfo); + updateSrcAndSwitchTab(blobInfo.blobUri()); + api.unblock(); + } + }); + }); + }; - let tipo_autor = 'author_imagen_' + numero_imagenes; - let tipo_titulo = 'title_imagen_' + numero_imagenes; - let tipo_licencia = 'license_imagen_' + numero_imagenes; - let htmlAutor = CustomHtml(attrAutor, attrLinkAutor, false, tipo_autor); - let htmlTitulo = CustomHtml(attrTitulo, attrLinkTitulo, true, tipo_titulo); - let htmlLicencia = CustomHtml(attrLicense, attrLinkLicense, false, tipo_licencia); + var changeHandler = function (helpers, info, state) { + return function (api, evt) { + if (evt.name === 'src') { + changeSrc(helpers, info, state, api); + } else if (evt.name === 'images') { + changeImages(helpers, info, state, api); + } else if (evt.name === 'alt') { + state.prevAlt = api.getData().alt; + } else if (evt.name === 'style') { + changeStyle(helpers, api); + } else if (evt.name === 'vspace' || evt.name === 'hspace' || evt.name === 'border' || evt.name === 'borderstyle') { + changeAStyle(helpers, info, api); + } else if (evt.name === 'alignstyle') { + alignImageStyle = api.getData().alignstyle; + + var hasNoAttribution = idSelectedImage == '' && api.getData().attr_imageheader == '' + && api.getData().attr_imagetitle == '' && api.getData().attr_imagetitlelink == '' + && api.getData().authorname == '' && api.getData().authornamelink == '' && api.getData().captionlicense == ''; + + if (alignImageStyle !== '') { + if (alignImageStyle === 'right' || alignImageStyle === 'left') { + // Image is not figure and not will be figure + if (hasNoAttribution) { + style_for_div = 'float: ' + alignImageStyle; + } else { + style_for_div = ''; + } + } else if (alignImageStyle === 'center') { + style_for_div = 'display: block; margin-left: auto; margin-right: auto'; + } else if (alignImageStyle === 'baseline' || alignImageStyle === 'top' || + alignImageStyle === 'middle' || alignImageStyle === 'bottom' || + alignImageStyle === 'text-top' || alignImageStyle === 'text-bottom') { + style_for_div = 'vertical-align: ' + alignImageStyle; + } else { + style_for_div = ''; + } + } - if (htmlAutor !== '' || htmlTitulo !== '' || htmlLicencia !== '') { - custom_div_html += '
'; - if (htmlAutor !== '') { - custom_div_html += htmlAutor + ". "; - } - if (htmlTitulo) { - custom_div_html += htmlTitulo; - } - if (htmlLicencia !== '') { - custom_div_html += ' (' + htmlLicencia + ')'; - } - custom_div_html += '
'; + changeAStyle(helpers, info, api); + currentAlignStyle = api.getData().alignstyle; + var styleText = api.getData().style; + var styleArray = styleText.split(";"); + + for (var i = 0; i < styleArray.length; i++) { + var stylePart = styleArray[i].trim().toLowerCase(); + if (stylePart.includes("float") || + stylePart.includes("vertical-align") || + stylePart.includes("display") || + stylePart.startsWith("margin-left") || + stylePart.startsWith("margin-right")) { + styleArray.splice(i, 1); + i--; + } + } + + styleText = styleArray.toString(); + styleText = styleText.replaceAll(",", ";"); + styleText = styleText.replace(/;+/g, ';').replace(/^\s*;\s*/, '').trim(); + + if (hasNoAttribution) { + api.setData({ style: styleText }); + } else { + if (api.getData().alignstyle === 'left' || api.getData().alignstyle === 'right') { + if (styleText && !styleText.endsWith(';')) styleText += ';'; + api.setData({ style: styleText + " float: " + api.getData().alignstyle + ";" }); + } else if (api.getData().alignstyle === 'baseline' || api.getData().alignstyle === 'top' || + api.getData().alignstyle === 'middle' || api.getData().alignstyle === 'bottom' || + api.getData().alignstyle === 'text-top' || api.getData().alignstyle === 'text-bottom') { + if (styleText && !styleText.endsWith(';')) styleText += ';'; + api.setData({ style: styleText + " vertical-align: " + api.getData().alignstyle + ";" }); + } else if (api.getData().alignstyle === 'center') { + if (styleText && !styleText.endsWith(';')) styleText += ';'; + api.setData({ style: styleText + " display: block; margin-left: auto; margin-right: auto;" }); + } else { + api.setData({ style: styleText }); + } + } + } + + else if (evt.name === 'fileinput') { + changeFileInput(helpers, info, state, api); + } else if (evt.name === 'isDecorative') { + if (api.getData().isDecorative) { + api.disable('alt'); + } else { + api.enable('alt'); + } + } + + // attribution, enable custom license + else if (evt.name === 'captionlicense') { + + if (api.getData().captionlicense === 'custom') { + api.enable('customcaptionlicense'); + } else { + api.disable('customcaptionlicense'); + api.setData({ customcaptionlicense: '' }); + } + } + + else if (evt.name === 'captionlicense' || evt.name === 'authornamelink' || evt.name === 'authorname' || + evt.name === 'attr_imagetitlelink' || evt.name === 'attr_imagetitle' || evt.name === 'attr_imageheader') { + if (api.setData({ style: api.getData().style.replace('float: right;', '') })) { + api.setData({ alignstyle: 'right' }); + } else if (api.setData({ style: api.getData().style.replace('float: left;', '') })) { + api.setData({ alignstyle: 'left' }); + } + + } + + // / attribution tab + + }; + }; + var closeHandler = function (state) { + return function () { + state.open = false; + }; + }; + var makeDialogBody = function (info) { + if (info.hasAdvTab || info.hasUploadUrl || info.hasUploadHandler) { + var tabPanel = { + type: 'tabpanel', + tabs: flatten([ + [MainTab.makeTab(info)], + info.hasAdvTab ? [AdvTab.makeTab(info)] : [], + info.hasUploadTab && (info.hasUploadUrl || info.hasUploadHandler) ? [UploadTab.makeTab(info)] : [], + // attibution tab + info.hasAttributionTab ? [AttributionTab.makeTab(info)] : [] + ]) + }; + return tabPanel; + } else { + var panel = { + type: 'panel', + items: MainTab.makeItems(info) + }; + return panel; } + }; + var makeDialog = function (helpers) { + return function (info) { + var state = createState(info); + return { + title: _('Insert/Edit Image'), + size: 'normal', + body: makeDialogBody(info), + buttons: [ + { + type: 'cancel', + name: 'cancel', + text: _('Cancel') + }, + { + type: 'submit', + name: 'save', + text: _('Save'), + primary: true + } + ], + initialData: fromImageData(info.image), + onSubmit: helpers.onSubmit(info), + onAction: function (api, details) {}, + onChange: changeHandler(helpers, info, state), + onTabChange: function (api, details) { + // Save data from the tab we are leaving (if it's the “attribution” tab) + if (details.oldTabName === 'attribution') { + var d = api.getData(); + // Store the attribution fields for later restoration + this._savedAttributionData = { + attr_imageheader: d.attr_imageheader, + attr_imagetitle: d.attr_imagetitle, + attr_imagetitlelink: d.attr_imagetitlelink, + authorname: d.authorname, + authornamelink: d.authornamelink, + captionlicense: d.captionlicense, + customcaptionlicense: d.customcaptionlicense + }; + } + if (details.newTabName === 'general') { + setGenTabStyle(); + } + else if (details.newTabName === 'advanced') { + setAdvTabStyle(); + if (img_v_style === '' || img_v_style === 'baseline' || img_v_style === 'top' + || img_v_style === 'middle' || img_v_style === 'bottom' || img_v_style === 'text-top' + || img_v_style === 'text-bottom' || img_v_style === 'left' || img_v_style === 'right') { + api.setData({ alignstyle: img_v_style }); + } + } else if (details.newTabName === 'attribution') { + setAttributionTabStyle(); + attibutionTab = true; + + const currentData = api.getData(); + + if (currentData.captionlicense === 'custom' || + (currentData.customcaptionlicense && currentData.customcaptionlicense.trim() !== '')) { + api.setData({ captionlicense: 'custom' }); + api.enable('customcaptionlicense'); + } + + } + }, + onClose: closeHandler(state) + }; + }; + }; - let imageWidth = api.getData().dimensions.width; - style_for_div += `width: ${imageWidth}px;`; + function GetLinkLicense(attrLicense) { - let y = editor.dom.create('figure', { 'id': 'figure_imagen_' + numero_imagenes, 'class': `exe-figure ${actualAlign}`, 'style': style_for_div }, custom_div_html); + let linkLicense = ''; + if (attrLicense === 'pd' || attrLicense === 'copyright' || attrLicense === '') { + linkLicense = ''; + } + else if (attrLicense === 'gnu-gpl') { + linkLicense = 'http://www.gnu.org/licenses/gpl.html'; + } + else if (attrLicense === 'CC0') { + linkLicense = 'http://creativecommons.org/publicdomain/zero/1.0/deed.' + lang; - container.parentNode.replaceChild(y, container); + } else { + linkLicense = 'http://creativecommons.org/licenses/'; + } - let my_p = editor.dom.create('p', {}, ''); - editor.selection.setCursorLocation(editor.selection.getNode(), 1);//move cursor to the end and create new empty

- editor.selection.setNode(my_p); - } + return linkLicense; + } + + function FormatLicenseName(attrLicense) { + let newName = ''; + if (attrLicense === 'pd') { + newName = _('Public Domain'); + } else if (attrLicense === 'gnu-gpl') { + newName = 'GNU-GPL'; + } else if (attrLicense === 'copyright') { + newName = _("All Rights Reserved"); + } else { + newName = attrLicense.replace('CC-', 'CC '); + } + return newName; } - else { - - if (!attibutionTab) { - let actHeader = editor.dom.get('header_' + idSelectedImage); - let actHeaderHTML = ''; - if (actHeader != null) { - actHeaderHTML = editor.selection.select(actHeader).innerHTML; - actHeaderHTML = actHeaderHTML.replace('', ''); - actHeaderHTML = actHeaderHTML.replace('', ''); - } - let actTitle = editor.dom.get('title_' + idSelectedImage); - let actTitleHTML = ''; - let actLinkTitleHTML = ''; - if (actTitle != null) { - actTitleHTML = editor.selection.select(actTitle).innerHTML; - actTitleHTML = actTitleHTML.replace('', ''); - actTitleHTML = actTitleHTML.replace('', ''); - actLinkTitleHTML = editor.dom.getAttrib(actTitle, 'href'); - } - let actAuthor = editor.dom.get('author_' + idSelectedImage); - let actAuthorHTML = ''; - let actLinkAuthorHTML = ''; - if (actAuthor != null) { - actAuthorHTML = editor.selection.select(actAuthor).innerHTML; - actLinkAuthorHTML = editor.dom.getAttrib(actAuthor, 'href'); - } - let actLicense = editor.dom.get('license_' + idSelectedImage); - let actLicenseHTML = ''; - let actLinkLicenseHTML = ''; - if (actLicense != null) { - actLicenseHTML = editor.selection.select(actLicense).innerHTML; - actLinkLicenseHTML = editor.dom.getAttrib(actLicense, 'href'); - } - - attrEncabezado = actHeaderHTML; - attrTitulo = actTitleHTML; - attrLinkTitulo = actLinkTitleHTML; - attrAutor = actAuthorHTML; - attrLinkAutor = actLinkAuthorHTML; - attrLicense = actLicenseHTML; - attrLinkLicense = actLinkLicenseHTML; - } - - // Create new image - - // let container = editor.selection.getSel().anchorNode; - - let figure_old = editor.dom.get('figure_' + idSelectedImage); - - // Skip figure update if figure_old doesn't exist (e.g., image without figure wrapper) - if (!figure_old) { - api.close(); - return; - } - let imagen_html = editor.dom.getOuterHTML(idSelectedImage); + var submitHandler = function (editor) { - editor.dom.setHTML(figure_old, ''); + return function (info) { + return function (api) { + if (api.getData().alt == "") { + editor.windowManager.confirm( + _("Are you sure you want to continue without including an Image Description? Without it the image may not be accessible to some users with disabilities, or to those using a text browser, or browsing the Web with images turned off."), + function (s) { + if (s) { + mySubmit(editor, info, api); + } + } + ); + } else { + mySubmit(editor, info, api); + } + }; + }; + }; - let custom_div_html = ''; + function mySubmit(editor, info, api) { + + //set selection to bookmark + tinymce.activeEditor.selection.moveToBookmark(bm); + + var data = deepMerge(fromImageData(info.image), api.getData()); + var imageData = toImageData(data, info.hasAccessibilityOptions); + + let attrEncabezado = api.getData().attr_imageheader; + let attrTitulo = api.getData().attr_imagetitle; + let attrLinkTitulo = api.getData().attr_imagetitlelink; + let attrAutor = api.getData().authorname; + let attrLinkAutor = api.getData().authornamelink; + let attrLicense = ''; + let attrLinkLicense = ''; + + let hasAttribution = attrEncabezado != '' || attrTitulo != '' || attrLinkTitulo != '' || + attrAutor != '' || attrLinkAutor != '' || + api.getData().captionlicense != ''; + + let advAlignStyle = api.getData().alignstyle; + + let alignStyle = ''; + let isVerticalAlign = false; + + // Convert alignstyle to inline styles + if (advAlignStyle === 'left') { + alignStyle = 'float: left; margin-right: 1.5em;'; + } else if (advAlignStyle === 'right') { + alignStyle = 'float: right; margin-left: 1.5em;'; + } else if (advAlignStyle === 'center') { + alignStyle = 'display: block; margin-left: auto; margin-right: auto;'; + } else if (advAlignStyle === 'baseline' || advAlignStyle === 'top' || advAlignStyle === 'middle' || + advAlignStyle === 'bottom' || advAlignStyle === 'text-top' || advAlignStyle === 'text-bottom') { + if (hasAttribution) { + alignStyle = 'display: block; margin-left: auto; margin-right: auto;'; + } else { + alignStyle = 'vertical-align: ' + advAlignStyle + ';'; + isVerticalAlign = true; + } + } - if (attrEncabezado !== '') { - let id_header_img = 'header_' + idSelectedImage; - custom_div_html = '

' + attrEncabezado + '
' + imagen_html; - } else { - custom_div_html = imagen_html; - } + if (api.getData().captionlicense !== 'custom') { + attrLicense = api.getData().captionlicense; + attrLinkLicense = GetLinkLicense(attrLicense); + attrLicense = FormatLicenseName(attrLicense); + } + else { + attrLicense = api.getData().customcaptionlicense; + } - let tipo_autor = 'author_' + idSelectedImage; - let tipo_titulo = 'title_' + idSelectedImage; - let tipo_licencia = 'license_' + idSelectedImage; - let htmlAutor = CustomHtml(attrAutor, attrLinkAutor, false, tipo_autor); - let htmlTitulo = CustomHtml(attrTitulo, attrLinkTitulo, true, tipo_titulo); - let htmlLicencia = CustomHtml(attrLicense, attrLinkLicense, false, tipo_licencia); + var selectedNode = editor.selection.getNode(); + var existingFigure = editor.dom.getParent(selectedNode, 'figure.exe-figure', editor.getBody()); + var isEditingExistingFigure = existingFigure !== null; + + var existingImage = getSelectedImage(editor); + var isEditingExistingImage = existingImage !== null; + + if (!hasAttribution && isVerticalAlign && isEditingExistingImage) { + let currentStyle = imageData.style || ''; + currentStyle = currentStyle.replace(/vertical-align\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/float\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/display\s*:\s*(block|inline-block)\s*;?/gi, ''); + currentStyle = currentStyle.replace(/margin\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/margin-(left|right|top|bottom)\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.trim(); + + if (currentStyle && !currentStyle.endsWith(';')) { + currentStyle += ';'; + } + if (currentStyle) { + imageData.style = currentStyle + ' ' + alignStyle; + } else { + imageData.style = alignStyle; + } + imageData.style = imageData.style.trim(); + } - if (htmlAutor !== '' || htmlTitulo !== '' || htmlLicencia !== '') { - custom_div_html += '
'; - if (htmlAutor !== '') { - custom_div_html += htmlAutor + ". "; + if (!hasAttribution && !isEditingExistingFigure) { + editor.execCommand('mceUpdateImage', false, imageData); + + if (isEditingExistingImage && isVerticalAlign) { + setTimeout(function() { + let imgNode = editor.selection.getNode(); + if (imgNode && imgNode.nodeName === 'IMG') { + let currentClass = imgNode.getAttribute('class') || ''; + currentClass = currentClass.replace(/\s*float-left\s*/g, ' ') + .replace(/\s*float-right\s*/g, ' ') + .replace(/\s*position-left\s*/g, ' ') + .replace(/\s*position-center\s*/g, ' ') + .replace(/\s*position-right\s*/g, ' ') + .trim(); + + if (currentClass === '') { + imgNode.removeAttribute('class'); + } else { + imgNode.setAttribute('class', currentClass); + } + + editor.nodeChanged(); + editor.undoManager.add(); + } + }, 10); + } + + else if (isEditingExistingImage && advAlignStyle && !isVerticalAlign) { + setTimeout(function() { + let imgNode = editor.selection.getNode(); + if (imgNode && imgNode.nodeName === 'IMG') { + let currentStyle = imgNode.getAttribute('style') || ''; + currentStyle = currentStyle.replace(/float\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/display\s*:\s*(block|inline-block)\s*;?/gi, ''); + currentStyle = currentStyle.replace(/vertical-align\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/margin\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/margin-(left|right|top|bottom)\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.trim(); + + if (advAlignStyle === 'left' || advAlignStyle === 'right' || advAlignStyle === 'center') { + let currentClass = imgNode.getAttribute('class') || ''; + currentClass = currentClass.replace(/\s*float-left\s*/g, ' ') + .replace(/\s*float-right\s*/g, ' ') + .replace(/\s*position-left\s*/g, ' ') + .replace(/\s*position-center\s*/g, ' ') + .replace(/\s*position-right\s*/g, ' ') + .trim(); + + if (advAlignStyle === 'left') { + currentClass = (currentClass + ' position-left').trim(); + } else if (advAlignStyle === 'right') { + currentClass = (currentClass + ' position-right').trim(); + } else if (advAlignStyle === 'center') { + currentClass = (currentClass + ' position-center').trim(); + } + + if (currentClass === '') { + imgNode.removeAttribute('class'); + } else { + imgNode.setAttribute('class', currentClass); + } + + if (currentStyle) { + imgNode.setAttribute('style', currentStyle); + } else { + imgNode.removeAttribute('style'); + } + } + else if (advAlignStyle === 'baseline' || advAlignStyle === 'top' || advAlignStyle === 'middle' || + advAlignStyle === 'bottom' || advAlignStyle === 'text-top' || advAlignStyle === 'text-bottom') { + let currentClass = imgNode.getAttribute('class') || ''; + currentClass = currentClass.replace(/\s*float-left\s*/g, ' ') + .replace(/\s*float-right\s*/g, ' ') + .replace(/\s*position-left\s*/g, ' ') + .replace(/\s*position-center\s*/g, ' ') + .replace(/\s*position-right\s*/g, ' ') + .trim(); + + if (currentClass === '') { + imgNode.removeAttribute('class'); + } else { + imgNode.setAttribute('class', currentClass); + } + + currentStyle = currentStyle.replace(/vertical-align\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.trim(); + + if (currentStyle && !currentStyle.endsWith(';')) { + currentStyle += ';'; + } + if (currentStyle) { + currentStyle += ' vertical-align: ' + advAlignStyle + ';'; + } else { + currentStyle = 'vertical-align: ' + advAlignStyle + ';'; + } + currentStyle = currentStyle.trim(); + + imgNode.setAttribute('style', currentStyle); + } + } + + editor.nodeChanged(); + editor.undoManager.add(); + }, 10); + } + + editor.editorUpload.uploadImagesAuto(); + api.close(); + return; } - if (htmlTitulo) { - custom_div_html += htmlTitulo; + else if (!hasAttribution && isEditingExistingFigure) { + // Check if the existing figure has figcaptions that need to be preserved + var hasFigcaptions = false; + var headerHtml = ''; + var footerHtml = ''; + + if (existingFigure) { + var headerElement = editor.dom.select('.figcaption.header', existingFigure)[0]; + var footerElement = editor.dom.select('figcaption.figcaption', existingFigure)[0]; + + if (headerElement) { + headerHtml = editor.dom.getOuterHTML(headerElement); + hasFigcaptions = true; + } + if (footerElement) { + footerHtml = editor.dom.getOuterHTML(footerElement); + hasFigcaptions = true; + } + } + + // If figure has figcaptions, we need to preserve them + if (hasFigcaptions) { + var figureAlignmentClasses = ''; + if (existingFigure) { + var figureClass = existingFigure.getAttribute('class') || ''; + if (figureClass.includes('position-left')) figureAlignmentClasses = 'position-left'; + else if (figureClass.includes('position-center')) figureAlignmentClasses = 'position-center'; + else if (figureClass.includes('position-right')) figureAlignmentClasses = 'position-right'; + else if (figureClass.includes('float-left')) figureAlignmentClasses = 'float-left'; + else if (figureClass.includes('float-right')) figureAlignmentClasses = 'float-right'; + } + + let currentImage = editor.dom.select('img', existingFigure)[0]; + if (currentImage) { + imageData.hspace = ''; + imageData.vspace = ''; + + write(function (css) { + return normalizeCss$1(editor, css); + }, imageData, currentImage); + syncSrcAttr(editor, currentImage); + + let currentStyle = currentImage.getAttribute('style') || 'position-center'; + currentStyle = currentStyle.replace(/float\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/display\s*:\s*(block|inline-block)\s*;?/gi, ''); + currentStyle = currentStyle.replace(/vertical-align\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/margin\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/margin-(left|right|top|bottom)\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.trim(); + + if (currentStyle) { + currentImage.setAttribute('style', currentStyle); + } else { + currentImage.removeAttribute('style'); + } + + // Rebuild figure with preserved figcaptions + let newFigureHtml = ''; + if (headerHtml) { + newFigureHtml += headerHtml; + } + newFigureHtml += editor.dom.getOuterHTML(currentImage); + if (footerHtml) { + newFigureHtml += footerHtml; + } + + let imageWidth = imageData.width; + let figureClasses = 'exe-figure'; + if (figureAlignmentClasses) { + figureClasses += ' ' + figureAlignmentClasses; + } + + let figureStyle = ''; + if (imageWidth && imageWidth > 0) { + figureStyle = 'width: ' + imageWidth + 'px;'; + } + + let newFigure = editor.dom.create('figure', { + 'class': figureClasses, + 'style': figureStyle.trim() + }, newFigureHtml); + + existingFigure.parentNode.replaceChild(newFigure, existingFigure); + + // Select the image inside the figure, not the figure itself + var newImage = editor.dom.select('img', newFigure)[0]; + if (newImage) { + editor.selection.select(newImage); + } else { + editor.selection.select(newFigure); + } + } + } else { + // No figcaptions, convert figure to plain image + var figureAlignmentClasses = ''; + if (existingFigure) { + var figureClass = existingFigure.getAttribute('class') || ''; + if (figureClass.includes('position-left')) figureAlignmentClasses = 'position-left'; + else if (figureClass.includes('position-center')) figureAlignmentClasses = 'position-center'; + else if (figureClass.includes('position-right')) figureAlignmentClasses = 'position-right'; + else if (figureClass.includes('float-left')) figureAlignmentClasses = 'float-left'; + else if (figureClass.includes('float-right')) figureAlignmentClasses = 'float-right'; + } + + let currentImage = editor.dom.select('img', existingFigure)[0]; + if (currentImage) { + imageData.hspace = ''; + imageData.vspace = ''; + + write(function (css) { + return normalizeCss$1(editor, css); + }, imageData, currentImage); + syncSrcAttr(editor, currentImage); + + let currentStyle = currentImage.getAttribute('style') || ''; + currentStyle = currentStyle.replace(/float\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/display\s*:\s*(block|inline-block)\s*;?/gi, ''); + currentStyle = currentStyle.replace(/vertical-align\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/margin\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.replace(/margin-(left|right|top|bottom)\s*:\s*[^;]+\s*;?/gi, ''); + currentStyle = currentStyle.trim(); + + let currentClass = currentImage.getAttribute('class') || ''; + currentClass = currentClass.replace(/\s*float-left\s*/g, ' ') + .replace(/\s*float-right\s*/g, ' ') + .replace(/\s*position-left\s*/g, ' ') + .replace(/\s*position-center\s*/g, ' ') + .replace(/\s*position-right\s*/g, ' ') + .trim(); + + if (figureAlignmentClasses) { + currentClass = (currentClass + ' ' + figureAlignmentClasses).trim(); + } + + if (advAlignStyle === 'baseline' || advAlignStyle === 'top' || advAlignStyle === 'middle' || + advAlignStyle === 'bottom' || advAlignStyle === 'text-top' || advAlignStyle === 'text-bottom') { + currentClass = currentClass.replace(/\s*float-left\s*/g, ' ') + .replace(/\s*float-right\s*/g, ' ') + .replace(/\s*position-left\s*/g, ' ') + .replace(/\s*position-center\s*/g, ' ') + .replace(/\s*position-right\s*/g, ' ') + .trim(); + + if (currentClass === '') { + currentImage.removeAttribute('class'); + } else { + currentImage.setAttribute('class', currentClass); + } + + if (currentStyle && !currentStyle.endsWith(';')) { + currentStyle += ';'; + } + currentStyle += ' vertical-align: ' + advAlignStyle + ';'; + currentStyle = currentStyle.trim(); + + currentImage.setAttribute('style', currentStyle); + } else { + if (currentClass === '') { + currentImage.removeAttribute('class'); + } else { + currentImage.setAttribute('class', currentClass); + } + + if (currentStyle) { + currentImage.setAttribute('style', currentStyle); + } else { + currentImage.removeAttribute('style'); + } + } + + existingFigure.parentNode.insertBefore(currentImage, existingFigure); + editor.dom.remove(existingFigure); + + editor.selection.select(currentImage); + } + } + + editor.editorUpload.uploadImagesAuto(); + api.close(); + return; } - if (htmlLicencia !== '') { - custom_div_html += ' (' + htmlLicencia + ')'; + else if (hasAttribution && !isEditingExistingFigure) { + var existingImage = getSelectedImage(editor); + var alignmentClasses = 'position-center'; // Default alignment + if (existingImage) { + var imgClass = existingImage.getAttribute('class') || ''; + if (imgClass.includes('position-left')) alignmentClasses = 'position-left'; + else if (imgClass.includes('position-center')) alignmentClasses = 'position-center'; + else if (imgClass.includes('position-right')) alignmentClasses = 'position-right'; + else if (imgClass.includes('float-left')) alignmentClasses = 'float-left'; + else if (imgClass.includes('float-right')) alignmentClasses = 'float-right'; + } + + editor.execCommand('mceUpdateImage', false, imageData); + editor.editorUpload.uploadImagesAuto(); + + setTimeout(function() { + let imgNode = editor.selection.getNode(); + if (imgNode.nodeName !== 'IMG') { + let src = imageData.src; + let imgs = editor.dom.select('img[src="' + src + '"]'); + if (imgs.length > 0) { + imgNode = imgs[imgs.length - 1]; + } + } + + if (imgNode && imgNode.nodeName === 'IMG') { + let imgStyle = imgNode.getAttribute('style') || ''; + imgStyle = imgStyle.replace(/float\s*:\s*[^;]+\s*;?/gi, ''); + imgStyle = imgStyle.replace(/display\s*:\s*(block|inline-block)\s*;?/gi, ''); + imgStyle = imgStyle.replace(/vertical-align\s*:\s*[^;]+\s*;?/gi, ''); + imgStyle = imgStyle.replace(/margin\s*:\s*[^;]+\s*;?/gi, ''); + imgStyle = imgStyle.replace(/margin-(left|right|top|bottom)\s*:\s*[^;]+\s*;?/gi, ''); + imgStyle = imgStyle.trim(); + + if (imgStyle) { + imgNode.setAttribute('style', imgStyle); + } else { + imgNode.removeAttribute('style'); + } + + let imgClass = imgNode.getAttribute('class') || ''; + imgClass = imgClass.replace(/\s*float-left\s*/g, ' ') + .replace(/\s*float-right\s*/g, ' ') + .replace(/\s*position-left\s*/g, ' ') + .replace(/\s*position-center\s*/g, ' ') + .replace(/\s*position-right\s*/g, ' ') + .trim(); + if (imgClass) { + imgNode.setAttribute('class', imgClass); + } else { + imgNode.removeAttribute('class'); + } + + let custom_div_html = ''; + + if (attrEncabezado !== '') { + custom_div_html = '
' + attrEncabezado + '
'; + } + + custom_div_html += imgNode.outerHTML; + + let htmlAutor = CustomHtml(attrAutor, attrLinkAutor, false, 'author'); + let htmlTitulo = CustomHtml(attrTitulo, attrLinkTitulo, true, 'title'); + let htmlLicencia = CustomHtml(attrLicense, attrLinkLicense, false, 'license'); + + if (htmlAutor !== '' || htmlTitulo !== '' || htmlLicencia !== '') { + custom_div_html += '
'; + if (htmlAutor !== '') { + custom_div_html += htmlAutor + ". "; + } + if (htmlTitulo) { + custom_div_html += htmlTitulo; + } + if (htmlLicencia !== '') { + custom_div_html += ' (' + htmlLicencia + ')'; + } + custom_div_html += '
'; + } + + let imageWidth = imageData.width; + + let figureClasses = 'exe-figure'; + if (alignmentClasses) { + figureClasses += ' ' + alignmentClasses; + } + + let figureStyle = ''; + if (imageWidth && imageWidth > 0) { + figureStyle = 'width: ' + imageWidth + 'px;'; + } + + let y = editor.dom.create('figure', { 'class': figureClasses, 'style': figureStyle.trim() }, custom_div_html); + + if (imgNode.parentNode) { + imgNode.parentNode.replaceChild(y, imgNode); + + // Only add paragraph when inserting NEW image/figure, not when editing existing + // isEditingExistingImage = true means we're converting img → figure (editing) + var isExemediaFigure = editor.dom.hasClass(y, 'exe-media'); + + // Check if there's any content after the figure (not just immediate sibling) + var hasContentAfter = false; + var nextNode = y.nextSibling; + while (nextNode) { + // Check if it's a meaningful element (not just whitespace or empty text nodes) + if (nextNode.nodeType === 1 || // Element node + (nextNode.nodeType === 3 && nextNode.nodeValue.trim() !== '')) { // Text node with content + hasContentAfter = true; + break; + } + nextNode = nextNode.nextSibling; + } + + if (!isExemediaFigure && !isEditingExistingImage && !hasContentAfter) { + let my_p = editor.dom.create('p', {}, '
'); + y.parentNode.appendChild(my_p); + editor.selection.setCursorLocation(my_p, 0); + } else { + editor.selection.select(y); + } + } + } + }, 100); + + api.close(); + return; } - custom_div_html += '
'; - } + else { + let figure_old = existingFigure; + let currentImage = null; + if (!figure_old) { + api.close(); + return; + } + currentImage = editor.dom.select('img', figure_old)[0]; + + if (!currentImage) { + api.close(); + return; + } + + if (!attibutionTab) { + let actHeader = editor.dom.select('.figcaption.header', figure_old)[0]; + let actHeaderHTML = ''; + if (actHeader != null) { + actHeaderHTML = actHeader.innerHTML; + actHeaderHTML = actHeaderHTML.replace('', ''); + actHeaderHTML = actHeaderHTML.replace('', ''); + } + let actTitle = editor.dom.select('.title', figure_old)[0]; + let actTitleHTML = ''; + let actLinkTitleHTML = ''; + if (actTitle != null) { + actTitleHTML = actTitle.innerHTML; + actTitleHTML = actTitleHTML.replace('', ''); + actTitleHTML = actTitleHTML.replace('', ''); + actLinkTitleHTML = editor.dom.getAttrib(actTitle, 'href'); + } + let actAuthor = editor.dom.select('.author', figure_old)[0]; + let actAuthorHTML = ''; + let actLinkAuthorHTML = ''; + if (actAuthor != null) { + actAuthorHTML = actAuthor.innerHTML; + actLinkAuthorHTML = editor.dom.getAttrib(actAuthor, 'href'); + } + let actLicense = editor.dom.select('.custom-license, .license span', figure_old)[0]; + let actLicenseHTML = ''; + let actLinkLicenseHTML = ''; + if (actLicense != null) { + actLicenseHTML = actLicense.innerHTML || actLicense.textContent; + actLinkLicenseHTML = editor.dom.getAttrib(actLicense, 'href') || ''; + } - let imageWidth = api.getData().dimensions.width; - style_for_div += `width: ${imageWidth}px;`; + attrEncabezado = actHeaderHTML; + attrTitulo = actTitleHTML; + attrLinkTitulo = actLinkTitleHTML; + attrAutor = actAuthorHTML; + attrLinkAutor = actLinkAuthorHTML; + attrLicense = actLicenseHTML; + attrLinkLicense = actLinkLicenseHTML; + } + + imageData.hspace = ''; + imageData.vspace = ''; + + write(function (css) { + return normalizeCss$1(editor, css); + }, imageData, currentImage); + syncSrcAttr(editor, currentImage); + + let imgStyle = currentImage.getAttribute('style') || ''; + imgStyle = imgStyle.replace(/float\s*:\s*[^;]+\s*;?/gi, ''); + imgStyle = imgStyle.replace(/display\s*:\s*(block|inline-block)\s*;?/gi, ''); + imgStyle = imgStyle.replace(/vertical-align\s*:\s*[^;]+\s*;?/gi, ''); + imgStyle = imgStyle.replace(/margin\s*:\s*[^;]+\s*;?/gi, ''); + imgStyle = imgStyle.replace(/margin-(left|right|top|bottom)\s*:\s*[^;]+\s*;?/gi, ''); + + imgStyle = imgStyle.trim(); + if (imgStyle) { + currentImage.setAttribute('style', imgStyle); + editor.dom.setAttrib(currentImage, 'style', imgStyle); + } else { + currentImage.removeAttribute('style'); + } + + let imagen_html = editor.dom.getOuterHTML(currentImage); + + let custom_div_html = ''; - let y = editor.dom.create('figure', { 'id': 'figure_' + idSelectedImage, 'class': `exe-figure ${actualAlign}`, 'style': style_for_div }, custom_div_html); + if (attrEncabezado !== '') { + custom_div_html = '
' + attrEncabezado + '
' + imagen_html; + } else { + custom_div_html = imagen_html; + } - figure_old.parentNode.replaceChild(y, figure_old); + let htmlAutor = CustomHtml(attrAutor, attrLinkAutor, false, 'author'); + let htmlTitulo = CustomHtml(attrTitulo, attrLinkTitulo, true, 'title'); + let htmlLicencia = CustomHtml(attrLicense, attrLinkLicense, false, 'license'); + if (htmlAutor !== '' || htmlTitulo !== '' || htmlLicencia !== '') { + custom_div_html += '
'; + if (htmlAutor !== '') { + custom_div_html += htmlAutor + ". "; + } + if (htmlTitulo) { + custom_div_html += htmlTitulo; + } + if (htmlLicencia !== '') { + custom_div_html += ' (' + htmlLicencia + ')'; + } + custom_div_html += '
'; + } + let imageWidth = api.getData().dimensions.width; + + // Check if figure_old is an exemedia figure (has exe-media class) + var isExemediaFigure = figure_old && editor.dom.hasClass(figure_old, 'exe-media'); + + let figureClasses = 'exe-figure'; + + // Only apply position-* classes to non-exemedia figures + if (!isExemediaFigure) { + if (advAlignStyle === 'left') { + figureClasses += ' float-left'; + } else if (advAlignStyle === 'center') { + figureClasses += ' position-center'; + } else if (advAlignStyle === 'right') { + figureClasses += ' float-right'; + } + else if (!advAlignStyle) { + let oldFigureClass = figure_old.getAttribute('class') || ''; + if (oldFigureClass.includes('position-left')) figureClasses += ' position-left'; + else if (oldFigureClass.includes('position-center')) figureClasses += ' position-center'; + else if (oldFigureClass.includes('position-right')) figureClasses += ' position-right'; + else if (oldFigureClass.includes('float-left')) figureClasses += ' float-left'; + else if (oldFigureClass.includes('float-right')) figureClasses += ' float-right'; + } + } + + let figureStyle = ''; + if (imageWidth && imageWidth > 0) { + figureStyle = `width: ${imageWidth}px;`; + } + + let newFigure = editor.dom.create('figure', { + 'class': figureClasses, + 'style': figureStyle.trim() + }, custom_div_html); + + figure_old.parentNode.replaceChild(newFigure, figure_old); + + // Only add paragraph when inserting NEW figure, not when editing existing + // isEditingExistingFigure = true means we're modifying an existing figure (editing) + var isExemediaFigure = editor.dom.hasClass(newFigure, 'exe-media'); + + // Check if there's any content after the figure (not just immediate sibling) + var hasContentAfter = false; + var nextNode = newFigure.nextSibling; + while (nextNode) { + // Check if it's a meaningful element (not just whitespace or empty text nodes) + if (nextNode.nodeType === 1 || // Element node + (nextNode.nodeType === 3 && nextNode.nodeValue.trim() !== '')) { // Text node with content + hasContentAfter = true; + break; + } + nextNode = nextNode.nextSibling; + } + + if (!isExemediaFigure && !isEditingExistingFigure && !hasContentAfter) { + let my_p = editor.dom.create('p', {}, '
'); + newFigure.parentNode.appendChild(my_p); + } + + // Select the image inside the figure, not the figure itself + var newImage = editor.dom.select('img', newFigure)[0]; + if (newImage) { + editor.selection.select(newImage); + } else { + editor.selection.select(newFigure); + } + } + + api.close(); } - api.close(); - } - - function CustomHtml(value, linkValue, emBool, tipo) { - - let customHtml - if (value !== '' && linkValue !== '') { - value = emBool ? '' + value + '' : value; - customHtml = '' + value + '' - } else if (value === '' && linkValue !== '') { - value = emBool ? '' + linkValue + '' : linkValue; - customHtml = '' + value + '' - } else if (value !== '' && linkValue === '') { - value = emBool ? '' + value + '' : value; - customHtml = '' + value + '' - } else { - customHtml = ''; + function CustomHtml(value, linkValue, emBool, tipo) { + let customHtml = ''; + + let classType = ''; + if (tipo === 'author' || tipo.includes('author')) { + classType = 'author'; + } else if (tipo === 'title' || tipo.includes('title')) { + classType = 'title'; + } else if (tipo === 'license' || tipo.includes('license')) { + classType = (linkValue === '' && value !== '') ? 'custom-license' : 'license'; + } + + if (value !== '' && linkValue !== '') { + value = emBool ? '' + value + '' : value; + customHtml = '' + value + ''; + } else if (value === '' && linkValue !== '') { + value = emBool ? '' + linkValue + '' : linkValue; + customHtml = '' + value + ''; + } else if (value !== '' && linkValue === '') { + value = emBool ? '' + value + '' : value; + customHtml = '' + value + ''; + } else { + customHtml = ''; + } + + return customHtml; } - return customHtml; - } + var imageSize = function (editor) { + return function (url) { + if (!isSafeImageUrl(editor, url)) { + return global$4.resolve({ + width: '', + height: '' + }); + } else { + return getImageSize(editor.documentBaseURI.toAbsolute(url)).then(function (dimensions) { + return { + width: String(dimensions.width), + height: String(dimensions.height) + }; + }); + } + }; + }; + var createBlobCache = function (editor) { + return function (file, blobUri, dataUrl) { + return editor.editorUpload.blobCache.create({ + blob: file, + blobUri: blobUri, + name: file.name ? file.name.replace(/\.[^\.]+$/, '') : null, + filename: file.name, + base64: dataUrl.split(',')[1] + }); + }; + }; + var addToBlobCache = function (editor) { + return function (blobInfo) { + editor.editorUpload.blobCache.add(blobInfo); + }; + }; + var alertErr = function (editor) { + return function (message) { + editor.windowManager.alert(message); + }; + }; + var normalizeCss = function (editor) { + return function (cssText) { + return normalizeCss$1(editor, cssText); + }; + }; + var parseStyle = function (editor) { + return function (cssText) { + return editor.dom.parseStyle(cssText); + }; + }; + var serializeStyle = function (editor) { + return function (stylesArg, name) { + return editor.dom.serializeStyle(stylesArg, name); + }; + }; + var uploadImage = function (editor) { + return function (blobInfo) { + return global$1(editor).upload([blobInfo], false).then(function (results) { + if (results.length === 0) { + return global$4.reject('Failed to upload image'); + } else if (results[0].status === false) { + return global$4.reject(results[0].error.message); + } else { + return results[0]; + } + }); + }; + }; + var Dialog = function (editor) { + var helpers = { + onSubmit: submitHandler(editor), + imageSize: imageSize(editor), + addToBlobCache: addToBlobCache(editor), + createBlobCache: createBlobCache(editor), + alertErr: alertErr(editor), + normalizeCss: normalizeCss(editor), + parseStyle: parseStyle(editor), + serializeStyle: serializeStyle(editor), + uploadImage: uploadImage(editor) + }; + var open = function () { + collect(editor).then(makeDialog(helpers)).then(editor.windowManager.open); + }; + return { open: open }; + }; + + var register$1 = function (editor) { + editor.addCommand('mceImage', Dialog(editor).open); + // editor.addCommand('mceImage', doit(editor)); + editor.addCommand('mceUpdateImage', function (_ui, data) { + editor.undoManager.transact(function () { + return insertOrUpdateImage(editor, data); + }); + }); + + editor.getSelectedImageData = function() { + return readImageDataFromSelection(editor); + }; + + editor.getSelectedImageElement = function() { + return getSelectedImage(editor); + }; + + editor.selectImageFigure = function(imageElement) { + var figureElement = editor.dom.getParent(imageElement, 'figure.exe-figure'); + if (figureElement) { + editor.selection.select(figureElement); + return figureElement; + } + return null; + }; + }; + + var hasImageClass = function (node) { + var className = node.attr('class'); + return className && /\bimage\b/.test(className); + }; + var toggleContentEditableState = function (state) { + return function (nodes) { + var i = nodes.length; + var toggleContentEditable = function (node) { + node.attr('contenteditable', state ? 'true' : null); + }; + while (i--) { + var node = nodes[i]; + if (hasImageClass(node)) { + node.attr('contenteditable', state ? 'false' : null); + global.each(node.getAll('figcaption'), toggleContentEditable); + } + } + }; + }; + var setup = function (editor) { + editor.on('PreInit', function () { + editor.parser.addNodeFilter('figure', toggleContentEditableState(true)); + editor.serializer.addNodeFilter('figure', toggleContentEditableState(false)); + // load css + var loc = window.location.pathname; + var dir = loc.substring(0, loc.lastIndexOf('/')); + editor.dom.loadCSS(dir + "/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css"); + }); + + // Handle image resize - update figure width + editor.on('ObjectResized', function(e) { + var target = e.target; + if (target && target.nodeName === 'IMG') { + var figure = editor.dom.getParent(target, 'figure.exe-figure'); + if (figure && !editor.dom.hasClass(figure, 'exe-media')) { + // Only handle exe-figure images, not exe-media + var newWidth = e.width; + if (newWidth && newWidth > 0) { + editor.dom.setStyle(figure, 'width', newWidth + 'px'); + } + } + } + }); + + editor.on('keydown', function(e) { + // Handle Enter key in figcaption + if (e.keyCode === 13) { + var node = editor.selection.getNode(); + var figcaption = editor.dom.getParent(node, 'figcaption.figcaption'); + + if (figcaption) { + var parentFigure = editor.dom.getParent(figcaption, 'figure.exe-figure'); + + if (parentFigure && !editor.dom.hasClass(parentFigure, 'exe-media')) { + var figureStyle = parentFigure.getAttribute('style') || ''; + var hasFloat = figureStyle.indexOf('float:') !== -1; + + if (hasFloat) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + var nextSibling = parentFigure.nextSibling; + + while (nextSibling && nextSibling.nodeType === 3 && nextSibling.nodeValue.trim() === '') { + nextSibling = nextSibling.nextSibling; + } + + if (nextSibling) { + editor.selection.setCursorLocation(nextSibling, 0); + } else { + var newP = editor.dom.create('p', {}, '
'); + parentFigure.parentNode.appendChild(newP); + editor.selection.setCursorLocation(newP, 0); + } + + return false; + } + } + } + + // Handle Delete and Backspace keys + if (e.keyCode === 46 || e.keyCode === 8) { + var node = editor.selection.getNode(); + var selection = editor.selection; + + // Check if we're editing inside a header or figcaption + var inEditableCaption = editor.dom.getParent(node, '.figcaption.header, figcaption.figcaption'); + if (inEditableCaption) { + // Allow normal text editing in captions + return; + } + + if (node.nodeName === 'FIGURE' && editor.dom.hasClass(node, 'exe-figure')) { + // Skip if it's a media figure (exemedia plugin handles those) + if (editor.dom.hasClass(node, 'exe-media')) { + return; + } + var hasImg = node.querySelector('img'); + if (hasImg) { + e.preventDefault(); + e.stopPropagation(); + + editor.undoManager.transact(function() { + var nextSibling = node.nextSibling; + var parent = node.parentNode; + editor.dom.remove(node); + + if (nextSibling) { + editor.selection.setCursorLocation(nextSibling, 0); + } else if (parent) { + editor.selection.setCursorLocation(parent, parent.childNodes.length); + } + editor.nodeChanged(); + }); + return false; + } + } + + if (node.nodeName === 'IMG' && !selection.isCollapsed()) { + var parentFigure = editor.dom.getParent(node, 'figure.exe-figure'); + if (parentFigure) { + // Skip if it's a media figure (exemedia plugin handles those) + if (editor.dom.hasClass(parentFigure, 'exe-media')) { + return; + } + if (parentFigure.querySelector('img')) { + e.preventDefault(); + e.stopPropagation(); + + editor.undoManager.transact(function() { + var nextSibling = parentFigure.nextSibling; + var parent = parentFigure.parentNode; + editor.dom.remove(parentFigure); + + if (nextSibling) { + editor.selection.setCursorLocation(nextSibling, 0); + } else if (parent) { + editor.selection.setCursorLocation(parent, parent.childNodes.length); + } + editor.nodeChanged(); + }); + return false; + } + } + } + } + }); + }; - var imageSize = function (editor) { - return function (url) { - if (!isSafeImageUrl(editor, url)) { - return global$4.resolve({ - width: '', - height: '' + var register = function (editor) { + editor.ui.registry.addIcon('exeimage', ''); + editor.ui.registry.addToggleButton('exeimage', { + icon: 'image', + tooltip: 'Insert/edit image', + onAction: function () { + let $ = tinymce.dom.DomQuery; + lang = $('html').attr('lang'); + attibutionTab = false; + doit(editor); + }, + onSetup: function (buttonApi) { + var editorEventCallback = function (eventApi) { + try{ + const el = eventApi.element; + + // Don't auto-select figure when clicking image - allow image resizing + // Only handle button state + + if (el.nodeName == 'FIGURE') { + if (el.classList.contains("exe-figure") && !el.classList.contains("exe-media")) { + // Only select figure if no image is selected + const selectedImg = getSelectedImage(editor); + if (!selectedImg) { + editor.selection.select(el); + } + } + } + + const hasImageOrFigure = isNonNullable(getSelectedImage(editor)) || + (el.nodeName === 'FIGURE' && el.classList.contains('exe-figure') && !el.classList.contains('exe-media') && !el.querySelector('video, audio, iframe, embed, img[data-mce-object="embed"], img[data-mce-object="video"], img[data-mce-object="audio"]')); + buttonApi.setActive(hasImageOrFigure); + + }catch(e){ + } + }; + + var nodeChangeCallback = function () { + const selectedNode = editor.selection.getNode(); + const hasImageOrFigure = isNonNullable(getSelectedImage(editor)) || + (selectedNode.nodeName === 'FIGURE' && selectedNode.classList.contains('exe-figure') && !selectedNode.classList.contains('exe-media') && !selectedNode.querySelector('video, audio, iframe, embed, img[data-mce-object="embed"], img[data-mce-object="video"], img[data-mce-object="audio"]')); + buttonApi.setActive(hasImageOrFigure); + }; + + editor.on('NodeChange', editorEventCallback); + editor.on('NodeChange', nodeChangeCallback); + + const initialNode = editor.selection.getNode(); + const hasInitialImageOrFigure = isNonNullable(getSelectedImage(editor)) || + (initialNode.nodeName === 'FIGURE' && initialNode.classList.contains('exe-figure') && !initialNode.querySelector('video, audio, iframe, embed')); + buttonApi.setActive(hasInitialImageOrFigure); + + return function() { + editor.off('NodeChange', editorEventCallback); + editor.off('NodeChange', nodeChangeCallback); + }; + } }); - } else { - return getImageSize(editor.documentBaseURI.toAbsolute(url)).then(function (dimensions) { - return { - width: String(dimensions.width), - height: String(dimensions.height) - }; + editor.ui.registry.addMenuItem('exeimage', { + icon: 'image', + text: 'Image...', + onAction: function () { + let $ = tinymce.dom.DomQuery; + lang = $('html').attr('lang'); + attibutionTab = false; + // Dialog(editor).open(); + doit(editor); + } }); - } - }; - }; - var createBlobCache = function (editor) { - return function (file, blobUri, dataUrl) { - return editor.editorUpload.blobCache.create({ - blob: file, - blobUri: blobUri, - name: file.name ? file.name.replace(/\.[^\.]+$/, '') : null, - filename: file.name, - base64: dataUrl.split(',')[1] - }); - }; - }; - var addToBlobCache = function (editor) { - return function (blobInfo) { - editor.editorUpload.blobCache.add(blobInfo); - }; - }; - var alertErr = function (editor) { - return function (message) { - editor.windowManager.alert(message); - }; - }; - var normalizeCss = function (editor) { - return function (cssText) { - return normalizeCss$1(editor, cssText); - }; - }; - var parseStyle = function (editor) { - return function (cssText) { - return editor.dom.parseStyle(cssText); - }; - }; - var serializeStyle = function (editor) { - return function (stylesArg, name) { - return editor.dom.serializeStyle(stylesArg, name); - }; - }; - var uploadImage = function (editor) { - return function (blobInfo) { - return global$1(editor).upload([blobInfo], false).then(function (results) { - if (results.length === 0) { - return global$4.reject('Failed to upload image'); - } else if (results[0].status === false) { - return global$4.reject(results[0].error.message); - } else { - return results[0]; - } - }); - }; - }; - var Dialog = function (editor) { - var helpers = { - onSubmit: submitHandler(editor), - imageSize: imageSize(editor), - addToBlobCache: addToBlobCache(editor), - createBlobCache: createBlobCache(editor), - alertErr: alertErr(editor), - normalizeCss: normalizeCss(editor), - parseStyle: parseStyle(editor), - serializeStyle: serializeStyle(editor), - uploadImage: uploadImage(editor) - }; - var open = function () { - collect(editor).then(makeDialog(helpers)).then(editor.windowManager.open); - }; - return { open: open }; - }; - - var register$1 = function (editor) { - editor.addCommand('mceImage', Dialog(editor).open); - // editor.addCommand('mceImage', doit(editor)); - editor.addCommand('mceUpdateImage', function (_ui, data) { - editor.undoManager.transact(function () { - return insertOrUpdateImage(editor, data); - }); - }); - }; - - var hasImageClass = function (node) { - var className = node.attr('class'); - return className && /\bimage\b/.test(className); - }; - var toggleContentEditableState = function (state) { - return function (nodes) { - var i = nodes.length; - var toggleContentEditable = function (node) { - node.attr('contenteditable', state ? 'true' : null); - }; - while (i--) { - var node = nodes[i]; - if (hasImageClass(node)) { - node.attr('contenteditable', state ? 'false' : null); - global.each(node.getAll('figcaption'), toggleContentEditable); - } - } - }; - }; - var setup = function (editor) { - editor.on('PreInit', function () { - editor.parser.addNodeFilter('figure', toggleContentEditableState(true)); - editor.serializer.addNodeFilter('figure', toggleContentEditableState(false)); - // load css - var loc = window.location.pathname; - var dir = loc.substring(0, loc.lastIndexOf('/')); - editor.dom.loadCSS(dir + "/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css"); - }); - }; - - var register = function (editor) { - editor.ui.registry.addIcon('exeimage', ''); - editor.ui.registry.addToggleButton('exeimage', { - icon: 'image', - tooltip: 'Insert/edit image', - onAction: function () { - let $ = tinymce.dom.DomQuery; - lang = $('html').attr('lang'); - attibutionTab = false; - doit(editor); - }, - onSetup: function (buttonApi) { - var editorEventCallback = function (eventApi) { - try{ - const el = eventApi.element; - if (el.nodeName == 'FIGURE') { - if (el.classList.contains("exe-figure")) { - editor.selection.select($("img",el)[0]); - } - } - }catch(e){} - }; - editor.on('NodeChange', editorEventCallback); - buttonApi.setActive(isNonNullable(getSelectedImage(editor))); - return editor.selection.selectorChangedWithUnbind('img:not([data-mce-object],[data-mce-placeholder]),figure.image', buttonApi.setActive).unbind; - } - }); - editor.ui.registry.addMenuItem('exeimage', { - icon: 'image', - text: 'Image...', - onAction: function () { - let $ = tinymce.dom.DomQuery; - lang = $('html').attr('lang'); - attibutionTab = false; - // Dialog(editor).open(); - doit(editor); - } - }); - editor.ui.registry.addContextMenu('exeimage', { - update: function (element) { - return isFigure(element) || isImage(element) && !isPlaceholderImage(element) ? ['image'] : []; - } - }); - }; - - function Plugin() { - global$6.add('exeimage', function (editor, url) { - setup(editor, url); - register(editor); - register$1(editor); - }); - } - - Plugin(); + editor.ui.registry.addContextMenu('exeimage', { + update: function (element) { + return isFigure(element) || isImage(element) && !isPlaceholderImage(element) ? ['image'] : []; + } + }); + }; + + function Plugin() { + global$6.add('exeimage', function (editor, url) { + setup(editor, url); + register(editor); + register$1(editor); + }); + } + + Plugin(); }()); diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css b/public/libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css index 91a50939c..8ec35d33a 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css +++ b/public/libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css @@ -1,10 +1,9 @@ /* The New eXeLearning */ .exe-figure{margin:1.5em 0} -.position-center{margin:1.5em auto; text-align: center;} -.position-right{margin:1.5em 0 1.5em auto;} -.float-left{float:left;margin:.5em 1.5em 1em 0;} -.float-right{float:right;margin:.5em 0 1em 1.5em;} -.audio-position{display: table;} -.figcaption{padding-top:.2em; text-align: center;} -.figcaption.header{padding-top:0;padding-bottom:.2em; text-align: left;} +.position-center{margin:1.5em auto} +.position-right{margin:1.5em 0 1.5em auto} +.float-left{float:left;margin:.5em 1.5em 1em 0} +.float-right{float:right;margin:.5em 0 1em 1.5em} +.figcaption{padding-top:.2em;line-height:1em} +.figcaption.header{padding-top:0;padding-bottom:.2em} /* / The New eXeLearning */ \ No newline at end of file diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exemedia/plugin.min.js b/public/libs/tinymce_5/js/tinymce/plugins/exemedia/plugin.min.js index 0998c49bb..a5dc4ce15 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/exemedia/plugin.min.js +++ b/public/libs/tinymce_5/js/tinymce/plugins/exemedia/plugin.min.js @@ -7,10 +7,15 @@ * Version: 5.10.3 (2022-02-09) */ +/** + * "Title and Attribution" tab by Manuel Narváez (https://github.com/mnarvaezm) + */ + (function () { 'use strict'; // global values + let bm = ''; // bookmark to preserve selection let actObject = ''; let actualMedia = ''; let actualContainer = ''; @@ -267,14 +272,37 @@ function GetActualData(editor) { + // Restore bookmark to get back to the media element + if (bm) { + // To review editor.selection.moveToBookmark(bm); + } + actualMedia = editor.selection.getNode(); - actualContainer = actualMedia.parentNode; + + // Only handle figures with exe-media class (exemedia plugin's figures) + actualContainer = editor.dom.getParent(actualMedia, 'figure.exe-media'); + if (!actualContainer) { + // Also check for figure.exe-figure.exe-media (both classes) + var tempContainer = editor.dom.getParent(actualMedia, 'figure.exe-figure'); + if (tempContainer && editor.dom.hasClass(tempContainer, 'exe-media')) { + actualContainer = tempContainer; + } + } + + // If we're in a FIGURE with exe-media, find the actual media element inside + if (actualContainer && editor.dom.hasClass(actualContainer, 'exe-media')) { + // Look for the media element inside the figure (including IMG placeholders) + var mediaInFigure = actualContainer.querySelector('video, audio, iframe, embed, span[data-mce-object], img[data-mce-object]'); + if (mediaInFigure) { + actualMedia = mediaInFigure; + } + } - if (actualMedia.getAttribute("data-mce-object") !== null) { + if (actualMedia.getAttribute && actualMedia.getAttribute("data-mce-object") !== null) { generalType = actualMedia.getAttribute("data-mce-object"); } - if (actualContainer != null&&actualContainer.nodeName=="FIGURE") { + if (actualContainer != null && actualContainer.nodeName == "FIGURE" && editor.dom.hasClass(actualContainer, 'exe-media')) { // mediaHeader let actHeader = jQuery(".header",actualContainer); @@ -352,19 +380,6 @@ } } - - // Alignment - actualFigureClass = ''; - if (actualContainer.className.indexOf("exe-figure exe-media")==0) { - actualFigureClass = actualContainer.className; - actualFigureClass = actualFigureClass.split(" "); - // Remove any license CSS class (it'll be added when saving) - let tmpFigureClass = ""; - for (let x=0;x' + (data.altsource ? '\n\n' : '') + ''; + // Set default dimensions for audio if not provided + var audioWidth = data.width || '300'; + var audioHeight = data.height || '40'; + return ''; } }; var getVideoHtml = function (data, videoTemplateCallback) { @@ -1156,8 +1224,107 @@ } var audioTemplateCallback = getAudioTemplateCallback(editor); var videoTemplateCallback = getVideoTemplateCallback(editor); - data.width = data.width || '300'; - data.height = data.height || '150'; + + // Detect if we're inside a column + var isInColumn = false; + var maxColumnWidth = 0; + var currentNode = editor.selection.getNode(); + var parentEl = currentNode; + + while (parentEl && parentEl !== editor.getBody()) { + if (parentEl.classList && ( + parentEl.classList.contains('exe-col') || + parentEl.classList.contains('exe-col-1') || + parentEl.classList.contains('exe-col-2') || + parentEl.classList.contains('exe-col-3') + )) { + isInColumn = true; + var bodyWidth = editor.getBody().clientWidth || 800; + var layoutParent = parentEl.parentNode; + + if (layoutParent && layoutParent.classList) { + if (layoutParent.classList.contains('exe-layout-3-cols')) { + maxColumnWidth = Math.floor(bodyWidth * 0.32); + } else if (layoutParent.classList.contains('exe-layout-2-70-30')) { + maxColumnWidth = parentEl.classList.contains('exe-col-1') ? + Math.floor(bodyWidth * 0.69) : Math.floor(bodyWidth * 0.29); + } else if (layoutParent.classList.contains('exe-layout-2-30-70')) { + maxColumnWidth = parentEl.classList.contains('exe-col-1') ? + Math.floor(bodyWidth * 0.29) : Math.floor(bodyWidth * 0.69); + } else { + maxColumnWidth = Math.floor(bodyWidth * 0.49); + } + } else { + maxColumnWidth = Math.floor(bodyWidth * 0.49); + } + break; + } + parentEl = parentEl.parentNode; + } + + // Set default dimensions based on media type + // Check if it's audio by sourcemime or type + var isAudio = (data.sourcemime && data.sourcemime.indexOf('audio') !== -1) || + (data.source && (data.source.endsWith('.mp3') || data.source.endsWith('.wav') || + data.source.endsWith('.ogg') || data.source.endsWith('.m4a'))); + + // Check if it's embed (PDF) by sourcemime or type + var isEmbed = (data.sourcemime && data.sourcemime === 'application/pdf') || + (data.source && data.source.endsWith('.pdf')); + + // Check if it's iframe (YouTube, Vimeo, etc.) + var isIframe = data.type === 'iframe' || (data.source && ( + data.source.indexOf('youtube.com') !== -1 || + data.source.indexOf('youtu.be') !== -1 || + data.source.indexOf('vimeo.com') !== -1 || + data.source.indexOf('dailymotion.com') !== -1 + )); + + // Check if it's video + var isVideo = (data.sourcemime && data.sourcemime.indexOf('video') !== -1) || + (data.source && (data.source.endsWith('.mp4') || data.source.endsWith('.webm') || + data.source.endsWith('.ogv') || data.source.endsWith('.m4v'))); + + if (isEmbed) { + // For embed (PDF), adjust width based on column constraints + var embedWidth = 600; + var embedHeight = 300; + + if (isInColumn && maxColumnWidth > 0 && maxColumnWidth < embedWidth) { + embedWidth = maxColumnWidth - 20; + embedHeight = Math.round(embedWidth / 2); + } + + data.width = data.width || String(embedWidth); + data.height = data.height || String(embedHeight); + } else if (isIframe || isVideo) { + // For iframe (YouTube, Vimeo) and video, use 16:9 aspect ratio + var defaultWidth = 560; + var defaultHeight = 315; + + if (isInColumn && maxColumnWidth > 0 && maxColumnWidth < defaultWidth) { + defaultWidth = maxColumnWidth - 20; + defaultHeight = Math.round(defaultWidth * 9 / 16); + } + + data.width = data.width || String(defaultWidth); + data.height = data.height || String(defaultHeight); + } else if (isAudio) { + data.width = data.width || '300'; + data.height = data.height || '40'; + } else { + // Fallback for other media types + var fallbackWidth = 300; + var fallbackHeight = 150; + + if (isInColumn && maxColumnWidth > 0 && maxColumnWidth < fallbackWidth) { + fallbackWidth = maxColumnWidth - 20; + fallbackHeight = Math.round(fallbackWidth / 2); + } + + data.width = data.width || String(fallbackWidth); + data.height = data.height || String(fallbackHeight); + } global$8.each(data, function (value, key) { data[key] = editor.dom.encode('' + value); }); @@ -1178,6 +1345,10 @@ var isMediaElement = function (element) { return element.hasAttribute('data-mce-object') || element.hasAttribute('data-ephox-embed-iri'); }; + + // Store previous alignment state for figures + var previousFigureStates = {}; + var setup$2 = function (editor) { editor.on('click keyup touchend', function () { var selectedNode = editor.selection.getNode(); @@ -1187,6 +1358,93 @@ } } }); + + // Handle media resize - update figure width + editor.on('ObjectResized', function(e) { + var target = e.target; + + // Check if it's a SPAN with data-mce-object (TinyMCE placeholder) + if (target && target.nodeName === 'SPAN' && target.getAttribute('data-mce-object')) { + var figure = editor.dom.getParent(target, 'figure.exe-media'); + + if (figure) { + var newWidth = e.width; + var newHeight = e.height; + if (newWidth && newWidth > 0) { + // Update figure width + editor.dom.setStyle(figure, 'width', newWidth + 'px'); + + // Update data-mce-p-width and data-mce-p-height attributes + target.setAttribute('data-mce-p-width', newWidth); + if (newHeight && newHeight > 0) { + target.setAttribute('data-mce-p-height', newHeight); + } + + // Find and update the real media element inside the placeholder + var mediaElement = target.querySelector('video, iframe, audio, embed'); + if (mediaElement) { + mediaElement.setAttribute('width', newWidth); + if (newHeight && newHeight > 0) { + mediaElement.setAttribute('height', newHeight); + } + } + } + } + } + // Also handle direct VIDEO, AUDIO, IFRAME, EMBED elements + else if (target && (target.nodeName === 'VIDEO' || target.nodeName === 'AUDIO' || + target.nodeName === 'IFRAME' || target.nodeName === 'EMBED')) { + var figure = editor.dom.getParent(target, 'figure.exe-media'); + if (figure) { + var newWidth = e.width; + if (newWidth && newWidth > 0) { + editor.dom.setStyle(figure, 'width', newWidth + 'px'); + } + } + } + }); + + editor.on('NodeChange', function (e) { + var el = e.element; + + var isMediaContainer = el.nodeName === 'SPAN' && el.getAttribute('data-mce-object'); + + var isMediaElement = el.nodeName === 'VIDEO' || el.nodeName === 'AUDIO' || + el.nodeName === 'IFRAME' || el.nodeName === 'EMBED'; + + if (isMediaContainer || isMediaElement) { + // Only select figures with exe-media class (exemedia's figures) + var parentFigure = editor.dom.getParent(el, 'figure.exe-media'); + + if (parentFigure) { + // Check if alignment styles have changed + var figureId = parentFigure.getAttribute('id') || ('fig_' + Math.random().toString(36).substr(2, 9)); + if (!parentFigure.getAttribute('id')) { + parentFigure.setAttribute('id', figureId); + } + + var currentStyle = parentFigure.getAttribute('style') || ''; + var previousStyle = previousFigureStates[figureId] || ''; + + if (currentStyle !== previousStyle) { + + // Mark editor as dirty to ensure save + editor.setDirty(true); + + // Update the stored state + previousFigureStates[figureId] = currentStyle; + } + + // Don't auto-select figure - allow media resizing + // var currentSelection = editor.selection.getNode(); + // if (currentSelection !== parentFigure) { + // editor.selection.select(parentFigure); + // } + return; + } + } + }); + editor.on('ObjectSelected', function (e) { var objectType = e.target.getAttribute('data-mce-object'); if (objectType === 'script') { @@ -1206,6 +1464,148 @@ } } }); + + editor.on('keydown', function(e) { + // Handle Enter key in figcaption to create paragraph after figure + if (e.keyCode === 13) { // Enter key + var node = editor.selection.getNode(); + + // Check if we're in the footer figcaption (not header) + var figcaption = editor.dom.getParent(node, 'figcaption.figcaption'); + if (figcaption) { + // Verify it's inside an exe-media figure + var parentFigure = editor.dom.getParent(figcaption, 'figure.exe-media', editor.getBody()); + if (parentFigure) { + // Check if figure has float class + var figureStyle = parentFigure.getAttribute('style') || ''; + var hasFloat = figureStyle.indexOf('float: left') !== -1 || + figureStyle.indexOf('float: right') !== -1 || + figureStyle.indexOf('float:left') !== -1 || + figureStyle.indexOf('float:right') !== -1; + + // If floating, do nothing (allow default behavior) + if (hasFloat) { + return; + } + + // Only handle if NOT floating + e.preventDefault(); + e.stopPropagation(); + + // Check if there's any content after the figure + var nextSibling = parentFigure.nextSibling; + + // Skip empty text nodes (whitespace) + while (nextSibling && nextSibling.nodeType === 3 && nextSibling.nodeValue.trim() === '') { + nextSibling = nextSibling.nextSibling; + } + + // Check if there's any meaningful content where we can write + var hasContentAfter = nextSibling !== null; + + if (hasContentAfter) { + // There's something after, move cursor there + editor.selection.setCursorLocation(nextSibling, 0); + } else { + // Nothing after, create a new paragraph + var newP = editor.dom.create('p', {}, '
'); + parentFigure.parentNode.appendChild(newP); + editor.selection.setCursorLocation(newP, 0); + } + + editor.nodeChanged(); + return false; + } + } + } + + if (e.keyCode === 46 || e.keyCode === 8) { + var node = editor.selection.getNode(); + var selection = editor.selection; + + // Check if we're editing inside a header or figcaption + var inEditableCaption = editor.dom.getParent(node, '.figcaption.header, figcaption.figcaption'); + if (inEditableCaption) { + // Allow normal text editing in captions + return; + } + + // Caso 1: El FIGURE está seleccionado directamente + // Only handle figures with exe-media class (exemedia plugin's figures) + if (node.nodeName === 'FIGURE' && editor.dom.hasClass(node, 'exe-media')) { + var hasMedia = node.querySelector('video, audio, iframe, embed, span[data-mce-object], img[data-mce-object]'); + if (hasMedia) { + e.preventDefault(); + e.stopPropagation(); + + editor.undoManager.transact(function() { + var nextSibling = node.nextSibling; + var parent = node.parentNode; + editor.dom.remove(node); + + if (nextSibling) { + editor.selection.setCursorLocation(nextSibling, 0); + } else if (parent) { + editor.selection.setCursorLocation(parent, parent.childNodes.length); + } + editor.nodeChanged(); + }); + return false; + } + } + + // Caso 2: Un elemento multimedia está seleccionado + var isMediaElement = node.nodeName === 'VIDEO' || node.nodeName === 'AUDIO' || + node.nodeName === 'IFRAME' || node.nodeName === 'EMBED' || + (node.nodeName === 'SPAN' && node.getAttribute('data-mce-object')); + + if (isMediaElement && !selection.isCollapsed()) { + var parentFigure = editor.dom.getParent(node, 'figure.exe-figure'); + if (!parentFigure) { + parentFigure = editor.dom.getParent(node, 'figure.exe-media'); + } + if (parentFigure) { + e.preventDefault(); + e.stopPropagation(); + + editor.undoManager.transact(function() { + var nextSibling = parentFigure.nextSibling; + var parent = parentFigure.parentNode; + editor.dom.remove(parentFigure); + + if (nextSibling) { + editor.selection.setCursorLocation(nextSibling, 0); + } else if (parent) { + editor.selection.setCursorLocation(parent, parent.childNodes.length); + } + editor.nodeChanged(); + }); + return false; + } + } + } + }); + + var cleanupTimeout = null; + editor.on('keyup', function(e) { + if (e.keyCode === 46 || e.keyCode === 8) { + clearTimeout(cleanupTimeout); + cleanupTimeout = setTimeout(function() { + // Only cleanup figures with exe-media class (not exeimage figures) + var figures = editor.dom.select('figure.exe-figure.exe-media'); + for (var i = 0; i < figures.length; i++) { + var figure = figures[i]; + var hasMedia = figure.querySelector('video, audio, iframe, embed, span[data-mce-object], img[data-mce-object]'); + if (!hasMedia) { + // Verify the figure still exists in the document before removing + if (editor.dom.getParent(figure, 'body')) { + editor.dom.remove(figure); + } + } + } + }, 100); + } + }); }; var global$3 = tinymce.util.Tools.resolve('tinymce.util.Promise'); @@ -1366,28 +1766,197 @@ editor.selection.select(afterObjects[0]); }; var handleInsert = function (editor, html) { + // Add the FIGURE and other properties if needed: html = getCustomHTML(html); - // Get the current element + var elm = editor.selection.getNode(); - // See if it's a FIGURE - var elmParent = elm.parentElement; - if (typeof elmParent=='object'&&elmParent.nodeName=="FIGURE") { - jQuery(elmParent).before("%exemedia_tmp_figure%"); - editor.dom.remove(elmParent); + + // Only handle figures with exe-media class (exemedia plugin's figures) + var figure = editor.dom.getParent(elm, 'figure.exe-media'); + if (!figure) { + var tempFigure = editor.dom.getParent(elm, 'figure.exe-figure'); + if (tempFigure && editor.dom.hasClass(tempFigure, 'exe-media')) { + figure = tempFigure; + } + } + + if (figure && editor.dom.hasClass(figure, 'exe-media')) { + // Preserve the previous CSS class + var tmpClassName = figure.className; + if (tmpClassName.indexOf(' position-center')==-1) { + html = html.replace(' position-center', ''); + } + if (tmpClassName.indexOf(' float-left')!=-1) { + html = html.replace("' style='width", " float-left' style='width"); + } else if (tmpClassName.indexOf(' position-right')!=-1) { + html = html.replace("' style='width", " position-right' style='width"); + } else if (tmpClassName.indexOf(' float-right')!=-1) { + html = html.replace("' style='width", " float-right' style='width"); + } + jQuery(figure).before("%exemedia_tmp_figure%"); + editor.dom.remove(figure); editor.setContent(editor.getContent().replace("%exemedia_tmp_figure%",html)); return false; } - // If it's not a FIGURE: + + // Check if we're replacing existing media (not in figure) OR if we had a figure before + var isReplacingMedia = false; + var existingMedia = editor.dom.getParent(elm, 'span[data-mce-object],img[data-mce-object],video,audio,iframe,embed', editor.getBody()); + + if (existingMedia || figure) { + // We're editing existing content (either media or figure) + isReplacingMedia = true; + } + var beforeObjects = editor.dom.select('*[data-mce-object]'); - let extra = "
"; - if (elm.nextElementSibling&&elm.nextElementSibling.nodeName&&elm.nextElementSibling.nodeName=="BR") extra = ""; - editor.insertContent(html+extra); + + editor.insertContent(html); selectPlaceholder(editor, beforeObjects); + + // Only add paragraph if inserting NEW content, not when replacing existing + if (!isReplacingMedia) { + setTimeout(function() { + // Only look for figures with exe-media class (exemedia plugin's figures) + var figures = editor.dom.select('figure.exe-media'); + if (figures.length > 0) { + var lastFigure = figures[figures.length - 1]; + // Check if there's a next sibling that allows writing + var nextSibling = lastFigure.nextSibling; + var needsParagraph = false; + + if (!nextSibling) { + // No sibling at all, need paragraph + needsParagraph = true; + } else { + // Check if the next sibling is a valid block element for writing + // If it's just whitespace text node or not a block element, we need a paragraph + if (nextSibling.nodeType === 3) { + // Text node - check if it's just whitespace + if (nextSibling.textContent.trim() === '') { + needsParagraph = true; + } + } else if (nextSibling.nodeName !== 'P' && nextSibling.nodeName !== 'DIV' && + nextSibling.nodeName !== 'H1' && nextSibling.nodeName !== 'H2' && + nextSibling.nodeName !== 'H3' && nextSibling.nodeName !== 'H4' && + nextSibling.nodeName !== 'H5' && nextSibling.nodeName !== 'H6') { + // Not a block element that can contain text + needsParagraph = true; + } + } + + if (needsParagraph) { + var p = editor.dom.create('p', {}, '
'); + lastFigure.parentNode.appendChild(p); + editor.selection.setCursorLocation(p, 0); + } + } else { + var mediaElements = editor.dom.select('video,audio,iframe,embed,span[data-mce-object],img[data-mce-object]'); + if (mediaElements.length > 0) { + var lastMedia = mediaElements[mediaElements.length - 1]; + var parentBlock = editor.dom.getParent(lastMedia, 'p,div', editor.getBody()); + if (parentBlock) { + var nextSibling = parentBlock.nextSibling; + var needsParagraph = false; + + if (!nextSibling) { + needsParagraph = true; + } else { + if (nextSibling.nodeType === 3) { + if (nextSibling.textContent.trim() === '') { + needsParagraph = true; + } + } else if (nextSibling.nodeName !== 'P' && nextSibling.nodeName !== 'DIV' && + nextSibling.nodeName !== 'H1' && nextSibling.nodeName !== 'H2' && + nextSibling.nodeName !== 'H3' && nextSibling.nodeName !== 'H4' && + nextSibling.nodeName !== 'H5' && nextSibling.nodeName !== 'H6') { + needsParagraph = true; + } + } + + if (needsParagraph) { + var p = editor.dom.create('p', {}, '
'); + parentBlock.parentNode.appendChild(p); + editor.selection.setCursorLocation(p, 0); + } + } + } + } + }, 50); + } + editor.nodeChanged(); }; + // Get alignment options for media (no vertical-align for multimedia) + var getAlignmentOptions = function () { + var baseOptions = [ + { + text: _('-- Not Set --'), + value: '' + }, + { + text: _('Left'), + value: 'left' + }, + { + text: _('Right'), + value: 'right' + }, + { + text: _('Center'), + value: 'center' + } + ]; + + return baseOptions; + }; + + // Helper function to extract alignment styles from an element + var getAlignmentStyles = function(element) { + if (!element || !element.getAttribute) return null; + + var style = element.getAttribute('style') || ''; + var alignment = { + hasFloat: false, + floatDirection: null, + hasBlockCenter: false, + hasBlockLeft: false, + hasBlockRight: false, + rawStyle: style + }; + + // Detect float + if (style.indexOf('float:') !== -1 || style.indexOf('float :') !== -1) { + alignment.hasFloat = true; + if (style.indexOf('float: left') !== -1 || style.indexOf('float:left') !== -1) { + alignment.floatDirection = 'left'; + } else if (style.indexOf('float: right') !== -1 || style.indexOf('float:right') !== -1) { + alignment.floatDirection = 'right'; + } + } + + // Detect block center (margin: auto) + if ((style.indexOf('margin-left: auto') !== -1 || style.indexOf('margin-left:auto') !== -1) && + (style.indexOf('margin-right: auto') !== -1 || style.indexOf('margin-right:auto') !== -1)) { + alignment.hasBlockCenter = true; + } + + // Detect block left/right + if (style.indexOf('display: block') !== -1 || style.indexOf('display:block') !== -1) { + if (style.indexOf('margin-left: 0') !== -1 || style.indexOf('margin-left:0') !== -1) { + alignment.hasBlockLeft = true; + } + if (style.indexOf('margin-right: 0') !== -1 || style.indexOf('margin-right:0') !== -1) { + alignment.hasBlockRight = true; + } + } + + return alignment; + }; + var getCustomHTML = function(html){ + // Extra attributes let tmpDiv = $("
"); // JavaScript player and autoplay @@ -1406,14 +1975,6 @@ if (attrSrt1!="") { html = html.replace('','') } - if (attrID!="" || attrClass!="" || attrStyle!="") { - tmpDiv.html(html); - let elm = jQuery("video,audio,iframe,embed",tmpDiv); - if (attrID!="") elm.attr("id",attrID.replace(/ /g,"")); - if (attrClass!="") elm.addClass(attrClass); - if (attrStyle!="") elm.attr("style",attrStyle); - html = tmpDiv.html(); - } // Mediateca EducaMadrid let mDomain = "https://mediateca.educa.madrid.org/"; if (html.indexOf(' 0) { + + // Get the new dimensions from the HTML (these come from updateHtml) + var newWidth = mediaElement.attr('width'); + var newHeight = mediaElement.attr('height'); + + // If dimensions are set in the media element, use them for the figure + if (newWidth && !isNaN(newWidth)) { + mediaWidth = newWidth; + } + + // Update the media element to ensure it matches the figure width + if (mediaWidth) { + mediaElement.attr('width', mediaWidth); + // Also update height to maintain aspect ratio if possible + if (newHeight && !isNaN(newHeight) && newWidth && !isNaN(newWidth)) { + var aspectRatio = parseFloat(newHeight) / parseFloat(newWidth); + var adjustedHeight = Math.round(parseFloat(mediaWidth) * aspectRatio); + mediaElement.attr('height', adjustedHeight); + } + } + + var mediaStyle = mediaElement.attr('style') || ''; + // Remove alignment-related styles from media element + mediaStyle = mediaStyle.replace(/float\s*:\s*(left|right)\s*;?\s*/gi, ''); + mediaStyle = mediaStyle.replace(/margin-(left|right)\s*:\s*[^;]+;?\s*/gi, ''); + mediaStyle = mediaStyle.replace(/display\s*:\s*(block|inline-block)\s*;?\s*/gi, ''); + mediaStyle = mediaStyle.replace(/text-align\s*:\s*[^;]+;?\s*/gi, ''); + // Also remove width/height from style (use attributes instead) + mediaStyle = mediaStyle.replace(/width\s*:\s*[^;]+;?\s*/gi, ''); + mediaStyle = mediaStyle.replace(/height\s*:\s*[^;]+;?\s*/gi, ''); + + if (mediaStyle.trim()) { + mediaElement.attr('style', mediaStyle.trim()); + } else { + mediaElement.removeAttr('style'); + } + + html = tmpDiv.html(); + } + + // Build figure inline styles (width + alignment) + var extraStyle = "width:" + mediaWidth + "px;"; var fText = ""; if (cText!="" || license!="") fText = "<"+footerFigcaptionTag+" class='figcaption'>"+cText+license+""; res = "<"+figureTag+" class='"+cssClass+"' style='"+extraStyle+"'>"+hText+html+fText+""; + } else { + // Simple media (video/audio/iframe/embed without figure) + // Apply inline styles for alignment to the media element + + tmpDiv.html(html); + var mediaElement = tmpDiv.find("video, audio, iframe, embed").first(); + + if (mediaElement.length > 0) { + + var mediaStyle = mediaElement.attr('style') || ''; + var mediaWidth = mediaElement.attr('width'); + var isAudio = mediaElement[0].tagName === 'AUDIO'; + + // Remove existing alignment styles + mediaStyle = mediaStyle.replace(/float\s*:\s*(left|right|none)\s*;?\s*/gi, ''); + mediaStyle = mediaStyle.replace(/margin-(left|right)\s*:\s*[^;]+;?\s*/gi, ''); + mediaStyle = mediaStyle.replace(/display\s*:\s*(block|inline-block)\s*;?\s*/gi, ''); + mediaStyle = mediaStyle.replace(/text-align\s*:\s*[^;]+;?\s*/gi, ''); + mediaStyle = mediaStyle.replace(/width\s*:\s*[^;]+;?\s*/gi, ''); + mediaStyle = mediaStyle.trim(); + + var widthStyle = ''; + if (mediaWidth) { + if (!isNaN(mediaWidth)) mediaWidth += 'px'; + widthStyle = ' width: ' + mediaWidth + ';'; + } + + var heightStyle = ''; + var mediaHeight = mediaElement.attr('height'); + if (mediaHeight) { + if (!isNaN(mediaHeight)) mediaHeight += 'px'; + heightStyle = ' height: ' + mediaHeight + ';'; + } + + // For audio, ALWAYS set default dimensions (override any existing values) + if (isAudio) { + if (!widthStyle) { + widthStyle = ' width: 300px;'; + } + // Always set height to 40px for audio, even if it has a different value + heightStyle = ' height: 40px;'; + } + + // Clean up double semicolons and trim + mediaStyle = mediaStyle.replace(/;+/g, ';').replace(/^\s*;+\s*/, '').replace(/\s*;+\s*$/, '').trim(); + if (mediaStyle !== '') { + mediaElement.attr('style', mediaStyle); + } + + html = tmpDiv.html(); + } + res = html; } return res; } // / getCustomHTML var submitForm = function (prevData, newData, editor) { + + // Restore selection bookmark (like exeimage does) + editor.selection.moveToBookmark(bm); + newData.embed = updateHtml(newData.embed, newData); + if (newData.embed && (prevData.source === newData.source || isCached(newData.source))) { handleInsert(editor, newData.embed); } else { @@ -1574,7 +2256,10 @@ } }; var showDialog = function (editor) { + GetActualData(editor); + + var editorData = getEditorData(editor); var currentData = Cell(editorData); var initialData = wrap(editorData); @@ -1655,7 +2340,9 @@ label: _('Source'), placeholder : _("Choose a file or paste a link (URL)") }]; - var sizeInput = !hasDimensions(editor) ? [] : [{ + // Hide size input for audio - audio player size should not be modified + var isAudioType = (generalType === 'audio'); + var sizeInput = (!hasDimensions(editor) || isAudioType) ? [] : [{ type: 'sizeinput', name: 'dimensions', label: 'Constrain proportions', @@ -1774,13 +2461,6 @@ case 'source': handleSource(currentData.get(), api); let selectedType = api.getData().generaltype; - // Auto-detect PDF and set type to iframe - let sourceUrl = api.getData().source.value || ''; - if (isLocalPDF(sourceUrl) && selectedType !== 'iframe') { - selectedType = 'iframe'; - generalType = 'iframe'; - api.setData({ generaltype: 'iframe' }); - } api.setData({ embed: SetNewEmbedData(api, selectedType) }); document.getElementById("exemedia-type").children[0].value = ''; document.getElementById("exemedia-type").children[0].value = selectedType; @@ -1869,6 +2549,10 @@ }, 500); } }); + + // bookmark selected media (preserve selection like exeimage does) + bm = editor.selection.getBookmark(); + // Muted option hidden by default if (generalAutoplay!=true) $(".tox-checkbox").eq(3).css("visibility","hidden"); }; @@ -1883,24 +2567,39 @@ var register$1 = function (editor) { var showDialog$1 = function () { - // Is in figure? var elm = editor.selection.getNode(); - var figure = editor.dom.getParents(elm, '.exe-figure'); - var isIframe = false; - if (elm.nodeName=="SPAN" && elm.className && elm.className.indexOf("mce-preview-object")!=-1) isIframe = true; - if (figure.length==1 && elm.nodeName!="IMG" && !isIframe) { - var figureHTML = figure[0].innerHTML; - // When it's a figure containing an media element you have to select that element to open the dialog - // Otherwise the "Title and Attribution" panel will not load the previous values - if (figureHTML.indexOf(' Date: Sat, 17 Jan 2026 12:14:08 +0100 Subject: [PATCH 02/41] The default alt text for an image selected from the File Manager should be empty. --- public/app/editor/tinymce_5_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/app/editor/tinymce_5_settings.js b/public/app/editor/tinymce_5_settings.js index 234adb821..fd5a439c0 100644 --- a/public/app/editor/tinymce_5_settings.js +++ b/public/app/editor/tinymce_5_settings.js @@ -367,7 +367,7 @@ var $exeTinyMCE = { cb(result.blobUrl, { title: result.asset.filename || '', - alt: result.asset.filename || '', + alt: '', 'data-asset-id': result.asset.id // CRITICAL: Used by convertBlobURLsToAssetRefs }); } From 0b6dbc9d2407d72f69c037d656d3d8e383662264 Mon Sep 17 00:00:00 2001 From: ignaciogros Date: Sat, 17 Jan 2026 12:19:07 +0100 Subject: [PATCH 03/41] FIGURE elements, like IMG elements, should have a max-width of 100% relative to their container to avoid breaking the layout. --- .../libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css | 2 +- .../libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css index bcdcb1a01..aefce2606 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css +++ b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/css/content.css @@ -1,5 +1,5 @@ /* The New eXeLearning */ -.exe-figure{margin:1.5em 0} +.exe-figure{margin:1.5em 0;max-width:100%} .position-center{margin:1.5em auto} .position-right{margin:1.5em 0 1.5em auto} .float-left{float:left;margin:.5em 1.5em 1em 0} diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css b/public/libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css index 8ec35d33a..cfb0db562 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css +++ b/public/libs/tinymce_5/js/tinymce/plugins/exemedia/css/content.css @@ -1,5 +1,5 @@ /* The New eXeLearning */ -.exe-figure{margin:1.5em 0} +.exe-figure{margin:1.5em 0;max-width:100%} .position-center{margin:1.5em auto} .position-right{margin:1.5em 0 1.5em auto} .float-left{float:left;margin:.5em 1.5em 1em 0} From 79558085211481022394af0de9720fccf5857dec Mon Sep 17 00:00:00 2001 From: ignaciogros Date: Sat, 17 Jan 2026 12:43:01 +0100 Subject: [PATCH 04/41] Preserve existing classes and captions (custom license). --- .../js/tinymce/plugins/exeimage/plugin.min.js | 47 ++++++++++++++++--- .../js/tinymce/plugins/exemedia/plugin.min.js | 2 + 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/plugin.min.js b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/plugin.min.js index 68ccf7903..f241cfcf5 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/exeimage/plugin.min.js +++ b/public/libs/tinymce_5/js/tinymce/plugins/exeimage/plugin.min.js @@ -2772,7 +2772,7 @@ custom_div_html += htmlTitulo; } if (htmlLicencia !== '') { - custom_div_html += ' (' + htmlLicencia + ')'; + custom_div_html += ' (' + htmlLicencia + ')'; } custom_div_html += ''; } @@ -2863,7 +2863,22 @@ actAuthorHTML = actAuthor.innerHTML; actLinkAuthorHTML = editor.dom.getAttrib(actAuthor, 'href'); } - let actLicense = editor.dom.select('.custom-license, .license span', figure_old)[0]; + // First try to find .custom-license, then look for license link or span (excluding .sep) + let actLicense = editor.dom.select('.custom-license', figure_old)[0]; + if (!actLicense) { + // Look for a link inside .license + actLicense = editor.dom.select('.license a', figure_old)[0]; + } + if (!actLicense) { + // Look for a span inside .license that is NOT .sep + var licenseSpans = editor.dom.select('.license span', figure_old); + for (var i = 0; i < licenseSpans.length; i++) { + if (!editor.dom.hasClass(licenseSpans[i], 'sep')) { + actLicense = licenseSpans[i]; + break; + } + } + } let actLicenseHTML = ''; let actLinkLicenseHTML = ''; if (actLicense != null) { @@ -2926,7 +2941,7 @@ custom_div_html += htmlTitulo; } if (htmlLicencia !== '') { - custom_div_html += ' (' + htmlLicencia + ')'; + custom_div_html += ' (' + htmlLicencia + ')'; } custom_div_html += ''; } @@ -2934,9 +2949,27 @@ // Check if figure_old is an exemedia figure (has exe-media class) var isExemediaFigure = figure_old && editor.dom.hasClass(figure_old, 'exe-media'); - - let figureClasses = 'exe-figure'; - + + // Preserve existing classes from figure_old, filtering out position/float classes that will be re-added + let oldFigureClass = figure_old ? (figure_old.getAttribute('class') || '') : ''; + let preservedClasses = oldFigureClass.split(' ').filter(function(cls) { + cls = cls.trim(); + // Keep classes that are not position/float related (those will be re-added based on alignment) + return cls && + cls !== 'position-left' && + cls !== 'position-center' && + cls !== 'position-right' && + cls !== 'float-left' && + cls !== 'float-right'; + }); + + // Ensure exe-figure is present + if (preservedClasses.indexOf('exe-figure') === -1) { + preservedClasses.unshift('exe-figure'); + } + + let figureClasses = preservedClasses.join(' '); + // Only apply position-* classes to non-exemedia figures if (!isExemediaFigure) { if (advAlignStyle === 'left') { @@ -2947,7 +2980,7 @@ figureClasses += ' float-right'; } else if (!advAlignStyle) { - let oldFigureClass = figure_old.getAttribute('class') || ''; + // Preserve original position class if no new alignment specified if (oldFigureClass.includes('position-left')) figureClasses += ' position-left'; else if (oldFigureClass.includes('position-center')) figureClasses += ' position-center'; else if (oldFigureClass.includes('position-right')) figureClasses += ' position-right'; diff --git a/public/libs/tinymce_5/js/tinymce/plugins/exemedia/plugin.min.js b/public/libs/tinymce_5/js/tinymce/plugins/exemedia/plugin.min.js index a5dc4ce15..85b966770 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/exemedia/plugin.min.js +++ b/public/libs/tinymce_5/js/tinymce/plugins/exemedia/plugin.min.js @@ -1789,6 +1789,8 @@ } if (tmpClassName.indexOf(' float-left')!=-1) { html = html.replace("' style='width", " float-left' style='width"); + } else if (tmpClassName.indexOf(' position-left')!=-1) { + html = html.replace("' style='width", " position-left' style='width"); } else if (tmpClassName.indexOf(' position-right')!=-1) { html = html.replace("' style='width", " position-right' style='width"); } else if (tmpClassName.indexOf(' float-right')!=-1) { From 02d3ef3093def3161ad9752d91100bf0bb0a1408 Mon Sep 17 00:00:00 2001 From: ignaciogros Date: Sat, 17 Jan 2026 13:06:05 +0100 Subject: [PATCH 05/41] Update the test to check if the accessibility warning is displayed when an image has no alternative text. --- public/app/editor/tinymce_5_settings.test.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/public/app/editor/tinymce_5_settings.test.js b/public/app/editor/tinymce_5_settings.test.js index 1f03aa4b7..e584e4038 100644 --- a/public/app/editor/tinymce_5_settings.test.js +++ b/public/app/editor/tinymce_5_settings.test.js @@ -424,9 +424,11 @@ describe('TinyMCE 5 Settings', () => { // Should use blob URL directly (not convert to data:URL) // CRITICAL: Must include data-asset-id for reliable blob→asset conversion + // alt is empty by default - user should provide their own description + // (exeimage plugin shows warning if alt is empty when saving) expect(cb).toHaveBeenCalledWith('blob://file', { title: 'file.png', - alt: 'file.png', + alt: '', 'data-asset-id': 'abc123', // CRITICAL: Used by convertBlobURLsToAssetRefs }); @@ -460,9 +462,10 @@ describe('TinyMCE 5 Settings', () => { // Should still work with blob URL even without AssetManager // data-asset-id is always included for reliable conversion + // alt is empty by default - exeimage plugin warns if empty when saving expect(cb).toHaveBeenCalledWith('blob://file', { title: 'file.png', - alt: 'file.png', + alt: '', 'data-asset-id': 'abc123', }); }); @@ -537,9 +540,10 @@ describe('TinyMCE 5 Settings', () => { await new Promise((resolve) => setTimeout(resolve, 0)); // Videos should also use blob URL directly with data-asset-id + // alt is empty by default (videos don't require alt text, only images do) expect(cb).toHaveBeenCalledWith('blob:http://localhost:8081/video-blob', { title: 'video.mp4', - alt: 'video.mp4', + alt: '', 'data-asset-id': assetUUID, // CRITICAL: Used by convertBlobURLsToAssetRefs }); // And should register UUID (not asset:// URL) in cache From 49adeadd1be07e7a3ddff608add571b8fbdec0c6 Mon Sep 17 00:00:00 2001 From: ignaciogros Date: Sat, 17 Jan 2026 17:30:07 +0100 Subject: [PATCH 06/41] Update test to handle the accessibility warning dialog. --- test/e2e/playwright/specs/collaborative/text.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/e2e/playwright/specs/collaborative/text.spec.ts b/test/e2e/playwright/specs/collaborative/text.spec.ts index dae0f02a1..535c45f29 100644 --- a/test/e2e/playwright/specs/collaborative/text.spec.ts +++ b/test/e2e/playwright/specs/collaborative/text.spec.ts @@ -166,6 +166,17 @@ async function insertImageViaTinyMCE(page: Page, fixturePath: string): Promise 0) { await tinyMceSaveBtn.click(); } + + // Handle accessibility warning dialog that appears when no alt text is provided + // The dialog asks: "Are you sure you want to continue without including an Image Description?" + const accessibilityConfirmBtn = page.locator('.tox-dialog button:has-text("Yes")'); + try { + await accessibilityConfirmBtn.waitFor({ state: 'visible', timeout: 3000 }); + await accessibilityConfirmBtn.click(); + } catch { + // Dialog may not appear if alt text was provided + } + await page.waitForTimeout(1000); } From b115f9a933694616bbfb2c6e9055973218396244 Mon Sep 17 00:00:00 2001 From: ignaciogros Date: Sat, 17 Jan 2026 18:10:38 +0100 Subject: [PATCH 07/41] Fix "TinyMCE Advanced Editor (CodeMagic)" test. --- .../js/tinymce/plugins/codemagic/codemagic.html | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/public/libs/tinymce_5/js/tinymce/plugins/codemagic/codemagic.html b/public/libs/tinymce_5/js/tinymce/plugins/codemagic/codemagic.html index 650fba73c..c51981f58 100644 --- a/public/libs/tinymce_5/js/tinymce/plugins/codemagic/codemagic.html +++ b/public/libs/tinymce_5/js/tinymce/plugins/codemagic/codemagic.html @@ -8,7 +8,15 @@ */ --> - + From d4d43edc58287f9742906acf11fdcf71b94d48a7 Mon Sep 17 00:00:00 2001 From: ignaciogros Date: Sat, 17 Jan 2026 18:42:47 +0100 Subject: [PATCH 08/41] Fix PDF preview test to work with either