js-kompo-utils 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/app.js +22 -0
- package/components/VlApexChart.vue +89 -0
- package/components/VlCKEditorTemplate.vue +52 -0
- package/components/VlCKEditorWithVars.vue +80 -0
- package/components/VlCollapsible.vue +91 -0
- package/components/VlCopyBox.vue +61 -0
- package/components/VlCreditCardInput.vue +104 -0
- package/components/VlDateMonthDay.vue +327 -0
- package/components/VlDateTextInput.vue +151 -0
- package/components/VlInternationalPhoneInput.vue +902 -0
- package/components/VlPartialDate.vue +283 -0
- package/components/VlPasswordInput.vue +358 -0
- package/components/VlQrCodeScanner.vue +91 -0
- package/components/VlSwipeableTab.vue +35 -0
- package/components/VlSwipeableTabs.vue +325 -0
- package/components/VlText.vue +72 -0
- package/components/VlTutorialStepBuilder.vue +671 -0
- package/components/VlValidatedInput.vue +262 -0
- package/components/data/countries.js +253 -0
- package/components/getAllDefaultComponents.js +12 -0
- package/docs/architecture.md +71 -0
- package/docs/tutorials.md +247 -0
- package/docs/usage.md +128 -0
- package/functions/index.js +7 -0
- package/functions/inputs.js +64 -0
- package/functions/loading.js +20 -0
- package/kompo-ckeditor-1.0.17/index.js +5 -0
- package/kompo-ckeditor-1.0.17/komponents/CKEditor.vue +36 -0
- package/kompo-ckeditor-1.0.17/komponents/CKEditorExtended.vue +66 -0
- package/kompo-ckeditor-1.0.17/komponents/EditableCKEditor.vue +42 -0
- package/kompo-ckeditor-1.0.17/komponents/TranslatableEditor.vue +28 -0
- package/kompo-ckeditor-1.0.17/mixins/CKEditor.js +291 -0
- package/kompo-ckeditor-1.0.17/mixins/CKEditorUtilities.js +52 -0
- package/kompo-ckeditor-1.0.17/mixins/CkUploadAdapter.js +95 -0
- package/kompo-ckeditor-1.0.17/mixins/translations/en.js +1 -0
- package/kompo-ckeditor-1.0.17/mixins/translations/fr.js +1 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockCommand.js +100 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockEditing.js +225 -0
- package/kompo-ckeditor-1.0.17/plugins/JsonBlockUi.js +21 -0
- package/kompo-ckeditor-1.0.17/plugins/MentionPlugin.js +118 -0
- package/orchestrator/KompoJsBuilder.js +333 -0
- package/package.json +25 -0
- package/raw_animations/count-up-animation.js +220 -0
- package/raw_animations/magic-poof.js +210 -0
- package/tutorials/TutorialConfig.js +34 -0
- package/tutorials/TutorialEngine.js +448 -0
- package/tutorials/index.js +2 -0
- package/tutorials/modules/AutoNext.js +86 -0
- package/tutorials/modules/Avatar.js +94 -0
- package/tutorials/modules/Bubble.js +617 -0
- package/tutorials/modules/Cursor.js +315 -0
- package/tutorials/modules/Highlight.js +282 -0
- package/tutorials/modules/HoverForcer.js +250 -0
- package/tutorials/modules/Mobile.js +148 -0
- package/tutorials/modules/Options.js +98 -0
- package/tutorials/modules/Overlay.js +142 -0
- package/tutorials/modules/Scroll.js +37 -0
- package/tutorials/modules/Typewriter.js +132 -0
- package/tutorials/step-builder/step-builder-bridge.js +436 -0
- package/tutorials/step-builder/utils/bezier-editor.js +379 -0
- package/tutorials/step-builder/utils/code-generator.js +127 -0
- package/tutorials/step-builder/utils/element-picker.js +125 -0
- package/tutorials/tutorial.scss +816 -0
- package/tutorials/utils/selectors.js +225 -0
- package/tutorials/utils/stability.js +76 -0
- package/tutorials/utils/svg.js +46 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import CKEditor from '@ckeditor/ckeditor5-vue2'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ClassicEditor,
|
|
5
|
+
Essentials,
|
|
6
|
+
CodeBlock,
|
|
7
|
+
Alignment,
|
|
8
|
+
Font,
|
|
9
|
+
FontBackgroundColor,
|
|
10
|
+
FontColor,
|
|
11
|
+
Bold,
|
|
12
|
+
Italic,
|
|
13
|
+
Underline,
|
|
14
|
+
Heading,
|
|
15
|
+
List,
|
|
16
|
+
Link,
|
|
17
|
+
Table,
|
|
18
|
+
BlockQuote,
|
|
19
|
+
Widget
|
|
20
|
+
} from 'ckeditor5';
|
|
21
|
+
|
|
22
|
+
import Field from 'vue-kompo/js/form/mixins/Field'
|
|
23
|
+
import DoesAxiosRequests from 'vue-kompo/js/form/mixins/DoesAxiosRequests'
|
|
24
|
+
|
|
25
|
+
import CkUploadAdapter from './CkUploadAdapter';
|
|
26
|
+
|
|
27
|
+
Vue.use( CKEditor );
|
|
28
|
+
|
|
29
|
+
import 'ckeditor5/ckeditor5.css';
|
|
30
|
+
|
|
31
|
+
import './translations/fr';
|
|
32
|
+
import './translations/en';
|
|
33
|
+
|
|
34
|
+
import MentionPlugin from '../plugins/MentionPlugin';
|
|
35
|
+
import { JsonBlockEditing } from '../plugins/JsonBlockEditing';
|
|
36
|
+
import { JsonBlockUI } from '../plugins/JsonBlockUi';
|
|
37
|
+
|
|
38
|
+
function CkCustomUploadAdapterPlugin( editor ) {
|
|
39
|
+
editor.plugins.get( 'FileRepository' ).createUploadAdapter = ( loader ) => {
|
|
40
|
+
// Configure the URL to the upload script in your back-end here!
|
|
41
|
+
console.log(CkUploadAdapter)
|
|
42
|
+
return new CkUploadAdapter( loader );
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default {
|
|
47
|
+
mixins: [Field, DoesAxiosRequests],
|
|
48
|
+
data(){
|
|
49
|
+
return {
|
|
50
|
+
editor: ClassicEditor,
|
|
51
|
+
editorConfig: null,
|
|
52
|
+
mentionNotFound: false,
|
|
53
|
+
newMentions: {},
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
computed:{
|
|
58
|
+
toolbar(){ return this.$_config('toolbar') },
|
|
59
|
+
mentions(){ return this.$_config('mentions') || [] },
|
|
60
|
+
focusOnLoad(){ return this.$_config('focusOnLoad') },
|
|
61
|
+
customTextInsertLabel(){ return this.$_config('customTextInsertLabel') },
|
|
62
|
+
customTextInsertion(){ return this.$_config('customTextInsertion') },
|
|
63
|
+
},
|
|
64
|
+
methods: {
|
|
65
|
+
onReady(editor, el) {
|
|
66
|
+
el.ckInstance = editor;
|
|
67
|
+
|
|
68
|
+
el.typeSimulated = async (text, options) => {
|
|
69
|
+
const {
|
|
70
|
+
delay = 15
|
|
71
|
+
} = options;
|
|
72
|
+
|
|
73
|
+
const model = editor.model;
|
|
74
|
+
const doc = model.document;
|
|
75
|
+
|
|
76
|
+
model.change(writer => {
|
|
77
|
+
writer.remove(writer.createRangeIn(doc.getRoot()));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
for (let i = 0; i < text.length; i++) {
|
|
81
|
+
model.change(writer => {
|
|
82
|
+
writer.insertText(text[i], doc.selection.getFirstPosition());
|
|
83
|
+
});
|
|
84
|
+
await new Promise(res => setTimeout(res, delay - (Math.abs(text.length / 2 - Math.abs((text.length / 2 - (i + 1))))) / delay));
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
focus(){
|
|
89
|
+
//focusing CKeditor not working when click on label :/
|
|
90
|
+
this.$_focusAction()
|
|
91
|
+
setTimeout( () => {
|
|
92
|
+
this.$refs.content.$_instance.editing.view.focus()
|
|
93
|
+
}, 50 )
|
|
94
|
+
},
|
|
95
|
+
$_inputAction(){
|
|
96
|
+
if(typeof window.KCK !== 'undefined') {
|
|
97
|
+
clearTimeout(window.KCK);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
window.KCK = setTimeout(() => {
|
|
101
|
+
this.$_changeAction()
|
|
102
|
+
this.$emit('change', this.$_value)
|
|
103
|
+
}, this.$_debounce || 1200);
|
|
104
|
+
},
|
|
105
|
+
/*$_inputAction: _.debounce(function () { //this works too but doesn't allow me to add this.$_debounce
|
|
106
|
+
this.$_changeAction()
|
|
107
|
+
this.$emit('change', this.$_value)
|
|
108
|
+
}, 1500), */
|
|
109
|
+
|
|
110
|
+
getButtonHtml(mention){
|
|
111
|
+
return '<div aria-label="'+mention.itemLabel+'" data-balloon-pos="up">'+mention.iconHtml+'</div>'
|
|
112
|
+
},
|
|
113
|
+
insertHTML(html){
|
|
114
|
+
// See: https://ckeditor.com/docs/ckeditor5/latest/builds/guides/faq.html#where-are-the-editorinserthtml-and-editorinserttext-methods-how-to-insert-some-content
|
|
115
|
+
const editor = this.$refs.content?.$_instance
|
|
116
|
+
const viewFragment = editor.data.processor.toView( html )
|
|
117
|
+
const modelFragment = editor.data.toModel( viewFragment )
|
|
118
|
+
editor.model.insertContent(modelFragment)
|
|
119
|
+
},
|
|
120
|
+
insertTextAndFocus(text){
|
|
121
|
+
const editor = this.$refs.content?.$_instance
|
|
122
|
+
|
|
123
|
+
editor.editing.view.focus()
|
|
124
|
+
|
|
125
|
+
editor.model.change( writer => {
|
|
126
|
+
//I had to focus first then insert the marker then refocus...
|
|
127
|
+
writer.setSelection( writer.createPositionAt( editor.model.document.selection.getFirstPosition(), 'end' ))
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
this.$nextTick(()=> {
|
|
131
|
+
|
|
132
|
+
editor.model.change( writer => {
|
|
133
|
+
|
|
134
|
+
writer.insertText(text, editor.model.document.selection.getFirstPosition() )
|
|
135
|
+
|
|
136
|
+
writer.setSelectionFocus( writer.createPositionAt( editor.model.document.selection.getFirstPosition(), 'end' ))
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
})
|
|
140
|
+
},
|
|
141
|
+
activateMention(mention){
|
|
142
|
+
|
|
143
|
+
this.$_focusAction()
|
|
144
|
+
|
|
145
|
+
this.insertTextAndFocus(' '+mention.marker)
|
|
146
|
+
},
|
|
147
|
+
insertCustomText(){
|
|
148
|
+
this.insertHTML(this.customTextInsertion)
|
|
149
|
+
},
|
|
150
|
+
customItemRenderer( item ) {
|
|
151
|
+
const itemElement = document.createElement( 'div' )
|
|
152
|
+
|
|
153
|
+
itemElement.classList.add('mention-option')
|
|
154
|
+
|
|
155
|
+
item.iconClass.split(' ').forEach(iconClass => {
|
|
156
|
+
itemElement.classList.add(iconClass)
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
itemElement.textContent = item.text
|
|
160
|
+
|
|
161
|
+
return itemElement;
|
|
162
|
+
},
|
|
163
|
+
createFeedCallback( feedItems, itemType ) {
|
|
164
|
+
return feedText => {
|
|
165
|
+
|
|
166
|
+
this.mentionNotFound = null
|
|
167
|
+
|
|
168
|
+
const filteredItems = _.toArray(feedItems) //crazy: sometimes feedItems is an object...
|
|
169
|
+
.concat(this.newMentions[itemType] || [])
|
|
170
|
+
.filter( item => {
|
|
171
|
+
// Item might be defined as object.
|
|
172
|
+
const itemId = typeof item == 'string' ? item : String( item.id );
|
|
173
|
+
|
|
174
|
+
// The default feed is case insensitive.
|
|
175
|
+
return itemId
|
|
176
|
+
.normalize('NFD').replace(/[\u0300-\u036f]/g, "") //replace accents
|
|
177
|
+
.toLowerCase().includes( feedText.toLowerCase() );
|
|
178
|
+
} )
|
|
179
|
+
//.slice( 0, 10 ); //had to add this function to disable filtering 10 items...
|
|
180
|
+
|
|
181
|
+
if(!filteredItems.length)
|
|
182
|
+
this.mentionNotFound = itemType
|
|
183
|
+
|
|
184
|
+
return filteredItems;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
mounted(){
|
|
189
|
+
if(this.focusOnLoad)
|
|
190
|
+
this.focus()
|
|
191
|
+
},
|
|
192
|
+
created(){
|
|
193
|
+
const originalCreate = ClassicEditor.create;
|
|
194
|
+
const self = this;
|
|
195
|
+
ClassicEditor.create = function (element, config) {
|
|
196
|
+
return originalCreate.call(this, element, config).then(editor => {
|
|
197
|
+
element.ckeditorInstance = editor;
|
|
198
|
+
self.onReady(editor, element);
|
|
199
|
+
return editor;
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
let extraPlugins = [
|
|
204
|
+
Essentials,
|
|
205
|
+
Alignment,
|
|
206
|
+
Font,
|
|
207
|
+
FontBackgroundColor,
|
|
208
|
+
FontColor,
|
|
209
|
+
Bold,
|
|
210
|
+
Italic,
|
|
211
|
+
Underline,
|
|
212
|
+
Heading,
|
|
213
|
+
List,
|
|
214
|
+
Link,
|
|
215
|
+
Table,
|
|
216
|
+
BlockQuote,
|
|
217
|
+
Widget,
|
|
218
|
+
MentionPlugin,
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
// Add plugins from windo global var if it's defined
|
|
222
|
+
if (window.CKEditorExtraPlugins && Array.isArray(window.CKEditorExtraPlugins)) {
|
|
223
|
+
extraPlugins = extraPlugins.concat(window.CKEditorExtraPlugins);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* MENTIONS */
|
|
227
|
+
var mentions = []
|
|
228
|
+
|
|
229
|
+
this.mentions.forEach((mention) => {
|
|
230
|
+
|
|
231
|
+
mentions.push(Object.assign(mention, {
|
|
232
|
+
feed: this.createFeedCallback(mention.initialFeed, mention.itemType),
|
|
233
|
+
itemRenderer: this.customItemRenderer
|
|
234
|
+
}))
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
/* IMAGE UPLOAD */
|
|
238
|
+
let hasImageUploadPlugin = this.toolbar.indexOf('imageUpload') > -1
|
|
239
|
+
if (hasImageUploadPlugin) {
|
|
240
|
+
extraPlugins.push(CkCustomUploadAdapterPlugin)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* CODE BLOCK */
|
|
244
|
+
let hasCodeBlockPlugin = this.toolbar.indexOf('codeBlock') > -1
|
|
245
|
+
if (hasCodeBlockPlugin) {
|
|
246
|
+
extraPlugins.push(CodeBlock)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* JSON FORMATTER */
|
|
250
|
+
let hasJsonFormatterPlugin = this.toolbar.indexOf('jsonBlock') > -1
|
|
251
|
+
if (hasJsonFormatterPlugin) {
|
|
252
|
+
extraPlugins.push(JsonBlockEditing)
|
|
253
|
+
extraPlugins.push(JsonBlockUI)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
//$_config toolbar was undefined if declared in data()
|
|
257
|
+
this.editorConfig = Object.assign({
|
|
258
|
+
licenseKey: 'GPL',
|
|
259
|
+
alignment: { options: [ 'left', 'right', 'center', 'justify' ] },
|
|
260
|
+
toolbar: this.toolbar,
|
|
261
|
+
table: {
|
|
262
|
+
contentToolbar: [
|
|
263
|
+
'tableColumn', 'tableRow', 'mergeTableCells'
|
|
264
|
+
]
|
|
265
|
+
},
|
|
266
|
+
codeBlock: {
|
|
267
|
+
languages: [
|
|
268
|
+
{ language: 'css', label: 'CSS' },
|
|
269
|
+
{ language: 'html', label: 'HTML' },
|
|
270
|
+
{ language: 'javascript', label: 'JavaScript' },
|
|
271
|
+
{ language: 'json', label: 'JSON' },
|
|
272
|
+
]
|
|
273
|
+
},
|
|
274
|
+
list: {
|
|
275
|
+
properties: {
|
|
276
|
+
styles: true,
|
|
277
|
+
startIndex: true,
|
|
278
|
+
reversed: true
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
language: this.$_config('kompo_locale') || 'en',
|
|
282
|
+
}, !this.mentions.length ? {} : {
|
|
283
|
+
|
|
284
|
+
mention: {
|
|
285
|
+
feeds: mentions,
|
|
286
|
+
}
|
|
287
|
+
}, !extraPlugins.length ? {} : {
|
|
288
|
+
extraPlugins: extraPlugins,
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
methods: {
|
|
3
|
+
insertVariable(payload) {
|
|
4
|
+
// Insertar el span sin espacios adicionales para evitar problemas con el cursor
|
|
5
|
+
const html = '<span class="mention" contenteditable="false" data-mention="' + payload.type + '" data-original="' + payload.label + '">' + payload.label + '</span>'
|
|
6
|
+
this.insertHtml(html)
|
|
7
|
+
|
|
8
|
+
// Ahora asegurémonos de que el cursor quede correctamente posicionado después del span
|
|
9
|
+
const editor = this.$refs.content?.$_instance
|
|
10
|
+
editor.model.change(writer => {
|
|
11
|
+
// Este paso adicional ayuda a asegurar que el cursor quede en una posición editable
|
|
12
|
+
const selection = editor.model.document.selection
|
|
13
|
+
if (!selection.isCollapsed) {
|
|
14
|
+
writer.setSelection(selection.getLastPosition())
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
this.variableToInsert = null
|
|
19
|
+
},
|
|
20
|
+
insertHtml(html) {
|
|
21
|
+
const editor = this.$refs.content?.$_instance
|
|
22
|
+
|
|
23
|
+
if (!editor) return
|
|
24
|
+
|
|
25
|
+
editor.model.change(writer => {
|
|
26
|
+
// Si el editor está vacío (no hay ningún bloque), creamos un <paragraph> de arranque
|
|
27
|
+
if (editor.model.document.getRoot().childCount === 0) {
|
|
28
|
+
const p = writer.createElement('paragraph')
|
|
29
|
+
writer.insert(p, editor.model.document.getRoot(), 0)
|
|
30
|
+
// Movemos la selección al inicio del nuevo párrafo
|
|
31
|
+
writer.setSelection(p, 0)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Procesar la cadena HTML al modelo
|
|
35
|
+
const viewFragment = editor.data.processor.toView(html)
|
|
36
|
+
const modelFragment = editor.data.toModel(viewFragment)
|
|
37
|
+
|
|
38
|
+
// Insertar el fragmento en la posición de la selección
|
|
39
|
+
editor.model.insertContent(modelFragment, editor.model.document.selection)
|
|
40
|
+
})
|
|
41
|
+
},
|
|
42
|
+
insertText(text) {
|
|
43
|
+
const editor = this.$refs.content?.$_instance
|
|
44
|
+
if (!editor) return
|
|
45
|
+
|
|
46
|
+
editor.model.change(writer => {
|
|
47
|
+
const insertPosition = editor.model.document.selection.getFirstPosition()
|
|
48
|
+
writer.insertText(text, insertPosition)
|
|
49
|
+
})
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export default class CkUploadAdapter {
|
|
2
|
+
constructor( loader ) {
|
|
3
|
+
// The file loader instance to use during the upload.
|
|
4
|
+
this.loader = loader;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Starts the upload process.
|
|
8
|
+
upload() {
|
|
9
|
+
return this.loader.file
|
|
10
|
+
.then( file => new Promise( ( resolve, reject ) => {
|
|
11
|
+
this._initRequest();
|
|
12
|
+
this._initListeners( resolve, reject, file );
|
|
13
|
+
this._sendRequest( file );
|
|
14
|
+
} ) );
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Aborts the upload process.
|
|
18
|
+
abort() {
|
|
19
|
+
if ( this.xhr ) {
|
|
20
|
+
this.xhr.abort();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Initializes the XMLHttpRequest object using the URL passed to the constructor.
|
|
25
|
+
_initRequest() {
|
|
26
|
+
const xhr = this.xhr = new XMLHttpRequest();
|
|
27
|
+
|
|
28
|
+
// Note that your request may look different. It is up to you and your editor
|
|
29
|
+
// integration to choose the right communication channel. This example uses
|
|
30
|
+
// a POST request with JSON as a data structure but your configuration
|
|
31
|
+
// could be different.
|
|
32
|
+
xhr.open( 'POST', '/ckeditor-image-upload', true )
|
|
33
|
+
xhr.setRequestHeader('X-CSRF-TOKEN', $('meta[name="csrf-token"]').attr('content'))
|
|
34
|
+
xhr.responseType = 'json';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Initializes XMLHttpRequest listeners.
|
|
38
|
+
_initListeners( resolve, reject, file ) {
|
|
39
|
+
const xhr = this.xhr;
|
|
40
|
+
const loader = this.loader;
|
|
41
|
+
const genericErrorText = `Couldn't upload file: ${ file.name }.`;
|
|
42
|
+
|
|
43
|
+
xhr.addEventListener( 'error', () => reject( genericErrorText ) );
|
|
44
|
+
xhr.addEventListener( 'abort', () => reject() );
|
|
45
|
+
xhr.addEventListener( 'load', () => {
|
|
46
|
+
const response = xhr.response;
|
|
47
|
+
|
|
48
|
+
// This example assumes the XHR server's "response" object will come with
|
|
49
|
+
// an "error" which has its own "message" that can be passed to reject()
|
|
50
|
+
// in the upload promise.
|
|
51
|
+
//
|
|
52
|
+
// Your integration may handle upload errors in a different way so make sure
|
|
53
|
+
// it is done properly. The reject() function must be called when the upload fails.
|
|
54
|
+
if ( !response || response.error ) {
|
|
55
|
+
return reject( response && response.error ? response.error.message : genericErrorText );
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// If the upload is successful, resolve the upload promise with an object containing
|
|
59
|
+
// at least the "default" URL, pointing to the image on the server.
|
|
60
|
+
// This URL will be used to display the image in the content. Learn more in the
|
|
61
|
+
// UploadAdapter#upload documentation.
|
|
62
|
+
resolve( {
|
|
63
|
+
default: response.url
|
|
64
|
+
} );
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
// Upload progress when it is supported. The file loader has the #uploadTotal and #uploaded
|
|
68
|
+
// properties which are used e.g. to display the upload progress bar in the editor
|
|
69
|
+
// user interface.
|
|
70
|
+
if ( xhr.upload ) {
|
|
71
|
+
xhr.upload.addEventListener( 'progress', evt => {
|
|
72
|
+
if ( evt.lengthComputable ) {
|
|
73
|
+
loader.uploadTotal = evt.total;
|
|
74
|
+
loader.uploaded = evt.loaded;
|
|
75
|
+
}
|
|
76
|
+
} );
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Prepares the data and sends the request.
|
|
81
|
+
_sendRequest( file ) {
|
|
82
|
+
// Prepare the form data.
|
|
83
|
+
const data = new FormData();
|
|
84
|
+
|
|
85
|
+
data.append( 'upload', file );
|
|
86
|
+
|
|
87
|
+
// Important note: This is the right place to implement security mechanisms
|
|
88
|
+
// like authentication and CSRF protection. For instance, you can use
|
|
89
|
+
// XMLHttpRequest.setRequestHeader() to set the request headers containing
|
|
90
|
+
// the CSRF token generated earlier by your application.
|
|
91
|
+
|
|
92
|
+
// Send the request.
|
|
93
|
+
this.xhr.send( data );
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e){const i=e["en-gb"]=e["en-gb"]||{};i.dictionary=Object.assign(i.dictionary||{},{"%0 of %1":"%0 of %1","Align center":"Align center","Align left":"Align left","Align right":"Align right",Aquamarine:"Aquamarine",Big:"Big",Black:"Black","Block quote":"Block quote",Blue:"Blue",Bold:"Bold","Bulleted List":"Bulleted List",Cancel:"Cancel","Cannot upload file:":"Cannot upload file:","Centered image":"Centred image","Change image text alternative":"Change image text alternative","Choose heading":"Choose heading",Column:"Column","Could not insert image at the current position.":"Could not insert image at the current position.","Could not obtain resized image URL.":"Could not obtain resized image URL.","Decrease indent":"Decrease indent",Default:"Default","Delete column":"Delete column","Delete row":"Delete row","Dim grey":"Dim grey","Document colors":"Document colours",Downloadable:"Downloadable","Dropdown toolbar":"","Edit link":"Edit link","Editor toolbar":"","Enter image caption":"Enter image caption","Font Background Color":"Font Background Colour","Font Color":"Font Colour","Font Family":"Font Family","Font Size":"Font Size","Full size image":"Full size image",Green:"Green",Grey:"Grey","Header column":"Header column","Header row":"Header row",Heading:"Heading","Heading 1":"Heading 1","Heading 2":"Heading 2","Heading 3":"Heading 3","Heading 4":"Heading 4","Heading 5":"Heading 5","Heading 6":"Heading 6",Huge:"Huge","Image toolbar":"","image widget":"Image widget","Increase indent":"Increase indent","Insert column left":"Insert column left","Insert column right":"Insert column right","Insert image":"Insert image","Insert image or file":"Insert image or file","Insert media":"Insert media","Insert row above":"Insert row above","Insert row below":"Insert row below","Insert table":"Insert table","Inserting image failed":"Inserting image failed",Italic:"Italic",Justify:"Justify","Left aligned image":"Left aligned image","Light blue":"Light blue","Light green":"Light green","Light grey":"Light grey",Link:"Link","Link URL":"Link URL","Media URL":"Media URL","media widget":"Media widget","Merge cell down":"Merge cell down","Merge cell left":"Merge cell left","Merge cell right":"Merge cell right","Merge cell up":"Merge cell up","Merge cells":"Merge cells",Next:"Next","Numbered List":"Numbered List","Open in a new tab":"Open in a new tab","Open link in new tab":"Open link in new tab",Orange:"Orange",Paragraph:"Paragraph","Paste the media URL in the input.":"Paste the media URL in the input.",Previous:"Previous",Purple:"Purple",Red:"Red",Redo:"Redo","Remove color":"Remove colour","Rich Text Editor":"Rich Text Editor","Rich Text Editor, %0":"Rich Text Editor, %0","Right aligned image":"Right aligned image",Row:"Row",Save:"Save","Select column":"","Select row":"","Selecting resized image failed":"Selecting resized image failed","Show more items":"","Side image":"Side image",Small:"Small","Split cell horizontally":"Split cell horizontally","Split cell vertically":"Split cell vertically","Table toolbar":"","Text alignment":"Text alignment","Text alignment toolbar":"","Text alternative":"Text alternative","The URL must not be empty.":"The URL must not be empty.","This link has no URL":"This link has no URL","This media URL is not supported.":"This media URL is not supported.",Tiny:"Tiny","Tip: Paste the URL into the content to embed faster.":"Tip: Paste the URL into the content to embed faster.",Turquoise:"Turquoise",Underline:"Underline",Undo:"Undo",Unlink:"Unlink","Upload failed":"Upload failed","Upload in progress":"Upload in progress",White:"White",Yellow:"Yellow"}),i.getPluralForm=function(e){return 1!=e}}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e){const i=e.fr=e.fr||{};i.dictionary=Object.assign(i.dictionary||{},{"%0 of %1":"%0 sur %1","Align center":"Centrer","Align left":"Aligner à gauche","Align right":"Aligner à droite",Aquamarine:"Bleu vert",Big:"Grand",Black:"Noir","Block quote":"Citation",Blue:"Bleu",Bold:"Gras","Bulleted List":"Liste à puces",Cancel:"Annuler","Cannot upload file:":"Envoi du fichier échoué :","Centered image":"Image centrée","Change image text alternative":"Changer le texte alternatif à l’image","Choose heading":"Choisir l'en-tête",Column:"Colonne","Could not insert image at the current position.":"Impossible d'insérer l'image à la position courante.","Could not obtain resized image URL.":"Impossible d'obtenir l'image redimensionnée","Decrease indent":"Diminuer le retrait",Default:"Par défaut","Delete column":"Supprimer la colonne","Delete row":"Supprimer la ligne","Dim grey":"Gris pâle","Document colors":"Couleurs du document",Downloadable:"Fichier téléchargeable","Dropdown toolbar":"Barre d'outils dans un menu déroulant","Edit link":"Modifier le lien","Editor toolbar":"Barre d'outils de l'éditeur","Enter image caption":"Saisir la légende de l’image","Font Background Color":"Couleur d'arrière-plan","Font Color":"Couleur de police","Font Family":"Police","Font Size":"Taille de police","Full size image":"Image taille réelle",Green:"Vert",Grey:"Gris","Header column":"Colonne d'entête","Header row":"Ligne d'entête",Heading:"En-tête","Heading 1":"Titre 1","Heading 2":"Titre 2","Heading 3":"Titre 3","Heading 4":"Titre 4","Heading 5":"Titre 5","Heading 6":"Titre 6",Huge:"Enorme","Image toolbar":"Barre d'outils des images","image widget":"Objet image","Increase indent":"Augmenter le retrait","Insert column left":"Insérer une colonne à gauche","Insert column right":"Insérer une colonne à droite","Insert image":"Insérer une image","Insert image or file":"Insérer une image ou un fichier","Insert media":"Insérer un média","Insert paragraph after block":"","Insert paragraph before block":"","Insert row above":"Insérer une ligne au-dessus","Insert row below":"Insérer une ligne en-dessous","Insert table":"Insérer un tableau","Inserting image failed":"L'insertion d'image a échoué.",Italic:"Italique",Justify:"Justifier","Left aligned image":"Image alignée à gauche","Light blue":"Bleu clair","Light green":"Vert clair","Light grey":"Gris clair",Link:"Lien","Link URL":"URL du lien","Media URL":"URL de média","media widget":"widget média","Merge cell down":"Fusionner la cellule en-dessous","Merge cell left":"Fusionner la cellule à gauche","Merge cell right":"Fusionner la cellule à droite","Merge cell up":"Fusionner la cellule au-dessus","Merge cells":"Fusionner les cellules",Next:"Suivant","Numbered List":"Liste numérotée","Open in a new tab":"Ouvrir dans un nouvel onglet","Open link in new tab":"Ouvrir le lien dans un nouvel onglet",Orange:"Orange",Paragraph:"Paragraphe","Paste the media URL in the input.":"Coller l'URL du média",Previous:"Précedent",Purple:"Violet",Red:"Rouge",Redo:"Restaurer","Remove color":"Enlever la couleur","Rich Text Editor":"Éditeur de texte enrichi","Rich Text Editor, %0":"Éditeur de texte enrichi, %0","Right aligned image":"Image alignée à droite",Row:"Ligne",Save:"Enregistrer","Select column":"","Select row":"","Selecting resized image failed":"La sélection de l'image redimensionnée a échoué.","Show more items":"Montrer plus d'éléments","Side image":"Image latérale",Small:"Petit","Split cell horizontally":"Scinder la cellule horizontalement","Split cell vertically":"Scinder la cellule verticalement","Table toolbar":"Barre d'outils des tableaux","Text alignment":"Alignement du texte","Text alignment toolbar":"Barre d'outils d'alignement du texte","Text alternative":"Texte alternatif","The URL must not be empty.":"L'URL ne doit pas être vide.","This link has no URL":"Ce lien n'a pas d'URL","This media URL is not supported.":"Cette URL de média n'est pas supportée.",Tiny:"Minuscule","Tip: Paste the URL into the content to embed faster.":"Astuce : Copier l'URL du média dans le contenu pour l'insérer plus rapidement",Turquoise:"Turquoise",Underline:"Souligné",Undo:"Annuler",Unlink:"Supprimer le lien","Upload failed":"Échec de l'envoi","Upload in progress":"Téléchargement en cours",White:"Blanc","Widget toolbar":"Barre d'outils du widget",Yellow:"Jaune"}),i.getPluralForm=function(e){return e>1}}(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {Command} from 'ckeditor5';
|
|
2
|
+
|
|
3
|
+
export class JsonBlockCommand extends Command {
|
|
4
|
+
refresh() {
|
|
5
|
+
const model = this.editor.model;
|
|
6
|
+
const selection = model.document.selection;
|
|
7
|
+
// Determine if the command can be used at the current position
|
|
8
|
+
const allowedParent = model.schema.findAllowedParent(selection.getFirstPosition(), 'jsonBlock');
|
|
9
|
+
this.isEnabled = allowedParent !== null;
|
|
10
|
+
// Determine state: true if the selection is inside an existing JSON block
|
|
11
|
+
const jsonBlock = selection.getFirstPosition().findAncestor('jsonBlock');
|
|
12
|
+
this.value = !!jsonBlock;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
execute() {
|
|
16
|
+
const model = this.editor.model;
|
|
17
|
+
const selection = model.document.selection;
|
|
18
|
+
const jsonBlockElem = selection.getFirstPosition().findAncestor('jsonBlock');
|
|
19
|
+
|
|
20
|
+
model.change(writer => {
|
|
21
|
+
if (jsonBlockElem) {
|
|
22
|
+
// Move the selection out of the block before deleting it
|
|
23
|
+
const nextPosition = writer.createPositionBefore(jsonBlockElem);
|
|
24
|
+
writer.setSelection(nextPosition);
|
|
25
|
+
|
|
26
|
+
// Get the text of the jsonBlock
|
|
27
|
+
const jsonText = getJsonText(jsonBlockElem);
|
|
28
|
+
const lines = jsonText.split('\n');
|
|
29
|
+
|
|
30
|
+
let prevParagraph = null;
|
|
31
|
+
for (let i = 0; i < lines.length; i++) {
|
|
32
|
+
const paragraph = writer.createElement('paragraph');
|
|
33
|
+
if (lines[i] !== '') {
|
|
34
|
+
writer.insertText(lines[i], paragraph, 0);
|
|
35
|
+
}
|
|
36
|
+
if (prevParagraph) {
|
|
37
|
+
writer.insert(paragraph, writer.createPositionAfter(prevParagraph));
|
|
38
|
+
} else {
|
|
39
|
+
writer.insert(paragraph, writer.createPositionBefore(jsonBlockElem));
|
|
40
|
+
}
|
|
41
|
+
prevParagraph = paragraph;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Remove the JSON block
|
|
45
|
+
writer.remove(jsonBlockElem);
|
|
46
|
+
|
|
47
|
+
// Place the cursor back in the last inserted paragraph
|
|
48
|
+
if (prevParagraph) {
|
|
49
|
+
writer.setSelection(prevParagraph, 'end');
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
// Insert: create a new jsonBlock element at the current position
|
|
53
|
+
const jsonBlock = writer.createElement('jsonBlock');
|
|
54
|
+
// If there was selected content, move it as text inside the new JSON block
|
|
55
|
+
if (!selection.isCollapsed) {
|
|
56
|
+
const plainText = getSelectionPlainText(selection);
|
|
57
|
+
if (plainText) {
|
|
58
|
+
writer.insertText(plainText, jsonBlock, 0);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
model.insertContent(jsonBlock);
|
|
62
|
+
// Place cursor inside the JSON block to start editing
|
|
63
|
+
writer.setSelection(jsonBlock, 'end');
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Helper function: gets the plain text (with \n) of a jsonBlock element from the model
|
|
70
|
+
function getJsonText(jsonBlockElement) {
|
|
71
|
+
let text = '';
|
|
72
|
+
for (const child of jsonBlockElement.getChildren()) {
|
|
73
|
+
if (child.is('$text')) {
|
|
74
|
+
text += child.data;
|
|
75
|
+
} else if (child.name === 'softBreak') {
|
|
76
|
+
text += '\n';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return text;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Helper function: converts the selected content (model.DocumentSelection) to plain text
|
|
83
|
+
function getSelectionPlainText(selection) {
|
|
84
|
+
const fragment = selection.getFirstRange() ? selection.getFirstRange().clone().getItems() : [];
|
|
85
|
+
let text = '';
|
|
86
|
+
// Iterate through all nodes within the selection (fragment)
|
|
87
|
+
for (const node of fragment) {
|
|
88
|
+
if (node.is('$text')) {
|
|
89
|
+
text += node.data;
|
|
90
|
+
} else if (node.is('element')) {
|
|
91
|
+
if (node.name === 'softBreak') {
|
|
92
|
+
text += '\n';
|
|
93
|
+
} else if (selection.containsEntireContent(node)) {
|
|
94
|
+
// If it's a fully selected block element, add its text with a newline
|
|
95
|
+
text += getJsonText(node) + '\n';
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return text;
|
|
100
|
+
}
|