@ckeditor/ckeditor5-paste-from-office 41.3.0-alpha.3 → 41.3.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.
@@ -1,4 +1,4 @@
1
1
  /*!
2
2
  * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md.
4
- */(()=>{var e={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")},237:e=>{"use strict";e.exports=CKEditor5.dll}},t={};function n(r){var s=t[r];if(void 0!==s)return s.exports;var i=t[r]={exports:{}};return e[r](i,i.exports,n),i.exports}n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},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 r={};(()=>{"use strict";n.r(r),n.d(r,{MSWordNormalizer:()=>d,PasteFromOffice:()=>C,parseHtml:()=>x});var e=n(782),t=n(331),s=n(783);function i(e,t){if(!e.childCount)return;const n=new s.UpcastWriter(e.document),r=function(e,t){const n=t.createRangeIn(e),r=new s.Matcher({name:/^p|h\d+$/,styles:{"mso-list":/.*/}}),i=[];for(const e of n)if("elementStart"===e.type&&r.match(e.item)){const t=l(e.item);i.push({element:e.item,id:t.id,order:t.order,indent:t.indent})}return i}(e,n);if(!r.length)return;let i=null,a=1;r.forEach(((e,l)=>{const u=function(e,t){if(!e)return!0;if(e.id!==t.id)return t.indent-e.indent!=1;const n=t.element.previousSibling;if(!n)return!0;return r=n,!(r.is("element","ol")||r.is("element","ul"));var r}(r[l-1],e),m=u?null:r[l-1],f=(p=e,(d=m)?p.indent-d.indent:p.indent-1);var d,p;if(u&&(i=null,a=1),!i||0!==f){const r=function(e,t){const n=new RegExp(`@list l${e.id}:level${e.indent}\\s*({[^}]*)`,"gi"),r=/mso-level-number-format:([^;]{0,100});/gi,s=/mso-level-start-at:\s{0,100}([0-9]{0,10})\s{0,100};/gi,i=n.exec(t);let c="decimal",l="ol",a=null;if(i&&i[1]){const t=r.exec(i[1]);if(t&&t[1]&&(c=t[1].trim(),l="bullet"!==c&&"image"!==c?"ol":"ul"),"bullet"===c){const t=function(e){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&&(c=t)}else{const e=s.exec(i[1]);e&&e[1]&&(a=parseInt(e[1]))}}return{type:l,startIndex:a,style:o(c)}}(e,t);if(i){if(e.indent>a){const e=i.getChild(i.childCount-1),t=e.getChild(e.childCount-1);i=c(r,t,n),a+=1}else if(e.indent<a){const t=a-e.indent;i=function(e,t){const n=e.getAncestors({parentFirst:!0});let r=null,s=0;for(const e of n)if((e.is("element","ul")||e.is("element","ol"))&&s++,s===t){r=e;break}return r}(i,t),a=e.indent}}else i=c(r,e.element,n);e.indent<=a&&(i.is("element",r.type)||(i=n.rename(r.type,i)))}const g=function(e,t){return function(e,t){const n=new s.Matcher({name:"span",styles:{"mso-list":"Ignore"}}),r=t.createRangeIn(e);for(const e of r)"elementStart"===e.type&&n.match(e.item)&&t.remove(e.item)}(e,t),t.removeStyle("text-indent",e),t.rename("li",e)}(e.element,n);n.appendChild(g,i)}))}function o(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 c(e,t,n){const r=t.parent,s=n.createElement(e.type),i=r.getChildIndex(t)+1;return n.insertChild(i,s,r),e.style&&n.setStyle("list-style-type",e.style,s),e.startIndex&&e.startIndex>1&&n.setAttribute("start",e.startIndex,s),s}function l(e){const t={},n=e.getStyle("mso-list");if(n){const e=n.match(/(^|\s{1,100})l(\d+)/i),r=n.match(/\s{0,100}lfo(\d+)/i),s=n.match(/\s{0,100}level(\d+)/i);e&&r&&s&&(t.id=e[2],t.order=r[1],t.indent=parseInt(s[1]))}return t}function a(e,t){if(!e.childCount)return;const n=new s.UpcastWriter(e.document),r=function(e,t){const n=t.createRangeIn(e),r=new s.Matcher({name:/v:(.+)/}),i=[];for(const e of n){if("elementStart"!=e.type)continue;const t=e.item,n=t.previousSibling,s=n&&n.is("element")?n.name:null,o=["Chart"],c=r.match(t),l=t.getAttribute("o:gfxdata"),a="v:shapetype"===s,u=l&&o.some((e=>t.getAttribute("id").includes(e)));c&&l&&!a&&!u&&i.push(e.item.getAttribute("id"))}return i}(e,n);!function(e,t,n){const r=n.createRangeIn(t),i=new s.Matcher({name:"img"}),o=[];for(const t of r)if(t.item.is("element")&&i.match(t.item)){const n=t.item,r=n.getAttribute("v:shapes")?n.getAttribute("v:shapes").split(" "):[];r.length&&r.every((t=>e.indexOf(t)>-1))?o.push(n):n.getAttribute("src")||o.push(n)}for(const e of o)n.remove(e)}(r,e,n),function(e,t,n){const r=n.createRangeIn(t),s=[];for(const t of r)if("elementStart"==t.type&&t.item.is("element","v:shape")){const n=t.item.getAttribute("id");if(e.includes(n))continue;i(t.item.parent.getChildren(),n)||s.push(t.item)}for(const e of s){const t={src:o(e)};e.hasAttribute("alt")&&(t.alt=e.getAttribute("alt"));const r=n.createElement("img",t);n.insertChild(e.index+1,r,e.parent)}function i(e,t){for(const n of e)if(n.is("element")){if("img"==n.name&&n.getAttribute("v:shapes")==t)return!0;if(i(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")}}(r,e,n),function(e,t){const n=t.createRangeIn(e),r=new s.Matcher({name:/v:(.+)/}),i=[];for(const e of n)"elementStart"==e.type&&r.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),r=new s.Matcher({name:"img"}),i=[];for(const e of n)e.item.is("element")&&r.match(e.item)&&e.item.getAttribute("src").startsWith("file://")&&i.push(e.item);return i}(e,n);i.length&&function(e,t,n){if(e.length===t.length)for(let r=0;r<e.length;r++){const s=`data:${t[r].type};base64,${u(t[r].hex)}`;n.setAttribute("src",s,e[r])}}(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"),r=e.match(n),s=[];if(r)for(const e of r){let n=!1;e.includes("\\pngblip")?n="image/png":e.includes("\\jpegblip")&&(n="image/jpeg"),n&&s.push({hex:e.replace(t,"").replace(/[^\da-fA-F]/g,""),type:n})}return s}(t),n)}function u(e){return btoa(e.match(/\w{2}/g).map((e=>String.fromCharCode(parseInt(e,16)))).join(""))}const m=/<meta\s*name="?generator"?\s*content="?microsoft\s*word\s*\d+"?\/?>/i,f=/xmlns:o="urn:schemas-microsoft-com/i;class d{constructor(e){this.document=e}isActive(e){return m.test(e)||f.test(e)}execute(e){const{body:t,stylesString:n}=e._parsedData;i(t,n),a(t,e.dataTransfer.getData("text/rtf")),function(e){const t=[],n=new s.UpcastWriter(e.document);for(const{item:r}of n.createRangeIn(e))if(r.is("element")){for(const e of r.getClassNames())/\bmso/gi.exec(e)&&n.removeClass(e,r);for(const e of r.getStyleNames())/\bmso/gi.exec(e)&&n.removeStyle(e,r);r.is("element","w:sdt")&&t.push(r)}for(const e of t){const t=e.parent,r=t.getChildIndex(e);n.insertChild(r,e.getChildren(),t),n.remove(e)}}(t),e.content=t}}function p(e,t,n,{blockElements:r,inlineObjectElements:s}){let i=n.createPositionAt(e,"forward"==t?"after":"before");return i=i.getLastMatchingPosition((({item:e})=>e.is("element")&&!r.includes(e.name)&&!s.includes(e.name)),{direction:t}),"forward"==t?i.nodeAfter:i.nodeBefore}function g(e,t){return!!e&&e.is("element")&&t.includes(e.name)}const h=/id=("|')docs-internal-guid-[-0-9a-f]+("|')/i;class b{constructor(e){this.document=e}isActive(e){return h.test(e)}execute(e){const t=new s.UpcastWriter(this.document),{body:n}=e._parsedData;!function(e,t){for(const n of e.getChildren())if(n.is("element","b")&&"normal"===n.getStyle("font-weight")){const r=e.getChildIndex(n);t.remove(n),t.insertChild(r,n.getChildren(),e)}}(n,t),function(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)}}}(n,t),function(e,t){const n=new s.ViewDocument(t.document.stylesProcessor),r=new s.DomConverter(n,{renderingMode:"data"}),i=r.blockElements,o=r.inlineObjectElements,c=[];for(const n of t.createRangeIn(e)){const e=n.item;if(e.is("element","br")){const n=p(e,"forward",t,{blockElements:i,inlineObjectElements:o}),r=p(e,"backward",t,{blockElements:i,inlineObjectElements:o}),s=g(n,i);(g(r,i)||s)&&c.push(e)}}for(const e of c)e.hasClass("Apple-interchange-newline")?t.remove(e):t.replace(e,t.createElement("p"))}(n,t),e.content=n}}const y=/<google-sheets-html-origin/i;class v{constructor(e){this.document=e}isActive(e){return y.test(e)}execute(e){const t=new s.UpcastWriter(this.document),{body:n}=e._parsedData;!function(e,t){for(const n of e.getChildren())if(n.is("element","google-sheets-html-origin")){const r=e.getChildIndex(n);t.remove(n),t.insertChild(r,n.getChildren(),e)}}(n,t),function(e,t){for(const n of e.getChildren())n.is("element","table")&&n.hasAttribute("xmlns")&&t.removeAttribute("xmlns",n)}(n,t),function(e,t){for(const n of e.getChildren())n.is("element","table")&&"0px"===n.getStyle("width")&&t.removeStyle("width",n)}(n,t),function(e,t){for(const n of Array.from(e.getChildren()))n.is("element","style")&&t.remove(n)}(n,t),e.content=n}}function w(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 x(e,t){const n=new DOMParser,r=function(e){return w(w(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(e){const t="</body>",n="</html>",r=e.indexOf(t);if(r<0)return e;const s=e.indexOf(n,r+t.length);return e.substring(0,r+t.length)+(s>=0?e.substring(s):"")}(e=(e=e.replace(/<!--\[if gte vml 1]>/g,"")).replace(/<o:SmartTagType(?:\s+[^\s>=]+(?:="[^"]*")?)*\s*\/?>/gi,""))),i=n.parseFromString(r,"text/html");!function(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)}))}(i);const o=i.body.innerHTML,c=function(e,t){const n=new s.ViewDocument(t),r=new s.DomConverter(n,{renderingMode:"data"}),i=e.createDocumentFragment(),o=e.body.childNodes;for(;o.length>0;)i.appendChild(o[0]);return r.domToView(i,{skipComments:!0})}(i,t),l=function(e){const t=[],n=[],r=Array.from(e.getElementsByTagName("style"));for(const e of r)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:c,bodyString:o,styles:l.styles,stylesString:l.stylesString}}class C extends e.Plugin{static get pluginName(){return"PasteFromOffice"}static get requires(){return[t.ClipboardPipeline]}init(){const e=this.editor,t=e.plugins.get("ClipboardPipeline"),n=e.editing.view.document,r=[];r.push(new d(n)),r.push(new b(n)),r.push(new v(n)),t.on("inputTransformation",((t,s)=>{if(s._isTransformedWithPasteFromOffice)return;if(e.model.document.selection.getFirstPosition().parent.is("element","codeBlock"))return;const i=s.dataTransfer.getData("text/html"),o=r.find((e=>e.isActive(i)));o&&(s._parsedData||(s._parsedData=x(i,n.stylesProcessor)),o.execute(s),s._isTransformedWithPasteFromOffice=!0)}),{priority:"high"})}}})(),(window.CKEditor5=window.CKEditor5||{}).pasteFromOffice=r})();
4
+ */(()=>{var e={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")},237:e=>{"use strict";e.exports=CKEditor5.dll}},t={};function n(r){var s=t[r];if(void 0!==s)return s.exports;var i=t[r]={exports:{}};return e[r](i,i.exports,n),i.exports}n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},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 r={};(()=>{"use strict";n.r(r),n.d(r,{MSWordNormalizer:()=>x,PasteFromOffice:()=>P,parseHtml:()=>M});var e=n(782),t=n(331),s=n(783);function i(e){return void 0!==e&&e.endsWith("px")}function o(e){return e.toFixed(2).replace(/\.?0+$/,"")+"px"}function l(e,t,n){if(!e.childCount)return;const r=new s.UpcastWriter(e.document),l=function(e,t){const n=t.createRangeIn(e),r=[],s=new Set;for(const e of n.getItems()){if(!e.is("element")||!e.name.match(/^(p|h\d+|li|div)$/))continue;let t=g(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&&s.has(t)){const n=f(e);r.push({element:e,id:n.id,order:n.order,indent:n.indent,marginLeft:t}),void 0!==t&&s.add(t)}else s.clear()}return r}(e,r);if(!l.length)return;const a={},u=[];for(const e of l)if(void 0!==e.indent){c(e)||(u.length=0);const s=`${e.id}:${e.indent}`,l=Math.min(e.indent-1,u.length);if(l<u.length&&u[l].id!==e.id&&(u.length=l),l<u.length-1)u.length=l+1;else{const c=m(e,t);if(l>u.length-1||u[l].listElement.name!=c.type){0==l&&"ol"==c.type&&void 0!==e.id&&a[s]&&(c.startIndex=a[s]);const t=d(c,r,n);if(i(e.marginLeft)&&(0==l||i(u[l-1].marginLeft))){let n=e.marginLeft;l>0&&(n=o(parseFloat(n)-parseFloat(u[l-1].marginLeft))),r.setStyle("padding-left",n,t)}if(0==u.length){const n=e.element.parent,s=n.getChildIndex(e.element)+1;r.insertChild(s,t,n)}else{const e=u[l-1].listItemElements;r.appendChild(t,e[e.length-1])}u[l]={...e,listElement:t,listItemElements:[]},0==l&&void 0!==e.id&&(a[s]=c.startIndex||1)}}const f="li"==e.element.name?e.element:r.createElement("li");r.appendChild(f,u[l].listElement),u[l].listItemElements.push(f),0==l&&void 0!==e.id&&a[s]++,e.element!=f&&r.appendChild(e.element,f),p(e.element,r),r.removeStyle("text-indent",e.element),r.removeStyle("margin-left",e.element)}else{const t=u.find((t=>t.marginLeft==e.marginLeft));if(t){const n=t.listItemElements;r.appendChild(e.element,n[n.length-1]),r.removeStyle("margin-left",e.element)}else u.length=0}}function c(e){const t=e.element.previousSibling;return a(t||e.element.parent)}function a(e){return e.is("element","ol")||e.is("element","ul")}function m(e,t){const n=new RegExp(`@list l${e.id}:level${e.indent}\\s*({[^}]*)`,"gi"),r=/mso-level-number-format:([^;]{0,100});/gi,s=/mso-level-start-at:\s{0,100}([0-9]{0,10})\s{0,100};/gi,i=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=i.exec(t),c=o.exec(t),a=l&&!c,m=n.exec(t);let d="decimal",f="ol",p=null;if(m&&m[1]){const t=r.exec(m[1]);if(t&&t[1]&&(d=t[1].trim(),f="bullet"!==d&&"image"!==d?"ol":"ul"),"bullet"===d){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&&(d=t)}else{const e=s.exec(m[1]);e&&e[1]&&(p=parseInt(e[1]))}a&&(f="ol")}return{type:f,startIndex:p,style:u(d),isLegalStyleList:a}}function u(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 d(e,t,n){const r=t.createElement(e.type);return e.style&&t.setStyle("list-style-type",e.style,r),e.startIndex&&e.startIndex>1&&t.setAttribute("start",e.startIndex,r),e.isLegalStyleList&&n&&t.addClass("legal-list",r),r}function f(e){const t=e.getStyle("mso-list");if(void 0===t)return{};const n=t.match(/(^|\s{1,100})l(\d+)/i),r=t.match(/\s{0,100}lfo(\d+)/i),s=t.match(/\s{0,100}level(\d+)/i);return n&&r&&s?{id:n[2],order:r[1],indent:parseInt(s[1])}:{indent:1}}function p(e,t){const n=new s.Matcher({name:"span",styles:{"mso-list":"Ignore"}}),r=t.createRangeIn(e);for(const e of r)"elementStart"===e.type&&n.match(e.item)&&t.remove(e.item)}function g(e){const t=e.getStyle("margin-left");return void 0===t||t.endsWith("px")?t:function(e){const t=parseFloat(e);return e.endsWith("pt")?o(96*t/72):e.endsWith("pc")?o(12*t*96/72):e.endsWith("in")?o(96*t):e.endsWith("cm")?o(96*t/2.54):e.endsWith("mm")?o(t/10*96/2.54):e}(t)}function h(e,t){if(!e.childCount)return;const n=new s.UpcastWriter(e.document),r=function(e,t){const n=t.createRangeIn(e),r=new s.Matcher({name:/v:(.+)/}),i=[];for(const e of n){if("elementStart"!=e.type)continue;const t=e.item,n=t.previousSibling,s=n&&n.is("element")?n.name:null,o=["Chart"],l=r.match(t),c=t.getAttribute("o:gfxdata"),a="v:shapetype"===s,m=c&&o.some((e=>t.getAttribute("id").includes(e)));l&&c&&!a&&!m&&i.push(e.item.getAttribute("id"))}return i}(e,n);!function(e,t,n){const r=n.createRangeIn(t),i=new s.Matcher({name:"img"}),o=[];for(const t of r)if(t.item.is("element")&&i.match(t.item)){const n=t.item,r=n.getAttribute("v:shapes")?n.getAttribute("v:shapes").split(" "):[];r.length&&r.every((t=>e.indexOf(t)>-1))?o.push(n):n.getAttribute("src")||o.push(n)}for(const e of o)n.remove(e)}(r,e,n),function(e,t,n){const r=n.createRangeIn(t),s=[];for(const t of r)if("elementStart"==t.type&&t.item.is("element","v:shape")){const n=t.item.getAttribute("id");if(e.includes(n))continue;i(t.item.parent.getChildren(),n)||s.push(t.item)}for(const e of s){const t={src:o(e)};e.hasAttribute("alt")&&(t.alt=e.getAttribute("alt"));const r=n.createElement("img",t);n.insertChild(e.index+1,r,e.parent)}function i(e,t){for(const n of e)if(n.is("element")){if("img"==n.name&&n.getAttribute("v:shapes")==t)return!0;if(i(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")}}(r,e,n),function(e,t){const n=t.createRangeIn(e),r=new s.Matcher({name:/v:(.+)/}),i=[];for(const e of n)"elementStart"==e.type&&r.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),r=new s.Matcher({name:"img"}),i=[];for(const e of n)e.item.is("element")&&r.match(e.item)&&e.item.getAttribute("src").startsWith("file://")&&i.push(e.item);return i}(e,n);i.length&&function(e,t,n){if(e.length===t.length)for(let r=0;r<e.length;r++){const s=`data:${t[r].type};base64,${y(t[r].hex)}`;n.setAttribute("src",s,e[r])}}(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"),r=e.match(n),s=[];if(r)for(const e of r){let n=!1;e.includes("\\pngblip")?n="image/png":e.includes("\\jpegblip")&&(n="image/jpeg"),n&&s.push({hex:e.replace(t,"").replace(/[^\da-fA-F]/g,""),type:n})}return s}(t),n)}function y(e){return btoa(e.match(/\w{2}/g).map((e=>String.fromCharCode(parseInt(e,16)))).join(""))}const b=/<meta\s*name="?generator"?\s*content="?microsoft\s*word\s*\d+"?\/?>/i,v=/xmlns:o="urn:schemas-microsoft-com/i;class x{constructor(e,t=!1){this.document=e,this.hasMultiLevelListPlugin=t}isActive(e){return b.test(e)||v.test(e)}execute(e){const{body:t,stylesString:n}=e._parsedData;l(t,n,this.hasMultiLevelListPlugin),h(t,e.dataTransfer.getData("text/rtf")),function(e){const t=[],n=new s.UpcastWriter(e.document);for(const{item:r}of n.createRangeIn(e))if(r.is("element")){for(const e of r.getClassNames())/\bmso/gi.exec(e)&&n.removeClass(e,r);for(const e of r.getStyleNames())/\bmso/gi.exec(e)&&n.removeStyle(e,r);(r.is("element","w:sdt")||r.is("element","w:sdtpr")&&r.isEmpty||r.is("element","o:p")&&r.isEmpty)&&t.push(r)}for(const e of t){const t=e.parent,r=t.getChildIndex(e);n.insertChild(r,e.getChildren(),t),n.remove(e)}}(t),e.content=t}}function w(e,t,n,{blockElements:r,inlineObjectElements:s}){let i=n.createPositionAt(e,"forward"==t?"after":"before");return i=i.getLastMatchingPosition((({item:e})=>e.is("element")&&!r.includes(e.name)&&!s.includes(e.name)),{direction:t}),"forward"==t?i.nodeAfter:i.nodeBefore}function C(e,t){return!!e&&e.is("element")&&t.includes(e.name)}const S=/id=("|')docs-internal-guid-[-0-9a-f]+("|')/i;class A{constructor(e){this.document=e}isActive(e){return S.test(e)}execute(e){const t=new s.UpcastWriter(this.document),{body:n}=e._parsedData;!function(e,t){for(const n of e.getChildren())if(n.is("element","b")&&"normal"===n.getStyle("font-weight")){const r=e.getChildIndex(n);t.remove(n),t.insertChild(r,n.getChildren(),e)}}(n,t),function(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)}}}(n,t),function(e,t){const n=new s.ViewDocument(t.document.stylesProcessor),r=new s.DomConverter(n,{renderingMode:"data"}),i=r.blockElements,o=r.inlineObjectElements,l=[];for(const n of t.createRangeIn(e)){const e=n.item;if(e.is("element","br")){const n=w(e,"forward",t,{blockElements:i,inlineObjectElements:o}),r=w(e,"backward",t,{blockElements:i,inlineObjectElements:o}),s=C(n,i);(C(r,i)||s)&&l.push(e)}}for(const e of l)e.hasClass("Apple-interchange-newline")?t.remove(e):t.replace(e,t.createElement("p"))}(n,t),e.content=n}}const E=/<google-sheets-html-origin/i;class I{constructor(e){this.document=e}isActive(e){return E.test(e)}execute(e){const t=new s.UpcastWriter(this.document),{body:n}=e._parsedData;!function(e,t){for(const n of e.getChildren())if(n.is("element","google-sheets-html-origin")){const r=e.getChildIndex(n);t.remove(n),t.insertChild(r,n.getChildren(),e)}}(n,t),function(e,t){for(const n of e.getChildren())n.is("element","table")&&n.hasAttribute("xmlns")&&t.removeAttribute("xmlns",n)}(n,t),function(e,t){for(const n of e.getChildren())n.is("element","table")&&"0px"===n.getStyle("width")&&t.removeStyle("width",n)}(n,t),function(e,t){for(const n of Array.from(e.getChildren()))n.is("element","style")&&t.remove(n)}(n,t),e.content=n}}function L(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 M(e,t){const n=new DOMParser,r=function(e){return L(L(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(e){const t="</body>",n="</html>",r=e.indexOf(t);if(r<0)return e;const s=e.indexOf(n,r+t.length);return e.substring(0,r+t.length)+(s>=0?e.substring(s):"")}(e=(e=e.replace(/<!--\[if gte vml 1]>/g,"")).replace(/<o:SmartTagType(?:\s+[^\s>=]+(?:="[^"]*")?)*\s*\/?>/gi,""))),i=n.parseFromString(r,"text/html");!function(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)}))}(i);const o=i.body.innerHTML,l=function(e,t){const n=new s.ViewDocument(t),r=new s.DomConverter(n,{renderingMode:"data"}),i=e.createDocumentFragment(),o=e.body.childNodes;for(;o.length>0;)i.appendChild(o[0]);return r.domToView(i,{skipComments:!0})}(i,t),c=function(e){const t=[],n=[],r=Array.from(e.getElementsByTagName("style"));for(const e of r)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:c.styles,stylesString:c.stylesString}}class P extends e.Plugin{static get pluginName(){return"PasteFromOffice"}static get requires(){return[t.ClipboardPipeline]}init(){const e=this.editor,t=e.plugins.get("ClipboardPipeline"),n=e.editing.view.document,r=[],s=this.editor.plugins.has("MultiLevelList");r.push(new x(n,s)),r.push(new A(n)),r.push(new I(n)),t.on("inputTransformation",((t,s)=>{if(s._isTransformedWithPasteFromOffice)return;if(e.model.document.selection.getFirstPosition().parent.is("element","codeBlock"))return;const i=s.dataTransfer.getData("text/html"),o=r.find((e=>e.isActive(i)));o&&(s._parsedData||(s._parsedData=M(i,n.stylesProcessor)),o.execute(s),s._isTransformedWithPasteFromOffice=!0)}),{priority:"high"})}}})(),(window.CKEditor5=window.CKEditor5||{}).pasteFromOffice=r})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-paste-from-office",
3
- "version": "41.3.0-alpha.3",
3
+ "version": "41.3.0",
4
4
  "description": "Paste from Office feature for CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -13,7 +13,7 @@
13
13
  "type": "module",
14
14
  "main": "src/index.js",
15
15
  "dependencies": {
16
- "ckeditor5": "41.3.0-alpha.3"
16
+ "ckeditor5": "41.3.0"
17
17
  },
18
18
  "author": "CKSource (http://cksource.com/)",
19
19
  "license": "GPL-2.0-or-later",
@@ -25,7 +25,6 @@
25
25
  "directory": "packages/ckeditor5-paste-from-office"
26
26
  },
27
27
  "files": [
28
- "dist",
29
28
  "lang",
30
29
  "src/**/*.js",
31
30
  "src/**/*.d.ts",
@@ -19,7 +19,7 @@ import { UpcastWriter, type ViewDocumentFragment } from 'ckeditor5/src/engine.js
19
19
  * @param documentFragment The view structure to be transformed.
20
20
  * @param stylesString Styles from which list-like elements styling will be extracted.
21
21
  */
22
- export declare function transformListItemLikeElementsIntoLists(documentFragment: ViewDocumentFragment, stylesString: string): void;
22
+ export declare function transformListItemLikeElementsIntoLists(documentFragment: ViewDocumentFragment, stylesString: string, hasMultiLevelListPlugin: boolean): void;
23
23
  /**
24
24
  * Removes paragraph wrapping content inside a list item.
25
25
  */
@@ -6,6 +6,7 @@
6
6
  * @module paste-from-office/filters/list
7
7
  */
8
8
  import { Matcher, UpcastWriter } from 'ckeditor5/src/engine.js';
9
+ import { convertCssLengthToPx, isPx, toPx } from './utils.js';
9
10
  /**
10
11
  * Transforms Word specific list-like elements to the semantic HTML lists.
11
12
  *
@@ -19,7 +20,7 @@ import { Matcher, UpcastWriter } from 'ckeditor5/src/engine.js';
19
20
  * @param documentFragment The view structure to be transformed.
20
21
  * @param stylesString Styles from which list-like elements styling will be extracted.
21
22
  */
22
- export function transformListItemLikeElementsIntoLists(documentFragment, stylesString) {
23
+ export function transformListItemLikeElementsIntoLists(documentFragment, stylesString, hasMultiLevelListPlugin) {
23
24
  if (!documentFragment.childCount) {
24
25
  return;
25
26
  }
@@ -28,41 +29,103 @@ export function transformListItemLikeElementsIntoLists(documentFragment, stylesS
28
29
  if (!itemLikeElements.length) {
29
30
  return;
30
31
  }
31
- let currentList = null;
32
- let currentIndentation = 1;
33
- itemLikeElements.forEach((itemLikeElement, i) => {
34
- const isDifferentList = isNewListNeeded(itemLikeElements[i - 1], itemLikeElement);
35
- const previousItemLikeElement = isDifferentList ? null : itemLikeElements[i - 1];
36
- const indentationDifference = getIndentationDifference(previousItemLikeElement, itemLikeElement);
37
- if (isDifferentList) {
38
- currentList = null;
39
- currentIndentation = 1;
40
- }
41
- if (!currentList || indentationDifference !== 0) {
42
- const listStyle = detectListStyle(itemLikeElement, stylesString);
43
- if (!currentList) {
44
- currentList = insertNewEmptyList(listStyle, itemLikeElement.element, writer);
32
+ const encounteredLists = {};
33
+ const stack = [];
34
+ for (const itemLikeElement of itemLikeElements) {
35
+ if (itemLikeElement.indent !== undefined) {
36
+ if (!isListContinuation(itemLikeElement)) {
37
+ stack.length = 0;
45
38
  }
46
- else if (itemLikeElement.indent > currentIndentation) {
47
- const lastListItem = currentList.getChild(currentList.childCount - 1);
48
- const lastListItemChild = lastListItem.getChild(lastListItem.childCount - 1);
49
- currentList = insertNewEmptyList(listStyle, lastListItemChild, writer);
50
- currentIndentation += 1;
39
+ // Combined list ID for addressing encounter lists counters.
40
+ const originalListId = `${itemLikeElement.id}:${itemLikeElement.indent}`;
41
+ // Normalized list item indentation.
42
+ const indent = Math.min(itemLikeElement.indent - 1, stack.length);
43
+ // Trimming of the list stack on list ID change.
44
+ if (indent < stack.length && stack[indent].id !== itemLikeElement.id) {
45
+ stack.length = indent;
51
46
  }
52
- else if (itemLikeElement.indent < currentIndentation) {
53
- const differentIndentation = currentIndentation - itemLikeElement.indent;
54
- currentList = findParentListAtLevel(currentList, differentIndentation);
55
- currentIndentation = itemLikeElement.indent;
47
+ // Trimming of the list stack on lower indent list encountered.
48
+ if (indent < stack.length - 1) {
49
+ stack.length = indent + 1;
56
50
  }
57
- if (itemLikeElement.indent <= currentIndentation) {
58
- if (!currentList.is('element', listStyle.type)) {
59
- currentList = writer.rename(listStyle.type, currentList);
51
+ else {
52
+ const listStyle = detectListStyle(itemLikeElement, stylesString);
53
+ // Create a new OL/UL if required (greater indent or different list type).
54
+ if (indent > stack.length - 1 || stack[indent].listElement.name != listStyle.type) {
55
+ // Check if there is some start index to set from a previous list.
56
+ if (indent == 0 &&
57
+ listStyle.type == 'ol' &&
58
+ itemLikeElement.id !== undefined &&
59
+ encounteredLists[originalListId]) {
60
+ listStyle.startIndex = encounteredLists[originalListId];
61
+ }
62
+ const listElement = createNewEmptyList(listStyle, writer, hasMultiLevelListPlugin);
63
+ // Apply list padding only if we have margins for the item and the parent item.
64
+ if (isPx(itemLikeElement.marginLeft) &&
65
+ (indent == 0 || isPx(stack[indent - 1].marginLeft))) {
66
+ let marginLeft = itemLikeElement.marginLeft;
67
+ if (indent > 0) {
68
+ // Convert the padding from absolute to relative.
69
+ marginLeft = toPx(parseFloat(marginLeft) - parseFloat(stack[indent - 1].marginLeft));
70
+ }
71
+ writer.setStyle('padding-left', marginLeft, listElement);
72
+ }
73
+ // Insert the new OL/UL.
74
+ if (stack.length == 0) {
75
+ const parent = itemLikeElement.element.parent;
76
+ const index = parent.getChildIndex(itemLikeElement.element) + 1;
77
+ writer.insertChild(index, listElement, parent);
78
+ }
79
+ else {
80
+ const parentListItems = stack[indent - 1].listItemElements;
81
+ writer.appendChild(listElement, parentListItems[parentListItems.length - 1]);
82
+ }
83
+ // Update the list stack for other items to reference.
84
+ stack[indent] = {
85
+ ...itemLikeElement,
86
+ listElement,
87
+ listItemElements: []
88
+ };
89
+ // Prepare list counter for start index.
90
+ if (indent == 0 && itemLikeElement.id !== undefined) {
91
+ encounteredLists[originalListId] = listStyle.startIndex || 1;
92
+ }
60
93
  }
61
94
  }
95
+ // Use LI if it is already it or create a new LI element.
96
+ // https://github.com/ckeditor/ckeditor5/issues/15964
97
+ const listItem = itemLikeElement.element.name == 'li' ? itemLikeElement.element : writer.createElement('li');
98
+ // Append the LI to OL/UL.
99
+ writer.appendChild(listItem, stack[indent].listElement);
100
+ stack[indent].listItemElements.push(listItem);
101
+ // Increment list counter.
102
+ if (indent == 0 && itemLikeElement.id !== undefined) {
103
+ encounteredLists[originalListId]++;
104
+ }
105
+ // Append list block to LI.
106
+ if (itemLikeElement.element != listItem) {
107
+ writer.appendChild(itemLikeElement.element, listItem);
108
+ }
109
+ // Clean list block.
110
+ removeBulletElement(itemLikeElement.element, writer);
111
+ writer.removeStyle('text-indent', itemLikeElement.element); // #12361
112
+ writer.removeStyle('margin-left', itemLikeElement.element);
62
113
  }
63
- const listItem = transformElementIntoListItem(itemLikeElement.element, writer);
64
- writer.appendChild(listItem, currentList);
65
- });
114
+ else {
115
+ // Other blocks in a list item.
116
+ const stackItem = stack.find(stackItem => stackItem.marginLeft == itemLikeElement.marginLeft);
117
+ // This might be a paragraph that has known margin, but it is not a real list block.
118
+ if (stackItem) {
119
+ const listItems = stackItem.listItemElements;
120
+ // Append block to LI.
121
+ writer.appendChild(itemLikeElement.element, listItems[listItems.length - 1]);
122
+ writer.removeStyle('margin-left', itemLikeElement.element);
123
+ }
124
+ else {
125
+ stack.length = 0;
126
+ }
127
+ }
128
+ }
66
129
  }
67
130
  /**
68
131
  * Removes paragraph wrapping content inside a list item.
@@ -87,27 +150,58 @@ export function unwrapParagraphInListItem(documentFragment, writer) {
87
150
  */
88
151
  function findAllItemLikeElements(documentFragment, writer) {
89
152
  const range = writer.createRangeIn(documentFragment);
90
- // Matcher for finding list-like elements.
91
- const itemLikeElementsMatcher = new Matcher({
92
- name: /^p|h\d+$/,
93
- styles: {
94
- 'mso-list': /.*/
95
- }
96
- });
97
153
  const itemLikeElements = [];
98
- for (const value of range) {
99
- if (value.type === 'elementStart' && itemLikeElementsMatcher.match(value.item)) {
100
- const itemData = getListItemData(value.item);
154
+ const foundMargins = new Set();
155
+ for (const item of range.getItems()) {
156
+ // https://github.com/ckeditor/ckeditor5/issues/15964
157
+ if (!item.is('element') || !item.name.match(/^(p|h\d+|li|div)$/)) {
158
+ continue;
159
+ }
160
+ // Try to rely on margin-left style to find paragraphs visually aligned with previously encountered list item.
161
+ let marginLeft = getMarginLeftNormalized(item);
162
+ // Ignore margin-left 0 style if there is no MsoList... class.
163
+ if (marginLeft !== undefined &&
164
+ parseFloat(marginLeft) == 0 &&
165
+ !Array.from(item.getClassNames()).find(className => className.startsWith('MsoList'))) {
166
+ marginLeft = undefined;
167
+ }
168
+ // List item or a following list item block.
169
+ if (item.hasStyle('mso-list') || marginLeft !== undefined && foundMargins.has(marginLeft)) {
170
+ const itemData = getListItemData(item);
101
171
  itemLikeElements.push({
102
- element: value.item,
172
+ element: item,
103
173
  id: itemData.id,
104
174
  order: itemData.order,
105
- indent: itemData.indent
175
+ indent: itemData.indent,
176
+ marginLeft
106
177
  });
178
+ if (marginLeft !== undefined) {
179
+ foundMargins.add(marginLeft);
180
+ }
181
+ }
182
+ // Clear found margins as we found block after a list.
183
+ else {
184
+ foundMargins.clear();
107
185
  }
108
186
  }
109
187
  return itemLikeElements;
110
188
  }
189
+ /**
190
+ * Whether the given element is possibly a list continuation. Previous element was wrapped into a list
191
+ * or the current element already is inside a list.
192
+ */
193
+ function isListContinuation(currentItem) {
194
+ const previousSibling = currentItem.element.previousSibling;
195
+ if (!previousSibling) {
196
+ // If it's a li inside ul or ol like in here: https://github.com/ckeditor/ckeditor5/issues/15964.
197
+ return isList(currentItem.element.parent);
198
+ }
199
+ // Even with the same id the list does not have to be continuous (#43).
200
+ return isList(previousSibling);
201
+ }
202
+ function isList(element) {
203
+ return element.is('element', 'ol') || element.is('element', 'ul');
204
+ }
111
205
  /**
112
206
  * Extracts list item style from the provided CSS.
113
207
  *
@@ -139,6 +233,14 @@ function detectListStyle(listLikeItem, stylesString) {
139
233
  const listStyleRegexp = new RegExp(`@list l${listLikeItem.id}:level${listLikeItem.indent}\\s*({[^}]*)`, 'gi');
140
234
  const listStyleTypeRegex = /mso-level-number-format:([^;]{0,100});/gi;
141
235
  const listStartIndexRegex = /mso-level-start-at:\s{0,100}([0-9]{0,10})\s{0,100};/gi;
236
+ const legalStyleListRegex = new RegExp(`@list\\s+l${listLikeItem.id}:level\\d\\s*{[^{]*mso-level-text:"%\\d\\\\.`, 'gi');
237
+ const multiLevelNumberFormatTypeRegex = new RegExp(`@list l${listLikeItem.id}:level\\d\\s*{[^{]*mso-level-number-format:`, 'gi');
238
+ const legalStyleListMatch = legalStyleListRegex.exec(stylesString);
239
+ const multiLevelNumberFormatMatch = multiLevelNumberFormatTypeRegex.exec(stylesString);
240
+ // Multi level lists in Word have mso-level-number-format attribute except legal lists,
241
+ // so we used that. If list has legal list match and doesn't has mso-level-number-format
242
+ // then this is legal-list.
243
+ const islegalStyleList = legalStyleListMatch && !multiLevelNumberFormatMatch;
142
244
  const listStyleMatch = listStyleRegexp.exec(stylesString);
143
245
  let listStyleType = 'decimal'; // Decimal is default one.
144
246
  let type = 'ol'; // <ol> is default list.
@@ -165,17 +267,25 @@ function detectListStyle(listLikeItem, stylesString) {
165
267
  startIndex = parseInt(listStartIndexMatch[1]);
166
268
  }
167
269
  }
270
+ if (islegalStyleList) {
271
+ type = 'ol';
272
+ }
168
273
  }
169
274
  return {
170
275
  type,
171
276
  startIndex,
172
- style: mapListStyleDefinition(listStyleType)
277
+ style: mapListStyleDefinition(listStyleType),
278
+ isLegalStyleList: islegalStyleList
173
279
  };
174
280
  }
175
281
  /**
176
282
  * Tries to extract the `list-style-type` value based on the marker element for bulleted list.
177
283
  */
178
284
  function findBulletedListStyle(element) {
285
+ // https://github.com/ckeditor/ckeditor5/issues/15964
286
+ if (element.name == 'li' && element.parent.name == 'ul' && element.parent.hasAttribute('type')) {
287
+ return element.parent.getAttribute('type');
288
+ }
179
289
  const listMarkerElement = findListMarkerNode(element);
180
290
  if (!listMarkerElement) {
181
291
  return null;
@@ -246,18 +356,10 @@ function mapListStyleDefinition(value) {
246
356
  }
247
357
  }
248
358
  /**
249
- * Creates an empty list of a given type and inserts it after a specified element.
250
- *
251
- * @param listStyle List style object which determines the type of newly created list.
252
- * Usually a result of `detectListStyle()` function.
253
- * @param element Element after which list is inserted.
254
- * @returns Newly created list element.
359
+ * Creates a new list OL/UL element.
255
360
  */
256
- function insertNewEmptyList(listStyle, element, writer) {
257
- const parent = element.parent;
361
+ function createNewEmptyList(listStyle, writer, hasMultiLevelListPlugin) {
258
362
  const list = writer.createElement(listStyle.type);
259
- const position = parent.getChildIndex(element) + 1;
260
- writer.insertChild(position, list, parent);
261
363
  // We do not support modifying the marker for a particular list item.
262
364
  // Set the value for the `list-style-type` property directly to the list container.
263
365
  if (listStyle.style) {
@@ -266,21 +368,11 @@ function insertNewEmptyList(listStyle, element, writer) {
266
368
  if (listStyle.startIndex && listStyle.startIndex > 1) {
267
369
  writer.setAttribute('start', listStyle.startIndex, list);
268
370
  }
371
+ if (listStyle.isLegalStyleList && hasMultiLevelListPlugin) {
372
+ writer.addClass('legal-list', list);
373
+ }
269
374
  return list;
270
375
  }
271
- /**
272
- * Transforms a given element into a semantic list item. As the function operates on a provided
273
- * {module:engine/src/view/element~Element element} it will modify the view structure to which this element belongs.
274
- *
275
- * @param element Element which will be transformed into a list item.
276
- * @returns New element to which the given one was transformed. It is
277
- * inserted in place of the old element (the reference to the old element is lost due to renaming).
278
- */
279
- function transformElementIntoListItem(element, writer) {
280
- removeBulletElement(element, writer);
281
- writer.removeStyle('text-indent', element); // #12361
282
- return writer.rename('li', element);
283
- }
284
376
  /**
285
377
  * Extracts list item information from Word specific list-like element style:
286
378
  *
@@ -299,19 +391,23 @@ function transformElementIntoListItem(element, writer) {
299
391
  * @param element Element from which style data is extracted.
300
392
  */
301
393
  function getListItemData(element) {
302
- const data = {};
303
394
  const listStyle = element.getStyle('mso-list');
304
- if (listStyle) {
305
- const idMatch = listStyle.match(/(^|\s{1,100})l(\d+)/i);
306
- const orderMatch = listStyle.match(/\s{0,100}lfo(\d+)/i);
307
- const indentMatch = listStyle.match(/\s{0,100}level(\d+)/i);
308
- if (idMatch && orderMatch && indentMatch) {
309
- data.id = idMatch[2];
310
- data.order = orderMatch[1];
311
- data.indent = parseInt(indentMatch[1]);
312
- }
395
+ if (listStyle === undefined) {
396
+ return {};
397
+ }
398
+ const idMatch = listStyle.match(/(^|\s{1,100})l(\d+)/i);
399
+ const orderMatch = listStyle.match(/\s{0,100}lfo(\d+)/i);
400
+ const indentMatch = listStyle.match(/\s{0,100}level(\d+)/i);
401
+ if (idMatch && orderMatch && indentMatch) {
402
+ return {
403
+ id: idMatch[2],
404
+ order: orderMatch[1],
405
+ indent: parseInt(indentMatch[1])
406
+ };
313
407
  }
314
- return data;
408
+ return {
409
+ indent: 1 // Handle empty mso-list style as a marked for default list item.
410
+ };
315
411
  }
316
412
  /**
317
413
  * Removes span with a numbering/bullet from a given element.
@@ -332,64 +428,12 @@ function removeBulletElement(element, writer) {
332
428
  }
333
429
  }
334
430
  /**
335
- * Whether the previous and current items belong to the same list. It is determined based on `item.id`
336
- * (extracted from `mso-list` style, see #getListItemData) and a previous sibling of the current item.
337
- *
338
- * However, it's quite easy to change the `id` attribute for nested lists in Word. It will break the list feature while pasting.
339
- * Let's check also the `indent` attribute. If the difference between those two elements is equal to 1, we can assume that
340
- * the `currentItem` is a beginning of the nested list because lists in CKEditor 5 always start with the `indent=0` attribute.
341
- * See: https://github.com/ckeditor/ckeditor5/issues/7805.
342
- */
343
- function isNewListNeeded(previousItem, currentItem) {
344
- if (!previousItem) {
345
- return true;
346
- }
347
- if (previousItem.id !== currentItem.id) {
348
- // See: https://github.com/ckeditor/ckeditor5/issues/7805.
349
- //
350
- // * List item 1.
351
- // - Nested list item 1.
352
- if (currentItem.indent - previousItem.indent === 1) {
353
- return false;
354
- }
355
- return true;
356
- }
357
- const previousSibling = currentItem.element.previousSibling;
358
- if (!previousSibling) {
359
- return true;
360
- }
361
- // Even with the same id the list does not have to be continuous (#43).
362
- return !isList(previousSibling);
363
- }
364
- function isList(element) {
365
- return element.is('element', 'ol') || element.is('element', 'ul');
366
- }
367
- /**
368
- * Calculates the indentation difference between two given list items (based on the indent attribute
369
- * extracted from the `mso-list` style, see #getListItemData).
431
+ * Returns element left margin normalized to 'px' if possible.
370
432
  */
371
- function getIndentationDifference(previousItem, currentItem) {
372
- return previousItem ? currentItem.indent - previousItem.indent : currentItem.indent - 1;
373
- }
374
- /**
375
- * Finds the parent list element (ul/ol) of a given list element with indentation level lower by a given value.
376
- *
377
- * @param listElement List element from which to start looking for a parent list.
378
- * @param indentationDifference Indentation difference between lists.
379
- * @returns Found list element with indentation level lower by a given value.
380
- */
381
- function findParentListAtLevel(listElement, indentationDifference) {
382
- const ancestors = listElement.getAncestors({ parentFirst: true });
383
- let parentList = null;
384
- let levelChange = 0;
385
- for (const ancestor of ancestors) {
386
- if (ancestor.is('element', 'ul') || ancestor.is('element', 'ol')) {
387
- levelChange++;
388
- }
389
- if (levelChange === indentationDifference) {
390
- parentList = ancestor;
391
- break;
392
- }
433
+ function getMarginLeftNormalized(element) {
434
+ const value = element.getStyle('margin-left');
435
+ if (value === undefined || value.endsWith('px')) {
436
+ return value;
393
437
  }
394
- return parentList;
438
+ return convertCssLengthToPx(value);
395
439
  }
@@ -28,7 +28,9 @@ export default function removeMSAttributes(documentFragment) {
28
28
  writer.removeStyle(styleName, item);
29
29
  }
30
30
  }
31
- if (item.is('element', 'w:sdt')) {
31
+ if (item.is('element', 'w:sdt') ||
32
+ item.is('element', 'w:sdtpr') && item.isEmpty ||
33
+ item.is('element', 'o:p') && item.isEmpty) {
32
34
  elementsToUnwrap.push(item);
33
35
  }
34
36
  }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module paste-from-office/filters/utils
7
+ */
8
+ /**
9
+ * Normalizes CSS length value to 'px'.
10
+ *
11
+ * @internal
12
+ */
13
+ export declare function convertCssLengthToPx(value: string): string;
14
+ /**
15
+ * Returns true for value with 'px' unit.
16
+ *
17
+ * @internal
18
+ */
19
+ export declare function isPx(value?: string): value is string;
20
+ /**
21
+ * Returns a rounded 'px' value.
22
+ *
23
+ * @internal
24
+ */
25
+ export declare function toPx(value: number): string;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module paste-from-office/filters/utils
7
+ */
8
+ /**
9
+ * Normalizes CSS length value to 'px'.
10
+ *
11
+ * @internal
12
+ */
13
+ export function convertCssLengthToPx(value) {
14
+ const numericValue = parseFloat(value);
15
+ if (value.endsWith('pt')) {
16
+ // 1pt = 1in / 72
17
+ return toPx(numericValue * 96 / 72);
18
+ }
19
+ else if (value.endsWith('pc')) {
20
+ // 1pc = 12pt = 1in / 6.
21
+ return toPx(numericValue * 12 * 96 / 72);
22
+ }
23
+ else if (value.endsWith('in')) {
24
+ // 1in = 2.54cm = 96px
25
+ return toPx(numericValue * 96);
26
+ }
27
+ else if (value.endsWith('cm')) {
28
+ // 1cm = 96px / 2.54
29
+ return toPx(numericValue * 96 / 2.54);
30
+ }
31
+ else if (value.endsWith('mm')) {
32
+ // 1mm = 1cm / 10
33
+ return toPx(numericValue / 10 * 96 / 2.54);
34
+ }
35
+ return value;
36
+ }
37
+ /**
38
+ * Returns true for value with 'px' unit.
39
+ *
40
+ * @internal
41
+ */
42
+ export function isPx(value) {
43
+ return value !== undefined && value.endsWith('px');
44
+ }
45
+ /**
46
+ * Returns a rounded 'px' value.
47
+ *
48
+ * @internal
49
+ */
50
+ export function toPx(value) {
51
+ return value.toFixed(2).replace(/\.?0+$/, '') + 'px';
52
+ }
package/src/index.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * @module paste-from-office
7
7
  */
8
8
  export { default as PasteFromOffice } from './pastefromoffice.js';
9
- export type { Normalizer, NormalizerData } from './normalizer.js';
9
+ export { Normalizer, type NormalizerData } from './normalizer.js';
10
10
  export { default as MSWordNormalizer } from './normalizers/mswordnormalizer.js';
11
11
  export { parseHtml } from './filters/parse.js';
12
12
  import './augmentation.js';
@@ -9,12 +9,13 @@ import type { Normalizer, NormalizerData } from '../normalizer.js';
9
9
  */
10
10
  export default class MSWordNormalizer implements Normalizer {
11
11
  readonly document: ViewDocument;
12
+ readonly hasMultiLevelListPlugin: boolean;
12
13
  /**
13
14
  * Creates a new `MSWordNormalizer` instance.
14
15
  *
15
16
  * @param document View document.
16
17
  */
17
- constructor(document: ViewDocument);
18
+ constructor(document: ViewDocument, hasMultiLevelListPlugin?: boolean);
18
19
  /**
19
20
  * @inheritDoc
20
21
  */
@@ -19,8 +19,9 @@ export default class MSWordNormalizer {
19
19
  *
20
20
  * @param document View document.
21
21
  */
22
- constructor(document) {
22
+ constructor(document, hasMultiLevelListPlugin = false) {
23
23
  this.document = document;
24
+ this.hasMultiLevelListPlugin = hasMultiLevelListPlugin;
24
25
  }
25
26
  /**
26
27
  * @inheritDoc
@@ -33,7 +34,7 @@ export default class MSWordNormalizer {
33
34
  */
34
35
  execute(data) {
35
36
  const { body: documentFragment, stylesString } = data._parsedData;
36
- transformListItemLikeElementsIntoLists(documentFragment, stylesString);
37
+ transformListItemLikeElementsIntoLists(documentFragment, stylesString, this.hasMultiLevelListPlugin);
37
38
  replaceImagesSourceWithBase64(documentFragment, data.dataTransfer.getData('text/rtf'));
38
39
  removeMSAttributes(documentFragment);
39
40
  data.content = documentFragment;
@@ -45,7 +45,8 @@ export default class PasteFromOffice extends Plugin {
45
45
  const clipboardPipeline = editor.plugins.get('ClipboardPipeline');
46
46
  const viewDocument = editor.editing.view.document;
47
47
  const normalizers = [];
48
- normalizers.push(new MSWordNormalizer(viewDocument));
48
+ const hasMultiLevelListPlugin = this.editor.plugins.has('MultiLevelList');
49
+ normalizers.push(new MSWordNormalizer(viewDocument, hasMultiLevelListPlugin));
49
50
  normalizers.push(new GoogleDocsNormalizer(viewDocument));
50
51
  normalizers.push(new GoogleSheetsNormalizer(viewDocument));
51
52
  clipboardPipeline.on('inputTransformation', (evt, data) => {
@@ -1,4 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
@@ -1,4 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
package/dist/index.css DELETED
@@ -1,4 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
@@ -1,14 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- import type { PasteFromOffice } from './index.js';
10
- declare module '@ckeditor/ckeditor5-core' {
11
- interface PluginsMap {
12
- [PasteFromOffice.pluginName]: PasteFromOffice;
13
- }
14
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/br
11
- */
12
- import { type UpcastWriter, type ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Transforms `<br>` elements that are siblings to some block element into a paragraphs.
15
- *
16
- * @param documentFragment The view structure to be transformed.
17
- */
18
- export default function transformBlockBrsToParagraphs(documentFragment: ViewDocumentFragment, writer: UpcastWriter): void;
@@ -1,28 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/image
11
- */
12
- import { type ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Replaces source attribute of all `<img>` elements representing regular
15
- * images (not the Word shapes) with inlined base64 image representation extracted from RTF or Blob data.
16
- *
17
- * @param documentFragment Document fragment on which transform images.
18
- * @param rtfData The RTF data from which images representation will be used.
19
- */
20
- export declare function replaceImagesSourceWithBase64(documentFragment: ViewDocumentFragment, rtfData: string): void;
21
- /**
22
- * Converts given HEX string to base64 representation.
23
- *
24
- * @internal
25
- * @param hexString The HEX string to be converted.
26
- * @returns Base64 representation of a given HEX string.
27
- */
28
- export declare function _convertHexToBase64(hexString: string): string;
@@ -1,30 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/list
11
- */
12
- import { UpcastWriter, type ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Transforms Word specific list-like elements to the semantic HTML lists.
15
- *
16
- * Lists in Word are represented by block elements with special attributes like:
17
- *
18
- * ```xml
19
- * <p class=MsoListParagraphCxSpFirst style='mso-list:l1 level1 lfo1'>...</p> // Paragraph based list.
20
- * <h1 style='mso-list:l0 level1 lfo1'>...</h1> // Heading 1 based list.
21
- * ```
22
- *
23
- * @param documentFragment The view structure to be transformed.
24
- * @param stylesString Styles from which list-like elements styling will be extracted.
25
- */
26
- export declare function transformListItemLikeElementsIntoLists(documentFragment: ViewDocumentFragment, stylesString: string): void;
27
- /**
28
- * Removes paragraph wrapping content inside a list item.
29
- */
30
- export declare function unwrapParagraphInListItem(documentFragment: ViewDocumentFragment, writer: UpcastWriter): void;
@@ -1,39 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/parse
11
- */
12
- import { type StylesProcessor, type ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Parses the provided HTML extracting contents of `<body>` and `<style>` tags.
15
- *
16
- * @param htmlString HTML string to be parsed.
17
- */
18
- export declare function parseHtml(htmlString: string, stylesProcessor: StylesProcessor): ParseHtmlResult;
19
- /**
20
- * The result of {@link ~parseHtml}.
21
- */
22
- export interface ParseHtmlResult {
23
- /**
24
- * Parsed body content as a traversable structure.
25
- */
26
- body: ViewDocumentFragment;
27
- /**
28
- * Entire body content as a string.
29
- */
30
- bodyString: string;
31
- /**
32
- * Array of native `CSSStyleSheet` objects, each representing separate `style` tag from the source HTML.
33
- */
34
- styles: Array<CSSStyleSheet>;
35
- /**
36
- * All `style` tags contents combined in the order of occurrence into one string.
37
- */
38
- stylesString: string;
39
- }
@@ -1,18 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/removeboldwrapper
11
- */
12
- import type { UpcastWriter, ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Removes the `<b>` tag wrapper added by Google Docs to a copied content.
15
- *
16
- * @param documentFragment element `data.content` obtained from clipboard
17
- */
18
- export default function removeBoldWrapper(documentFragment: ViewDocumentFragment, writer: UpcastWriter): void;
@@ -1,18 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/removegooglesheetstag
11
- */
12
- import type { UpcastWriter, ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Removes the `<google-sheets-html-origin>` tag wrapper added by Google Sheets to a copied content.
15
- *
16
- * @param documentFragment element `data.content` obtained from clipboard
17
- */
18
- export default function removeGoogleSheetsTag(documentFragment: ViewDocumentFragment, writer: UpcastWriter): void;
@@ -1,18 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/removeinvalidtablewidth
11
- */
12
- import type { UpcastWriter, ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Removes the `width:0px` style from table pasted from Google Sheets.
15
- *
16
- * @param documentFragment element `data.content` obtained from clipboard
17
- */
18
- export default function removeInvalidTableWidth(documentFragment: ViewDocumentFragment, writer: UpcastWriter): void;
@@ -1,18 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/removemsattributes
11
- */
12
- import { type ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Cleanup MS attributes like styles, attributes and elements.
15
- *
16
- * @param documentFragment element `data.content` obtained from clipboard.
17
- */
18
- export default function removeMSAttributes(documentFragment: ViewDocumentFragment): void;
@@ -1,18 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/removestyleblock
11
- */
12
- import type { UpcastWriter, ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Removes `<style>` block added by Google Sheets to a copied content.
15
- *
16
- * @param documentFragment element `data.content` obtained from clipboard
17
- */
18
- export default function removeStyleBlock(documentFragment: ViewDocumentFragment, writer: UpcastWriter): void;
@@ -1,18 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/removexmlns
11
- */
12
- import type { UpcastWriter, ViewDocumentFragment } from 'ckeditor5/src/engine.js';
13
- /**
14
- * Removes the `xmlns` attribute from table pasted from Google Sheets.
15
- *
16
- * @param documentFragment element `data.content` obtained from clipboard
17
- */
18
- export default function removeXmlns(documentFragment: ViewDocumentFragment, writer: UpcastWriter): void;
@@ -1,29 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/filters/space
11
- */
12
- /**
13
- * Replaces last space preceding elements closing tag with `&nbsp;`. Such operation prevents spaces from being removed
14
- * during further DOM/View processing (see especially {@link module:engine/view/domconverter~DomConverter#_processDomInlineNodes}).
15
- * This method also takes into account Word specific `<o:p></o:p>` empty tags.
16
- * Additionally multiline sequences of spaces and new lines between tags are removed (see #39 and #40).
17
- *
18
- * @param htmlString HTML string in which spacing should be normalized.
19
- * @returns Input HTML with spaces normalized.
20
- */
21
- export declare function normalizeSpacing(htmlString: string): string;
22
- /**
23
- * Normalizes spacing in special Word `spacerun spans` (`<span style='mso-spacerun:yes'>\s+</span>`) by replacing
24
- * all spaces with `&nbsp; ` pairs. This prevents spaces from being removed during further DOM/View processing
25
- * (see especially {@link module:engine/view/domconverter~DomConverter#_processDomInlineNodes}).
26
- *
27
- * @param htmlDocument Native `Document` object in which spacing should be normalized.
28
- */
29
- export declare function normalizeSpacerunSpans(htmlDocument: Document): void;
@@ -1,16 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office
11
- */
12
- export { default as PasteFromOffice } from './pastefromoffice.js';
13
- export type { Normalizer, NormalizerData } from './normalizer.js';
14
- export { default as MSWordNormalizer } from './normalizers/mswordnormalizer.js';
15
- export { parseHtml } from './filters/parse.js';
16
- import './augmentation.js';
@@ -1,34 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/normalizer
11
- */
12
- import type { ClipboardInputTransformationData } from 'ckeditor5/src/clipboard.js';
13
- import type { ParseHtmlResult } from './filters/parse.js';
14
- /**
15
- * Interface defining a content transformation pasted from an external editor.
16
- *
17
- * Normalizers are registered by the {@link module:paste-from-office/pastefromoffice~PasteFromOffice} plugin and run on
18
- * {@link module:clipboard/clipboardpipeline~ClipboardPipeline#event:inputTransformation inputTransformation event}.
19
- * They detect environment-specific quirks and transform it into a form compatible with other CKEditor features.
20
- */
21
- export interface Normalizer {
22
- /**
23
- * Must return `true` if the `htmlString` contains content which this normalizer can transform.
24
- */
25
- isActive(htmlString: string): boolean;
26
- /**
27
- * Executes the normalization of a given data.
28
- */
29
- execute(data: NormalizerData): void;
30
- }
31
- export interface NormalizerData extends ClipboardInputTransformationData {
32
- _isTransformedWithPasteFromOffice?: boolean;
33
- _parsedData: ParseHtmlResult;
34
- }
@@ -1,33 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/normalizers/googledocsnormalizer
11
- */
12
- import { type ViewDocument } from 'ckeditor5/src/engine.js';
13
- import type { Normalizer, NormalizerData } from '../normalizer.js';
14
- /**
15
- * Normalizer for the content pasted from Google Docs.
16
- */
17
- export default class GoogleDocsNormalizer implements Normalizer {
18
- readonly document: ViewDocument;
19
- /**
20
- * Creates a new `GoogleDocsNormalizer` instance.
21
- *
22
- * @param document View document.
23
- */
24
- constructor(document: ViewDocument);
25
- /**
26
- * @inheritDoc
27
- */
28
- isActive(htmlString: string): boolean;
29
- /**
30
- * @inheritDoc
31
- */
32
- execute(data: NormalizerData): void;
33
- }
@@ -1,33 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/normalizers/googlesheetsnormalizer
11
- */
12
- import { type ViewDocument } from 'ckeditor5/src/engine.js';
13
- import type { Normalizer, NormalizerData } from '../normalizer.js';
14
- /**
15
- * Normalizer for the content pasted from Google Sheets.
16
- */
17
- export default class GoogleSheetsNormalizer implements Normalizer {
18
- readonly document: ViewDocument;
19
- /**
20
- * Creates a new `GoogleSheetsNormalizer` instance.
21
- *
22
- * @param document View document.
23
- */
24
- constructor(document: ViewDocument);
25
- /**
26
- * @inheritDoc
27
- */
28
- isActive(htmlString: string): boolean;
29
- /**
30
- * @inheritDoc
31
- */
32
- execute(data: NormalizerData): void;
33
- }
@@ -1,30 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- import type { ViewDocument } from 'ckeditor5/src/engine.js';
10
- import type { Normalizer, NormalizerData } from '../normalizer.js';
11
- /**
12
- * Normalizer for the content pasted from Microsoft Word.
13
- */
14
- export default class MSWordNormalizer implements Normalizer {
15
- readonly document: ViewDocument;
16
- /**
17
- * Creates a new `MSWordNormalizer` instance.
18
- *
19
- * @param document View document.
20
- */
21
- constructor(document: ViewDocument);
22
- /**
23
- * @inheritDoc
24
- */
25
- isActive(htmlString: string): boolean;
26
- /**
27
- * @inheritDoc
28
- */
29
- execute(data: NormalizerData): void;
30
- }
@@ -1,40 +0,0 @@
1
- /**
2
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
- */
5
- /**
6
- * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
7
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
8
- */
9
- /**
10
- * @module paste-from-office/pastefromoffice
11
- */
12
- import { Plugin } from 'ckeditor5/src/core.js';
13
- import { ClipboardPipeline } from 'ckeditor5/src/clipboard.js';
14
- /**
15
- * The Paste from Office plugin.
16
- *
17
- * This plugin handles content pasted from Office apps and transforms it (if necessary)
18
- * to a valid structure which can then be understood by the editor features.
19
- *
20
- * Transformation is made by a set of predefined {@link module:paste-from-office/normalizer~Normalizer normalizers}.
21
- * This plugin includes following normalizers:
22
- * * {@link module:paste-from-office/normalizers/mswordnormalizer~MSWordNormalizer Microsoft Word normalizer}
23
- * * {@link module:paste-from-office/normalizers/googledocsnormalizer~GoogleDocsNormalizer Google Docs normalizer}
24
- *
25
- * For more information about this feature check the {@glink api/paste-from-office package page}.
26
- */
27
- export default class PasteFromOffice extends Plugin {
28
- /**
29
- * @inheritDoc
30
- */
31
- static get pluginName(): "PasteFromOffice";
32
- /**
33
- * @inheritDoc
34
- */
35
- static get requires(): readonly [typeof ClipboardPipeline];
36
- /**
37
- * @inheritDoc
38
- */
39
- init(): void;
40
- }