@ckeditor/ckeditor5-paste-from-office 47.1.0 → 47.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@ CKEditor 5 paste from Office feature
2
2
  ==================================
3
3
 
4
4
  [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-paste-from-office.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-paste-from-office)
5
- [![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
5
+ [![codecov](https://codecov.io/gh/ckeditor/ckeditor5/branch/master/graph/badge.svg)](https://codecov.io/gh/ckeditor/ckeditor5)
6
6
  [![CircleCI](https://circleci.com/gh/ckeditor/ckeditor5.svg?style=shield)](https://app.circleci.com/pipelines/github/ckeditor/ckeditor5?branch=master)
7
7
 
8
8
  This package implements the [paste from Office](https://docs.ckeditor.com/ckeditor5/latest/features/paste-from-office.html) feature for CKEditor 5.
@@ -1,4 +1,4 @@
1
1
  /*!
2
2
  * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md.
4
- */(()=>{var e={237:e=>{"use strict";e.exports=CKEditor5.dll},331:(e,t,n)=>{e.exports=n(237)("./src/clipboard.js")},782:(e,t,n)=>{e.exports=n(237)("./src/core.js")},783:(e,t,n)=>{e.exports=n(237)("./src/engine.js")}},t={};function n(s){var i=t[s];if(void 0!==i)return i.exports;var r=t[s]={exports:{}};return e[s](r,r.exports,n),r.exports}n.d=(e,t)=>{for(var s in t)n.o(t,s)&&!n.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var s={};(()=>{"use strict";n.r(s),n.d(s,{PasteFromOffice:()=>U,PasteFromOfficeGoogleDocsNormalizer:()=>R,PasteFromOfficeGoogleSheetsNormalizer:()=>V,PasteFromOfficeMSWordNormalizer:()=>A,_convertHexToBase64:()=>x,_convertPasteOfficeCssLengthToPx:()=>o,_isPasteOfficePxValue:()=>l,_normalizePasteOfficeSpaceRunSpans:()=>B,_normalizePasteOfficeSpacing:()=>k,_removePasteGoogleOfficeSheetsTag:()=>F,_removePasteMSOfficeAttributes:()=>w,_removePasteOfficeBoldWrapper:()=>O,_removePasteOfficeInvalidTableWidths:()=>C,_removePasteOfficeStyleBlock:()=>j,_removePasteOfficeXmlnsAttributes:()=>$,_replacePasteOfficeImagesSourceWithBase64:()=>v,_toPasteOfficePxValue:()=>a,_transformPasteOfficeBlockBrsToParagraphs:()=>E,_transformPasteOfficeBookmarks:()=>i,_transformPasteOfficeListItemLikeElementsIntoLists:()=>c,_transformPasteOfficeTables:()=>S,_unwrapPasteOfficeParagraphInListItem:()=>m,parsePasteOfficeHtml:()=>z});var e=n(782),t=n(331);function i(e,t){const n=[];for(const s of t.createRangeIn(e)){const e=s.item;e.is("element","a")&&!e.hasAttribute("href")&&(e.hasAttribute("id")||e.hasAttribute("name"))&&n.push(e)}for(const e of n){const n=e.parent.getChildIndex(e)+1,s=e.getChildren();t.insertChild(n,s,e.parent)}}var r=n(783);function o(e){const t=parseFloat(e);return e.endsWith("pt")?a(96*t/72):e.endsWith("pc")?a(12*t*96/72):e.endsWith("in")?a(96*t):e.endsWith("cm")?a(96*t/2.54):e.endsWith("mm")?a(t/10*96/2.54):e}function l(e){return void 0!==e&&e.endsWith("px")}function a(e){return Math.round(e)+"px"}function c(e,t,n){if(!e.childCount)return;const s=new r.ViewUpcastWriter(e.document),i=function(e,t){const n=t.createRangeIn(e),s=[],i=new Set;for(const e of n.getItems()){if(!e.is("element")||!e.name.match(/^(p|h\d+|li|div)$/))continue;let t=b(e);if(void 0===t||0!=parseFloat(t)||Array.from(e.getClassNames()).find(e=>e.startsWith("MsoList"))||(t=void 0),e.hasStyle("mso-list")||void 0!==t&&i.has(t)){const n=h(e);s.push({element:e,id:n.id,order:n.order,indent:n.indent,marginLeft:t}),void 0!==t&&i.add(t)}else i.clear()}return s}(e,s);if(!i.length)return;const o={},c=[];for(const e of i)if(void 0!==e.indent){f(e)||(c.length=0);const i=`${e.id}:${e.indent}`,r=Math.min(e.indent-1,c.length);if(r<c.length&&c[r].id!==e.id&&(c.length=r),r<c.length-1)c.length=r+1;else{const m=d(e,t);if(r>c.length-1||c[r].listElement.name!=m.type){0==r&&"ol"==m.type&&void 0!==e.id&&o[i]&&(m.startIndex=o[i]);const t=g(m,s,n);if(l(e.marginLeft)&&(0==r||l(c[r-1].marginLeft))){let n=e.marginLeft;r>0&&(n=a(parseFloat(n)-parseFloat(c[r-1].marginLeft))),s.setStyle("padding-left",n,t)}if(0==c.length){const n=e.element.parent,i=n.getChildIndex(e.element)+1;s.insertChild(i,t,n)}else{const e=c[r-1].listItemElements;s.appendChild(t,e[e.length-1])}c[r]={...e,listElement:t,listItemElements:[]},0==r&&void 0!==e.id&&(o[i]=m.startIndex||1)}}const m="li"==e.element.name?e.element:s.createElement("li");s.appendChild(m,c[r].listElement),c[r].listItemElements.push(m),0==r&&void 0!==e.id&&o[i]++,e.element!=m&&s.appendChild(e.element,m),y(e.element,s),s.removeStyle("text-indent",e.element),s.removeStyle("margin-left",e.element)}else{const t=c.find(t=>t.marginLeft==e.marginLeft);if(t){const n=t.listItemElements;s.appendChild(e.element,n[n.length-1]),s.removeStyle("margin-left",e.element)}else c.length=0}}function m(e,t){for(const n of t.createRangeIn(e)){const e=n.item;if(e.is("element","li")){const n=e.getChild(0);n&&n.is("element","p")&&t.unwrapElement(n)}}}function f(e){const t=e.element.previousSibling;if(!t){const t=e.element.parent;return u(t)&&(!t.previousSibling||u(t.previousSibling))}return u(t)}function u(e){return e.is("element","ol")||e.is("element","ul")}function d(e,t){const n=new RegExp(`@list l${e.id}:level${e.indent}\\s*({[^}]*)`,"gi"),s=/mso-level-number-format:([^;]{0,100});/gi,i=/mso-level-start-at:\s{0,100}([0-9]{0,10})\s{0,100};/gi,r=new RegExp(`@list\\s+l${e.id}:level\\d\\s*{[^{]*mso-level-text:"%\\d\\\\.`,"gi"),o=new RegExp(`@list l${e.id}:level\\d\\s*{[^{]*mso-level-number-format:`,"gi"),l=r.exec(t),a=o.exec(t),c=l&&!a,m=n.exec(t);let f="decimal",u="ol",d=null;if(m&&m[1]){const t=s.exec(m[1]);if(t&&t[1]&&(f=t[1].trim(),u="bullet"!==f&&"image"!==f?"ol":"ul"),"bullet"===f){const t=function(e){if("li"==e.name&&"ul"==e.parent.name&&e.parent.hasAttribute("type"))return e.parent.getAttribute("type");const t=function(e){if(e.getChild(0).is("$text"))return null;for(const t of e.getChildren()){if(!t.is("element","span"))continue;const e=t.getChild(0);if(e)return e.is("$text")?e:e.getChild(0)}return null}(e);if(!t)return null;const n=t._data;if("o"===n)return"circle";if("·"===n)return"disc";if("§"===n)return"square";return null}(e.element);t&&(f=t)}else{const e=i.exec(m[1]);e&&e[1]&&(d=parseInt(e[1]))}c&&(u="ol")}return{type:u,startIndex:d,style:p(f),isLegalStyleList:c}}function p(e){if(e.startsWith("arabic-leading-zero"))return"decimal-leading-zero";switch(e){case"alpha-upper":return"upper-alpha";case"alpha-lower":return"lower-alpha";case"roman-upper":return"upper-roman";case"roman-lower":return"lower-roman";case"circle":case"disc":case"square":return e;default:return null}}function g(e,t,n){const s=t.createElement(e.type);return e.style&&t.setStyle("list-style-type",e.style,s),e.startIndex&&e.startIndex>1&&t.setAttribute("start",e.startIndex,s),e.isLegalStyleList&&n&&t.addClass("legal-list",s),s}function h(e){const t=e.getStyle("mso-list");if(void 0===t)return{};const n=t.match(/(^|\s{1,100})l(\d+)/i),s=t.match(/\s{0,100}lfo(\d+)/i),i=t.match(/\s{0,100}level(\d+)/i);return n&&s&&i?{id:n[2],order:s[1],indent:parseInt(i[1])}:{indent:1}}function y(e,t){const n=new r.Matcher({name:"span",styles:{"mso-list":"Ignore"}}),s=t.createRangeIn(e);for(const e of s)"elementStart"===e.type&&n.match(e.item)&&t.remove(e.item)}function b(e){const t=e.getStyle("margin-left");return void 0===t||t.endsWith("px")?t:o(t)}function v(e,t){if(!e.childCount)return;const n=new r.ViewUpcastWriter(e.document),s=function(e,t){const n=t.createRangeIn(e),s=new r.Matcher({name:/v:(.+)/}),i=[];for(const e of n){if("elementStart"!=e.type)continue;const t=e.item,n=t.previousSibling,r=n&&n.is("element")?n.name:null,o=["Chart"],l=s.match(t),a=t.getAttribute("o:gfxdata"),c="v:shapetype"===r,m=a&&o.some(e=>t.getAttribute("id").includes(e));l&&a&&!c&&!m&&i.push(e.item.getAttribute("id"))}return i}(e,n);!function(e,t,n){const s=n.createRangeIn(t),i=new r.Matcher({name:"img"}),o=[];for(const t of s)if(t.item.is("element")&&i.match(t.item)){const n=t.item,s=n.getAttribute("v:shapes")?n.getAttribute("v:shapes").split(" "):[];s.length&&s.every(t=>e.indexOf(t)>-1)?o.push(n):n.getAttribute("src")||o.push(n)}for(const e of o)n.remove(e)}(s,e,n),function(e,t,n){const s=n.createRangeIn(t),i=[];for(const t of s)if("elementStart"==t.type&&t.item.is("element","v:shape")){const n=t.item.getAttribute("id");if(e.includes(n))continue;r(t.item.parent.getChildren(),n)||i.push(t.item)}for(const e of i){const t={src:o(e)};e.hasAttribute("alt")&&(t.alt=e.getAttribute("alt"));const s=n.createElement("img",t);n.insertChild(e.index+1,s,e.parent)}function r(e,t){for(const n of e)if(n.is("element")){if("img"==n.name&&n.getAttribute("v:shapes")==t)return!0;if(r(n.getChildren(),t))return!0}return!1}function o(e){for(const t of e.getChildren())if(t.is("element")&&t.getAttribute("src"))return t.getAttribute("src")}}(s,e,n),function(e,t){const n=t.createRangeIn(e),s=new r.Matcher({name:/v:(.+)/}),i=[];for(const e of n)"elementStart"==e.type&&s.match(e.item)&&i.push(e.item);for(const e of i)t.remove(e)}(e,n);const i=function(e,t){const n=t.createRangeIn(e),s=new r.Matcher({name:"img"}),i=[];let o=0;for(const e of n)e.item.is("element")&&s.match(e.item)&&(e.item.getAttribute("src").startsWith("file://")&&i.push({element:e.item,imageIndex:o}),o++);return i}(e,n);i.length&&function(e,t,n){for(let s=0;s<e.length;s++){const{element:i,imageIndex:r}=e[s],o=t[r];if(o){const e=`data:${o.type};base64,${x(o.hex)}`;n.setAttribute("src",e,i)}}}(i,function(e){if(!e)return[];const t=/{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\blipuid\s?[\da-fA-F]+)?[\s}]*?/,n=new RegExp("(?:("+t.source+"))([\\da-fA-F\\s]+)\\}","g"),s=e.match(n),i=[];if(s)for(const e of s){let n=!1;e.includes("\\pngblip")?n="image/png":e.includes("\\jpegblip")&&(n="image/jpeg"),n&&i.push({hex:e.replace(t,"").replace(/[^\da-fA-F]/g,""),type:n})}return i}(t),n)}function x(e){return btoa(e.match(/\w{2}/g).map(e=>String.fromCharCode(parseInt(e,16))).join(""))}function w(e){const t=[],n=new r.ViewUpcastWriter(e.document);for(const{item:s}of n.createRangeIn(e))if(s.is("element")){for(const e of s.getClassNames())/\bmso/gi.exec(e)&&n.removeClass(e,s);for(const e of s.getStyleNames())/\bmso/gi.exec(e)&&n.removeStyle(e,s);(s.is("element","w:sdt")||s.is("element","w:sdtpr")&&s.isEmpty||s.is("element","o:p")&&s.isEmpty)&&t.push(s)}for(const e of t){const t=e.parent,s=t.getChildIndex(e);n.insertChild(s,e.getChildren(),t),n.remove(e)}}function S(e,t){for(const n of t.createRangeIn(e).getItems()){if(!n.is("element","table")&&!n.is("element","td")&&!n.is("element","th"))continue;const e=["left","top","right","bottom"];if(e.every(e=>!n.hasStyle(`border-${e}-style`)))t.setStyle("border-style","none",n);else for(const s of e)n.hasStyle(`border-${s}-style`)||t.setStyle(`border-${s}-style`,"none",n);const s=["width","height",...e.map(e=>`border-${e}-width`),...e.map(e=>`padding-${e}`)];for(const e of s)n.hasStyle(e)&&t.setStyle(e,o(n.getStyle(e)),n)}}function C(e,t){for(const n of t.createRangeIn(e).getItems())n.is("element","table")&&("0px"===n.getStyle("width")&&t.removeStyle("width",n),"0"===n.getAttribute("width")&&t.removeAttribute("width",n))}const P=/<meta\s*name="?generator"?\s*content="?microsoft\s*word\s*\d+"?\/?>/i,I=/xmlns:o="urn:schemas-microsoft-com/i;class A{document;hasMultiLevelListPlugin;constructor(e,t=!1){this.document=e,this.hasMultiLevelListPlugin=t}isActive(e){return P.test(e)||I.test(e)}execute(e){const t=new r.ViewUpcastWriter(this.document),{body:n,stylesString:s}=e._parsedData;i(n,t),c(n,s,this.hasMultiLevelListPlugin),v(n,e.dataTransfer.getData("text/rtf")),S(n,t),C(n,t),w(n),e.content=n}}function O(e,t){for(const n of e.getChildren())if(n.is("element","b")&&"normal"===n.getStyle("font-weight")){const s=e.getChildIndex(n);t.remove(n),t.insertChild(s,n.getChildren(),e)}}function E(e,t){const n=new r.ViewDocument(t.document.stylesProcessor),s=new r.ViewDomConverter(n,{renderingMode:"data"}),i=s.blockElements,o=s.inlineObjectElements,l=[];for(const n of t.createRangeIn(e)){const e=n.item;if(e.is("element","br")){const n=L(e,"forward",t,{blockElements:i,inlineObjectElements:o}),s=L(e,"backward",t,{blockElements:i,inlineObjectElements:o}),r=_(n,i);(_(s,i)||r)&&l.push(e)}}for(const e of l)e.hasClass("Apple-interchange-newline")?t.remove(e):t.replace(e,t.createElement("p"))}function L(e,t,n,{blockElements:s,inlineObjectElements:i}){let r=n.createPositionAt(e,"forward"==t?"after":"before");return r=r.getLastMatchingPosition(({item:e})=>e.is("element")&&!s.includes(e.name)&&!i.includes(e.name),{direction:t}),"forward"==t?r.nodeAfter:r.nodeBefore}function _(e,t){return!!e&&e.is("element")&&t.includes(e.name)}function M(e){let t=e;for(;t;){if(t.is("element")){const e=t.getStyle?.("white-space");if("pre-wrap"===e)return!0}t=t.parent}return!1}function T(e,t,n){const{parent:s,data:i}=e,r=i.replaceAll("\t"," ".repeat(n)),o=s.getChildIndex(e);t.remove(e),t.insertChild(o,t.createText(r),s)}const W=/id=("|')docs-internal-guid-[-0-9a-f]+("|')/i;class R{document;constructor(e){this.document=e}isActive(e){return W.test(e)}execute(e){const t=new r.ViewUpcastWriter(this.document),{body:n}=e._parsedData;O(n,t),m(n,t),E(n,t),function(e,t,n){const s=new Set;for(const n of t.createRangeIn(e).getItems())n.is("view:$textProxy")&&n.data.includes("\t")&&M(n.parent)&&s.add(n.textNode);for(const e of s)T(e,t,n)}(n,t,8),e.content=n}}function $(e,t){for(const n of e.getChildren())n.is("element","table")&&n.hasAttribute("xmlns")&&t.removeAttribute("xmlns",n)}function F(e,t){for(const n of e.getChildren())if(n.is("element","google-sheets-html-origin")){const s=e.getChildIndex(n);t.remove(n),t.insertChild(s,n.getChildren(),e)}}function j(e,t){for(const n of Array.from(e.getChildren()))n.is("element","style")&&t.remove(n)}const D=/<google-sheets-html-origin/i;class V{document;constructor(e){this.document=e}isActive(e){return D.test(e)}execute(e){const t=new r.ViewUpcastWriter(this.document),{body:n}=e._parsedData;F(n,t),$(n,t),C(n,t),j(n,t),e.content=n}}function k(e){return N(N(e)).replace(/(<span\s+style=['"]mso-spacerun:yes['"]>[^\S\r\n]*?)[\r\n]+([^\S\r\n]*<\/span>)/g,"$1$2").replace(/<span\s+style=['"]mso-spacerun:yes['"]><\/span>/g,"").replace(/(<span\s+style=['"]letter-spacing:[^'"]+?['"]>)[\r\n]+(<\/span>)/g,"$1 $2").replace(/ <\//g," </").replace(/ <o:p><\/o:p>/g," <o:p></o:p>").replace(/<o:p>(&nbsp;|\u00A0)<\/o:p>/g,"").replace(/>([^\S\r\n]*[\r\n]\s*)</g,"><")}function B(e){e.querySelectorAll("span[style*=spacerun]").forEach(e=>{const t=e,n=t.innerText.length||0;t.innerText=Array(n+1).join("  ").substr(0,n)})}function N(e){return e.replace(/<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g,(e,t)=>1===t.length?" ":Array(t.length+1).join("  ").substr(0,t.length))}function z(e,t){const n=new DOMParser,s=k(function(e){const t="</body>",n="</html>",s=e.indexOf(t);if(s<0)return e;const i=e.indexOf(n,s+t.length);return e.substring(0,s+t.length)+(i>=0?e.substring(i):"")}(e=(e=e.replace(/<!--\[if gte vml 1]>/g,"")).replace(/<o:SmartTagType(?:\s+[^\s>=]+(?:="[^"]*")?)*\s*\/?>/gi,""))),i=n.parseFromString(s,"text/html");B(i);const o=i.body.innerHTML,l=function(e,t){const n=new r.ViewDocument(t),s=new r.ViewDomConverter(n,{renderingMode:"data"}),i=e.createDocumentFragment(),o=e.body.childNodes;for(;o.length>0;)i.appendChild(o[0]);return s.domToView(i,{skipComments:!0})}(i,t),a=function(e){const t=[],n=[],s=Array.from(e.getElementsByTagName("style"));for(const e of s)e.sheet&&e.sheet.cssRules&&e.sheet.cssRules.length&&(t.push(e.sheet),n.push(e.innerHTML));return{styles:t,stylesString:n.join(" ")}}(i);return{body:l,bodyString:o,styles:a.styles,stylesString:a.stylesString}}class U extends e.Plugin{static get pluginName(){return"PasteFromOffice"}static get licenseFeatureCode(){return"PFO"}static get isOfficialPlugin(){return!0}static get isPremiumPlugin(){return!0}static get requires(){return[t.ClipboardPipeline]}init(){const e=this.editor,t=e.plugins.get("ClipboardPipeline"),n=e.editing.view.document,s=[],i=this.editor.plugins.has("MultiLevelList");s.push(new A(n,i)),s.push(new R(n)),s.push(new V(n)),t.on("inputTransformation",(t,i)=>{if(i._isTransformedWithPasteFromOffice)return;if(e.model.document.selection.getFirstPosition().parent.is("element","codeBlock"))return;const r=i.dataTransfer.getData("text/html"),o=s.find(e=>e.isActive(r));o&&(i._parsedData||(i._parsedData=z(r,n.stylesProcessor)),o.execute(i),i._isTransformedWithPasteFromOffice=!0)},{priority:"high"})}}})(),(window.CKEditor5=window.CKEditor5||{}).pasteFromOffice=s})();
4
+ */(()=>{var e={237:e=>{"use strict";e.exports=CKEditor5.dll},331:(e,t,n)=>{e.exports=n(237)("./src/clipboard.js")},782:(e,t,n)=>{e.exports=n(237)("./src/core.js")},783:(e,t,n)=>{e.exports=n(237)("./src/engine.js")}},t={};function n(s){var i=t[s];if(void 0!==i)return i.exports;var r=t[s]={exports:{}};return e[s](r,r.exports,n),r.exports}n.d=(e,t)=>{for(var s in t)n.o(t,s)&&!n.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:t[s]})},n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var s={};(()=>{"use strict";n.r(s),n.d(s,{PasteFromOffice:()=>G,PasteFromOfficeGoogleDocsNormalizer:()=>j,PasteFromOfficeGoogleSheetsNormalizer:()=>N,PasteFromOfficeMSWordNormalizer:()=>L,_convertHexToBase64:()=>x,_convertPasteOfficeCssLengthToPx:()=>o,_isPasteOfficePxValue:()=>l,_normalizePasteOfficeSpaceRunSpans:()=>U,_normalizePasteOfficeSpacing:()=>z,_removePasteGoogleOfficeSheetsTag:()=>D,_removePasteMSOfficeAttributes:()=>w,_removePasteOfficeBoldWrapper:()=>_,_removePasteOfficeInvalidTableWidths:()=>C,_removePasteOfficeStyleBlock:()=>V,_removePasteOfficeXmlnsAttributes:()=>k,_replacePasteOfficeImagesSourceWithBase64:()=>v,_toPasteOfficePxValue:()=>a,_transformPasteOfficeBlockBrsToParagraphs:()=>$,_transformPasteOfficeBookmarks:()=>i,_transformPasteOfficeListItemLikeElementsIntoLists:()=>c,_transformPasteOfficeTables:()=>S,_unwrapPasteOfficeParagraphInListItem:()=>f,parsePasteOfficeHtml:()=>H});var e=n(782),t=n(331);function i(e,t){const n=[];for(const s of t.createRangeIn(e)){const e=s.item;e.is("element","a")&&!e.hasAttribute("href")&&(e.hasAttribute("id")||e.hasAttribute("name"))&&n.push(e)}for(const e of n){const n=e.parent.getChildIndex(e)+1,s=e.getChildren();t.insertChild(n,s,e.parent)}}var r=n(783);function o(e){const t=parseFloat(e);return e.endsWith("pt")?a(96*t/72):e.endsWith("pc")?a(12*t*96/72):e.endsWith("in")?a(96*t):e.endsWith("cm")?a(96*t/2.54):e.endsWith("mm")?a(t/10*96/2.54):e}function l(e){return void 0!==e&&e.endsWith("px")}function a(e){return Math.round(e)+"px"}function c(e,t,n){if(!e.childCount)return;const s=new r.ViewUpcastWriter(e.document),i=function(e,t){const n=t.createRangeIn(e),s=[],i=new Set;for(const e of n.getItems()){if(!e.is("element")||!e.name.match(/^(p|h\d+|li|div)$/))continue;let t=b(e);if(void 0===t||0!=parseFloat(t)||Array.from(e.getClassNames()).find(e=>e.startsWith("MsoList"))||(t=void 0),e.hasStyle("mso-list")&&"none"!==e.getStyle("mso-list")||void 0!==t&&i.has(t)){const n=h(e);s.push({element:e,id:n.id,order:n.order,indent:n.indent,marginLeft:t}),void 0!==t&&i.add(t)}else i.clear()}return s}(e,s);if(!i.length)return;const o={},c=[];for(const e of i)if(void 0!==e.indent){m(e)||(c.length=0);const i=`${e.id}:${e.indent}`,r=Math.min(e.indent-1,c.length);if(r<c.length&&c[r].id!==e.id&&(c.length=r),r<c.length-1)c.length=r+1;else{const f=d(e,t);if(r>c.length-1||c[r].listElement.name!=f.type){0==r&&"ol"==f.type&&void 0!==e.id&&o[i]&&(f.startIndex=o[i]);const t=g(f,s,n);if(l(e.marginLeft)&&(0==r||l(c[r-1].marginLeft))){let n=e.marginLeft;r>0&&(n=a(parseFloat(n)-parseFloat(c[r-1].marginLeft))),s.setStyle("padding-left",n,t)}if(0==c.length){const n=e.element.parent,i=n.getChildIndex(e.element)+1;s.insertChild(i,t,n)}else{const e=c[r-1].listItemElements;s.appendChild(t,e[e.length-1])}c[r]={...e,listElement:t,listItemElements:[]},0==r&&void 0!==e.id&&(o[i]=f.startIndex||1)}}const f="li"==e.element.name?e.element:s.createElement("li");s.appendChild(f,c[r].listElement),c[r].listItemElements.push(f),0==r&&void 0!==e.id&&o[i]++,e.element!=f&&s.appendChild(e.element,f),y(e.element,s),s.removeStyle("text-indent",e.element),s.removeStyle("margin-left",e.element)}else{const t=c.find(t=>t.marginLeft==e.marginLeft);if(t){const n=t.listItemElements;s.appendChild(e.element,n[n.length-1]),s.removeStyle("margin-left",e.element)}else c.length=0}}function f(e,t){for(const n of t.createRangeIn(e)){const e=n.item;if(e.is("element","li")){const n=e.getChild(0);n&&n.is("element","p")&&t.unwrapElement(n)}}}function m(e){const t=e.element.previousSibling;if(!t){const t=e.element.parent;return u(t)&&(!t.previousSibling||u(t.previousSibling))}return u(t)}function u(e){return e.is("element","ol")||e.is("element","ul")}function d(e,t){const n=new RegExp(`@list l${e.id}:level${e.indent}\\s*({[^}]*)`,"gi"),s=/mso-level-number-format:([^;]{0,100});/gi,i=/mso-level-start-at:\s{0,100}([0-9]{0,10})\s{0,100};/gi,r=new RegExp(`@list\\s+l${e.id}:level\\d\\s*{[^{]*mso-level-text:"%\\d\\\\.`,"gi"),o=new RegExp(`@list l${e.id}:level\\d\\s*{[^{]*mso-level-number-format:`,"gi"),l=r.exec(t),a=o.exec(t),c=l&&!a,f=n.exec(t);let m="decimal",u="ol",d=null;if(f&&f[1]){const t=s.exec(f[1]);if(t&&t[1]&&(m=t[1].trim(),u="bullet"!==m&&"image"!==m?"ol":"ul"),"bullet"===m){const t=function(e){if("li"==e.name&&"ul"==e.parent.name&&e.parent.hasAttribute("type"))return e.parent.getAttribute("type");const t=function(e){if(e.getChild(0).is("$text"))return null;for(const t of e.getChildren()){if(!t.is("element","span"))continue;const e=t.getChild(0);if(e)return e.is("$text")?e:e.getChild(0)}return null}(e);if(!t)return null;const n=t._data;if("o"===n)return"circle";if("·"===n)return"disc";if("§"===n)return"square";return null}(e.element);t&&(m=t)}else{const e=i.exec(f[1]);e&&e[1]&&(d=parseInt(e[1]))}c&&(u="ol")}return{type:u,startIndex:d,style:p(m),isLegalStyleList:c}}function p(e){if(e.startsWith("arabic-leading-zero"))return"decimal-leading-zero";switch(e){case"alpha-upper":return"upper-alpha";case"alpha-lower":return"lower-alpha";case"roman-upper":return"upper-roman";case"roman-lower":return"lower-roman";case"circle":case"disc":case"square":return e;default:return null}}function g(e,t,n){const s=t.createElement(e.type);return e.style&&t.setStyle("list-style-type",e.style,s),e.startIndex&&e.startIndex>1&&t.setAttribute("start",e.startIndex,s),e.isLegalStyleList&&n&&t.addClass("legal-list",s),s}function h(e){const t=e.getStyle("mso-list");if(void 0===t)return{};const n=t.match(/(^|\s{1,100})l(\d+)/i),s=t.match(/\s{0,100}lfo(\d+)/i),i=t.match(/\s{0,100}level(\d+)/i);return n&&s&&i?{id:n[2],order:s[1],indent:parseInt(i[1])}:{indent:1}}function y(e,t){const n=new r.Matcher({name:"span",styles:{"mso-list":"Ignore"}}),s=t.createRangeIn(e);for(const e of s)"elementStart"===e.type&&n.match(e.item)&&t.remove(e.item)}function b(e){const t=e.getStyle("margin-left");return void 0===t||t.endsWith("px")?t:o(t)}function v(e,t){if(!e.childCount)return;const n=new r.ViewUpcastWriter(e.document),s=function(e,t){const n=t.createRangeIn(e),s=new r.Matcher({name:/v:(.+)/}),i=[];for(const e of n){if("elementStart"!=e.type)continue;const t=e.item,n=t.previousSibling,r=n&&n.is("element")?n.name:null,o=["Chart"],l=s.match(t),a=t.getAttribute("o:gfxdata"),c="v:shapetype"===r,f=a&&o.some(e=>t.getAttribute("id").includes(e));l&&a&&!c&&!f&&i.push(e.item.getAttribute("id"))}return i}(e,n);!function(e,t,n){const s=n.createRangeIn(t),i=new r.Matcher({name:"img"}),o=[];for(const t of s)if(t.item.is("element")&&i.match(t.item)){const n=t.item,s=n.getAttribute("v:shapes")?n.getAttribute("v:shapes").split(" "):[];s.length&&s.every(t=>e.indexOf(t)>-1)?o.push(n):n.getAttribute("src")||o.push(n)}for(const e of o)n.remove(e)}(s,e,n),function(e,t,n){const s=n.createRangeIn(t),i=[];for(const t of s)if("elementStart"==t.type&&t.item.is("element","v:shape")){const n=t.item.getAttribute("id");if(e.includes(n))continue;r(t.item.parent.getChildren(),n)||i.push(t.item)}for(const e of i){const t={src:o(e)};e.hasAttribute("alt")&&(t.alt=e.getAttribute("alt"));const s=n.createElement("img",t);n.insertChild(e.index+1,s,e.parent)}function r(e,t){for(const n of e)if(n.is("element")){if("img"==n.name&&n.getAttribute("v:shapes")==t)return!0;if(r(n.getChildren(),t))return!0}return!1}function o(e){for(const t of e.getChildren())if(t.is("element")&&t.getAttribute("src"))return t.getAttribute("src")}}(s,e,n),function(e,t){const n=t.createRangeIn(e),s=new r.Matcher({name:/v:(.+)/}),i=[];for(const e of n)"elementStart"==e.type&&s.match(e.item)&&i.push(e.item);for(const e of i)t.remove(e)}(e,n);const i=function(e,t){const n=t.createRangeIn(e),s=new r.Matcher({name:"img"}),i=[];let o=0;for(const e of n)e.item.is("element")&&s.match(e.item)&&(e.item.getAttribute("src").startsWith("file://")&&i.push({element:e.item,imageIndex:o}),o++);return i}(e,n);i.length&&function(e,t,n){for(let s=0;s<e.length;s++){const{element:i,imageIndex:r}=e[s],o=t[r];if(o){const e=`data:${o.type};base64,${x(o.hex)}`;n.setAttribute("src",e,i)}}}(i,function(e){if(!e)return[];const t=/{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\blipuid\s?[\da-fA-F]+)?[\s}]*?/,n=new RegExp("(?:("+t.source+"))([\\da-fA-F\\s]+)\\}","g"),s=e.match(n),i=[];if(s)for(const e of s){let n=!1;e.includes("\\pngblip")?n="image/png":e.includes("\\jpegblip")&&(n="image/jpeg"),n&&i.push({hex:e.replace(t,"").replace(/[^\da-fA-F]/g,""),type:n})}return i}(t),n)}function x(e){return btoa(e.match(/\w{2}/g).map(e=>String.fromCharCode(parseInt(e,16))).join(""))}function w(e){const t=[],n=new r.ViewUpcastWriter(e.document);for(const{item:s}of n.createRangeIn(e))if(s.is("element")){for(const e of s.getClassNames())/\bmso/gi.exec(e)&&n.removeClass(e,s);for(const e of s.getStyleNames())/\bmso/gi.exec(e)&&n.removeStyle(e,s);(s.is("element","w:sdt")||s.is("element","w:sdtpr")&&s.isEmpty||s.is("element","o:p")&&s.isEmpty)&&t.push(s)}for(const e of t){const t=e.parent,s=t.getChildIndex(e);n.insertChild(s,e.getChildren(),t),n.remove(e)}}function S(e,t){for(const n of t.createRangeIn(e).getItems()){if(!n.is("element","table")&&!n.is("element","td")&&!n.is("element","th"))continue;const e=["left","top","right","bottom"];if(e.every(e=>!n.hasStyle(`border-${e}-style`)))t.setStyle("border-style","none",n);else for(const s of e)n.hasStyle(`border-${s}-style`)||t.setStyle(`border-${s}-style`,"none",n);const s=["width","height",...e.map(e=>`border-${e}-width`),...e.map(e=>`padding-${e}`)];for(const e of s)n.hasStyle(e)&&t.setStyle(e,o(n.getStyle(e)),n)}}function C(e,t){for(const n of t.createRangeIn(e).getItems())n.is("element","table")&&("0px"===n.getStyle("width")&&t.removeStyle("width",n),"0"===n.getAttribute("width")&&t.removeAttribute("width",n))}function I(e,t){const n=[],s=[];for(const{item:i}of e.createRangeIn(t))if(i.is("element")&&i.getStyle("mso-footnote-id")){n.unshift(i);const{nextSibling:e}=i;e?.is("$text")&&e.data.startsWith(" ")&&s.unshift(e)}for(const t of n)e.remove(t);for(const t of s){const n=t.data.substring(1);if(n.length>0){const s=t.parent,i=s.getChildIndex(t),r=e.createText(n);e.remove(t),e.insertChild(i,r,s)}else e.remove(t)}return t}function P(e,t){const n=e.createElement("sup",{class:"footnote"}),s=e.createElement("a",{id:`ref-${t}`,href:`#${t}`});return e.appendChild(s,n),n}function A(e,t){const n=e.createElement("li",{id:t,class:"footnote-definition"}),s=e.createElement("a",{href:`#ref-${t}`,class:"footnote-backlink"}),i=e.createElement("div",{class:"footnote-content"});return e.appendChild(e.createText("^"),s),e.appendChild(s,n),e.appendChild(i,n),{listItem:n,content:i}}const O=/<meta\s*name="?generator"?\s*content="?microsoft\s*word\s*\d+"?\/?>/i,E=/xmlns:o="urn:schemas-microsoft-com/i;class L{document;hasMultiLevelListPlugin;constructor(e,t=!1){this.document=e,this.hasMultiLevelListPlugin=t}isActive(e){return O.test(e)||E.test(e)}execute(e){const t=new r.ViewUpcastWriter(this.document),{body:n,stylesString:s}=e._parsedData;i(n,t),c(n,s,this.hasMultiLevelListPlugin),v(n,e.dataTransfer.getData("text/rtf")),S(n,t),C(n,t),function(e,t){const n=new Map,s=new Map;let i=null;for(const{item:r}of t.createRangeIn(e))if(r.is("element"))if("footnote-list"!==r.getStyle("mso-element")){if(r.hasStyle("mso-footnote-id")){const e=r.findAncestor("element",e=>"footnote"===e.getStyle("mso-element"));if(e){const t=e.getAttribute("id");s.set(t,e)}else{const e=r.getStyle("mso-footnote-id");n.set(e,r)}continue}}else i=r;if(!n.size||!i)return;const r=function(e){return e.createElement("ol",{class:"footnotes"})}(t);t.replace(i,r);for(const[e,i]of n){const n=s.get(e);if(!n)continue;t.replace(i,P(t,e));const o=A(t,e);I(t,n);for(const e of n.getChildren()){let n=e;e.is("element")&&(n=t.clone(e,!0)),t.appendChild(n,o.content)}t.appendChild(o.listItem,r)}}(n,t),w(n),e.content=n}}function _(e,t){for(const n of e.getChildren())if(n.is("element","b")&&"normal"===n.getStyle("font-weight")){const s=e.getChildIndex(n);t.remove(n),t.insertChild(s,n.getChildren(),e)}}function $(e,t){const n=new r.ViewDocument(t.document.stylesProcessor),s=new r.ViewDomConverter(n,{renderingMode:"data"}),i=s.blockElements,o=s.inlineObjectElements,l=[];for(const n of t.createRangeIn(e)){const e=n.item;if(e.is("element","br")){const n=M(e,"forward",t,{blockElements:i,inlineObjectElements:o}),s=M(e,"backward",t,{blockElements:i,inlineObjectElements:o}),r=T(n,i);(T(s,i)||r)&&l.push(e)}}for(const e of l)e.hasClass("Apple-interchange-newline")?t.remove(e):t.replace(e,t.createElement("p"))}function M(e,t,n,{blockElements:s,inlineObjectElements:i}){let r=n.createPositionAt(e,"forward"==t?"after":"before");return r=r.getLastMatchingPosition(({item:e})=>e.is("element")&&!s.includes(e.name)&&!i.includes(e.name),{direction:t}),"forward"==t?r.nodeAfter:r.nodeBefore}function T(e,t){return!!e&&e.is("element")&&t.includes(e.name)}function R(e){let t=e;for(;t;){if(t.is("element")){const e=t.getStyle?.("white-space");if("pre-wrap"===e)return!0}t=t.parent}return!1}function W(e,t,n){const{parent:s,data:i}=e,r=i.replaceAll("\t"," ".repeat(n)),o=s.getChildIndex(e);t.remove(e),t.insertChild(o,t.createText(r),s)}const F=/id=("|')docs-internal-guid-[-0-9a-f]+("|')/i;class j{document;constructor(e){this.document=e}isActive(e){return F.test(e)}execute(e){const t=new r.ViewUpcastWriter(this.document),{body:n}=e._parsedData;_(n,t),f(n,t),$(n,t),function(e,t,n){const s=new Set;for(const n of t.createRangeIn(e).getItems())n.is("view:$textProxy")&&n.data.includes("\t")&&R(n.parent)&&s.add(n.textNode);for(const e of s)W(e,t,n)}(n,t,8),e.content=n}}function k(e,t){for(const n of e.getChildren())n.is("element","table")&&n.hasAttribute("xmlns")&&t.removeAttribute("xmlns",n)}function D(e,t){for(const n of e.getChildren())if(n.is("element","google-sheets-html-origin")){const s=e.getChildIndex(n);t.remove(n),t.insertChild(s,n.getChildren(),e)}}function V(e,t){for(const n of Array.from(e.getChildren()))n.is("element","style")&&t.remove(n)}const B=/<google-sheets-html-origin/i;class N{document;constructor(e){this.document=e}isActive(e){return B.test(e)}execute(e){const t=new r.ViewUpcastWriter(this.document),{body:n}=e._parsedData;D(n,t),k(n,t),C(n,t),V(n,t),e.content=n}}function z(e){return q(q(e)).replace(/(<span\s+style=['"]mso-spacerun:yes['"]>[^\S\r\n]*?)[\r\n]+([^\S\r\n]*<\/span>)/g,"$1$2").replace(/<span\s+style=['"]mso-spacerun:yes['"]><\/span>/g,"").replace(/(<span\s+style=['"]letter-spacing:[^'"]+?['"]>)[\r\n]+(<\/span>)/g,"$1 $2").replace(/ <\//g," </").replace(/ <o:p><\/o:p>/g," <o:p></o:p>").replace(/<o:p>(&nbsp;|\u00A0)<\/o:p>/g,"").replace(/>([^\S\r\n]*[\r\n]\s*)</g,"><")}function U(e){e.querySelectorAll("span[style*=spacerun]").forEach(e=>{const t=e,n=t.innerText.length||0;t.innerText=Array(n+1).join("  ").substr(0,n)})}function q(e){return e.replace(/<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g,(e,t)=>1===t.length?" ":Array(t.length+1).join("  ").substr(0,t.length))}function H(e,t){const n=new DOMParser,s=z(function(e){const t="</body>",n="</html>",s=e.indexOf(t);if(s<0)return e;const i=e.indexOf(n,s+t.length);return e.substring(0,s+t.length)+(i>=0?e.substring(i):"")}(e=(e=e.replace(/<!--\[if gte vml 1]>/g,"")).replace(/<o:SmartTagType(?:\s+[^\s>=]+(?:="[^"]*")?)*\s*\/?>/gi,""))),i=n.parseFromString(s,"text/html");U(i);const o=i.body.innerHTML,l=function(e,t){const n=new r.ViewDocument(t),s=new r.ViewDomConverter(n,{renderingMode:"data"}),i=e.createDocumentFragment(),o=e.body.childNodes;for(;o.length>0;)i.appendChild(o[0]);return s.domToView(i,{skipComments:!0})}(i,t),a=function(e){const t=[],n=[],s=Array.from(e.getElementsByTagName("style"));for(const e of s)e.sheet&&e.sheet.cssRules&&e.sheet.cssRules.length&&(t.push(e.sheet),n.push(e.innerHTML));return{styles:t,stylesString:n.join(" ")}}(i);return{body:l,bodyString:o,styles:a.styles,stylesString:a.stylesString}}class G extends e.Plugin{static get pluginName(){return"PasteFromOffice"}static get licenseFeatureCode(){return"PFO"}static get isOfficialPlugin(){return!0}static get isPremiumPlugin(){return!0}static get requires(){return[t.ClipboardPipeline]}init(){const e=this.editor,t=e.plugins.get("ClipboardPipeline"),n=e.editing.view.document,s=[],i=this.editor.plugins.has("MultiLevelList");s.push(new L(n,i)),s.push(new j(n)),s.push(new N(n)),t.on("inputTransformation",(t,i)=>{if(i._isTransformedWithPasteFromOffice)return;if(e.model.document.selection.getFirstPosition().parent.is("element","codeBlock"))return;const r=i.dataTransfer.getData("text/html"),o=s.find(e=>e.isActive(r));o&&(i._parsedData||(i._parsedData=H(r,n.stylesProcessor)),o.execute(i),i._isTransformedWithPasteFromOffice=!0)},{priority:"high"})}}})(),(window.CKEditor5=window.CKEditor5||{}).pasteFromOffice=s})();
package/dist/index.js CHANGED
@@ -224,7 +224,7 @@ import { ViewUpcastWriter, Matcher, ViewDocument, ViewDomConverter } from '@cked
224
224
  marginLeft = undefined;
225
225
  }
226
226
  // List item or a following list item block.
227
- if (item.hasStyle('mso-list') || marginLeft !== undefined && foundMargins.has(marginLeft)) {
227
+ if (item.hasStyle('mso-list') && item.getStyle('mso-list') !== 'none' || marginLeft !== undefined && foundMargins.has(marginLeft)) {
228
228
  const itemData = getListItemData(item);
229
229
  itemLikeElements.push({
230
230
  element: item,
@@ -825,6 +825,214 @@ function isList(element) {
825
825
  }
826
826
  }
827
827
 
828
+ /**
829
+ * @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
830
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
831
+ */ /**
832
+ * @module paste-from-office/filters/replacemsfootnotes
833
+ */ /**
834
+ * Replaces MS Word specific footnotes references and definitions with proper elements.
835
+ *
836
+ * Things to know about MS Word footnotes:
837
+ *
838
+ * * Footnote references in Word are marked with `mso-footnote-id` style.
839
+ * * Word does not support nested footnotes, so references within definitions are ignored.
840
+ * * Word appends extra spaces after footnote references within definitions, which are trimmed.
841
+ * * Footnote definitions list is marked with `mso-element: footnote-list` style it contain `mso-element: footnote` elements.
842
+ * * Footnote definition might contain tables, lists and other elements, not only text. They are placed directly within `li` element,
843
+ * without any wrapper (in opposition to text content of the definition, which is placed within `MsoFootnoteText` element).
844
+ *
845
+ * Example pseudo document showing MS Word footnote structure:
846
+ *
847
+ * ```html
848
+ * <p>Text with footnote<a style='mso-footnote-id:ftn1'>[1]</a> reference.</p>
849
+ *
850
+ * <div style='mso-element:footnote-list'>
851
+ * <div style='mso-element:footnote' id=ftn1>
852
+ * <p class=MsoFootnoteText><a style='mso-footnote-id:ftn1'>[1]</a> Footnote content</p>
853
+ * <table class="MsoTableGrid">...</table>
854
+ * </div>
855
+ * </div>
856
+ * ```
857
+ *
858
+ * Will be transformed into:
859
+ *
860
+ * ```html
861
+ * <p>Text with footnote<sup class="footnote"><a id="ref-footnote-ftn1" href="#footnote-ftn1">1</a></sup> reference.</p>
862
+ *
863
+ * <ol class="footnotes">
864
+ * <li class="footnote-definition" id="footnote-ftn1">
865
+ * <a href="#ref-footnote-ftn1" class="footnote-backlink">^</a>
866
+ * <div class="footnote-content">
867
+ * <p>Footnote content</p>
868
+ * <table>...</table>
869
+ * </div>
870
+ * </li>
871
+ * </ol>
872
+ * ```
873
+ *
874
+ * @param documentFragment `data.content` obtained from clipboard.
875
+ * @param writer The view writer instance.
876
+ * @internal
877
+ */ function replaceMSFootnotes(documentFragment, writer) {
878
+ const msFootnotesRefs = new Map();
879
+ const msFootnotesDefs = new Map();
880
+ let msFootnotesDefinitionsList = null;
881
+ // Phase 1: Collect all footnotes references and definitions. Find the footnotes definitions list element.
882
+ for (const { item } of writer.createRangeIn(documentFragment)){
883
+ if (!item.is('element')) {
884
+ continue;
885
+ }
886
+ // If spot a footnotes definitions element, let's store it. It'll be replaced later.
887
+ // There should be only one such element in the document.
888
+ if (item.getStyle('mso-element') === 'footnote-list') {
889
+ msFootnotesDefinitionsList = item;
890
+ continue;
891
+ }
892
+ // If spot a footnote reference or definition, store it in the corresponding map.
893
+ if (item.hasStyle('mso-footnote-id')) {
894
+ const msFootnoteDef = item.findAncestor('element', (el)=>el.getStyle('mso-element') === 'footnote');
895
+ if (msFootnoteDef) {
896
+ // If it's a reference within a definition, ignore it and track only the definition.
897
+ // MS Word do not support nested footnotes, so it's safe to assume that all references within
898
+ // a definition point to the same definition.
899
+ const msFootnoteDefId = msFootnoteDef.getAttribute('id');
900
+ msFootnotesDefs.set(msFootnoteDefId, msFootnoteDef);
901
+ } else {
902
+ // If it's a reference outside of a definition, track it as a reference.
903
+ const msFootnoteRefId = item.getStyle('mso-footnote-id');
904
+ msFootnotesRefs.set(msFootnoteRefId, item);
905
+ }
906
+ continue;
907
+ }
908
+ }
909
+ // If there are no footnotes references or definitions, or no definitions list, there's nothing to normalize.
910
+ if (!msFootnotesRefs.size || !msFootnotesDefinitionsList) {
911
+ return;
912
+ }
913
+ // Phase 2: Replace footnotes definitions list with proper element.
914
+ const footnotesDefinitionsList = createFootnotesListViewElement(writer);
915
+ writer.replace(msFootnotesDefinitionsList, footnotesDefinitionsList);
916
+ // Phase 3: Replace all footnotes references and add matching definitions to the definitions list.
917
+ for (const [footnoteId, msFootnoteRef] of msFootnotesRefs){
918
+ const msFootnoteDef = msFootnotesDefs.get(footnoteId);
919
+ if (!msFootnoteDef) {
920
+ continue;
921
+ }
922
+ // Replace footnote reference.
923
+ writer.replace(msFootnoteRef, createFootnoteRefViewElement(writer, footnoteId));
924
+ // Append found matching definition to the definitions list.
925
+ // Order doesn't matter here, as it'll be fixed in the post-fixer.
926
+ const defElements = createFootnoteDefViewElement(writer, footnoteId);
927
+ removeMSReferences(writer, msFootnoteDef);
928
+ // Insert content within the `MsoFootnoteText` element. It's usually a definition text content.
929
+ for (const child of msFootnoteDef.getChildren()){
930
+ let clonedChild = child;
931
+ if (child.is('element')) {
932
+ clonedChild = writer.clone(child, true);
933
+ }
934
+ writer.appendChild(clonedChild, defElements.content);
935
+ }
936
+ writer.appendChild(defElements.listItem, footnotesDefinitionsList);
937
+ }
938
+ }
939
+ /**
940
+ * Removes all MS Office specific references from the given element.
941
+ *
942
+ * It also removes leading space from text nodes following the references, as MS Word adds
943
+ * them to separate the reference from the rest of the text.
944
+ *
945
+ * @param writer The view writer.
946
+ * @param element The element to trim.
947
+ * @returns The trimmed element.
948
+ */ function removeMSReferences(writer, element) {
949
+ const elementsToRemove = [];
950
+ const textNodesToTrim = [];
951
+ for (const { item } of writer.createRangeIn(element)){
952
+ if (item.is('element') && item.getStyle('mso-footnote-id')) {
953
+ elementsToRemove.unshift(item);
954
+ // MS Word used to add spaces after footnote references within definitions. Let's check if there's a space after
955
+ // the footnote reference and mark it for trimming.
956
+ const { nextSibling } = item;
957
+ if (nextSibling?.is('$text') && nextSibling.data.startsWith(' ')) {
958
+ textNodesToTrim.unshift(nextSibling);
959
+ }
960
+ }
961
+ }
962
+ for (const element of elementsToRemove){
963
+ writer.remove(element);
964
+ }
965
+ // Remove only the leading space from text nodes following reference within definition, preserve the rest of the text.
966
+ for (const textNode of textNodesToTrim){
967
+ const trimmedData = textNode.data.substring(1);
968
+ if (trimmedData.length > 0) {
969
+ // Create a new text node and replace the old one.
970
+ const parent = textNode.parent;
971
+ const index = parent.getChildIndex(textNode);
972
+ const newTextNode = writer.createText(trimmedData);
973
+ writer.remove(textNode);
974
+ writer.insertChild(index, newTextNode, parent);
975
+ } else {
976
+ // If the text node contained only a space, remove it entirely.
977
+ writer.remove(textNode);
978
+ }
979
+ }
980
+ return element;
981
+ }
982
+ /**
983
+ * Creates a footnotes list view element.
984
+ *
985
+ * @param writer The view writer instance.
986
+ * @returns The footnotes list view element.
987
+ */ function createFootnotesListViewElement(writer) {
988
+ return writer.createElement('ol', {
989
+ class: 'footnotes'
990
+ });
991
+ }
992
+ /**
993
+ * Creates a footnote reference view element.
994
+ *
995
+ * @param writer The view writer instance.
996
+ * @param footnoteId The footnote ID.
997
+ * @returns The footnote reference view element.
998
+ */ function createFootnoteRefViewElement(writer, footnoteId) {
999
+ const sup = writer.createElement('sup', {
1000
+ class: 'footnote'
1001
+ });
1002
+ const link = writer.createElement('a', {
1003
+ id: `ref-${footnoteId}`,
1004
+ href: `#${footnoteId}`
1005
+ });
1006
+ writer.appendChild(link, sup);
1007
+ return sup;
1008
+ }
1009
+ /**
1010
+ * Creates a footnote definition view element with a backlink and a content container.
1011
+ *
1012
+ * @param writer The view writer instance.
1013
+ * @param footnoteId The footnote ID.
1014
+ * @returns An object containing the list item element, backlink and content container.
1015
+ */ function createFootnoteDefViewElement(writer, footnoteId) {
1016
+ const listItem = writer.createElement('li', {
1017
+ id: footnoteId,
1018
+ class: 'footnote-definition'
1019
+ });
1020
+ const backLink = writer.createElement('a', {
1021
+ href: `#ref-${footnoteId}`,
1022
+ class: 'footnote-backlink'
1023
+ });
1024
+ const content = writer.createElement('div', {
1025
+ class: 'footnote-content'
1026
+ });
1027
+ writer.appendChild(writer.createText('^'), backLink);
1028
+ writer.appendChild(backLink, listItem);
1029
+ writer.appendChild(content, listItem);
1030
+ return {
1031
+ listItem,
1032
+ content
1033
+ };
1034
+ }
1035
+
828
1036
  const msWordMatch1 = /<meta\s*name="?generator"?\s*content="?microsoft\s*word\s*\d+"?\/?>/i;
829
1037
  const msWordMatch2 = /xmlns:o="urn:schemas-microsoft-com/i;
830
1038
  /**
@@ -855,6 +1063,7 @@ const msWordMatch2 = /xmlns:o="urn:schemas-microsoft-com/i;
855
1063
  replaceImagesSourceWithBase64(documentFragment, data.dataTransfer.getData('text/rtf'));
856
1064
  transformTables(documentFragment, writer);
857
1065
  removeInvalidTableWidth(documentFragment, writer);
1066
+ replaceMSFootnotes(documentFragment, writer);
858
1067
  removeMSAttributes(documentFragment);
859
1068
  data.content = documentFragment;
860
1069
  }