@ckeditor/ckeditor5-ckbox 0.0.0-nightly-20230725.1 → 0.0.0-nightly-20230727.0

Sign up to get free protection for your applications and to get access to all the features.
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(n){var o=t[n];if(void 0!==o)return o.exports;var r=t[n]={exports:{}};return e[n](r,r.exports,i),r.exports}i.d=(e,t)=>{for(var n in t)i.o(t,n)&&!i.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},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 n={};(()=>{"use strict";i.r(n),i.d(n,{CKBox:()=>y,CKBoxEditing:()=>v,CKBoxUI:()=>o});var e=i(704),t=i(273);class o extends e.Plugin{static get pluginName(){return"CKBoxUI"}afterInit(){const e=this.editor,i=e.commands.get("ckbox");if(!i)return;const n=e.t;e.ui.componentFactory.add("ckbox",(o=>{const r=new t.ButtonView(o);return r.set({label:n("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);const a=4e3,c=80,d=10;function l({token:e,id:t,origin:i,width:n,extension:o}){const r=u(e),s=function(e){const t=[e*d/100,c],i=Math.floor(Math.max(...t)),n=[Math.min(e,a)];let o=n[0];for(;o-i>=i;)o-=i,n.unshift(o);return n}(n),l=function(e){if("bmp"===e||"tiff"===e||"jpg"===e)return"jpeg";return e}(o);return{imageFallbackUrl:g({environmentId:r,id:t,origin:i,width:n,extension:l}),imageSources:[{srcset:s.map((e=>`${g({environmentId:r,id:t,origin:i,width:e,extension:"webp"})} ${e}w`)).join(","),sizes:`(max-width: ${n}px) 100vw, ${n}px`,type:"image/webp"}]}}function u(e){const[,t]=e.value.split(".");return JSON.parse(atob(t)).aud}function g({environmentId:e,id:t,origin:i,width:n,extension:o}){return new URL(`${e}/assets/${t}/images/${n}.${o}`,i).toString()}class m 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,assetsOrigin:e.assetsOrigin,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)})),this.on("ckbox:choose",((n,o)=>{if(!this.isEnabled)return;const r=e.commands.get("insertImage"),s=e.commands.get("link"),a=e.plugins.get("CKBoxEditing"),c=function({assets:e,origin:t,token:i,isImageAllowed:n,isLinkAllowed:o}){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:h(e,i,t)}:{id:e.data.id,type:"link",attributes:b(e,i,t)})).filter((e=>"image"===e.type?n:o))}({assets:o,origin:e.config.get("ckbox.assetsOrigin"),token:a.getToken(),isImageAllowed:r.isEnabled,isLinkAllowed:s.isEnabled});0!==c.length&&t.change((e=>{for(const t of c){const n=t===c[c.length-1];this._insertAsset(t,n,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 n=this.editor.model.document.selection;i.removeSelectionAttribute("linkHref"),"image"===e.type?this._insertImage(e):this._insertLink(e,i),t||i.setSelection(n.getLastPosition())}_insertImage(e){const t=this.editor,{imageFallbackUrl:i,imageSources:n,imageTextAlternative:o}=e.attributes;t.execute("insertImage",{source:{src:i,sources:n,alt:o}})}_insertLink(e,t){const i=this.editor,n=i.model,o=n.document.selection,{linkName:r,linkHref:a}=e.attributes;if(o.isCollapsed){const e=(0,s.toMap)(o.getAttributes()),i=t.createText(r,e),a=n.insertContent(i);t.setSelection(a)}i.execute("link",a)}}function h(e,t,i){const{imageFallbackUrl:n,imageSources:o}=l({token:t,origin:i,id:e.data.id,width:e.data.metadata.width,extension:e.data.extension});return{imageFallbackUrl:n,imageSources:o,imageTextAlternative:e.data.metadata.description||""}}function b(e,t,i){return{linkName:e.data.name,linkHref:p(e,t,i)}}function p(e,t,i){const n=u(t),o=new URL(`${n}/assets/${e.data.id}/file`,i);return o.searchParams.set("download","true"),o.toString()}var k=i(448);class f extends e.Plugin{static get requires(){return["ImageUploadEditing","ImageUploadProgress",k.FileRepository,v]}static get pluginName(){return"CKBoxUploadAdapter"}async afterInit(){const e=this.editor,t=!!e.config.get("ckbox"),i=!!window.CKBox;if(!t&&!i)return;const n=e.plugins.get(k.FileRepository),o=e.plugins.get(v);n.createUploadAdapter=t=>new x(t,o.getToken(),e);const r=!e.config.get("ckbox.ignoreDataId"),s=e.plugins.get("ImageUploadEditing");r&&s.on("uploadComplete",((t,{imageElement:i,data:n})=>{e.model.change((e=>{e.setAttribute("ckboxImageId",n.ckboxImageId,i)}))}))}}class x{constructor(e,t,i){this.loader=e,this.token=t,this.editor=i,this.controller=new AbortController,this.serviceOrigin=i.config.get("ckbox.serviceOrigin"),this.assetsOrigin=i.config.get("ckbox.assetsOrigin")}async getAvailableCategories(e=0){const t=new URL("categories",this.serviceOrigin);return t.searchParams.set("limit",50..toString()),t.searchParams.set("offset",e.toString()),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=w(e.name),i=await this.getAvailableCategories();if(!i)return null;const n=this.editor.config.get("ckbox.defaultUploadCategories");if(n){const e=Object.keys(n).find((e=>n[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 o=i.find((e=>e.extensions.find((e=>e.toLowerCase()==t))));return o?o.id:null}async upload(){const e=this.editor.t,t=e("Cannot determine a category for the uploaded file."),i=await this.loader.file,n=await this.getCategoryIdForFile(i);if(!n)return Promise.reject(t);const o=new URL("assets",this.serviceOrigin),r=new FormData;r.append("categoryId",n),r.append("file",i);const s={method:"POST",url:o,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=await this._getImageWidth(),n=w(i.name),o=l({token:this.token,id:e.id,origin:this.assetsOrigin,width:t,extension:n});return{ckboxImageId:e.id,default:o.imageFallbackUrl,sources:o.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:n}){const o=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)=>{o.addEventListener("abort",s),r.addEventListener("loadstart",(()=>{o.addEventListener("abort",s)})),r.addEventListener("loadend",(()=>{o.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)})),n&&r.upload.addEventListener("progress",(e=>{n(e)})),r.send(i)}))}_getImageWidth(){return new Promise((e=>{const t=new Image;t.onload=()=>{URL.revokeObjectURL(t.src),e(t.width)},t.src=this.loader.data}))}}function w(e){return e.match(/\.(?<ext>[^.]+)$/).groups.ext.toLowerCase()}class v extends e.Plugin{static get pluginName(){return"CKBoxEditing"}static get requires(){return["CloudServices","LinkEditing","PictureEditing",f]}async init(){const e=this.editor,t=!!e.config.get("ckbox"),i=!!window.CKBox;if(!t&&!i)return;this._initConfig();const n=e.plugins.get("CloudServicesCore"),o=e.config.get("ckbox.tokenUrl");if(o===e.config.get("cloudServices.tokenUrl")){const t=e.plugins.get("CloudServices");this._token=t.token}else this._token=await n.createToken(o).init();e.config.get("ckbox.ignoreDataId")||(this._initSchema(),this._initConversion(),this._initFixers()),i&&e.commands.add("ckbox",new m(e))}getToken(){return this._token}_initConfig(){const e=this.editor;e.config.define("ckbox",{serviceOrigin:"https://api.ckbox.io",assetsOrigin:"https://ckbox.cloud",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:n,mapper:o,consumable:r}=i;if(!r.consume(t.item,e.name))return;const s=[...o.toViewElement(t.item).getChildren()].find((e=>"a"===e.name));s&&(t.item.hasAttribute("ckboxLinkId")?n.setAttribute("data-ckbox-resource-id",t.item.getAttribute("ckboxLinkId"),s):n.removeAttribute("data-ckbox-resource-id",s))}),{priority:"low"}),e.on("attribute:ckboxLinkId",((e,t,i)=>{const{writer:n,mapper:o,consumable:r}=i;if(r.consume(t.item,e.name)){if(t.attributeOldValue){const e=C(n,t.attributeOldValue);n.unwrap(o.toViewRange(t.range),e)}if(t.attributeNewValue){const e=C(n,t.attributeNewValue);if(t.item.is("selection")){const t=n.document.selection;n.wrap(t.getFirstRange(),e)}else n.wrap(o.toViewRange(t.range),e)}}}),{priority:"low"})})),e.conversion.for("upcast").add((e=>{e.on("element:a",((e,t,i)=>{const{writer:n,consumable:o}=i;if(!t.viewItem.getAttribute("href"))return;if(!o.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),A(e)&&n.setAttribute("ckboxLinkId",r,e);else{const e=t.modelCursor.nodeBefore||t.modelCursor.parent;n.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 n=e.model,o=e.commands.get("ckbox");if(!o)return i;for(const e of n.document.differ.getChanges()){if("insert"!==e.type&&"attribute"!==e.type)continue;const n="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 n.getItems()){if(s&&e.hasAttribute("ckboxLinkId")){t.removeAttribute("ckboxLinkId",e),i=!0;continue}const n=I(e,o._chosenAssets);for(const o of n){const n="image"===o.type?"ckboxImageId":"ckboxLinkId";o.id!==e.getAttribute(n)&&(t.setAttribute(n,o.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 I(e,t){const i=e.is("element","imageInline")||e.is("element","imageBlock"),n=e.hasAttribute("linkHref");return[...t].filter((t=>"image"===t.type&&i?t.attributes.imageFallbackUrl===e.getAttribute("src"):"link"===t.type&&n?t.attributes.linkHref===e.getAttribute("linkHref"):void 0))}function C(e,t){const i=e.createAttributeElement("a",{"data-ckbox-resource-id":t},{priority:5});return e.setCustomProperty("link",!0,i),i}function A(e){return!!e.is("$text")||!(!e.is("element","imageInline")&&!e.is("element","imageBlock"))}class y extends e.Plugin{static get pluginName(){return"CKBox"}static get requires(){return[v,o]}}})(),(window.CKEditor5=window.CKEditor5||{}).ckbox=n})();
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:()=>x,CKBoxEditing:()=>b,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",(()=>{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)})),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()}var g=i(448);class m extends e.Plugin{static get requires(){return["ImageUploadEditing","ImageUploadProgress",g.FileRepository,b]}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(g.FileRepository),n=e.plugins.get(b);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 b extends e.Plugin{static get pluginName(){return"CKBoxEditing"}static get requires(){return["CloudServices","LinkEditing","PictureEditing",m]}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=k(o,t.attributeOldValue);o.unwrap(n.toViewRange(t.range),e)}if(t.attributeNewValue){const e=k(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),f(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=p(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 p(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 k(e,t){const i=e.createAttributeElement("a",{"data-ckbox-resource-id":t},{priority:5});return e.setCustomProperty("link",!0,i),i}function f(e){return!!e.is("$text")||!(!e.is("element","imageInline")&&!e.is("element","imageBlock"))}class x extends e.Plugin{static get pluginName(){return"CKBox"}static get requires(){return[b,n]}}})(),(window.CKEditor5=window.CKEditor5||{}).ckbox=o})();
@@ -1,4 +1,5 @@
1
1
  {
2
2
  "Open file manager": "Toolbar button tooltip for opening a file browser that allows inserting an image or a file to the editor.",
3
- "Cannot determine a category for the uploaded file.": "A message is displayed when CKEditor 5 cannot associate an image with any of the categories defined in CKBox while uploading an asset."
3
+ "Cannot determine a category for the uploaded file.": "A message is displayed when CKEditor 5 cannot associate an image with any of the categories defined in CKBox while uploading an asset.",
4
+ "Cannot access default workspace.": "A message is displayed when the user is not authorised to access CKBox workspace configured as default one."
4
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-ckbox",
3
- "version": "0.0.0-nightly-20230725.1",
3
+ "version": "0.0.0-nightly-20230727.0",
4
4
  "description": "CKBox integration for CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "main": "src/index.js",
14
14
  "dependencies": {
15
- "ckeditor5": "0.0.0-nightly-20230725.1"
15
+ "ckeditor5": "0.0.0-nightly-20230727.0"
16
16
  },
17
17
  "engines": {
18
18
  "node": ">=16.0.0",
@@ -78,7 +78,6 @@ export default class CKBoxCommand extends Command {
78
78
  * - language The language for CKBox dialog.
79
79
  * - tokenUrl The token endpoint URL.
80
80
  * - serviceOrigin The base URL of the API service.
81
- * - assetsOrigin The base URL for assets inserted into the editor.
82
81
  * - dialog.onClose The callback function invoked after closing the CKBox dialog.
83
82
  * - assets.onChoose The callback function invoked after choosing the assets.
84
83
  */
@@ -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 { getEnvironmentId, getImageUrls } from './utils';
7
+ import { 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.
@@ -94,7 +94,6 @@ export default class CKBoxCommand extends Command {
94
94
  * - language The language for CKBox dialog.
95
95
  * - tokenUrl The token endpoint URL.
96
96
  * - serviceOrigin The base URL of the API service.
97
- * - assetsOrigin The base URL for assets inserted into the editor.
98
97
  * - dialog.onClose The callback function invoked after closing the CKBox dialog.
99
98
  * - assets.onChoose The callback function invoked after choosing the assets.
100
99
  */
@@ -106,7 +105,6 @@ export default class CKBoxCommand extends Command {
106
105
  language: ckboxConfig.language,
107
106
  tokenUrl: ckboxConfig.tokenUrl,
108
107
  serviceOrigin: ckboxConfig.serviceOrigin,
109
- assetsOrigin: ckboxConfig.assetsOrigin,
110
108
  dialog: {
111
109
  onClose: () => this.fire('ckbox:close')
112
110
  },
@@ -150,12 +148,8 @@ export default class CKBoxCommand extends Command {
150
148
  }
151
149
  const imageCommand = editor.commands.get('insertImage');
152
150
  const linkCommand = editor.commands.get('link');
153
- const ckboxEditing = editor.plugins.get('CKBoxEditing');
154
- const assetsOrigin = editor.config.get('ckbox.assetsOrigin');
155
151
  const assetsToProcess = prepareAssets({
156
152
  assets,
157
- origin: assetsOrigin,
158
- token: ckboxEditing.getToken(),
159
153
  isImageAllowed: imageCommand.isEnabled,
160
154
  isLinkAllowed: linkCommand.isEnabled
161
155
  });
@@ -247,18 +241,18 @@ export default class CKBoxCommand extends Command {
247
241
  /**
248
242
  * Parses the chosen assets into the internal data format. Filters out chosen assets that are not allowed.
249
243
  */
250
- function prepareAssets({ assets, origin, token, isImageAllowed, isLinkAllowed }) {
244
+ function prepareAssets({ assets, isImageAllowed, isLinkAllowed }) {
251
245
  return assets
252
246
  .map(asset => isImage(asset) ?
253
247
  {
254
248
  id: asset.data.id,
255
249
  type: 'image',
256
- attributes: prepareImageAssetAttributes(asset, token, origin)
250
+ attributes: prepareImageAssetAttributes(asset)
257
251
  } :
258
252
  {
259
253
  id: asset.data.id,
260
254
  type: 'link',
261
- attributes: prepareLinkAssetAttributes(asset, token, origin)
255
+ attributes: prepareLinkAssetAttributes(asset)
262
256
  })
263
257
  .filter(asset => asset.type === 'image' ? isImageAllowed : isLinkAllowed);
264
258
  }
@@ -267,14 +261,8 @@ function prepareAssets({ assets, origin, token, isImageAllowed, isLinkAllowed })
267
261
  *
268
262
  * @param origin The base URL for assets inserted into the editor.
269
263
  */
270
- function prepareImageAssetAttributes(asset, token, origin) {
271
- const { imageFallbackUrl, imageSources } = getImageUrls({
272
- token,
273
- origin,
274
- id: asset.data.id,
275
- width: asset.data.metadata.width,
276
- extension: asset.data.extension
277
- });
264
+ function prepareImageAssetAttributes(asset) {
265
+ const { imageFallbackUrl, imageSources } = getImageUrls(asset.data.imageUrls);
278
266
  return {
279
267
  imageFallbackUrl,
280
268
  imageSources,
@@ -286,10 +274,10 @@ function prepareImageAssetAttributes(asset, token, origin) {
286
274
  *
287
275
  * @param origin The base URL for assets inserted into the editor.
288
276
  */
289
- function prepareLinkAssetAttributes(asset, token, origin) {
277
+ function prepareLinkAssetAttributes(asset) {
290
278
  return {
291
279
  linkName: asset.data.name,
292
- linkHref: getAssetUrl(asset, token, origin)
280
+ linkHref: getAssetUrl(asset)
293
281
  };
294
282
  }
295
283
  /**
@@ -307,9 +295,8 @@ function isImage(asset) {
307
295
  *
308
296
  * @param origin The base URL for assets inserted into the editor.
309
297
  */
310
- function getAssetUrl(asset, token, origin) {
311
- const environmentId = getEnvironmentId(token);
312
- const url = new URL(`${environmentId}/assets/${asset.data.id}/file`, origin);
298
+ function getAssetUrl(asset) {
299
+ const url = new URL(asset.data.url);
313
300
  url.searchParams.set('download', 'true');
314
301
  return url.toString();
315
302
  }
@@ -36,7 +36,6 @@ import type { TokenUrl } from '@ckeditor/ckeditor5-cloud-services';
36
36
  * },
37
37
  * ignoreDataId: true,
38
38
  * serviceOrigin: 'https://example.com/',
39
- * assetsOrigin: 'https://example.cloud/',
40
39
  * tokenUrl: 'https://example.com/cs-token-endpoint'
41
40
  * }
42
41
  * } )
@@ -80,6 +79,12 @@ export interface CKBoxConfig {
80
79
  * @default null
81
80
  */
82
81
  defaultUploadCategories?: Record<string, Array<string>> | null;
82
+ /**
83
+ * Defines the workspace id to use during upload when the user has access to more than one workspace.
84
+ *
85
+ * If defined, it is an error, when the user has no access to the specified workspace.
86
+ */
87
+ defaultUploadWorkspaceId?: string;
83
88
  /**
84
89
  * Inserts the unique asset ID as the `data-ckbox-resource-id` attribute. To disable this behavior, set it to `true`.
85
90
  *
@@ -92,12 +97,6 @@ export interface CKBoxConfig {
92
97
  * @default 'https://api.ckbox.io'
93
98
  */
94
99
  serviceOrigin?: string;
95
- /**
96
- * Configures the base URL for assets inserted into the editor. Required only in on-premises installations.
97
- *
98
- * @default 'https://ckbox.cloud'
99
- */
100
- assetsOrigin?: string;
101
100
  /**
102
101
  * Configures the language for the CKBox dialog.
103
102
  *
@@ -219,6 +218,18 @@ export interface CKBoxAssetLinkAttributesDefinition {
219
218
  */
220
219
  linkHref: string;
221
220
  }
221
+ /**
222
+ * The source set of the responsive image provided by the CKBox backend.
223
+ *
224
+ * Each numeric key corresponds to display width of the image.
225
+ */
226
+ export interface CKBoxImageUrls {
227
+ [width: number]: string;
228
+ /**
229
+ * A fallback URL for browsers that do not support the "webp" format.
230
+ */
231
+ default: string;
232
+ }
222
233
  /**
223
234
  * Raw asset definition that is received from the CKBox feature.
224
235
  */
@@ -227,10 +238,6 @@ export interface CKBoxRawAssetDefinition {
227
238
  * A raw asset data definition.
228
239
  */
229
240
  data: CKBoxRawAssetDataDefinition;
230
- /**
231
- * An asset origin URL.
232
- */
233
- origin: string;
234
241
  }
235
242
  /**
236
243
  * Part of raw asset data that is received from the CKBox feature.
@@ -240,10 +247,6 @@ export interface CKBoxRawAssetDataDefinition {
240
247
  * An unique asset id.
241
248
  */
242
249
  id: string;
243
- /**
244
- * An asset extension.
245
- */
246
- extension: string;
247
250
  /**
248
251
  * An asset name.
249
252
  */
@@ -252,6 +255,14 @@ export interface CKBoxRawAssetDataDefinition {
252
255
  * A raw asset metadata definition.
253
256
  */
254
257
  metadata?: CKBoxRawAssetMetadataDefinition;
258
+ /**
259
+ * The source set of the responsive image.
260
+ */
261
+ imageUrls?: CKBoxImageUrls;
262
+ /**
263
+ * The asset location.
264
+ */
265
+ url: string;
255
266
  }
256
267
  /**
257
268
  * Part of raw asset data that is received from the CKBox feature. Properties are set only if the chosen asset is an image.
@@ -75,7 +75,6 @@ export default class CKBoxEditing extends Plugin {
75
75
  const editor = this.editor;
76
76
  editor.config.define('ckbox', {
77
77
  serviceOrigin: 'https://api.ckbox.io',
78
- assetsOrigin: 'https://ckbox.cloud',
79
78
  defaultUploadCategories: null,
80
79
  ignoreDataId: false,
81
80
  language: editor.locale.uiLanguage,
@@ -2,7 +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
- /* globals AbortController, FormData, URL, Image, XMLHttpRequest, window */
5
+ /* globals AbortController, FormData, URL, XMLHttpRequest, window */
6
6
  /**
7
7
  * @module ckbox/ckboxuploadadapter
8
8
  */
@@ -10,7 +10,7 @@ import { Plugin } from 'ckeditor5/src/core';
10
10
  import { FileRepository } from 'ckeditor5/src/upload';
11
11
  import { logError } from 'ckeditor5/src/utils';
12
12
  import CKBoxEditing from './ckboxediting';
13
- import { getImageUrls } from './utils';
13
+ import { getImageUrls, getWorkspaceId } from './utils';
14
14
  /**
15
15
  * A plugin that enables file uploads in CKEditor 5 using the CKBox server–side connector.
16
16
  * See the {@glink features/file-management/ckbox CKBox file manager integration} guide to learn how to configure
@@ -76,7 +76,25 @@ class Adapter {
76
76
  this.editor = editor;
77
77
  this.controller = new AbortController();
78
78
  this.serviceOrigin = editor.config.get('ckbox.serviceOrigin');
79
- this.assetsOrigin = editor.config.get('ckbox.assetsOrigin');
79
+ }
80
+ /**
81
+ * The ID of workspace to use.
82
+ */
83
+ getWorkspaceId() {
84
+ const t = this.editor.t;
85
+ const cannotAccessDefaultWorkspaceError = t('Cannot access default workspace.');
86
+ const defaultWorkspaceId = this.editor.config.get('ckbox.defaultUploadWorkspaceId');
87
+ const workspaceId = getWorkspaceId(this.token, defaultWorkspaceId);
88
+ if (workspaceId == null) {
89
+ /**
90
+ * The user is not authorized to access workspace defined in `ckbox.defaultUploadWorkspaceId` configuration.
91
+ *
92
+ * @error ckbox-access-default-workspace-error
93
+ */
94
+ logError('ckbox-access-default-workspace-error');
95
+ throw cannotAccessDefaultWorkspaceError;
96
+ }
97
+ return workspaceId;
80
98
  }
81
99
  /**
82
100
  * Resolves a promise with an array containing available categories with which the uploaded file can be associated.
@@ -88,6 +106,7 @@ class Adapter {
88
106
  const categoryUrl = new URL('categories', this.serviceOrigin);
89
107
  categoryUrl.searchParams.set('limit', ITEMS_PER_REQUEST.toString());
90
108
  categoryUrl.searchParams.set('offset', offset.toString());
109
+ categoryUrl.searchParams.set('workspaceId', this.getWorkspaceId());
91
110
  return this._sendHttpRequest({ url: categoryUrl })
92
111
  .then(async (data) => {
93
112
  const remainingItems = data.totalCount - (offset + ITEMS_PER_REQUEST);
@@ -158,6 +177,7 @@ class Adapter {
158
177
  }
159
178
  const uploadUrl = new URL('assets', this.serviceOrigin);
160
179
  const formData = new FormData();
180
+ uploadUrl.searchParams.set('workspaceId', this.getWorkspaceId());
161
181
  formData.append('categoryId', category);
162
182
  formData.append('file', file);
163
183
  const requestConfig = {
@@ -174,15 +194,7 @@ class Adapter {
174
194
  };
175
195
  return this._sendHttpRequest(requestConfig)
176
196
  .then(async (data) => {
177
- const width = await this._getImageWidth();
178
- const extension = getFileExtension(file.name);
179
- const imageUrls = getImageUrls({
180
- token: this.token,
181
- id: data.id,
182
- origin: this.assetsOrigin,
183
- width,
184
- extension
185
- });
197
+ const imageUrls = getImageUrls(data.imageUrls);
186
198
  return {
187
199
  ckboxImageId: data.id,
188
200
  default: imageUrls.imageFallbackUrl,
@@ -252,20 +264,6 @@ class Adapter {
252
264
  xhr.send(data);
253
265
  });
254
266
  }
255
- /**
256
- * Resolves a promise with a number representing the width of a given image file.
257
- */
258
- _getImageWidth() {
259
- return new Promise(resolve => {
260
- const image = new Image();
261
- image.onload = () => {
262
- // Let the browser know that it should not keep the reference any longer to avoid memory leeks.
263
- URL.revokeObjectURL(image.src);
264
- resolve(image.width);
265
- };
266
- image.src = this.loader.data;
267
- });
268
- }
269
267
  }
270
268
  /**
271
269
  * Returns an extension from the given value.
package/src/utils.d.ts CHANGED
@@ -6,18 +6,13 @@
6
6
  * @module ckbox/utils
7
7
  */
8
8
  import type { InitializedToken } from '@ckeditor/ckeditor5-cloud-services';
9
+ import type { CKBoxImageUrls } from './ckboxconfig';
9
10
  /**
10
- * Creates URLs for the image:
11
+ * Converts image source set provided by the CKBox into an object containing:
11
12
  * - responsive URLs for the "webp" image format,
12
13
  * - one fallback URL for browsers that do not support the "webp" format.
13
14
  */
14
- export declare function getImageUrls({ token, id, origin, width, extension }: {
15
- token: InitializedToken;
16
- id: string;
17
- origin: string;
18
- width: number;
19
- extension: string;
20
- }): {
15
+ export declare function getImageUrls(imageUrls: CKBoxImageUrls): {
21
16
  imageFallbackUrl: string;
22
17
  imageSources: Array<{
23
18
  srcset: string;
@@ -26,6 +21,8 @@ export declare function getImageUrls({ token, id, origin, width, extension }: {
26
21
  }>;
27
22
  };
28
23
  /**
29
- * Returns an environment id from a token used for communication with the CKBox service.
24
+ * Returns workspace id to use for communication with the CKBox service.
25
+ *
26
+ * @param defaultWorkspaceId The default workspace to use taken from editor config.
30
27
  */
31
- export declare function getEnvironmentId(token: InitializedToken): string;
28
+ export declare function getWorkspaceId(token: InitializedToken, defaultWorkspaceId?: string): string | null;
package/src/utils.js CHANGED
@@ -2,89 +2,48 @@
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
- const IMAGE_BREAKPOINT_MAX_WIDTH = 4000;
6
- const IMAGE_BREAKPOINT_PIXELS_THRESHOLD = 80;
7
- const IMAGE_BREAKPOINT_PERCENTAGE_THRESHOLD = 10;
8
5
  /**
9
- * Creates URLs for the image:
6
+ * Converts image source set provided by the CKBox into an object containing:
10
7
  * - responsive URLs for the "webp" image format,
11
8
  * - one fallback URL for browsers that do not support the "webp" format.
12
9
  */
13
- export function getImageUrls({ token, id, origin, width, extension }) {
14
- const environmentId = getEnvironmentId(token);
15
- const imageBreakpoints = getImageBreakpoints(width);
16
- const imageFallbackExtension = getImageFallbackExtension(extension);
17
- const imageFallbackUrl = getResponsiveImageUrl({ environmentId, id, origin, width, extension: imageFallbackExtension });
18
- const imageResponsiveUrls = imageBreakpoints.map(imageBreakpoint => {
19
- const responsiveImageUrl = getResponsiveImageUrl({ environmentId, id, origin, width: imageBreakpoint, extension: 'webp' });
20
- return `${responsiveImageUrl} ${imageBreakpoint}w`;
21
- });
22
- // Create just one image source definition containing all calculated URLs for each image breakpoint. Additionally, limit this source
23
- // image width by defining two allowed slot sizes:
24
- // - If the viewport width is not greater than the image width, make the image occupy the whole slot.
25
- // - Otherwise, limit the slot width to be equal to the image width, to avoid enlarging the image beyond its width.
26
- //
27
- // This is a kind of a workaround. In a perfect world we could use `sizes="100vw" width="real image width"` on our single `<source>`
28
- // element, but at the time of writing this code the `width` attribute is not supported in the `<source>` element in Firefox yet.
10
+ export function getImageUrls(imageUrls) {
11
+ const responsiveUrls = [];
12
+ let maxWidth = 0;
13
+ for (const key in imageUrls) {
14
+ const width = parseInt(key, 10);
15
+ if (!isNaN(width)) {
16
+ if (width > maxWidth) {
17
+ maxWidth = width;
18
+ }
19
+ responsiveUrls.push(`${imageUrls[key]} ${key}w`);
20
+ }
21
+ }
29
22
  const imageSources = [{
30
- srcset: imageResponsiveUrls.join(','),
31
- sizes: `(max-width: ${width}px) 100vw, ${width}px`,
23
+ srcset: responsiveUrls.join(','),
24
+ sizes: `(max-width: ${maxWidth}px) 100vw, ${maxWidth}px`,
32
25
  type: 'image/webp'
33
26
  }];
34
27
  return {
35
- imageFallbackUrl,
28
+ imageFallbackUrl: imageUrls.default,
36
29
  imageSources
37
30
  };
38
31
  }
39
32
  /**
40
- * Returns an environment id from a token used for communication with the CKBox service.
33
+ * Returns workspace id to use for communication with the CKBox service.
34
+ *
35
+ * @param defaultWorkspaceId The default workspace to use taken from editor config.
41
36
  */
42
- export function getEnvironmentId(token) {
37
+ export function getWorkspaceId(token, defaultWorkspaceId) {
43
38
  const [, binaryTokenPayload] = token.value.split('.');
44
39
  const payload = JSON.parse(atob(binaryTokenPayload));
45
- return payload.aud;
46
- }
47
- /**
48
- * Calculates the image breakpoints for the provided image width in the following way:
49
- *
50
- * 1) The breakpoint threshold (the breakpoint step in the calculations) should be equal to 10% of the image width, but not less than 80
51
- * pixels.
52
- *
53
- * 2) Set the max. allowed image breakpoint (4000px) or the image width (if it is smaller than 4000px) as the first calculated breakpoint.
54
- *
55
- * 3) From the last computed image breakpoint subtract the computed breakpoint threshold, as long as the calculated new breakpoint value is
56
- * greater than the threshold.
57
- */
58
- function getImageBreakpoints(width) {
59
- // Step 1) - calculating the breakpoint threshold.
60
- const imageBreakpointThresholds = [
61
- width * IMAGE_BREAKPOINT_PERCENTAGE_THRESHOLD / 100,
62
- IMAGE_BREAKPOINT_PIXELS_THRESHOLD
63
- ];
64
- const imageBreakpointThreshold = Math.floor(Math.max(...imageBreakpointThresholds));
65
- // Step 2) - set the first breakpoint.
66
- const imageBreakpoints = [Math.min(width, IMAGE_BREAKPOINT_MAX_WIDTH)];
67
- // Step 3) - calculate the next breakpoint as long as it is greater than the breakpoint threshold.
68
- let lastBreakpoint = imageBreakpoints[0];
69
- while (lastBreakpoint - imageBreakpointThreshold >= imageBreakpointThreshold) {
70
- lastBreakpoint -= imageBreakpointThreshold;
71
- imageBreakpoints.unshift(lastBreakpoint);
40
+ const workspaces = (payload.auth && payload.auth.ckbox && payload.auth.ckbox.workspaces) || [payload.aud];
41
+ if (!defaultWorkspaceId) {
42
+ return workspaces[0];
72
43
  }
73
- return imageBreakpoints;
74
- }
75
- /**
76
- * Returns the image extension for the fallback URL.
77
- */
78
- function getImageFallbackExtension(extension) {
79
- if (extension === 'bmp' || extension === 'tiff' || extension === 'jpg') {
80
- return 'jpeg';
44
+ const role = payload.auth && payload.auth.ckbox && payload.auth.ckbox.role;
45
+ if (role == 'superadmin' || workspaces.includes(defaultWorkspaceId)) {
46
+ return defaultWorkspaceId;
81
47
  }
82
- return extension;
83
- }
84
- /**
85
- * Creates the URL for the given image.
86
- */
87
- function getResponsiveImageUrl({ environmentId, id, origin, width, extension }) {
88
- const endpoint = `${environmentId}/assets/${id}/images/${width}.${extension}`;
89
- return new URL(endpoint, origin).toString();
48
+ return null;
90
49
  }