@ckeditor/ckeditor5-paste-from-office 47.1.0-alpha.2 → 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 +1 -1
- package/build/paste-from-office.js +1 -1
- package/dist/index.js +210 -1
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/filters/list.js +2 -1
- package/src/filters/replacemsfootnotes.d.ts +54 -0
- package/src/filters/replacemsfootnotes.js +209 -0
- package/src/normalizers/mswordnormalizer.js +2 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@ CKEditor 5 paste from Office feature
|
|
|
2
2
|
==================================
|
|
3
3
|
|
|
4
4
|
[](https://www.npmjs.com/package/@ckeditor/ckeditor5-paste-from-office)
|
|
5
|
-
[](https://codecov.io/gh/ckeditor/ckeditor5)
|
|
6
6
|
[](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>( |\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>( |\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
|
}
|