@ckeditor/ckeditor5-ckbox 39.0.2 → 40.1.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/LICENSE.md +6 -2
- package/build/ckbox.js +1 -1
- package/build/translations/ug.js +1 -1
- package/lang/translations/ug.po +1 -1
- package/package.json +3 -2
- package/src/ckboxcommand.d.ts +2 -0
- package/src/ckboxcommand.js +38 -43
- package/src/ckboxconfig.d.ts +16 -0
- package/src/ckboxediting.js +8 -1
- package/src/utils.d.ts +4 -0
- package/src/utils.js +33 -0
package/LICENSE.md
CHANGED
|
@@ -2,7 +2,7 @@ Software License Agreement
|
|
|
2
2
|
==========================
|
|
3
3
|
|
|
4
4
|
**CKEditor 5 CKBox feature** – https://github.com/ckeditor/ckeditor5-ckbox <br>
|
|
5
|
-
Copyright (c) 2003
|
|
5
|
+
Copyright (c) 2003–2023, [CKSource Holding sp. z o.o.](http://cksource.com) All rights reserved.
|
|
6
6
|
|
|
7
7
|
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
|
|
8
8
|
|
|
@@ -11,7 +11,11 @@ Sources of Intellectual Property Included in CKEditor
|
|
|
11
11
|
|
|
12
12
|
Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
|
|
13
13
|
|
|
14
|
+
The following libraries are included in CKEditor under the [MIT license](https://opensource.org/licenses/MIT):
|
|
15
|
+
|
|
16
|
+
* blurhash - Copyright (c) 2018 Wolt Enterprises (MIT license).
|
|
17
|
+
|
|
14
18
|
Trademarks
|
|
15
19
|
----------
|
|
16
20
|
|
|
17
|
-
**CKEditor** is a trademark of [CKSource Holding sp. z o.o.](http://cksource.com) All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.
|
|
21
|
+
**CKEditor** is a trademark of [CKSource Holding sp. z o.o.](http://cksource.com) All other brand and product names are trademarks, registered trademarks, or service marks of their respective holders.
|
package/build/ckbox.js
CHANGED
|
@@ -2,4 +2,4 @@
|
|
|
2
2
|
/*!
|
|
3
3
|
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
|
|
4
4
|
* For licensing, see LICENSE.md.
|
|
5
|
-
*/(()=>{var e={704:(e,t,i)=>{e.exports=i(79)("./src/core.js")},492:(e,t,i)=>{e.exports=i(79)("./src/engine.js")},273:(e,t,i)=>{e.exports=i(79)("./src/ui.js")},448:(e,t,i)=>{e.exports=i(79)("./src/upload.js")},209:(e,t,i)=>{e.exports=i(79)("./src/utils.js")},79:e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(o){var n=t[o];if(void 0!==n)return n.exports;var r=t[o]={exports:{}};return e[o](r,r.exports,i),r.exports}i.d=(e,t)=>{for(var o in t)i.o(t,o)&&!i.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var o={};(()=>{"use strict";i.r(o),i.d(o,{CKBox:()=>w,CKBoxEditing:()=>p,CKBoxUI:()=>n});var e=i(704),t=i(273);class n extends e.Plugin{static get pluginName(){return"CKBoxUI"}afterInit(){const e=this.editor,i=e.commands.get("ckbox");if(!i)return;const o=e.t;e.ui.componentFactory.add("ckbox",(n=>{const r=new t.ButtonView(n);return r.set({label:o("Open file manager"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M11.627 16.5zm5.873-.196zm0-7.001V8h-13v8.5h4.341c.191.54.457 1.044.785 1.5H2a1.5 1.5 0 0 1-1.5-1.5v-13A1.5 1.5 0 0 1 2 2h4.5a1.5 1.5 0 0 1 1.06.44L9.122 4H16a1.5 1.5 0 0 1 1.5 1.5v1A1.5 1.5 0 0 1 19 8v2.531a6.027 6.027 0 0 0-1.5-1.228zM16 6.5v-1H8.5l-2-2H2v13h1V8a1.5 1.5 0 0 1 1.5-1.5H16z"/><path d="M14.5 19.5a5 5 0 1 1 0-10 5 5 0 0 1 0 10zM15 14v-2h-1v2h-2v1h2v2h1v-2h2v-1h-2z"/></svg>',tooltip:!0}),r.bind("isOn","isEnabled").to(i,"value","isEnabled"),r.on("execute",(()=>{e.execute("ckbox")})),r}))}}var r=i(492),s=i(209);function a(e){const t=[];let i=0;for(const o in e){const n=parseInt(o,10);isNaN(n)||(n>i&&(i=n),t.push(`${e[o]} ${o}w`))}const o=[{srcset:t.join(","),sizes:`(max-width: ${i}px) 100vw, ${i}px`,type:"image/webp"}];return{imageFallbackUrl:e.default,imageSources:o}}class c extends e.Command{constructor(e){super(e),this._chosenAssets=new Set,this._wrapper=null,this._initListeners()}refresh(){this.value=this._getValue(),this.isEnabled=this._checkEnabled()}execute(){this.fire("ckbox:open")}_getValue(){return null!==this._wrapper}_checkEnabled(){const e=this.editor.commands.get("insertImage"),t=this.editor.commands.get("link");return!(!e.isEnabled&&!t.isEnabled)}_prepareOptions(){const e=this.editor.config.get("ckbox");return{theme:e.theme,language:e.language,tokenUrl:e.tokenUrl,serviceOrigin:e.serviceOrigin,dialog:{onClose:()=>this.fire("ckbox:close")},assets:{onChoose:e=>this.fire("ckbox:choose",e)}}}_initListeners(){const e=this.editor,t=e.model,i=!e.config.get("ckbox.ignoreDataId");this.on("ckbox",(()=>{this.refresh()}),{priority:"low"}),this.on("ckbox:open",(()=>{if(!this.isEnabled||this.value)return;this._wrapper=(0,s.createElement)(document,"div",{class:"ck ckbox-wrapper"}),document.body.appendChild(this._wrapper),window.CKBox.mount(this._wrapper,this._prepareOptions());g(50)})),this.on("ckbox:close",(()=>{this.value&&(this._wrapper.remove(),this._wrapper=null)})),this.on("ckbox:choose",((o,n)=>{if(!this.isEnabled)return;const r=e.commands.get("insertImage"),s=e.commands.get("link"),a=function({assets:e,isImageAllowed:t,isLinkAllowed:i}){return e.map((e=>function(e){const t=e.data.metadata;if(!t)return!1;return t.width&&t.height}(e)?{id:e.data.id,type:"image",attributes:l(e)}:{id:e.data.id,type:"link",attributes:d(e)})).filter((e=>"image"===e.type?t:i))}({assets:n,isImageAllowed:r.isEnabled,isLinkAllowed:s.isEnabled});0!==a.length&&t.change((e=>{for(const t of a){const o=t===a[a.length-1];this._insertAsset(t,o,e),i&&(setTimeout((()=>this._chosenAssets.delete(t)),1e3),this._chosenAssets.add(t))}}))})),this.listenTo(e,"destroy",(()=>{this.fire("ckbox:close"),this._chosenAssets.clear()}))}_insertAsset(e,t,i){const o=this.editor.model.document.selection;i.removeSelectionAttribute("linkHref"),"image"===e.type?this._insertImage(e):this._insertLink(e,i),t||i.setSelection(o.getLastPosition())}_insertImage(e){const t=this.editor,{imageFallbackUrl:i,imageSources:o,imageTextAlternative:n}=e.attributes;t.execute("insertImage",{source:{src:i,sources:o,alt:n}})}_insertLink(e,t){const i=this.editor,o=i.model,n=o.document.selection,{linkName:r,linkHref:a}=e.attributes;if(n.isCollapsed){const e=(0,s.toMap)(n.getAttributes()),i=t.createText(r,e),a=o.insertContent(i);t.setSelection(a)}i.execute("link",a)}}function l(e){const{imageFallbackUrl:t,imageSources:i}=a(e.data.imageUrls);return{imageFallbackUrl:t,imageSources:i,imageTextAlternative:e.data.metadata.description||""}}function d(e){return{linkName:e.data.name,linkHref:u(e)}}function u(e){const t=new URL(e.data.url);return t.searchParams.set("download","true"),t.toString()}function g(e){setTimeout((()=>{if(0===e)return;const t=document.querySelector(".ckbox-gallery .ckbox-gallery-item"),i=document.querySelector(".ckbox-empty-view .ckbox-btn");i&&i instanceof HTMLElement?i.focus():t&&t instanceof HTMLElement?t.focus():g(e-1)}),100)}var m=i(448);class b extends e.Plugin{static get requires(){return["ImageUploadEditing","ImageUploadProgress",m.FileRepository,p]}static get pluginName(){return"CKBoxUploadAdapter"}async afterInit(){const e=this.editor,t=!!e.config.get("ckbox"),i=!!window.CKBox;if(!t&&!i)return;const o=e.plugins.get(m.FileRepository),n=e.plugins.get(p);o.createUploadAdapter=t=>new h(t,n.getToken(),e);const r=!e.config.get("ckbox.ignoreDataId"),s=e.plugins.get("ImageUploadEditing");r&&s.on("uploadComplete",((t,{imageElement:i,data:o})=>{e.model.change((e=>{e.setAttribute("ckboxImageId",o.ckboxImageId,i)}))}))}}class h{constructor(e,t,i){this.loader=e,this.token=t,this.editor=i,this.controller=new AbortController,this.serviceOrigin=i.config.get("ckbox.serviceOrigin")}getWorkspaceId(){const e=(0,this.editor.t)("Cannot access default workspace."),t=this.editor.config.get("ckbox.defaultUploadWorkspaceId"),i=function(e,t){const[,i]=e.value.split("."),o=JSON.parse(atob(i)),n=o.auth&&o.auth.ckbox&&o.auth.ckbox.workspaces||[o.aud];return t?"superadmin"==(o.auth&&o.auth.ckbox&&o.auth.ckbox.role)||n.includes(t)?t:null:n[0]}(this.token,t);if(null==i)throw(0,s.logError)("ckbox-access-default-workspace-error"),e;return i}async getAvailableCategories(e=0){const t=new URL("categories",this.serviceOrigin);return t.searchParams.set("limit",50..toString()),t.searchParams.set("offset",e.toString()),t.searchParams.set("workspaceId",this.getWorkspaceId()),this._sendHttpRequest({url:t}).then((async t=>{if(t.totalCount-(e+50)>0){const i=await this.getAvailableCategories(e+50);return[...t.items,...i]}return t.items})).catch((()=>{this.controller.signal.throwIfAborted(),(0,s.logError)("ckbox-fetch-category-http-error")}))}async getCategoryIdForFile(e){const t=function(e){const t=/\.(?<ext>[^.]+)$/;return e.match(t).groups.ext.toLowerCase()}(e.name),i=await this.getAvailableCategories();if(!i)return null;const o=this.editor.config.get("ckbox.defaultUploadCategories");if(o){const e=Object.keys(o).find((e=>o[e].find((e=>e.toLowerCase()==t))));if(e){const t=i.find((t=>t.id===e||t.name===e));return t?t.id:null}}const n=i.find((e=>e.extensions.find((e=>e.toLowerCase()==t))));return n?n.id:null}async upload(){const e=this.editor.t,t=e("Cannot determine a category for the uploaded file."),i=await this.loader.file,o=await this.getCategoryIdForFile(i);if(!o)return Promise.reject(t);const n=new URL("assets",this.serviceOrigin),r=new FormData;n.searchParams.set("workspaceId",this.getWorkspaceId()),r.append("categoryId",o),r.append("file",i);const s={method:"POST",url:n,data:r,onUploadProgress:e=>{e.lengthComputable&&(this.loader.uploadTotal=e.total,this.loader.uploaded=e.loaded)}};return this._sendHttpRequest(s).then((async e=>{const t=a(e.imageUrls);return{ckboxImageId:e.id,default:t.imageFallbackUrl,sources:t.imageSources}})).catch((()=>{const t=e("Cannot upload file:")+` ${i.name}.`;return Promise.reject(t)}))}abort(){this.controller.abort()}_sendHttpRequest({url:e,method:t="GET",data:i,onUploadProgress:o}){const n=this.controller.signal,r=new XMLHttpRequest;r.open(t,e.toString(),!0),r.setRequestHeader("Authorization",this.token.value),r.setRequestHeader("CKBox-Version","CKEditor 5"),r.responseType="json";const s=()=>{r.abort()};return new Promise(((e,t)=>{n.addEventListener("abort",s),r.addEventListener("loadstart",(()=>{n.addEventListener("abort",s)})),r.addEventListener("loadend",(()=>{n.removeEventListener("abort",s)})),r.addEventListener("error",(()=>{t()})),r.addEventListener("abort",(()=>{t()})),r.addEventListener("load",(async()=>{const i=r.response;return!i||i.statusCode>=400?t(i&&i.message):e(i)})),o&&r.upload.addEventListener("progress",(e=>{o(e)})),r.send(i)}))}}class p extends e.Plugin{static get pluginName(){return"CKBoxEditing"}static get requires(){return["CloudServices","LinkEditing","PictureEditing",b]}async init(){const e=this.editor,t=!!e.config.get("ckbox"),i=!!window.CKBox;if(!t&&!i)return;this._initConfig();const o=e.plugins.get("CloudServicesCore"),n=e.config.get("ckbox.tokenUrl");if(n===e.config.get("cloudServices.tokenUrl")){const t=e.plugins.get("CloudServices");this._token=t.token}else this._token=await o.createToken(n).init();e.config.get("ckbox.ignoreDataId")||(this._initSchema(),this._initConversion(),this._initFixers()),i&&e.commands.add("ckbox",new c(e))}getToken(){return this._token}_initConfig(){const e=this.editor;e.config.define("ckbox",{serviceOrigin:"https://api.ckbox.io",defaultUploadCategories:null,ignoreDataId:!1,language:e.locale.uiLanguage,theme:"default",tokenUrl:e.config.get("cloudServices.tokenUrl")});if(!e.config.get("ckbox.tokenUrl"))throw new s.CKEditorError("ckbox-plugin-missing-token-url",this);e.plugins.has("ImageBlockEditing")||e.plugins.has("ImageInlineEditing")||(0,s.logError)("ckbox-plugin-image-feature-missing",e)}_initSchema(){const e=this.editor.model.schema;e.extend("$text",{allowAttributes:"ckboxLinkId"}),e.isRegistered("imageBlock")&&e.extend("imageBlock",{allowAttributes:["ckboxImageId","ckboxLinkId"]}),e.isRegistered("imageInline")&&e.extend("imageInline",{allowAttributes:["ckboxImageId","ckboxLinkId"]}),e.addAttributeCheck(((e,t)=>{if(!!!e.last.getAttribute("linkHref")&&"ckboxLinkId"===t)return!1}))}_initConversion(){const e=this.editor;e.conversion.for("downcast").add((e=>{e.on("attribute:ckboxLinkId:imageBlock",((e,t,i)=>{const{writer:o,mapper:n,consumable:r}=i;if(!r.consume(t.item,e.name))return;const s=[...n.toViewElement(t.item).getChildren()].find((e=>"a"===e.name));s&&(t.item.hasAttribute("ckboxLinkId")?o.setAttribute("data-ckbox-resource-id",t.item.getAttribute("ckboxLinkId"),s):o.removeAttribute("data-ckbox-resource-id",s))}),{priority:"low"}),e.on("attribute:ckboxLinkId",((e,t,i)=>{const{writer:o,mapper:n,consumable:r}=i;if(r.consume(t.item,e.name)){if(t.attributeOldValue){const e=f(o,t.attributeOldValue);o.unwrap(n.toViewRange(t.range),e)}if(t.attributeNewValue){const e=f(o,t.attributeNewValue);if(t.item.is("selection")){const t=o.document.selection;o.wrap(t.getFirstRange(),e)}else o.wrap(n.toViewRange(t.range),e)}}}),{priority:"low"})})),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const{writer:o,consumable:n}=i;if(!t.viewItem.getAttribute("href"))return;if(!n.consume(t.viewItem,{attributes:["data-ckbox-resource-id"]}))return;const r=t.viewItem.getAttribute("data-ckbox-resource-id");if(r)if(t.modelRange)for(let e of t.modelRange.getItems())e.is("$textProxy")&&(e=e.textNode),x(e)&&o.setAttribute("ckboxLinkId",r,e);else{const e=t.modelCursor.nodeBefore||t.modelCursor.parent;o.setAttribute("ckboxLinkId",r,e)}}),{priority:"low"})})),e.conversion.for("downcast").attributeToAttribute({model:"ckboxImageId",view:"data-ckbox-resource-id"}),e.conversion.for("upcast").elementToAttribute({model:{key:"ckboxImageId",value:e=>e.getAttribute("data-ckbox-resource-id")},view:{attributes:{"data-ckbox-resource-id":/[\s\S]+/}}})}_initFixers(){const e=this.editor,t=e.model,i=t.document.selection;t.document.registerPostFixer(function(e){return t=>{let i=!1;const o=e.model,n=e.commands.get("ckbox");if(!n)return i;for(const e of o.document.differ.getChanges()){if("insert"!==e.type&&"attribute"!==e.type)continue;const o="insert"===e.type?new r.Range(e.position,e.position.getShiftedBy(e.length)):e.range,s="attribute"===e.type&&"linkHref"===e.attributeKey&&null===e.attributeNewValue;for(const e of o.getItems()){if(s&&e.hasAttribute("ckboxLinkId")){t.removeAttribute("ckboxLinkId",e),i=!0;continue}const o=k(e,n._chosenAssets);for(const n of o){const o="image"===n.type?"ckboxImageId":"ckboxLinkId";n.id!==e.getAttribute(o)&&(t.setAttribute(o,n.id,e),i=!0)}}}return i}}(e)),t.document.registerPostFixer(function(e){return t=>!(e.hasAttribute("linkHref")||!e.hasAttribute("ckboxLinkId"))&&(t.removeSelectionAttribute("ckboxLinkId"),!0)}(i))}}function k(e,t){const i=e.is("element","imageInline")||e.is("element","imageBlock"),o=e.hasAttribute("linkHref");return[...t].filter((t=>"image"===t.type&&i?t.attributes.imageFallbackUrl===e.getAttribute("src"):"link"===t.type&&o?t.attributes.linkHref===e.getAttribute("linkHref"):void 0))}function f(e,t){const i=e.createAttributeElement("a",{"data-ckbox-resource-id":t},{priority:5});return e.setCustomProperty("link",!0,i),i}function x(e){return!!e.is("$text")||!(!e.is("element","imageInline")&&!e.is("element","imageBlock"))}class w extends e.Plugin{static get pluginName(){return"CKBox"}static get requires(){return[p,n]}}})(),(window.CKEditor5=window.CKEditor5||{}).ckbox=o})();
|
|
5
|
+
*/(()=>{var e={704:(e,t,i)=>{e.exports=i(79)("./src/core.js")},492:(e,t,i)=>{e.exports=i(79)("./src/engine.js")},273:(e,t,i)=>{e.exports=i(79)("./src/ui.js")},448:(e,t,i)=>{e.exports=i(79)("./src/upload.js")},209:(e,t,i)=>{e.exports=i(79)("./src/utils.js")},79:e=>{"use strict";e.exports=CKEditor5.dll}},t={};function i(o){var r=t[o];if(void 0!==r)return r.exports;var n=t[o]={exports:{}};return e[o](n,n.exports,i),n.exports}i.d=(e,t)=>{for(var o in t)i.o(t,o)&&!i.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var o={};(()=>{"use strict";i.r(o),i.d(o,{CKBox:()=>P,CKBoxEditing:()=>E,CKBoxUI:()=>r});var e=i(704),t=i(273);class r extends e.Plugin{static get pluginName(){return"CKBoxUI"}afterInit(){const e=this.editor,i=e.commands.get("ckbox");if(!i)return;const o=e.t;e.ui.componentFactory.add("ckbox",(r=>{const n=new t.ButtonView(r);return n.set({label:o("Open file manager"),icon:'<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M11.627 16.5zm5.873-.196zm0-7.001V8h-13v8.5h4.341c.191.54.457 1.044.785 1.5H2a1.5 1.5 0 0 1-1.5-1.5v-13A1.5 1.5 0 0 1 2 2h4.5a1.5 1.5 0 0 1 1.06.44L9.122 4H16a1.5 1.5 0 0 1 1.5 1.5v1A1.5 1.5 0 0 1 19 8v2.531a6.027 6.027 0 0 0-1.5-1.228zM16 6.5v-1H8.5l-2-2H2v13h1V8a1.5 1.5 0 0 1 1.5-1.5H16z"/><path d="M14.5 19.5a5 5 0 1 1 0-10 5 5 0 0 1 0 10zM15 14v-2h-1v2h-2v1h2v2h1v-2h2v-1h-2z"/></svg>',tooltip:!0}),n.bind("isOn","isEnabled").to(i,"value","isEnabled"),n.on("execute",(()=>{e.execute("ckbox")})),n}))}}var n=i(492),s=i(209),a=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","#","$","%","*","+",",","-",".",":",";","=","?","@","[","]","^","_","{","|","}","~"],c=e=>{let t=0;for(let i=0;i<e.length;i++){let o=e[i];t=83*t+a.indexOf(o)}return t},l=e=>{let t=e/255;return t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)},d=e=>{let t=Math.max(0,Math.min(1,e));return t<=.0031308?Math.trunc(12.92*t*255+.5):Math.trunc(255*(1.055*Math.pow(t,.4166666666666667)-.055)+.5)},u=(e,t)=>(e=>e<0?-1:1)(e)*Math.pow(Math.abs(e),t),g=class extends Error{constructor(e){super(e),this.name="ValidationError",this.message=e}},h=e=>{if(!e||e.length<6)throw new g("The blurhash string must be at least 6 characters");let t=c(e[0]),i=Math.floor(t/9)+1,o=t%9+1;if(e.length!==4+2*o*i)throw new g(`blurhash length mismatch: length is ${e.length} but it should be ${4+2*o*i}`)},m=e=>{let t=e>>8&255,i=255&e;return[l(e>>16),l(t),l(i)]},b=(e,t)=>{let i=Math.floor(e/361),o=Math.floor(e/19)%19,r=e%19;return[u((i-9)/9,2)*t,u((o-9)/9,2)*t,u((r-9)/9,2)*t]},p=(e,t,i,o)=>{h(e),o|=1;let r=c(e[0]),n=Math.floor(r/9)+1,s=r%9+1,a=(c(e[1])+1)/166,l=new Array(s*n);for(let t=0;t<l.length;t++)if(0===t){let i=c(e.substring(2,6));l[t]=m(i)}else{let i=c(e.substring(4+2*t,6+2*t));l[t]=b(i,a*o)}let u=4*t,g=new Uint8ClampedArray(u*i);for(let e=0;e<i;e++)for(let o=0;o<t;o++){let r=0,a=0,c=0;for(let d=0;d<n;d++)for(let n=0;n<s;n++){let u=Math.cos(Math.PI*o*n/t)*Math.cos(Math.PI*e*d/i),g=l[n+d*s];r+=g[0]*u,a+=g[1]*u,c+=g[2]*u}let h=d(r),m=d(a),b=d(c);g[4*o+0+e*u]=h,g[4*o+1+e*u]=m,g[4*o+2+e*u]=b,g[4*o+3+e*u]=255}return g};function f(e){const t=[];let i=0;for(const o in e){const r=parseInt(o,10);isNaN(r)||(r>i&&(i=r),t.push(`${e[o]} ${o}w`))}const o=[{srcset:t.join(","),sizes:`(max-width: ${i}px) 100vw, ${i}px`,type:"image/webp"}];return{imageFallbackUrl:e.default,imageSources:o}}const k=32;class x extends e.Command{constructor(e){super(e),this._chosenAssets=new Set,this._wrapper=null,this._initListeners()}refresh(){this.value=this._getValue(),this.isEnabled=this._checkEnabled()}execute(){this.fire("ckbox:open")}_getValue(){return null!==this._wrapper}_checkEnabled(){const e=this.editor.commands.get("insertImage"),t=this.editor.commands.get("link");return!(!e.isEnabled&&!t.isEnabled)}_prepareOptions(){const e=this.editor.config.get("ckbox");return{theme:e.theme,language:e.language,tokenUrl:e.tokenUrl,serviceOrigin:e.serviceOrigin,dialog:{onClose:()=>this.fire("ckbox:close")},assets:{onChoose:e=>this.fire("ckbox:choose",e)}}}_initListeners(){const e=this.editor,t=e.model,i=!e.config.get("ckbox.ignoreDataId");this.on("ckbox",(()=>{this.refresh()}),{priority:"low"}),this.on("ckbox:open",(()=>{this.isEnabled&&!this.value&&(this._wrapper=(0,s.createElement)(document,"div",{class:"ck ckbox-wrapper"}),document.body.appendChild(this._wrapper),window.CKBox.mount(this._wrapper,this._prepareOptions()))})),this.on("ckbox:close",(()=>{this.value&&(this._wrapper.remove(),this._wrapper=null,e.editing.view.focus())})),this.on("ckbox:choose",((o,r)=>{if(!this.isEnabled)return;const n=e.commands.get("insertImage"),s=e.commands.get("link"),a=function({assets:e,isImageAllowed:t,isLinkAllowed:i}){return e.map((e=>function(e){const t=e.data.metadata;if(!t)return!1;return t.width&&t.height}(e)?{id:e.data.id,type:"image",attributes:w(e)}:{id:e.data.id,type:"link",attributes:v(e)})).filter((e=>"image"===e.type?t:i))}({assets:r,isImageAllowed:n.isEnabled,isLinkAllowed:s.isEnabled}),c=a.length;0!==c&&(t.change((e=>{for(const t of a){const o=t===a[c-1],r=1===c;this._insertAsset(t,o,e,r),i&&(setTimeout((()=>this._chosenAssets.delete(t)),1e3),this._chosenAssets.add(t))}})),e.editing.view.focus())})),this.listenTo(e,"destroy",(()=>{this.fire("ckbox:close"),this._chosenAssets.clear()}))}_insertAsset(e,t,i,o){const r=this.editor.model.document.selection;i.removeSelectionAttribute("linkHref"),"image"===e.type?this._insertImage(e):this._insertLink(e,i,o),t||i.setSelection(r.getLastPosition())}_insertImage(e){const t=this.editor,{imageFallbackUrl:i,imageSources:o,imageTextAlternative:r,imageWidth:n,imageHeight:s,imagePlaceholder:a}=e.attributes;t.execute("insertImage",{source:{src:i,sources:o,alt:r,width:n,height:s,...a?{placeholder:a}:null}})}_insertLink(e,t,i){const o=this.editor,r=o.model,n=r.document.selection,{linkName:a,linkHref:c}=e.attributes;if(n.isCollapsed){const e=(0,s.toMap)(n.getAttributes()),l=t.createText(a,e);if(!i){const e=n.getLastPosition(),i=e.parent;"paragraph"===i.name&&i.isEmpty||o.execute("insertParagraph",{position:e});const s=r.insertContent(l);return t.setSelection(s),void o.execute("link",c)}const d=r.insertContent(l);t.setSelection(d)}o.execute("link",c)}}function w(e){const{imageFallbackUrl:t,imageSources:i}=f(e.data.imageUrls),{description:o,width:r,height:n,blurHash:s}=e.data.metadata,a=function(e){if(e)try{const t=`${k}px`,i=document.createElement("canvas");i.setAttribute("width",t),i.setAttribute("height",t);const o=i.getContext("2d");if(!o)return;const r=o.createImageData(k,k),n=p(e,k,k);return r.data.set(n),o.putImageData(r,0,0),i.toDataURL()}catch(e){return}}(s);return{imageFallbackUrl:t,imageSources:i,imageTextAlternative:o||"",imageWidth:r,imageHeight:n,...a?{imagePlaceholder:a}:null}}function v(e){return{linkName:e.data.name,linkHref:I(e)}}function I(e){const t=new URL(e.data.url);return t.searchParams.set("download","true"),t.toString()}var C=i(448);class A extends e.Plugin{static get requires(){return["ImageUploadEditing","ImageUploadProgress",C.FileRepository,E]}static get pluginName(){return"CKBoxUploadAdapter"}async afterInit(){const e=this.editor,t=!!e.config.get("ckbox"),i=!!window.CKBox;if(!t&&!i)return;const o=e.plugins.get(C.FileRepository),r=e.plugins.get(E);o.createUploadAdapter=t=>new y(t,r.getToken(),e);const n=!e.config.get("ckbox.ignoreDataId"),s=e.plugins.get("ImageUploadEditing");n&&s.on("uploadComplete",((t,{imageElement:i,data:o})=>{e.model.change((e=>{e.setAttribute("ckboxImageId",o.ckboxImageId,i)}))}))}}class y{constructor(e,t,i){this.loader=e,this.token=t,this.editor=i,this.controller=new AbortController,this.serviceOrigin=i.config.get("ckbox.serviceOrigin")}getWorkspaceId(){const e=(0,this.editor.t)("Cannot access default workspace."),t=this.editor.config.get("ckbox.defaultUploadWorkspaceId"),i=function(e,t){const[,i]=e.value.split("."),o=JSON.parse(atob(i)),r=o.auth&&o.auth.ckbox&&o.auth.ckbox.workspaces||[o.aud];return t?"superadmin"==(o.auth&&o.auth.ckbox&&o.auth.ckbox.role)||r.includes(t)?t:null:r[0]}(this.token,t);if(null==i)throw(0,s.logError)("ckbox-access-default-workspace-error"),e;return i}async getAvailableCategories(e=0){const t=new URL("categories",this.serviceOrigin);return t.searchParams.set("limit",50..toString()),t.searchParams.set("offset",e.toString()),t.searchParams.set("workspaceId",this.getWorkspaceId()),this._sendHttpRequest({url:t}).then((async t=>{if(t.totalCount-(e+50)>0){const i=await this.getAvailableCategories(e+50);return[...t.items,...i]}return t.items})).catch((()=>{this.controller.signal.throwIfAborted(),(0,s.logError)("ckbox-fetch-category-http-error")}))}async getCategoryIdForFile(e){const t=function(e){const t=/\.(?<ext>[^.]+)$/;return e.match(t).groups.ext.toLowerCase()}(e.name),i=await this.getAvailableCategories();if(!i)return null;const o=this.editor.config.get("ckbox.defaultUploadCategories");if(o){const e=Object.keys(o).find((e=>o[e].find((e=>e.toLowerCase()==t))));if(e){const t=i.find((t=>t.id===e||t.name===e));return t?t.id:null}}const r=i.find((e=>e.extensions.find((e=>e.toLowerCase()==t))));return r?r.id:null}async upload(){const e=this.editor.t,t=e("Cannot determine a category for the uploaded file."),i=await this.loader.file,o=await this.getCategoryIdForFile(i);if(!o)return Promise.reject(t);const r=new URL("assets",this.serviceOrigin),n=new FormData;r.searchParams.set("workspaceId",this.getWorkspaceId()),n.append("categoryId",o),n.append("file",i);const s={method:"POST",url:r,data:n,onUploadProgress:e=>{e.lengthComputable&&(this.loader.uploadTotal=e.total,this.loader.uploaded=e.loaded)}};return this._sendHttpRequest(s).then((async e=>{const t=f(e.imageUrls);return{ckboxImageId:e.id,default:t.imageFallbackUrl,sources:t.imageSources}})).catch((()=>{const t=e("Cannot upload file:")+` ${i.name}.`;return Promise.reject(t)}))}abort(){this.controller.abort()}_sendHttpRequest({url:e,method:t="GET",data:i,onUploadProgress:o}){const r=this.controller.signal,n=new XMLHttpRequest;n.open(t,e.toString(),!0),n.setRequestHeader("Authorization",this.token.value),n.setRequestHeader("CKBox-Version","CKEditor 5"),n.responseType="json";const s=()=>{n.abort()};return new Promise(((e,t)=>{r.addEventListener("abort",s),n.addEventListener("loadstart",(()=>{r.addEventListener("abort",s)})),n.addEventListener("loadend",(()=>{r.removeEventListener("abort",s)})),n.addEventListener("error",(()=>{t()})),n.addEventListener("abort",(()=>{t()})),n.addEventListener("load",(async()=>{const i=n.response;return!i||i.statusCode>=400?t(i&&i.message):e(i)})),o&&n.upload.addEventListener("progress",(e=>{o(e)})),n.send(i)}))}}class E extends e.Plugin{static get pluginName(){return"CKBoxEditing"}static get requires(){return["CloudServices","LinkEditing","PictureEditing",A]}async init(){const e=this.editor,t=!!e.config.get("ckbox"),i=!!window.CKBox;if(!t&&!i)return;this._initConfig();const o=e.plugins.get("CloudServicesCore"),r=e.config.get("ckbox.tokenUrl");if(r===e.config.get("cloudServices.tokenUrl")){const t=e.plugins.get("CloudServices");this._token=t.token}else this._token=await o.createToken(r).init();e.config.get("ckbox.ignoreDataId")||(this._initSchema(),this._initConversion(),this._initFixers()),i&&e.commands.add("ckbox",new x(e))}getToken(){return this._token}_initConfig(){const e=this.editor;e.config.define("ckbox",{serviceOrigin:"https://api.ckbox.io",defaultUploadCategories:null,ignoreDataId:!1,language:e.locale.uiLanguage,theme:"lark",tokenUrl:e.config.get("cloudServices.tokenUrl")});if(!e.config.get("ckbox.tokenUrl"))throw new s.CKEditorError("ckbox-plugin-missing-token-url",this);e.plugins.has("ImageBlockEditing")||e.plugins.has("ImageInlineEditing")||(0,s.logError)("ckbox-plugin-image-feature-missing",e)}_initSchema(){const e=this.editor.model.schema;e.extend("$text",{allowAttributes:"ckboxLinkId"}),e.isRegistered("imageBlock")&&e.extend("imageBlock",{allowAttributes:["ckboxImageId","ckboxLinkId"]}),e.isRegistered("imageInline")&&e.extend("imageInline",{allowAttributes:["ckboxImageId","ckboxLinkId"]}),e.addAttributeCheck(((e,t)=>{if(!!!e.last.getAttribute("linkHref")&&"ckboxLinkId"===t)return!1}))}_initConversion(){const e=this.editor;e.conversion.for("downcast").add((e=>{e.on("attribute:ckboxLinkId:imageBlock",((e,t,i)=>{const{writer:o,mapper:r,consumable:n}=i;if(!n.consume(t.item,e.name))return;const s=[...r.toViewElement(t.item).getChildren()].find((e=>"a"===e.name));s&&(t.item.hasAttribute("ckboxLinkId")?o.setAttribute("data-ckbox-resource-id",t.item.getAttribute("ckboxLinkId"),s):o.removeAttribute("data-ckbox-resource-id",s))}),{priority:"low"}),e.on("attribute:ckboxLinkId",((e,t,i)=>{const{writer:o,mapper:r,consumable:n}=i;if(n.consume(t.item,e.name)){if(t.attributeOldValue){const e=_(o,t.attributeOldValue);o.unwrap(r.toViewRange(t.range),e)}if(t.attributeNewValue){const e=_(o,t.attributeNewValue);if(t.item.is("selection")){const t=o.document.selection;o.wrap(t.getFirstRange(),e)}else o.wrap(r.toViewRange(t.range),e)}}}),{priority:"low"})})),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const{writer:o,consumable:r}=i;if(!t.viewItem.getAttribute("href"))return;if(!r.consume(t.viewItem,{attributes:["data-ckbox-resource-id"]}))return;const n=t.viewItem.getAttribute("data-ckbox-resource-id");if(n)if(t.modelRange)for(let e of t.modelRange.getItems())e.is("$textProxy")&&(e=e.textNode),S(e)&&o.setAttribute("ckboxLinkId",n,e);else{const e=t.modelCursor.nodeBefore||t.modelCursor.parent;o.setAttribute("ckboxLinkId",n,e)}}),{priority:"low"})})),e.conversion.for("downcast").attributeToAttribute({model:"ckboxImageId",view:"data-ckbox-resource-id"}),e.conversion.for("upcast").elementToAttribute({model:{key:"ckboxImageId",value:e=>e.getAttribute("data-ckbox-resource-id")},view:{attributes:{"data-ckbox-resource-id":/[\s\S]+/}}});const t=e.commands.get("replaceImageSource");t&&this.listenTo(t,"cleanupImage",((e,[t,i])=>{t.removeAttribute("ckboxImageId",i)}))}_initFixers(){const e=this.editor,t=e.model,i=t.document.selection;t.document.registerPostFixer(function(e){return t=>{let i=!1;const o=e.model,r=e.commands.get("ckbox");if(!r)return i;for(const e of o.document.differ.getChanges()){if("insert"!==e.type&&"attribute"!==e.type)continue;const o="insert"===e.type?new n.Range(e.position,e.position.getShiftedBy(e.length)):e.range,s="attribute"===e.type&&"linkHref"===e.attributeKey&&null===e.attributeNewValue;for(const e of o.getItems()){if(s&&e.hasAttribute("ckboxLinkId")){t.removeAttribute("ckboxLinkId",e),i=!0;continue}const o=L(e,r._chosenAssets);for(const r of o){const o="image"===r.type?"ckboxImageId":"ckboxLinkId";r.id!==e.getAttribute(o)&&(t.setAttribute(o,r.id,e),i=!0)}}}return i}}(e)),t.document.registerPostFixer(function(e){return t=>!(e.hasAttribute("linkHref")||!e.hasAttribute("ckboxLinkId"))&&(t.removeSelectionAttribute("ckboxLinkId"),!0)}(i))}}function L(e,t){const i=e.is("element","imageInline")||e.is("element","imageBlock"),o=e.hasAttribute("linkHref");return[...t].filter((t=>"image"===t.type&&i?t.attributes.imageFallbackUrl===e.getAttribute("src"):"link"===t.type&&o?t.attributes.linkHref===e.getAttribute("linkHref"):void 0))}function _(e,t){const i=e.createAttributeElement("a",{"data-ckbox-resource-id":t},{priority:5});return e.setCustomProperty("link",!0,i),i}function S(e){return!!e.is("$text")||!(!e.is("element","imageInline")&&!e.is("element","imageBlock"))}class P extends e.Plugin{static get pluginName(){return"CKBox"}static get requires(){return[E,r]}}})(),(window.CKEditor5=window.CKEditor5||{}).ckbox=o})();
|
package/build/translations/ug.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(n){const e=n.ug=n.ug||{};e.dictionary=Object.assign(e.dictionary||{},{"Cannot access default workspace.":"","Cannot determine a category for the uploaded file.":"يۈكلەيدىغان ھۆججەتنىڭ تۈرىنى جەزملىيەلمىدى.","Open file manager":"ھۆججەت باشقۇرغۇچنى ئاچ"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
|
1
|
+
!function(n){const e=n.ug=n.ug||{};e.dictionary=Object.assign(e.dictionary||{},{"Cannot access default workspace.":"كۆڭۈلدىكى خىزمەت بوشلۇقىنى زىيارەت قىلالمايدۇ","Cannot determine a category for the uploaded file.":"يۈكلەيدىغان ھۆججەتنىڭ تۈرىنى جەزملىيەلمىدى.","Open file manager":"ھۆججەت باشقۇرغۇچنى ئاچ"})}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
package/lang/translations/ug.po
CHANGED
|
@@ -27,4 +27,4 @@ msgstr "يۈكلەيدىغان ھۆججەتنىڭ تۈرىنى جەزملىيە
|
|
|
27
27
|
|
|
28
28
|
msgctxt "A message is displayed when the user is not authorised to access CKBox workspace configured as default one."
|
|
29
29
|
msgid "Cannot access default workspace."
|
|
30
|
-
msgstr ""
|
|
30
|
+
msgstr "كۆڭۈلدىكى خىزمەت بوشلۇقىنى زىيارەت قىلالمايدۇ"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ckeditor/ckeditor5-ckbox",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "40.1.0",
|
|
4
4
|
"description": "CKBox integration for CKEditor 5.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ckeditor",
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
],
|
|
13
13
|
"main": "src/index.js",
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"ckeditor5": "
|
|
15
|
+
"ckeditor5": "40.1.0",
|
|
16
|
+
"blurhash": "^2.0.5"
|
|
16
17
|
},
|
|
17
18
|
"author": "CKSource (http://cksource.com/)",
|
|
18
19
|
"license": "GPL-2.0-or-later",
|
package/src/ckboxcommand.d.ts
CHANGED
|
@@ -92,6 +92,7 @@ export default class CKBoxCommand extends Command {
|
|
|
92
92
|
* @param asset The asset to be inserted.
|
|
93
93
|
* @param isLastAsset Indicates if the current asset is the last one from the chosen set.
|
|
94
94
|
* @param writer An instance of the model writer.
|
|
95
|
+
* @param isSingleAsset It's true when only one asset is processed.
|
|
95
96
|
*/
|
|
96
97
|
private _insertAsset;
|
|
97
98
|
/**
|
|
@@ -105,6 +106,7 @@ export default class CKBoxCommand extends Command {
|
|
|
105
106
|
*
|
|
106
107
|
* @param asset The asset to be inserted.
|
|
107
108
|
* @param writer An instance of the model writer.
|
|
109
|
+
* @param isSingleAsset It's true when only one asset is processed.
|
|
108
110
|
*/
|
|
109
111
|
private _insertLink;
|
|
110
112
|
}
|
package/src/ckboxcommand.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Command } from 'ckeditor5/src/core';
|
|
6
6
|
import { createElement, toMap } from 'ckeditor5/src/utils';
|
|
7
|
-
import { getImageUrls } from './utils';
|
|
7
|
+
import { blurHashToDataUrl, getImageUrls } from './utils';
|
|
8
8
|
// Defines the waiting time (in milliseconds) for inserting the chosen asset into the model. The chosen asset is temporarily stored in the
|
|
9
9
|
// `CKBoxCommand#_chosenAssets` and it is removed from there automatically after this time. See `CKBoxCommand#_chosenAssets` for more
|
|
10
10
|
// details.
|
|
@@ -132,8 +132,6 @@ export default class CKBoxCommand extends Command {
|
|
|
132
132
|
this._wrapper = createElement(document, 'div', { class: 'ck ckbox-wrapper' });
|
|
133
133
|
document.body.appendChild(this._wrapper);
|
|
134
134
|
window.CKBox.mount(this._wrapper, this._prepareOptions());
|
|
135
|
-
const MAX_NUMBER_OF_ATTEMPTS_TO_FOCUS = 50;
|
|
136
|
-
focusCKBoxItem(MAX_NUMBER_OF_ATTEMPTS_TO_FOCUS);
|
|
137
135
|
});
|
|
138
136
|
// Handle closing of the CKBox dialog.
|
|
139
137
|
this.on('ckbox:close', () => {
|
|
@@ -142,6 +140,7 @@ export default class CKBoxCommand extends Command {
|
|
|
142
140
|
}
|
|
143
141
|
this._wrapper.remove();
|
|
144
142
|
this._wrapper = null;
|
|
143
|
+
editor.editing.view.focus();
|
|
145
144
|
});
|
|
146
145
|
// Handle choosing the assets.
|
|
147
146
|
this.on('ckbox:choose', (evt, assets) => {
|
|
@@ -155,14 +154,16 @@ export default class CKBoxCommand extends Command {
|
|
|
155
154
|
isImageAllowed: imageCommand.isEnabled,
|
|
156
155
|
isLinkAllowed: linkCommand.isEnabled
|
|
157
156
|
});
|
|
158
|
-
|
|
157
|
+
const assetsCount = assetsToProcess.length;
|
|
158
|
+
if (assetsCount === 0) {
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
// All assets are inserted in one undo step.
|
|
162
162
|
model.change(writer => {
|
|
163
163
|
for (const asset of assetsToProcess) {
|
|
164
|
-
const isLastAsset = asset === assetsToProcess[
|
|
165
|
-
|
|
164
|
+
const isLastAsset = asset === assetsToProcess[assetsCount - 1];
|
|
165
|
+
const isSingleAsset = assetsCount === 1;
|
|
166
|
+
this._insertAsset(asset, isLastAsset, writer, isSingleAsset);
|
|
166
167
|
// If asset ID must be set for the inserted model element, store the asset temporarily and remove it automatically
|
|
167
168
|
// after the timeout.
|
|
168
169
|
if (shouldInsertDataId) {
|
|
@@ -171,6 +172,7 @@ export default class CKBoxCommand extends Command {
|
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
});
|
|
175
|
+
editor.editing.view.focus();
|
|
174
176
|
});
|
|
175
177
|
// Clean up after the editor is destroyed.
|
|
176
178
|
this.listenTo(editor, 'destroy', () => {
|
|
@@ -184,8 +186,9 @@ export default class CKBoxCommand extends Command {
|
|
|
184
186
|
* @param asset The asset to be inserted.
|
|
185
187
|
* @param isLastAsset Indicates if the current asset is the last one from the chosen set.
|
|
186
188
|
* @param writer An instance of the model writer.
|
|
189
|
+
* @param isSingleAsset It's true when only one asset is processed.
|
|
187
190
|
*/
|
|
188
|
-
_insertAsset(asset, isLastAsset, writer) {
|
|
191
|
+
_insertAsset(asset, isLastAsset, writer, isSingleAsset) {
|
|
189
192
|
const editor = this.editor;
|
|
190
193
|
const model = editor.model;
|
|
191
194
|
const selection = model.document.selection;
|
|
@@ -195,7 +198,7 @@ export default class CKBoxCommand extends Command {
|
|
|
195
198
|
this._insertImage(asset);
|
|
196
199
|
}
|
|
197
200
|
else {
|
|
198
|
-
this._insertLink(asset, writer);
|
|
201
|
+
this._insertLink(asset, writer, isSingleAsset);
|
|
199
202
|
}
|
|
200
203
|
// Except for the last chosen asset, move the selection to the end of the current range to avoid overwriting other, already
|
|
201
204
|
// inserted assets.
|
|
@@ -210,12 +213,15 @@ export default class CKBoxCommand extends Command {
|
|
|
210
213
|
*/
|
|
211
214
|
_insertImage(asset) {
|
|
212
215
|
const editor = this.editor;
|
|
213
|
-
const { imageFallbackUrl, imageSources, imageTextAlternative } = asset.attributes;
|
|
216
|
+
const { imageFallbackUrl, imageSources, imageTextAlternative, imageWidth, imageHeight, imagePlaceholder } = asset.attributes;
|
|
214
217
|
editor.execute('insertImage', {
|
|
215
218
|
source: {
|
|
216
219
|
src: imageFallbackUrl,
|
|
217
220
|
sources: imageSources,
|
|
218
|
-
alt: imageTextAlternative
|
|
221
|
+
alt: imageTextAlternative,
|
|
222
|
+
width: imageWidth,
|
|
223
|
+
height: imageHeight,
|
|
224
|
+
...(imagePlaceholder ? { placeholder: imagePlaceholder } : null)
|
|
219
225
|
}
|
|
220
226
|
});
|
|
221
227
|
}
|
|
@@ -224,8 +230,9 @@ export default class CKBoxCommand extends Command {
|
|
|
224
230
|
*
|
|
225
231
|
* @param asset The asset to be inserted.
|
|
226
232
|
* @param writer An instance of the model writer.
|
|
233
|
+
* @param isSingleAsset It's true when only one asset is processed.
|
|
227
234
|
*/
|
|
228
|
-
_insertLink(asset, writer) {
|
|
235
|
+
_insertLink(asset, writer, isSingleAsset) {
|
|
229
236
|
const editor = this.editor;
|
|
230
237
|
const model = editor.model;
|
|
231
238
|
const selection = model.document.selection;
|
|
@@ -234,6 +241,20 @@ export default class CKBoxCommand extends Command {
|
|
|
234
241
|
if (selection.isCollapsed) {
|
|
235
242
|
const selectionAttributes = toMap(selection.getAttributes());
|
|
236
243
|
const textNode = writer.createText(linkName, selectionAttributes);
|
|
244
|
+
if (!isSingleAsset) {
|
|
245
|
+
const selectionLastPosition = selection.getLastPosition();
|
|
246
|
+
const parentElement = selectionLastPosition.parent;
|
|
247
|
+
// Insert new `paragraph` when selection is not in an empty `paragraph`.
|
|
248
|
+
if (!(parentElement.name === 'paragraph' && parentElement.isEmpty)) {
|
|
249
|
+
editor.execute('insertParagraph', {
|
|
250
|
+
position: selectionLastPosition
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
const range = model.insertContent(textNode);
|
|
254
|
+
writer.setSelection(range);
|
|
255
|
+
editor.execute('link', linkHref);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
237
258
|
const range = model.insertContent(textNode);
|
|
238
259
|
writer.setSelection(range);
|
|
239
260
|
}
|
|
@@ -260,15 +281,18 @@ function prepareAssets({ assets, isImageAllowed, isLinkAllowed }) {
|
|
|
260
281
|
}
|
|
261
282
|
/**
|
|
262
283
|
* Parses the assets attributes into the internal data format.
|
|
263
|
-
*
|
|
264
|
-
* @param origin The base URL for assets inserted into the editor.
|
|
265
284
|
*/
|
|
266
285
|
function prepareImageAssetAttributes(asset) {
|
|
267
286
|
const { imageFallbackUrl, imageSources } = getImageUrls(asset.data.imageUrls);
|
|
287
|
+
const { description, width, height, blurHash } = asset.data.metadata;
|
|
288
|
+
const imagePlaceholder = blurHashToDataUrl(blurHash);
|
|
268
289
|
return {
|
|
269
290
|
imageFallbackUrl,
|
|
270
291
|
imageSources,
|
|
271
|
-
imageTextAlternative:
|
|
292
|
+
imageTextAlternative: description || '',
|
|
293
|
+
imageWidth: width,
|
|
294
|
+
imageHeight: height,
|
|
295
|
+
...(imagePlaceholder ? { imagePlaceholder } : null)
|
|
272
296
|
};
|
|
273
297
|
}
|
|
274
298
|
/**
|
|
@@ -302,32 +326,3 @@ function getAssetUrl(asset) {
|
|
|
302
326
|
url.searchParams.set('download', 'true');
|
|
303
327
|
return url.toString();
|
|
304
328
|
}
|
|
305
|
-
/**
|
|
306
|
-
* Focuses the CKBox first item in gallery.
|
|
307
|
-
* This is a temporary fix. A permanent solution to this issue will be provided soon.
|
|
308
|
-
*
|
|
309
|
-
* @param limiter Max number of attempts to focus the ckbox item.
|
|
310
|
-
*/
|
|
311
|
-
function focusCKBoxItem(limiter) {
|
|
312
|
-
// Trying every 100 ms get access to the CKBox component until component will be loaded.
|
|
313
|
-
setTimeout(() => {
|
|
314
|
-
if (limiter === 0) {
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
const ckboxGalleryFirstItem = document.querySelector('.ckbox-gallery .ckbox-gallery-item');
|
|
318
|
-
// In case there is no items, "upload button" will be appeared in "div" with
|
|
319
|
-
// classname ".ckbox-empty-view".
|
|
320
|
-
const uploadButton = document.querySelector('.ckbox-empty-view .ckbox-btn');
|
|
321
|
-
// In case "upload button" is loaded in ".ckbox-empty-view" we focus actual button.
|
|
322
|
-
if (uploadButton && uploadButton instanceof HTMLElement) {
|
|
323
|
-
uploadButton.focus();
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
if (ckboxGalleryFirstItem && ckboxGalleryFirstItem instanceof HTMLElement) {
|
|
327
|
-
ckboxGalleryFirstItem.focus();
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
focusCKBoxItem(limiter - 1);
|
|
331
|
-
}
|
|
332
|
-
}, 100);
|
|
333
|
-
}
|
package/src/ckboxconfig.d.ts
CHANGED
|
@@ -195,6 +195,18 @@ export interface CKBoxAssetImageAttributesDefinition {
|
|
|
195
195
|
* An alternative text for an image.
|
|
196
196
|
*/
|
|
197
197
|
imageTextAlternative: string;
|
|
198
|
+
/**
|
|
199
|
+
* Image width.
|
|
200
|
+
*/
|
|
201
|
+
imageWidth?: number;
|
|
202
|
+
/**
|
|
203
|
+
* Image height.
|
|
204
|
+
*/
|
|
205
|
+
imageHeight?: number;
|
|
206
|
+
/**
|
|
207
|
+
* Image placeholder image.
|
|
208
|
+
*/
|
|
209
|
+
imagePlaceholder?: string;
|
|
198
210
|
}
|
|
199
211
|
/**
|
|
200
212
|
* Asset attributes definition for a link.
|
|
@@ -280,4 +292,8 @@ export interface CKBoxRawAssetMetadataDefinition {
|
|
|
280
292
|
* Image height.
|
|
281
293
|
*/
|
|
282
294
|
height?: number;
|
|
295
|
+
/**
|
|
296
|
+
* The blurhash placeholder value.
|
|
297
|
+
*/
|
|
298
|
+
blurHash?: string;
|
|
283
299
|
}
|
package/src/ckboxediting.js
CHANGED
|
@@ -7,6 +7,7 @@ import { Range } from 'ckeditor5/src/engine';
|
|
|
7
7
|
import { CKEditorError, logError } from 'ckeditor5/src/utils';
|
|
8
8
|
import CKBoxCommand from './ckboxcommand';
|
|
9
9
|
import CKBoxUploadAdapter from './ckboxuploadadapter';
|
|
10
|
+
const DEFAULT_CKBOX_THEME_NAME = 'lark';
|
|
10
11
|
/**
|
|
11
12
|
* The CKBox editing feature. It introduces the {@link module:ckbox/ckboxcommand~CKBoxCommand CKBox command} and
|
|
12
13
|
* {@link module:ckbox/ckboxuploadadapter~CKBoxUploadAdapter CKBox upload adapter}.
|
|
@@ -78,7 +79,7 @@ export default class CKBoxEditing extends Plugin {
|
|
|
78
79
|
defaultUploadCategories: null,
|
|
79
80
|
ignoreDataId: false,
|
|
80
81
|
language: editor.locale.uiLanguage,
|
|
81
|
-
theme:
|
|
82
|
+
theme: DEFAULT_CKBOX_THEME_NAME,
|
|
82
83
|
tokenUrl: editor.config.get('cloudServices.tokenUrl')
|
|
83
84
|
});
|
|
84
85
|
const tokenUrl = editor.config.get('ckbox.tokenUrl');
|
|
@@ -246,6 +247,12 @@ export default class CKBoxEditing extends Plugin {
|
|
|
246
247
|
}
|
|
247
248
|
}
|
|
248
249
|
});
|
|
250
|
+
const replaceImageSourceCommand = editor.commands.get('replaceImageSource');
|
|
251
|
+
if (replaceImageSourceCommand) {
|
|
252
|
+
this.listenTo(replaceImageSourceCommand, 'cleanupImage', (_, [writer, image]) => {
|
|
253
|
+
writer.removeAttribute('ckboxImageId', image);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
249
256
|
}
|
|
250
257
|
/**
|
|
251
258
|
* Registers post-fixers that add or remove the `ckboxLinkId` and `ckboxImageId` attributes.
|
package/src/utils.d.ts
CHANGED
|
@@ -26,3 +26,7 @@ export declare function getImageUrls(imageUrls: CKBoxImageUrls): {
|
|
|
26
26
|
* @param defaultWorkspaceId The default workspace to use taken from editor config.
|
|
27
27
|
*/
|
|
28
28
|
export declare function getWorkspaceId(token: InitializedToken, defaultWorkspaceId?: string): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* Generates an image data URL from its `blurhash` representation.
|
|
31
|
+
*/
|
|
32
|
+
export declare function blurHashToDataUrl(hash?: string): string | undefined;
|
package/src/utils.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
|
+
import { decode } from 'blurhash';
|
|
5
6
|
/**
|
|
6
7
|
* Converts image source set provided by the CKBox into an object containing:
|
|
7
8
|
* - responsive URLs for the "webp" image format,
|
|
@@ -47,3 +48,35 @@ export function getWorkspaceId(token, defaultWorkspaceId) {
|
|
|
47
48
|
}
|
|
48
49
|
return null;
|
|
49
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Default resolution for decoding blurhash values.
|
|
53
|
+
* Relatively small values must be used in order to ensure acceptable performance.
|
|
54
|
+
*/
|
|
55
|
+
const BLUR_RESOLUTION = 32;
|
|
56
|
+
/**
|
|
57
|
+
* Generates an image data URL from its `blurhash` representation.
|
|
58
|
+
*/
|
|
59
|
+
export function blurHashToDataUrl(hash) {
|
|
60
|
+
if (!hash) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const resolutionInPx = `${BLUR_RESOLUTION}px`;
|
|
65
|
+
const canvas = document.createElement('canvas');
|
|
66
|
+
canvas.setAttribute('width', resolutionInPx);
|
|
67
|
+
canvas.setAttribute('height', resolutionInPx);
|
|
68
|
+
const ctx = canvas.getContext('2d');
|
|
69
|
+
/* istanbul ignore next -- @preserve */
|
|
70
|
+
if (!ctx) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const imageData = ctx.createImageData(BLUR_RESOLUTION, BLUR_RESOLUTION);
|
|
74
|
+
const decoded = decode(hash, BLUR_RESOLUTION, BLUR_RESOLUTION);
|
|
75
|
+
imageData.data.set(decoded);
|
|
76
|
+
ctx.putImageData(imageData, 0, 0);
|
|
77
|
+
return canvas.toDataURL();
|
|
78
|
+
}
|
|
79
|
+
catch (e) {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|