PluginProbe ʕ •ᴥ•ʔ
File Manager Pro – Filester / 1.8
File Manager Pro – Filester v1.8
2.1.2 2.1.1 trunk 1.6.1 1.7.6 1.8 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 2.0 2.0.1 2.0.2 2.1.0
filester / includes / File_manager / lib / js / extras / editors.default.js
filester / includes / File_manager / lib / js / extras Last commit date
editors.default.js 2 years ago editors.default.min.js 2 years ago quicklook.googledocs.js 2 years ago quicklook.googledocs.min.js 2 years ago
editors.default.js
2648 lines
1 (function ($) {
2 (function (editors, elFinder) {
3 fm_menu_icon_base_url = '';
4 if (typeof define === 'function' && define.amd) {
5 define(['elfinder'], editors);
6 } else if (elFinder) {
7 var optEditors = elFinder.prototype._options.commandsOptions.edit.editors;
8 elFinder.prototype._options.commandsOptions.edit.editors = optEditors.concat(editors(elFinder));
9 }
10 }(function (elFinder) {
11 "use strict";
12 var apps = {},
13 // get query of getfile
14 getfile = window.location.search.match(/getfile=([a-z]+)/),
15 useRequire = elFinder.prototype.hasRequire,
16 ext2mime = {
17 bmp: 'image/x-ms-bmp',
18 dng: 'image/x-adobe-dng',
19 gif: 'image/gif',
20 jpeg: 'image/jpeg',
21 jpg: 'image/jpeg',
22 pdf: 'application/pdf',
23 png: 'image/png',
24 ppm: 'image/x-portable-pixmap',
25 psd: 'image/vnd.adobe.photoshop',
26 pxd: 'image/x-pixlr-data',
27 svg: 'image/svg+xml',
28 tiff: 'image/tiff',
29 webp: 'image/webp',
30 xcf: 'image/x-xcf',
31 sketch: 'application/x-sketch',
32 ico: 'image/x-icon',
33 dds: 'image/vnd-ms.dds',
34 emf: 'application/x-msmetafile'
35 },
36 mime2ext,
37 getExtention = function (mime, fm, jpeg) {
38 if (!mime2ext) {
39 mime2ext = fm.arrayFlip(ext2mime);
40 }
41 var ext = mime2ext[mime] || fm.mimeTypes[mime];
42 if (!jpeg) {
43 if (ext === 'jpeg') {
44 ext = 'jpg';
45 }
46 } else {
47 if (ext === 'jpg') {
48 ext = 'jpeg';
49 }
50 }
51 return ext;
52 },
53 changeImageType = function (src, toMime) {
54 var dfd = $.Deferred();
55 try {
56 var canvas = document.createElement('canvas'),
57 ctx = canvas.getContext('2d'),
58 img = new Image(),
59 conv = function () {
60 var url = canvas.toDataURL(toMime),
61 mime, m;
62 if (m = url.match(/^data:([a-z0-9]+\/[a-z0-9.+-]+)/i)) {
63 mime = m[1];
64 } else {
65 mime = '';
66 }
67 if (mime.toLowerCase() === toMime.toLowerCase()) {
68 dfd.resolve(canvas.toDataURL(toMime), canvas);
69 } else {
70 dfd.reject();
71 }
72 };
73
74 img.src = src;
75 $(img).on('load', function () {
76 try {
77 canvas.width = img.width;
78 canvas.height = img.height;
79 ctx.drawImage(img, 0, 0);
80 conv();
81 } catch (e) {
82 dfd.reject();
83 }
84 }).on('error', function () {
85 dfd.reject();
86 });
87 return dfd;
88 } catch (e) {
89 return dfd.reject();
90 }
91 },
92 initImgTag = function (id, file, content, fm) {
93 var node = $(this).children('img:first').data('ext', getExtention(file.mime, fm)),
94 spnr = $('<div class="elfinder-edit-spinner elfinder-edit-image"/>')
95 .html('<span class="elfinder-spinner-text">' + fm.i18n('ntfloadimg') + '</span><span class="elfinder-spinner"/>')
96 .hide()
97 .appendTo(this),
98 setup = function () {
99 node.attr('id', id + '-img')
100 .attr('src', url || content)
101 .css({ 'height': '', 'max-width': '100%', 'max-height': '100%', 'cursor': 'pointer' })
102 .data('loading', function (done) {
103 var btns = node.closest('.elfinder-dialog').find('button,.elfinder-titlebar-button');
104 btns.prop('disabled', !done)[done ? 'removeClass' : 'addClass']('ui-state-disabled');
105 node.css('opacity', done ? '' : '0.3');
106 spnr[done ? 'hide' : 'show']();
107 return node;
108 });
109 },
110 url;
111
112 if (!content.match(/^data:/)) {
113 fm.openUrl(file.hash, false, function (v) {
114 url = v;
115 node.attr('_src', content);
116 setup();
117 });
118 } else {
119 setup();
120 }
121 },
122 imgBase64 = function (node, mime) {
123 var style = node.attr('style'),
124 img, canvas, ctx, data;
125 try {
126 // reset css for getting image size
127 node.attr('style', '');
128 // img node
129 img = node.get(0);
130 // New Canvas
131 canvas = document.createElement('canvas');
132 canvas.width = img.width;
133 canvas.height = img.height;
134 // restore css
135 node.attr('style', style);
136 // Draw Image
137 canvas.getContext('2d').drawImage(img, 0, 0);
138 // To Base64
139 data = canvas.toDataURL(mime);
140 } catch (e) {
141 data = node.attr('src');
142 }
143 return data;
144 },
145 iframeClose = function (ifm) {
146 var $ifm = $(ifm),
147 dfd = $.Deferred().always(function () {
148 $ifm.off('load', load);
149 }),
150 ab = 'about:blank',
151 chk = function () {
152 tm = setTimeout(function () {
153 var src;
154 try {
155 src = base.contentWindow.location.href;
156 } catch (e) {
157 src = null;
158 }
159 if (src === ab) {
160 dfd.resolve();
161 } else if (--cnt > 0) {
162 chk();
163 } else {
164 dfd.reject();
165 }
166 }, 500);
167 },
168 load = function () {
169 tm && clearTimeout(tm);
170 dfd.resolve();
171 },
172 cnt = 20, // 500ms * 20 = 10sec wait
173 tm;
174 $ifm.one('load', load);
175 ifm.src = ab;
176 chk();
177 return dfd;
178 };
179
180 // check getfile callback function
181 if (getfile) {
182 getfile = getfile[1];
183 if (getfile === 'ckeditor') {
184 elFinder.prototype._options.getFileCallback = function (file, fm) {
185 window.opener.CKEDITOR.tools.callFunction((function () {
186 var reParam = new RegExp('(?:[\?&]|&amp;)CKEditorFuncNum=([^&]+)', 'i'),
187 match = window.location.search.match(reParam);
188 return (match && match.length > 1) ? match[1] : '';
189 })(), fm.convAbsUrl(file.url));
190 fm.destroy();
191 window.close();
192 };
193 }
194 }
195
196 // return editors Array
197 return [
198 {
199 // tui.image-editor - https://github.com/nhnent/tui.image-editor
200 info: {
201 id: 'tuiimgedit',
202 name: 'TUI Image Editor',
203 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -48',
204 dataScheme: true,
205 schemeContent: true,
206 openMaximized: true,
207 canMakeEmpty: false,
208 integrate: {
209 title: 'TOAST UI Image Editor',
210 link: 'http://ui.toast.com/tui-image-editor/'
211 }
212 },
213 // MIME types to accept
214 mimes: ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp'],
215 // HTML of this editor
216 html: '<div class="elfinder-edit-imageeditor"><canvas></canvas></div>',
217 // called on initialization of elFinder cmd edit (this: this editor's config object)
218 setup: function (opts, fm) {
219 if (fm.UA.ltIE8 || fm.UA.Mobile) {
220 this.disabled = true;
221 } else {
222 this.opts = Object.assign({
223 version: 'v3.9.0'
224 }, opts.extraOptions.tuiImgEditOpts || {}, {
225 iconsPath: fm.baseUrl + 'img/tui-',
226 theme: {}
227 });
228 if (!fm.isSameOrigin(this.opts.iconsPath)) {
229 this.disabled = true;
230 fm.debug('warning', 'Setting `commandOptions.edit.extraOptions.tuiImgEditOpts.iconsPath` MUST follow the same origin policy.');
231 }
232 }
233 },
234 // Initialization of editing node (this: this editors HTML node)
235 init: function (id, file, content, fm) {
236 this.data('url', content);
237 },
238 load: function (base) {
239 var self = this,
240 fm = this.fm,
241 dfrd = $.Deferred(),
242 cdns = fm.options.cdns,
243 ver = self.confObj.opts.version,
244 init = function (editor) {
245 var $base = $(base),
246 bParent = $base.parent(),
247 opts = self.confObj.opts,
248 iconsPath = opts.iconsPath,
249 tmpContainer = $('<div class="tui-image-editor-container">').appendTo(bParent),
250 tmpDiv = [
251 $('<div class="tui-image-editor-submenu"/>').appendTo(tmpContainer),
252 $('<div class="tui-image-editor-controls"/>').appendTo(tmpContainer)
253 ],
254 iEditor = new editor(base, {
255 includeUI: {
256 loadImage: {
257 path: $base.data('url'),
258 name: self.file.name
259 },
260 theme: Object.assign(opts.theme, {
261 'menu.normalIcon.path': iconsPath + 'icon-d.svg',
262 'menu.normalIcon.name': 'icon-d',
263 'menu.activeIcon.path': iconsPath + 'icon-b.svg',
264 'menu.activeIcon.name': 'icon-b',
265 'menu.disabledIcon.path': iconsPath + 'icon-a.svg',
266 'menu.disabledIcon.name': 'icon-a',
267 'menu.hoverIcon.path': iconsPath + 'icon-c.svg',
268 'menu.hoverIcon.name': 'icon-c',
269 'submenu.normalIcon.path': iconsPath + 'icon-d.svg',
270 'submenu.normalIcon.name': 'icon-d',
271 'submenu.activeIcon.path': iconsPath + 'icon-c.svg',
272 'submenu.activeIcon.name': 'icon-c'
273 }),
274 initMenu: 'filter',
275 menuBarPosition: 'bottom'
276 },
277 cssMaxWidth: Math.max(300, bParent.width()),
278 cssMaxHeight: Math.max(200, bParent.height() - (tmpDiv[0].height() + tmpDiv[1].height() + 3 /*margin*/)),
279 usageStatistics: false
280 }),
281 canvas = $base.find('canvas:first').get(0),
282 zoom = function (v) {
283 if (typeof v !== 'undefined') {
284 var c = $(canvas),
285 w = parseInt(c.attr('width')),
286 h = parseInt(c.attr('height')),
287 a = w / h,
288 mw, mh;
289 if (v === 0) {
290 mw = w;
291 mh = h;
292 } else {
293 mw = parseInt(c.css('max-width')) + Number(v);
294 mh = mw / a;
295 if (mw > w && mh > h) {
296 mw = w;
297 mh = h;
298 }
299 }
300 per.text(Math.round(mw / w * 100) + '%');
301 iEditor.resizeCanvasDimension({ width: mw, height: mh });
302 // continually change more
303 if (zoomMore) {
304 setTimeout(function () {
305 zoomMore && zoom(v);
306 }, 50);
307 }
308 }
309 },
310 zup = $('<span class="ui-icon ui-icon-plusthick"/>').data('val', 10),
311 zdown = $('<span class="ui-icon ui-icon-minusthick"/>').data('val', -10),
312 per = $('<button/>').css('width', '4em').text('%').attr('title', '100%').data('val', 0),
313 quty, qutyTm, zoomTm, zoomMore;
314
315 tmpContainer.remove();
316 $base.removeData('url').data('mime', self.file.mime);
317 // jpeg quality controls
318 if (self.file.mime === 'image/jpeg') {
319 $base.data('quality', fm.storage('jpgQuality') || fm.option('jpgQuality'));
320 quty = $('<input type="number" class="ui-corner-all elfinder-resize-quality elfinder-tabstop"/>')
321 .attr('min', '1')
322 .attr('max', '100')
323 .attr('title', '1 - 100')
324 .on('change', function () {
325 var q = quty.val();
326 $base.data('quality', q);
327 qutyTm && cancelAnimationFrame(qutyTm);
328 qutyTm = requestAnimationFrame(function () {
329 canvas.toBlob(function (blob) {
330 blob && quty.next('span').text(' (' + fm.formatSize(blob.size) + ')');
331 }, 'image/jpeg', Math.max(Math.min(q, 100), 1) / 100);
332 });
333 })
334 .val($base.data('quality'));
335 $('<div class="ui-dialog-buttonset elfinder-edit-extras elfinder-edit-extras-quality"/>')
336 .append(
337 $('<span>').html(fm.i18n('quality') + ' : '), quty, $('<span/>')
338 )
339 .prependTo($base.parent().next());
340 } else if (self.file.mime === 'image/svg+xml') {
341 $base.closest('.ui-dialog').trigger('changeType', {
342 extention: 'png',
343 mime: 'image/png',
344 keepEditor: true
345 });
346 }
347 // zoom scale controls
348 $('<div class="ui-dialog-buttonset elfinder-edit-extras"/>')
349 .append(
350 zdown, per, zup
351 )
352 .attr('title', fm.i18n('scale'))
353 .on('click', 'span,button', function () {
354 zoom($(this).data('val'));
355 })
356 .on('mousedown mouseup mouseleave', 'span', function (e) {
357 zoomMore = false;
358 zoomTm && clearTimeout(zoomTm);
359 if (e.type === 'mousedown') {
360 zoomTm = setTimeout(function () {
361 zoomMore = true;
362 zoom($(e.target).data('val'));
363 }, 500);
364 }
365 })
366 .prependTo($base.parent().next());
367
368 // wait canvas ready
369 setTimeout(function () {
370 dfrd.resolve(iEditor);
371 if (quty) {
372 quty.trigger('change');
373 iEditor.on('redoStackChanged undoStackChanged', function () {
374 quty.trigger('change');
375 });
376 }
377 // show initial scale
378 zoom(null);
379 }, 100);
380
381 // show color slider (maybe TUI-Image-Editor's bug)
382 // see https://github.com/nhn/tui.image-editor/issues/153
383 $base.find('.tui-colorpicker-palette-container').on('click', '.tui-colorpicker-palette-preview', function () {
384 $(this).closest('.color-picker-control').height('auto').find('.tui-colorpicker-slider-container').toggle();
385 });
386 $base.on('click', function () {
387 $base.find('.tui-colorpicker-slider-container').hide();
388 });
389 },
390 loader;
391
392 if (!self.confObj.editor) {
393 loader = $.Deferred();
394 fm.loadCss([
395 cdns.tui + '/tui-color-picker/latest/tui-color-picker.css',
396 cdns.tui + '/tui-image-editor/' + ver + '/tui-image-editor.css'
397 ]);
398 if (fm.hasRequire) {
399 require.config({
400 paths: {
401 'fabric/dist/fabric.require': cdns.fabric + '/fabric.require.min', // for fabric < 2.0.1
402 'fabric': cdns.fabric + '/fabric.min', // for fabric >= 2.0.1
403 'tui-code-snippet': cdns.tui + '/tui.code-snippet/latest/tui-code-snippet.min',
404 'tui-color-picker': cdns.tui + '/tui-color-picker/latest/tui-color-picker.min',
405 'tui-image-editor': cdns.tui + '/tui-image-editor/' + ver + '/tui-image-editor.min'
406 }
407 });
408 require(['tui-image-editor'], function (ImageEditor) {
409 loader.resolve(ImageEditor);
410 });
411 } else {
412 fm.loadScript([
413 cdns.fabric + '/fabric.min.js',
414 cdns.tui + '/tui.code-snippet/latest/tui-code-snippet.min.js'
415 ], function () {
416 fm.loadScript([
417 cdns.tui + '/tui-color-picker/latest/tui-color-picker.min.js'
418 ], function () {
419 fm.loadScript([
420 cdns.tui + '/tui-image-editor/' + ver + '/tui-image-editor.min.js'
421 ], function () {
422 loader.resolve(window.tui.ImageEditor);
423 }, {
424 loadType: 'tag'
425 });
426 }, {
427 loadType: 'tag'
428 });
429 }, {
430 loadType: 'tag'
431 });
432 }
433 loader.done(function (editor) {
434 self.confObj.editor = editor;
435 init(editor);
436 });
437 } else {
438 init(self.confObj.editor);
439 }
440 return dfrd;
441 },
442 getContent: function (base) {
443 var editor = this.editor,
444 fm = editor.fm,
445 $base = $(base),
446 quality = $base.data('quality');
447 if (editor.instance) {
448 if ($base.data('mime') === 'image/jpeg') {
449 quality = quality || fm.storage('jpgQuality') || fm.option('jpgQuality');
450 quality = Math.max(0.1, Math.min(1, quality / 100));
451 }
452 return editor.instance.toDataURL({
453 format: getExtention($base.data('mime'), fm, true),
454 quality: quality
455 });
456 }
457 },
458 save: function (base) {
459 var $base = $(base),
460 quality = $base.data('quality'),
461 hash = $base.data('hash'),
462 file;
463 this.instance.deactivateAll();
464 if (typeof quality !== 'undefined') {
465 this.fm.storage('jpgQuality', quality);
466 }
467 if (hash) {
468 file = this.fm.file(hash);
469 $base.data('mime', file.mime);
470 }
471 }
472 },
473 {
474 // Photopea advanced image editor
475 info: {
476 id: 'photopea',
477 name: 'Photopea',
478 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -160',
479 single: true,
480 noContent: true,
481 arrayBufferContent: true,
482 openMaximized: true,
483 // Disable file types that cannot be saved on Photopea.
484 canMakeEmpty: ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp', 'image/tiff', /*'image/x-adobe-dng',*/ 'image/webp', /*'image/x-xcf',*/ 'image/vnd.adobe.photoshop', 'application/pdf', 'image/x-portable-pixmap', 'image/x-sketch', 'image/x-icon', 'image/vnd-ms.dds', /*'application/x-msmetafile'*/],
485 integrate: {
486 title: 'Photopea',
487 link: 'https://www.photopea.com/learn/'
488 }
489 },
490 mimes: ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp', 'image/tiff', 'image/x-adobe-dng', 'image/webp', 'image/x-xcf', 'image/vnd.adobe.photoshop', 'application/pdf', 'image/x-portable-pixmap', 'image/x-sketch', 'image/x-icon', 'image/vnd-ms.dds', 'application/x-msmetafile'],
491 html: '<iframe style="width:100%;height:100%;border:none;"></iframe>',
492 // setup on elFinder bootup
493 setup: function (opts, fm) {
494 if (fm.UA.IE || fm.UA.Mobile) {
495 this.disabled = true;
496 }
497 },
498 // Initialization of editing node (this: this editors HTML node)
499 init: function (id, file, dum, fm) {
500 var orig = 'https://www.photopea.com',
501 ifm = $(this).hide()
502 //.css('box-sizing', 'border-box')
503 .on('load', function () {
504 //spnr.remove();
505 ifm.show();
506 })
507 .on('error', function () {
508 spnr.remove();
509 ifm.show();
510 }),
511 editor = this.editor,
512 confObj = editor.confObj,
513 spnr = $('<div class="elfinder-edit-spinner elfinder-edit-photopea"/>')
514 .html('<span class="elfinder-spinner-text">' + fm.i18n('nowLoading') + '</span><span class="elfinder-spinner"/>')
515 .appendTo(ifm.parent()),
516 saveMimes = fm.arrayFlip(confObj.info.canMakeEmpty),
517 getType = function (mime) {
518 var ext = getExtention(mime, fm),
519 extmime = ext2mime[ext];
520
521 if (!confObj.mimesFlip[extmime]) {
522 ext = '';
523 } else if (ext === 'jpeg') {
524 ext = 'jpg';
525 }
526 if (!ext || !saveMimes[extmime]) {
527 ext = 'psd';
528 extmime = ext2mime[ext];
529 ifm.closest('.ui-dialog').trigger('changeType', {
530 extention: ext,
531 mime: extmime,
532 keepEditor: true
533 });
534 }
535 return ext;
536 },
537 mime = file.mime,
538 liveMsg, type, quty;
539
540 if (!confObj.mimesFlip) {
541 confObj.mimesFlip = fm.arrayFlip(confObj.mimes, true);
542 }
543 if (!confObj.liveMsg) {
544 confObj.liveMsg = function (ifm, spnr, file) {
545 var wnd = ifm.get(0).contentWindow,
546 phase = 0,
547 data = null,
548 dfdIni = $.Deferred().done(function () {
549 spnr.remove();
550 phase = 1;
551 wnd.postMessage(data, orig);
552 }),
553 dfdGet;
554
555 this.load = function () {
556 return fm.getContents(file.hash, 'arraybuffer').done(function (d) {
557 data = d;
558 });
559 };
560
561 this.receive = function (e) {
562 var ev = e.originalEvent,
563 state;
564 if (ev.origin === orig && ev.source === wnd) {
565 if (ev.data === 'done') {
566 if (phase === 0) {
567 dfdIni.resolve();
568 } else if (phase === 1) {
569 phase = 2;
570 ifm.trigger('contentsloaded');
571 } else {
572 if (dfdGet && dfdGet.state() === 'pending') {
573 dfdGet.reject('errDataEmpty');
574 }
575 }
576 } else if (ev.data === 'Save') {
577 editor.doSave();
578 } else {
579 if (dfdGet && dfdGet.state() === 'pending') {
580 if (typeof ev.data === 'object') {
581 dfdGet.resolve('data:' + mime + ';base64,' + fm.arrayBufferToBase64(ev.data));
582 } else {
583 dfdGet.reject('errDataEmpty');
584 }
585 }
586 }
587 }
588 };
589
590 this.getContent = function () {
591 var type, q;
592 if (phase > 1) {
593 dfdGet && dfdGet.state() === 'pending' && dfdGet.reject();
594 dfdGet = null;
595 dfdGet = $.Deferred();
596 if (phase === 2) {
597 phase = 3;
598 dfdGet.resolve('data:' + mime + ';base64,' + fm.arrayBufferToBase64(data));
599 data = null;
600 return dfdGet;
601 }
602 if (ifm.data('mime')) {
603 mime = ifm.data('mime');
604 type = getType(mime);
605 }
606 if (q = ifm.data('quality')) {
607 type += ':' + (q / 100);
608 }
609 wnd.postMessage('app.activeDocument.saveToOE("' + type + '")', orig);
610 return dfdGet;
611 }
612 };
613 };
614 }
615
616 ifm.parent().css('padding', 0);
617 type = getType(file.mime);
618 liveMsg = editor.liveMsg = new confObj.liveMsg(ifm, spnr, file);
619 $(window).on('message.' + fm.namespace, liveMsg.receive);
620 liveMsg.load().done(function () {
621 var d = JSON.stringify({
622 files: [],
623 environment: {
624 lang: fm.lang.replace(/_/g, '-'),
625 customIO: { "save": "app.echoToOE(\"Save\");" }
626 }
627 });
628 ifm.attr('src', orig + '/#' + encodeURI(d));
629 }).fail(function (err) {
630 err && fm.error(err);
631 editor.initFail = true;
632 });
633
634 // jpeg quality controls
635 if (file.mime === 'image/jpeg' || file.mime === 'image/webp') {
636 ifm.data('quality', fm.storage('jpgQuality') || fm.option('jpgQuality'));
637 quty = $('<input type="number" class="ui-corner-all elfinder-resize-quality elfinder-tabstop"/>')
638 .attr('min', '1')
639 .attr('max', '100')
640 .attr('title', '1 - 100')
641 .on('change', function () {
642 var q = quty.val();
643 ifm.data('quality', q);
644 })
645 .val(ifm.data('quality'));
646 $('<div class="ui-dialog-buttonset elfinder-edit-extras elfinder-edit-extras-quality"/>')
647 .append(
648 $('<span>').html(fm.i18n('quality') + ' : '), quty, $('<span/>')
649 )
650 .prependTo(ifm.parent().next());
651 }
652 },
653 load: function (base) {
654 var dfd = $.Deferred(),
655 self = this,
656 fm = this.fm,
657 $base = $(base);
658 if (self.initFail) {
659 dfd.reject();
660 } else {
661 $base.on('contentsloaded', function () {
662 dfd.resolve(self.liveMsg);
663 });
664 }
665 return dfd;
666 },
667 getContent: function () {
668 return this.editor.liveMsg ? this.editor.liveMsg.getContent() : void (0);
669 },
670 save: function (base, liveMsg) {
671 var $base = $(base),
672 quality = $base.data('quality'),
673 hash = $base.data('hash'),
674 file;
675 if (typeof quality !== 'undefined') {
676 this.fm.storage('jpgQuality', quality);
677 }
678 if (hash) {
679 file = this.fm.file(hash);
680 $base.data('mime', file.mime);
681 } else {
682 $base.removeData('mime');
683 }
684 },
685 // On dialog closed
686 close: function (base, liveMsg) {
687 $(base).attr('src', '');
688 liveMsg && $(window).off('message.' + this.fm.namespace, liveMsg.receive);
689 }
690 },
691 {
692 // Pixo is cross-platform image editor
693 info: {
694 id: 'pixo',
695 name: 'Pixo Editor',
696 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -208',
697 dataScheme: true,
698 schemeContent: true,
699 single: true,
700 canMakeEmpty: false,
701 integrate: {
702 title: 'Pixo Editor',
703 link: 'https://pixoeditor.com/privacy-policy/'
704 }
705 },
706 // MIME types to accept
707 mimes: ['image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/x-ms-bmp'],
708 // HTML of this editor
709 html: '<div class="elfinder-edit-imageeditor"><img/></div>',
710 // called on initialization of elFinder cmd edit (this: this editor's config object)
711 setup: function (opts, fm) {
712 if (fm.UA.ltIE8 || !opts.extraOptions || !opts.extraOptions.pixo || !opts.extraOptions.pixo.apikey) {
713 this.disabled = true;
714 } else {
715 this.editorOpts = opts.extraOptions.pixo;
716 }
717 },
718 // Initialization of editing node (this: this editors HTML node)
719 init: function (id, file, content, fm) {
720 initImgTag.call(this, id, file, content, fm);
721 },
722 // Get data uri scheme (this: this editors HTML node)
723 getContent: function () {
724 return $(this).children('img:first').attr('src');
725 },
726 // Launch Pixo editor when dialog open
727 load: function (base) {
728 var self = this,
729 fm = this.fm,
730 $base = $(base),
731 node = $base.children('img:first'),
732 dialog = $base.closest('.ui-dialog'),
733 elfNode = fm.getUI(),
734 dfrd = $.Deferred(),
735 container = $('#elfinder-pixo-container'),
736 init = function (onload) {
737 var opts;
738
739 if (!container.length) {
740 container = $('<div id="elfinder-pixo-container" class="ui-front"/>').css({
741 position: 'fixed',
742 top: 0,
743 right: 0,
744 width: '100%',
745 height: $(window).height(),
746 overflow: 'hidden'
747 }).hide().appendTo(elfNode.hasClass('elfinder-fullscreen') ? elfNode : 'body');
748 // bind switch fullscreen event
749 elfNode.on('resize.' + fm.namespace, function (e, data) {
750 e.preventDefault();
751 e.stopPropagation();
752 data && data.fullscreen && container.appendTo(data.fullscreen === 'on' ? elfNode : 'body');
753 });
754 fm.bind('destroy', function () {
755 editor && editor.cancelEditing();
756 container.remove();
757 });
758 } else {
759 // always moves to last
760 container.appendTo(container.parent());
761 }
762 node.on('click', launch);
763 // Constructor options
764 opts = Object.assign({
765 type: 'child',
766 parent: container.get(0),
767 output: { format: 'png' },
768 onSave: function (arg) {
769 // Check current file.hash, all callbacks are called on multiple instances
770 var mime = arg.toBlob().type,
771 ext = getExtention(mime, fm),
772 draw = function (url) {
773 node.one('load error', function () {
774 node.data('loading') && node.data('loading')(true);
775 })
776 .attr('crossorigin', 'anonymous')
777 .attr('src', url);
778 },
779 url = arg.toDataURL();
780 node.data('loading')();
781 delete base._canvas;
782 if (node.data('ext') !== ext) {
783 changeImageType(url, self.file.mime).done(function (res, cv) {
784 if (cv) {
785 base._canvas = canvas = cv;
786 quty.trigger('change');
787 qBase && qBase.show();
788 }
789 draw(res);
790 }).fail(function () {
791 dialog.trigger('changeType', {
792 extention: ext,
793 mime: mime
794 });
795 draw(url);
796 });
797 } else {
798 draw(url);
799 }
800 },
801 onClose: function () {
802 dialog.removeClass(fm.res('class', 'preventback'));
803 fm.toggleMaximize(container, false);
804 container.hide();
805 fm.toFront(dialog);
806 }
807 }, self.confObj.editorOpts);
808 // trigger event 'editEditorPrepare'
809 self.trigger('Prepare', {
810 node: base,
811 editorObj: Pixo,
812 instance: void (0),
813 opts: opts
814 });
815 // make editor instance
816 editor = new Pixo.Bridge(opts);
817 dfrd.resolve(editor);
818 $base.on('saveAsFail', launch);
819 if (onload) {
820 onload();
821 }
822 },
823 launch = function () {
824 dialog.addClass(fm.res('class', 'preventback'));
825 fm.toggleMaximize(container, true);
826 fm.toFront(container);
827 container.show().data('curhash', self.file.hash);
828 editor.edit(node.get(0));
829 node.data('loading')(true);
830 },
831 qBase, quty, qutyTm, canvas, editor;
832
833 node.data('loading')();
834
835 // jpeg quality controls
836 if (self.file.mime === 'image/jpeg') {
837 quty = $('<input type="number" class="ui-corner-all elfinder-resize-quality elfinder-tabstop"/>')
838 .attr('min', '1')
839 .attr('max', '100')
840 .attr('title', '1 - 100')
841 .on('change', function () {
842 var q = quty.val();
843 qutyTm && cancelAnimationFrame(qutyTm);
844 qutyTm = requestAnimationFrame(function () {
845 if (canvas) {
846 canvas.toBlob(function (blob) {
847 blob && quty.next('span').text(' (' + fm.formatSize(blob.size) + ')');
848 }, 'image/jpeg', Math.max(Math.min(q, 100), 1) / 100);
849 }
850 });
851 })
852 .val(fm.storage('jpgQuality') || fm.option('jpgQuality'));
853 qBase = $('<div class="ui-dialog-buttonset elfinder-edit-extras elfinder-edit-extras-quality"/>')
854 .hide()
855 .append(
856 $('<span>').html(fm.i18n('quality') + ' : '), quty, $('<span/>')
857 )
858 .prependTo($base.parent().next());
859 $base.data('quty', quty);
860 }
861
862 // load script then init
863 if (typeof Pixo === 'undefined') {
864 fm.loadScript(['https://pixoeditor.com:8443/editor/scripts/bridge.m.js'], function () {
865 init(launch);
866 }, { loadType: 'tag' });
867 } else {
868 init();
869 launch();
870 }
871 return dfrd;
872 },
873 // Convert content url to data uri scheme to save content
874 save: function (base) {
875 var self = this,
876 $base = $(base),
877 node = $base.children('img:first'),
878 q;
879 if (base._canvas) {
880 if ($base.data('quty')) {
881 q = $base.data('quty').val();
882 q && this.fm.storage('jpgQuality', q);
883 }
884 node.attr('src', base._canvas.toDataURL(self.file.mime, q ? Math.max(Math.min(q, 100), 1) / 100 : void (0)));
885 } else if (node.attr('src').substr(0, 5) !== 'data:') {
886 node.attr('src', imgBase64(node, this.file.mime));
887 }
888 },
889 close: function (base, editor) {
890 editor && editor.destroy();
891 }
892 },
893 {
894 // ACE Editor
895 // called on initialization of elFinder cmd edit (this: this editor's config object)
896 setup: function (opts, fm) {
897 if (fm.UA.ltIE8 || !fm.options.cdns.ace) {
898 this.disabled = true;
899 }
900 },
901 // `mimes` is not set for support everything kind of text file
902 info: {
903 id: 'aceeditor',
904 name: 'ACE Editor',
905 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -96'
906 },
907 load: function (textarea) {
908 var self = this,
909 fm = this.fm,
910 dfrd = $.Deferred(),
911 cdn = fm.options.cdns.ace,
912 start = function () {
913 var editor, editorBase, mode,
914 ta = $(textarea),
915 taBase = ta.parent(),
916 dialog = taBase.parent(),
917 id = textarea.id + '_ace',
918 ext = self.file.name.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
919 // MIME/mode map
920 mimeMode = {
921 'text/x-php': 'php',
922 'application/x-php': 'php',
923 'text/html': 'html',
924 'application/xhtml+xml': 'html',
925 'text/javascript': 'javascript',
926 'application/javascript': 'javascript',
927 'text/css': 'css',
928 'text/x-c': 'c_cpp',
929 'text/x-csrc': 'c_cpp',
930 'text/x-chdr': 'c_cpp',
931 'text/x-c++': 'c_cpp',
932 'text/x-c++src': 'c_cpp',
933 'text/x-c++hdr': 'c_cpp',
934 'text/x-shellscript': 'sh',
935 'application/x-csh': 'sh',
936 'text/x-python': 'python',
937 'text/x-java': 'java',
938 'text/x-java-source': 'java',
939 'text/x-ruby': 'ruby',
940 'text/x-perl': 'perl',
941 'application/x-perl': 'perl',
942 'text/x-sql': 'sql',
943 'text/xml': 'xml',
944 'application/docbook+xml': 'xml',
945 'application/xml': 'xml'
946 };
947
948 // set base height
949 taBase.height(taBase.height());
950
951 // set basePath of ace
952 ace.config.set('basePath', cdn);
953
954 // Base node of Ace editor
955 editorBase = $('<div id="' + id + '" style="width:100%; height:100%;"/>').text(ta.val()).insertBefore(ta.hide());
956
957 // Editor flag
958 ta.data('ace', true);
959
960 // Aceeditor instance
961 editor = ace.edit(id);
962
963 // Ace editor configure
964 editor.$blockScrolling = Infinity;
965 editor.setOptions({
966 theme: 'ace/theme/monokai',
967 fontSize: '14px',
968 wrap: true,
969 });
970 ace.config.loadModule('ace/ext/modelist', function () {
971 // detect mode
972 mode = ace.require('ace/ext/modelist').getModeForPath('/' + self.file.name).name;
973 if (mode === 'text') {
974 if (mimeMode[self.file.mime]) {
975 mode = mimeMode[self.file.mime];
976 }
977 }
978 // show MIME:mode in title bar
979 taBase.prev().children('.elfinder-dialog-title').append(' (' + self.file.mime + ' : ' + mode.split(/[\/\\]/).pop() + ')');
980 editor.setOptions({
981 mode: 'ace/mode/' + mode
982 });
983 if (dfrd.state() === 'resolved') {
984 dialog.trigger('resize');
985 }
986 });
987 ace.config.loadModule('ace/ext/language_tools', function () {
988 ace.require('ace/ext/language_tools');
989 editor.setOptions({
990 enableBasicAutocompletion: true,
991 enableSnippets: true,
992 enableLiveAutocompletion: false
993 });
994 });
995 ace.config.loadModule('ace/ext/settings_menu', function () {
996 ace.require('ace/ext/settings_menu').init(editor);
997 });
998
999 // Short cuts
1000 editor.commands.addCommand({
1001 name: "saveFile",
1002 bindKey: {
1003 win: 'Ctrl-s',
1004 mac: 'Command-s'
1005 },
1006 exec: function (editor) {
1007 self.doSave();
1008 }
1009 });
1010 editor.commands.addCommand({
1011 name: "closeEditor",
1012 bindKey: {
1013 win: 'Ctrl-w|Ctrl-q',
1014 mac: 'Command-w|Command-q'
1015 },
1016 exec: function (editor) {
1017 self.doCancel();
1018 }
1019 });
1020
1021 editor.resize();
1022
1023 // TextArea button and Setting button
1024 $('<div class="ui-dialog-buttonset"/>').css('float', 'left')
1025 .append(
1026 $('<button/>').html(self.fm.i18n('TextArea'))
1027 .button()
1028 .on('click', function () {
1029 if (ta.data('ace')) {
1030 ta.removeData('ace');
1031 editorBase.hide();
1032 ta.val(editor.session.getValue()).show().trigger('focus');
1033 $(this).text('AceEditor');
1034 } else {
1035 ta.data('ace', true);
1036 editorBase.show();
1037 editor.setValue(ta.hide().val(), -1);
1038 editor.focus();
1039 $(this).html(self.fm.i18n('TextArea'));
1040 }
1041 })
1042 )
1043 .append(
1044 $('<button>Ace editor setting</button>')
1045 .button({
1046 icons: {
1047 primary: 'ui-icon-gear',
1048 secondary: 'ui-icon-triangle-1-e'
1049 },
1050 text: false
1051 })
1052 .on('click', function () {
1053 editor.showSettingsMenu();
1054 $('#ace_settingsmenu')
1055 .css({ 'font-size': '80%', 'margin-top': '50px' })
1056 .find('div[contains="setOptions"]').hide().end()
1057 .parent().appendTo($('#elfinder'));
1058 })
1059 ).prependTo(taBase.next());
1060
1061
1062 // trigger event 'editEditorPrepare'
1063 self.trigger('Prepare', {
1064 node: textarea,
1065 editorObj: ace,
1066 instance: editor,
1067 opts: {}
1068 });
1069
1070 //dialog.trigger('resize');
1071 dfrd.resolve(editor);
1072 };
1073
1074 // check ace & start
1075 if (!self.confObj.loader) {
1076 self.confObj.loader = $.Deferred();
1077 self.fm.loadScript([cdn + '/ace.js'], function () {
1078 self.confObj.loader.resolve();
1079 }, void 0, { obj: window, name: 'ace' });
1080 }
1081 self.confObj.loader.done(start);
1082
1083 return dfrd;
1084 },
1085 close: function (textarea, instance) {
1086 instance && instance.destroy();
1087 },
1088 save: function (textarea, instance) {
1089 instance && $(textarea).data('ace') && (textarea.value = instance.session.getValue());
1090 },
1091 focus: function (textarea, instance) {
1092 instance && $(textarea).data('ace') && instance.focus();
1093 },
1094 resize: function (textarea, instance, e, data) {
1095 instance && instance.resize();
1096 }
1097 },
1098 {
1099 // CodeMirror
1100 // called on initialization of elFinder cmd edit (this: this editor's config object)
1101 setup: function (opts, fm) {
1102 if (fm.UA.ltIE10 || !fm.options.cdns.codemirror) {
1103 this.disabled = true;
1104 }
1105 },
1106 // `mimes` is not set for support everything kind of text file
1107 info: {
1108 id: 'codemirror',
1109 name: 'CodeMirror',
1110 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -176'
1111 },
1112 load: function (textarea) {
1113 var fm = this.fm,
1114 cmUrl = fm.convAbsUrl(fm.options.cdns.codemirror),
1115 dfrd = $.Deferred(),
1116 self = this,
1117 start = function (CodeMirror) {
1118 var ta = $(textarea),
1119 base = ta.parent(),
1120 editor, editorBase, opts;
1121
1122 // set base height
1123 base.height(base.height());
1124
1125 // CodeMirror configure options
1126 opts = {
1127 lineNumbers: true,
1128 lineWrapping: true,
1129 extraKeys: {
1130 'Ctrl-S': function () { self.doSave(); },
1131 'Ctrl-Q': function () { self.doCancel(); },
1132 'Ctrl-W': function () { self.doCancel(); }
1133 }
1134 };
1135
1136 // trigger event 'editEditorPrepare'
1137 self.trigger('Prepare', {
1138 node: textarea,
1139 editorObj: CodeMirror,
1140 instance: void (0),
1141 opts: opts
1142 });
1143
1144 // CodeMirror configure
1145 editor = CodeMirror.fromTextArea(textarea, opts);
1146
1147 // return editor instance
1148 dfrd.resolve(editor);
1149
1150 // Auto mode set
1151 var info, m, mode, spec;
1152 if (!info) {
1153 info = CodeMirror.findModeByMIME(self.file.mime);
1154 }
1155 if (!info && (m = self.file.name.match(/.+\.([^.]+)$/))) {
1156 info = CodeMirror.findModeByExtension(m[1]);
1157 }
1158 if (info) {
1159 CodeMirror.modeURL = useRequire ? 'codemirror/mode/%N/%N.min' : cmUrl + '/mode/%N/%N.min.js';
1160 mode = info.mode;
1161 spec = info.mime;
1162 editor.setOption('mode', spec);
1163 CodeMirror.autoLoadMode(editor, mode);
1164 // show MIME:mode in title bar
1165 base.prev().children('.elfinder-dialog-title').append(' (' + spec + (mode != 'null' ? ' : ' + mode : '') + ')');
1166 }
1167
1168 // editor base node
1169 editorBase = $(editor.getWrapperElement()).css({
1170 // fix CSS conflict to SimpleMDE
1171 padding: 0,
1172 border: 'none'
1173 });
1174 ta.data('cm', true);
1175
1176 // fit height to base
1177 editorBase.height('100%');
1178
1179 // TextArea button and Setting button
1180 $('<div class="ui-dialog-buttonset"/>').css('float', 'left')
1181 .append(
1182 $('<button/>').html(self.fm.i18n('TextArea'))
1183 .button()
1184 .on('click', function () {
1185 if (ta.data('cm')) {
1186 ta.removeData('cm');
1187 editorBase.hide();
1188 ta.val(editor.getValue()).show().trigger('focus');
1189 $(this).text('CodeMirror');
1190 } else {
1191 ta.data('cm', true);
1192 editorBase.show();
1193 editor.setValue(ta.hide().val());
1194 editor.refresh();
1195 editor.focus();
1196 $(this).html(self.fm.i18n('TextArea'));
1197 }
1198 })
1199 )
1200 .prependTo(base.next());
1201 };
1202 // load script then start
1203 if (!self.confObj.loader) {
1204 self.confObj.loader = $.Deferred();
1205 if (useRequire) {
1206 require.config({
1207 packages: [{
1208 name: 'codemirror',
1209 location: cmUrl,
1210 main: 'codemirror.min'
1211 }],
1212 map: {
1213 'codemirror': {
1214 'codemirror/lib/codemirror': 'codemirror'
1215 }
1216 }
1217 });
1218 require([
1219 'codemirror',
1220 'codemirror/addon/mode/loadmode.min',
1221 'codemirror/mode/meta.min'
1222 ], function (CodeMirror) {
1223 self.confObj.loader.resolve(CodeMirror);
1224 });
1225 } else {
1226 self.fm.loadScript([
1227 cmUrl + '/codemirror.min.js'
1228 ], function () {
1229 self.fm.loadScript([
1230 cmUrl + '/addon/mode/loadmode.min.js',
1231 cmUrl + '/mode/meta.min.js'
1232 ], function () {
1233 self.confObj.loader.resolve(CodeMirror);
1234 });
1235 }, { loadType: 'tag' });
1236 }
1237 self.fm.loadCss(cmUrl + '/codemirror.css');
1238 }
1239 self.confObj.loader.done(start);
1240 return dfrd;
1241 },
1242 close: function (textarea, instance) {
1243 instance && instance.toTextArea();
1244 },
1245 save: function (textarea, instance) {
1246 instance && $(textarea).data('cm') && (textarea.value = instance.getValue());
1247 },
1248 focus: function (textarea, instance) {
1249 instance && $(textarea).data('cm') && instance.focus();
1250 },
1251 resize: function (textarea, instance, e, data) {
1252 instance && instance.refresh();
1253 }
1254 },
1255 {
1256 // SimpleMDE
1257 // called on initialization of elFinder cmd edit (this: this editor's config object)
1258 setup: function (opts, fm) {
1259 if (fm.UA.ltIE10 || !fm.options.cdns.simplemde) {
1260 this.disabled = true;
1261 }
1262 },
1263 info: {
1264 id: 'simplemde',
1265 name: 'SimpleMDE',
1266 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -80'
1267 },
1268 exts: ['md'],
1269 load: function (textarea) {
1270 var self = this,
1271 fm = this.fm,
1272 base = $(textarea).parent(),
1273 dfrd = $.Deferred(),
1274 cdn = fm.options.cdns.simplemde,
1275 start = function (SimpleMDE) {
1276 var h = base.height(),
1277 delta = base.outerHeight(true) - h + 14,
1278 editor, editorBase, opts;
1279
1280 // fit height function
1281 textarea._setHeight = function (height) {
1282 var h = height || base.height(),
1283 ctrH = 0,
1284 areaH;
1285 base.children('.editor-toolbar,.editor-statusbar').each(function () {
1286 ctrH += $(this).outerHeight(true);
1287 });
1288 areaH = h - ctrH - delta;
1289 editorBase.height(areaH);
1290 editor.codemirror.refresh();
1291 return areaH;
1292 };
1293
1294 // set base height
1295 base.height(h);
1296
1297 opts = {
1298 element: textarea,
1299 autofocus: true
1300 };
1301
1302 // trigger event 'editEditorPrepare'
1303 self.trigger('Prepare', {
1304 node: textarea,
1305 editorObj: SimpleMDE,
1306 instance: void (0),
1307 opts: opts
1308 });
1309
1310 // make editor
1311 editor = new SimpleMDE(opts);
1312 dfrd.resolve(editor);
1313
1314 // editor base node
1315 editorBase = $(editor.codemirror.getWrapperElement());
1316
1317 // fit height to base
1318 editorBase.css('min-height', '50px')
1319 .children('.CodeMirror-scroll').css('min-height', '50px');
1320 textarea._setHeight(h);
1321 };
1322
1323 // check SimpleMDE & start
1324 if (!self.confObj.loader) {
1325 self.confObj.loader = $.Deferred();
1326 self.fm.loadCss(cdn + '/simplemde.min.css');
1327 if (useRequire) {
1328 require([
1329 cdn + '/simplemde.min.js'
1330 ], function (SimpleMDE) {
1331 self.confObj.loader.resolve(SimpleMDE);
1332 });
1333 } else {
1334 self.fm.loadScript([cdn + '/simplemde.min.js'], function () {
1335 self.confObj.loader.resolve(SimpleMDE);
1336 }, { loadType: 'tag' });
1337 }
1338 }
1339 self.confObj.loader.done(start);
1340
1341 return dfrd;
1342 },
1343 close: function (textarea, instance) {
1344 instance && instance.toTextArea();
1345 instance = null;
1346 },
1347 save: function (textarea, instance) {
1348 instance && (textarea.value = instance.value());
1349 },
1350 focus: function (textarea, instance) {
1351 instance && instance.codemirror.focus();
1352 },
1353 resize: function (textarea, instance, e, data) {
1354 instance && textarea._setHeight();
1355 }
1356 },
1357 {
1358 // CKEditor for html file
1359 info: {
1360 id: 'ckeditor',
1361 name: 'CKEditor',
1362 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 0'
1363 },
1364 exts: ['htm', 'html', 'xhtml'],
1365 setup: function (opts, fm) {
1366 var confObj = this;
1367 if (!fm.options.cdns.ckeditor) {
1368 confObj.disabled = true;
1369 } else {
1370 confObj.ckeOpts = {};
1371 if (opts.extraOptions) {
1372 confObj.ckeOpts = Object.assign({}, opts.extraOptions.ckeditor || {});
1373 if (opts.extraOptions.managerUrl) {
1374 confObj.managerUrl = opts.extraOptions.managerUrl;
1375 }
1376 }
1377 }
1378 },
1379 load: function (textarea) {
1380 var self = this,
1381 fm = this.fm,
1382 dfrd = $.Deferred(),
1383 init = function () {
1384 var base = $(textarea).parent(),
1385 dlg = base.closest('.elfinder-dialog'),
1386 h = base.height(),
1387 reg = /([&?]getfile=)[^&]+/,
1388 loc = self.confObj.managerUrl || window.location.href.replace(/#.*$/, ''),
1389 name = 'ckeditor',
1390 opts;
1391
1392 // make manager location
1393 if (reg.test(loc)) {
1394 loc = loc.replace(reg, '$1' + name);
1395 } else {
1396 loc += '?getfile=' + name;
1397 }
1398 // set base height
1399 base.height(h);
1400
1401 // CKEditor configure options
1402 opts = {
1403 startupFocus: true,
1404 fullPage: true,
1405 allowedContent: true,
1406 filebrowserBrowseUrl: loc,
1407 toolbarCanCollapse: true,
1408 toolbarStartupExpanded: !fm.UA.Mobile,
1409 removePlugins: 'resize',
1410 extraPlugins: 'colorbutton,justify,docprops',
1411 on: {
1412 'instanceReady': function (e) {
1413 var editor = e.editor;
1414 editor.resize('100%', h);
1415 // re-build on dom move
1416 dlg.one('beforedommove.' + fm.namespace, function () {
1417 editor.destroy();
1418 }).one('dommove.' + fm.namespace, function () {
1419 self.load(textarea).done(function (editor) {
1420 self.instance = editor;
1421 });
1422 });
1423 // return editor instance
1424 dfrd.resolve(e.editor);
1425 }
1426 }
1427 };
1428
1429 // trigger event 'editEditorPrepare'
1430 self.trigger('Prepare', {
1431 node: textarea,
1432 editorObj: CKEDITOR,
1433 instance: void (0),
1434 opts: opts
1435 });
1436
1437 // CKEditor configure
1438 CKEDITOR.replace(textarea.id, Object.assign(opts, self.confObj.ckeOpts));
1439 CKEDITOR.on('dialogDefinition', function (e) {
1440 var dlg = e.data.definition.dialog;
1441 dlg.on('show', function (e) {
1442 fm.getUI().append($('.cke_dialog_background_cover')).append(this.getElement().$);
1443 });
1444 dlg.on('hide', function (e) {
1445 $('body:first').append($('.cke_dialog_background_cover')).append(this.getElement().$);
1446 });
1447 });
1448 };
1449
1450 if (!self.confObj.loader) {
1451 self.confObj.loader = $.Deferred();
1452 window.CKEDITOR_BASEPATH = fm.options.cdns.ckeditor + '/';
1453 $.getScript(fm.options.cdns.ckeditor + '/ckeditor.js', function () {
1454 self.confObj.loader.resolve();
1455 });
1456 }
1457 self.confObj.loader.done(init);
1458 return dfrd;
1459 },
1460 close: function (textarea, instance) {
1461 instance && instance.destroy();
1462 },
1463 save: function (textarea, instance) {
1464 instance && (textarea.value = instance.getData());
1465 },
1466 focus: function (textarea, instance) {
1467 instance && instance.focus();
1468 },
1469 resize: function (textarea, instance, e, data) {
1470 var self;
1471 if (instance) {
1472 if (instance.status === 'ready') {
1473 instance.resize('100%', $(textarea).parent().height());
1474 }
1475 }
1476 }
1477 },
1478 {
1479 // CKEditor5 balloon mode for html file
1480 info: {
1481 id: 'ckeditor5',
1482 name: 'CKEditor5',
1483 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -16'
1484 },
1485 exts: ['htm', 'html', 'xhtml'],
1486 html: '<div class="edit-editor-ckeditor5"></div>',
1487 setup: function (opts, fm) {
1488 var confObj = this;
1489 // check cdn and ES6 support
1490 if (!fm.options.cdns.ckeditor5 || typeof window.Symbol !== 'function' || typeof Symbol() !== 'symbol') {
1491 confObj.disabled = true;
1492 } else {
1493 confObj.ckeOpts = {};
1494 if (opts.extraOptions) {
1495 // @deprecated option extraOptions.ckeditor5Mode
1496 if (opts.extraOptions.ckeditor5Mode) {
1497 confObj.ckeditor5Mode = opts.extraOptions.ckeditor5Mode;
1498 }
1499 confObj.ckeOpts = Object.assign({}, opts.extraOptions.ckeditor5 || {});
1500 if (confObj.ckeOpts.mode) {
1501 confObj.ckeditor5Mode = confObj.ckeOpts.mode;
1502 delete confObj.ckeOpts.mode;
1503 }
1504 if (opts.extraOptions.managerUrl) {
1505 confObj.managerUrl = opts.extraOptions.managerUrl;
1506 }
1507 }
1508 }
1509 fm.bind('destroy', function () {
1510 confObj.editor = null;
1511 });
1512 },
1513 // Prepare on before show dialog
1514 prepare: function (base, dialogOpts, file) {
1515 $(base).height(base.editor.fm.getUI().height() - 100);
1516 },
1517 init: function (id, file, data, fm) {
1518 var m = data.match(/^([\s\S]*<body[^>]*>)([\s\S]+)(<\/body>[\s\S]*)$/i),
1519 header = '',
1520 body = '',
1521 footer = '';
1522 this.css({
1523 width: '100%',
1524 height: '100%',
1525 'box-sizing': 'border-box'
1526 });
1527 if (m) {
1528 header = m[1];
1529 body = m[2];
1530 footer = m[3];
1531 } else {
1532 body = data;
1533 }
1534 this.data('data', {
1535 header: header,
1536 body: body,
1537 footer: footer
1538 });
1539 this._setupSelEncoding(data);
1540 },
1541 load: function (editnode) {
1542 var self = this,
1543 fm = this.fm,
1544 dfrd = $.Deferred(),
1545 mode = self.confObj.ckeditor5Mode || 'decoupled-document',
1546 lang = (function () {
1547 var l = fm.lang.toLowerCase().replace('_', '-');
1548 if (l.substr(0, 2) === 'zh' && l !== 'zh-cn') {
1549 l = 'zh';
1550 }
1551 return l;
1552 })(),
1553 init = function (cEditor) {
1554 var base = $(editnode).parent(),
1555 opts;
1556
1557 // set base height
1558 base.height(fm.getUI().height() - 100);
1559
1560 // CKEditor5 configure options
1561 opts = Object.assign({
1562 toolbar: ["heading", "|", "fontSize", "fontFamily", "|", "bold", "italic", "underline", "strikethrough", "highlight", "|", "alignment", "|", "numberedList", "bulletedList", "blockQuote", "indent", "outdent", "|", "ckfinder", "link", "imageUpload", "insertTable", "mediaEmbed", "|", "undo", "redo"],
1563 language: lang
1564 }, self.confObj.ckeOpts);
1565
1566 // trigger event 'editEditorPrepare'
1567 self.trigger('Prepare', {
1568 node: editnode,
1569 editorObj: cEditor,
1570 instance: void (0),
1571 opts: opts
1572 });
1573
1574 cEditor
1575 .create(editnode, opts)
1576 .then(function (editor) {
1577 var ckf = editor.commands.get('ckfinder'),
1578 fileRepo = editor.plugins.get('FileRepository'),
1579 prevVars = {}, isImage, insertImages;
1580 if (editor.ui.view.toolbar && (mode === 'classic' || mode === 'decoupled-document')) {
1581 $(editnode).closest('.elfinder-dialog').children('.ui-widget-header').append($(editor.ui.view.toolbar.element).css({ marginRight: '-1em', marginLeft: '-1em' }));
1582 }
1583 if (mode === 'classic') {
1584 $(editnode).closest('.elfinder-edit-editor').css('overflow', 'auto');
1585 }
1586 // Set up this elFinder instead of CKFinder
1587 if (ckf) {
1588 isImage = function (f) {
1589 return f && f.mime.match(/^image\//i);
1590 };
1591 insertImages = function (urls) {
1592 var imgCmd = editor.commands.get('imageUpload');
1593 if (!imgCmd.isEnabled) {
1594 var ntf = editor.plugins.get('Notification'),
1595 i18 = editor.locale.t;
1596 ntf.showWarning(i18('Could not insert image at the current position.'), {
1597 title: i18('Inserting image failed'),
1598 namespace: 'ckfinder'
1599 });
1600 return;
1601 }
1602 editor.execute('imageInsert', { source: urls });
1603 };
1604 // Take over ckfinder execute()
1605 ckf.execute = function () {
1606 var dlg = base.closest('.elfinder-dialog'),
1607 gf = fm.getCommand('getfile'),
1608 rever = function () {
1609 if (prevVars.hasVar) {
1610 dlg.off('resize close', rever);
1611 gf.callback = prevVars.callback;
1612 gf.options.folders = prevVars.folders;
1613 gf.options.multiple = prevVars.multi;
1614 fm.commandMap.open = prevVars.open;
1615 prevVars.hasVar = false;
1616 }
1617 };
1618 dlg.trigger('togleminimize').one('resize close', rever);
1619 prevVars.callback = gf.callback;
1620 prevVars.folders = gf.options.folders;
1621 prevVars.multi = gf.options.multiple;
1622 prevVars.open = fm.commandMap.open;
1623 prevVars.hasVar = true;
1624 gf.callback = function (files) {
1625 var imgs = [];
1626 if (files.length === 1 && files[0].mime === 'directory') {
1627 fm.one('open', function () {
1628 fm.commandMap.open = 'getfile';
1629 }).getCommand('open').exec(files[0].hash);
1630 return;
1631 }
1632 fm.getUI('cwd').trigger('unselectall');
1633 $.each(files, function (i, f) {
1634 if (isImage(f)) {
1635 imgs.push(fm.convAbsUrl(f.url));
1636 } else {
1637 editor.execute('link', fm.convAbsUrl(f.url));
1638 }
1639 });
1640 if (imgs.length) {
1641 insertImages(imgs);
1642 }
1643 dlg.trigger('togleminimize');
1644 };
1645 gf.options.folders = true;
1646 gf.options.multiple = true;
1647 fm.commandMap.open = 'getfile';
1648 fm.toast({
1649 mode: 'info',
1650 msg: fm.i18n('dblclickToSelect')
1651 });
1652 };
1653 }
1654 // Set up image uploader
1655 fileRepo.createUploadAdapter = function (loader) {
1656 return new uploder(loader);
1657 };
1658 editor.setData($(editnode).data('data').body);
1659 // move .ck-body to elFinder node for fullscreen mode
1660 fm.getUI().append($('body > div.ck-body'));
1661 $('div.ck-balloon-panel').css({
1662 'z-index': fm.getMaximizeCss().zIndex + 1
1663 });
1664 dfrd.resolve(editor);
1665 /*fm.log({
1666 defaultConfig: cEditor.defaultConfig,
1667 plugins: cEditor.builtinPlugins.map(function(p) { return p.pluginName; }),
1668 toolbars: Array.from(editor.ui.componentFactory.names())
1669 });*/
1670 })
1671 ['catch'](function (error) { // ['cache'] instead .cache for fix error on ie8
1672 fm.error(error);
1673 });
1674 },
1675 uploder = function (loader) {
1676 var upload = function (file, resolve, reject) {
1677 fm.exec('upload', { files: [file] }, void (0), fm.cwd().hash)
1678 .done(function (data) {
1679 if (data.added && data.added.length) {
1680 fm.url(data.added[0].hash, { async: true }).done(function (url) {
1681 resolve({
1682 'default': fm.convAbsUrl(url)
1683 });
1684 }).fail(function () {
1685 reject('errFileNotFound');
1686 });
1687 } else {
1688 reject(fm.i18n(data.error ? data.error : 'errUpload'));
1689 }
1690 })
1691 .fail(function (err) {
1692 var error = fm.parseError(err);
1693 reject(fm.i18n(error ? (error === 'userabort' ? 'errAbort' : error) : 'errUploadNoFiles'));
1694 })
1695 .progress(function (data) {
1696 loader.uploadTotal = data.total;
1697 loader.uploaded = data.progress;
1698 });
1699 };
1700 this.upload = function () {
1701 return new Promise(function (resolve, reject) {
1702 if (loader.file instanceof Promise || (loader.file && typeof loader.file.then === 'function')) {
1703 loader.file.then(function (file) {
1704 upload(file, resolve, reject);
1705 });
1706 } else {
1707 upload(loader.file, resolve, reject);
1708 }
1709 });
1710 };
1711 this.abort = function () {
1712 fm.getUI().trigger('uploadabort');
1713 };
1714 }, loader;
1715
1716 if (!self.confObj.editor) {
1717 loader = $.Deferred();
1718 self.fm.loadScript([
1719 fm.options.cdns.ckeditor5 + '/' + mode + '/ckeditor.js'
1720 ], function (editor) {
1721 if (!editor) {
1722 editor = window.BalloonEditor || window.InlineEditor || window.ClassicEditor || window.DecoupledEditor;
1723 }
1724 if (fm.lang !== 'en') {
1725 self.fm.loadScript([
1726 fm.options.cdns.ckeditor5 + '/' + mode + '/translations/' + lang + '.js'
1727 ], function (obj) {
1728 loader.resolve(editor);
1729 }, {
1730 tryRequire: true,
1731 loadType: 'tag',
1732 error: function (obj) {
1733 lang = 'en';
1734 loader.resolve(editor);
1735 }
1736 });
1737 } else {
1738 loader.resolve(editor);
1739 }
1740 }, {
1741 tryRequire: true,
1742 loadType: 'tag'
1743 });
1744 loader.done(function (editor) {
1745 self.confObj.editor = editor;
1746 init(editor);
1747 });
1748 } else {
1749 init(self.confObj.editor);
1750 }
1751 return dfrd;
1752 },
1753 getContent: function () {
1754 var data = $(this).data('data');
1755 return data.header + data.body + data.footer;
1756 },
1757 close: function (editnode, instance) {
1758 instance && instance.destroy();
1759 },
1760 save: function (editnode, instance) {
1761 var elm = $(editnode),
1762 data = elm.data('data');
1763 if (instance) {
1764 data.body = instance.getData();
1765 elm.data('data', data);
1766 }
1767 },
1768 focus: function (editnode, instance) {
1769 $(editnode).trigger('focus');
1770 }
1771 },
1772 {
1773 // TinyMCE for html file
1774 info: {
1775 id: 'tinymce',
1776 name: 'TinyMCE',
1777 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -64'
1778 },
1779 exts: ['htm', 'html', 'xhtml'],
1780 setup: function (opts, fm) {
1781 var confObj = this;
1782 if (!fm.options.cdns.tinymce) {
1783 confObj.disabled = true;
1784 } else {
1785 confObj.mceOpts = {};
1786 if (opts.extraOptions) {
1787 confObj.uploadOpts = Object.assign({}, opts.extraOptions.uploadOpts || {});
1788 confObj.mceOpts = Object.assign({}, opts.extraOptions.tinymce || {});
1789 } else {
1790 confObj.uploadOpts = {};
1791 }
1792 }
1793 },
1794 load: function (textarea) {
1795 var self = this,
1796 fm = this.fm,
1797 dfrd = $.Deferred(),
1798 init = function () {
1799 var base = $(textarea).show().parent(),
1800 dlg = base.closest('.elfinder-dialog'),
1801 h = base.height(),
1802 delta = base.outerHeight(true) - h,
1803 // hide MCE dialog and modal block
1804 hideMceDlg = function () {
1805 var mceW;
1806 if (tinymce.activeEditor.windowManager.windows) {
1807 mceW = tinymce.activeEditor.windowManager.windows[0];
1808 mceDlg = $(mceW ? mceW.getEl() : void (0)).hide();
1809 mceCv = $('#mce-modal-block').hide();
1810 } else {
1811 mceDlg = $('.tox-dialog-wrap').hide();
1812 }
1813 },
1814 // Show MCE dialog and modal block
1815 showMceDlg = function () {
1816 mceCv && mceCv.show();
1817 mceDlg && mceDlg.show();
1818 },
1819 tVer = tinymce.majorVersion,
1820 opts, mceDlg, mceCv;
1821
1822 // set base height
1823 base.height(h);
1824 // fit height function
1825 textarea._setHeight = function (height) {
1826 if (tVer < 5) {
1827 var base = $(this).parent(),
1828 h = height || base.innerHeight(),
1829 ctrH = 0,
1830 areaH;
1831 base.find('.mce-container-body:first').children('.mce-top-part,.mce-statusbar').each(function () {
1832 ctrH += $(this).outerHeight(true);
1833 });
1834 areaH = h - ctrH - delta;
1835 base.find('.mce-edit-area iframe:first').height(areaH);
1836 }
1837 };
1838
1839 // TinyMCE configure options
1840 opts = {
1841 selector: '#' + textarea.id,
1842 resize: false,
1843 plugins: 'print preview fullpage searchreplace autolink directionality visualblocks visualchars fullscreen image link media template codesample table charmap hr pagebreak nonbreaking anchor toc insertdatetime advlist lists wordcount imagetools textpattern help',
1844 toolbar: 'formatselect | bold italic strikethrough forecolor backcolor | link image media | alignleft aligncenter alignright alignjustify | numlist bullist outdent indent | removeformat',
1845 image_advtab: true,
1846 init_instance_callback: function (editor) {
1847 // fit height on init
1848 textarea._setHeight(h);
1849 // re-build on dom move
1850 dlg.one('beforedommove.' + fm.namespace, function () {
1851 tinymce.execCommand('mceRemoveEditor', false, textarea.id);
1852 }).one('dommove.' + fm.namespace, function () {
1853 self.load(textarea).done(function (editor) {
1854 self.instance = editor;
1855 });
1856 });
1857 // return editor instance
1858 dfrd.resolve(editor);
1859 },
1860 file_picker_callback: function (callback, value, meta) {
1861 var gf = fm.getCommand('getfile'),
1862 revar = function () {
1863 if (prevVars.hasVar) {
1864 gf.callback = prevVars.callback;
1865 gf.options.folders = prevVars.folders;
1866 gf.options.multiple = prevVars.multi;
1867 fm.commandMap.open = prevVars.open;
1868 prevVars.hasVar = false;
1869 }
1870 dlg.off('resize close', revar);
1871 showMceDlg();
1872 },
1873 prevVars = {};
1874 prevVars.callback = gf.callback;
1875 prevVars.folders = gf.options.folders;
1876 prevVars.multi = gf.options.multiple;
1877 prevVars.open = fm.commandMap.open;
1878 prevVars.hasVar = true;
1879 gf.callback = function (file) {
1880 var url, info;
1881
1882 if (file.mime === 'directory') {
1883 fm.one('open', function () {
1884 fm.commandMap.open = 'getfile';
1885 }).getCommand('open').exec(file.hash);
1886 return;
1887 }
1888
1889 // URL normalization
1890 url = fm.convAbsUrl(file.url);
1891
1892 // Make file info
1893 info = file.name + ' (' + fm.formatSize(file.size) + ')';
1894
1895 // Provide file and text for the link dialog
1896 if (meta.filetype == 'file') {
1897 callback(url, { text: info, title: info });
1898 }
1899
1900 // Provide image and alt text for the image dialog
1901 if (meta.filetype == 'image') {
1902 callback(url, { alt: info });
1903 }
1904
1905 // Provide alternative source and posted for the media dialog
1906 if (meta.filetype == 'media') {
1907 callback(url);
1908 }
1909 dlg.trigger('togleminimize');
1910 };
1911 gf.options.folders = true;
1912 gf.options.multiple = false;
1913 fm.commandMap.open = 'getfile';
1914
1915 hideMceDlg();
1916 dlg.trigger('togleminimize').one('resize close', revar);
1917 fm.toast({
1918 mode: 'info',
1919 msg: fm.i18n('dblclickToSelect')
1920 });
1921
1922 return false;
1923 },
1924 images_upload_handler: function (blobInfo, success, failure) {
1925 var file = blobInfo.blob(),
1926 err = function (e) {
1927 var dlg = e.data.dialog || {};
1928 if (dlg.hasClass('elfinder-dialog-error') || dlg.hasClass('elfinder-confirm-upload')) {
1929 hideMceDlg();
1930 dlg.trigger('togleminimize').one('resize close', revert);
1931 fm.unbind('dialogopened', err);
1932 }
1933 },
1934 revert = function () {
1935 dlg.off('resize close', revert);
1936 showMceDlg();
1937 },
1938 clipdata = true;
1939
1940 // check file object
1941 if (file.name) {
1942 // file blob of client side file object
1943 clipdata = void (0);
1944 }
1945 fm.bind('dialogopened', err).exec('upload', Object.assign({
1946 files: [file],
1947 clipdata: clipdata // to get unique name on connector
1948 }, self.confObj.uploadOpts), void (0), fm.cwd().hash).done(function (data) {
1949 if (data.added && data.added.length) {
1950 fm.url(data.added[0].hash, { async: true }).done(function (url) {
1951 showMceDlg();
1952 success(fm.convAbsUrl(url));
1953 }).fail(function () {
1954 failure(fm.i18n('errFileNotFound'));
1955 });
1956 } else {
1957 failure(fm.i18n(data.error ? data.error : 'errUpload'));
1958 }
1959 }).fail(function (err) {
1960 var error = fm.parseError(err);
1961 if (error) {
1962 if (error === 'errUnknownCmd') {
1963 error = 'errPerm';
1964 } else if (error === 'userabort') {
1965 error = 'errAbort';
1966 }
1967 }
1968 failure(fm.i18n(error ? error : 'errUploadNoFiles'));
1969 });
1970 }
1971 };
1972
1973 // TinyMCE 5 supports "height: 100%"
1974 if (tVer >= 5) {
1975 opts.height = '100%';
1976 }
1977
1978 // trigger event 'editEditorPrepare'
1979 self.trigger('Prepare', {
1980 node: textarea,
1981 editorObj: tinymce,
1982 instance: void (0),
1983 opts: opts
1984 });
1985
1986 // TinyMCE configure
1987 tinymce.init(Object.assign(opts, self.confObj.mceOpts));
1988 };
1989
1990 if (!self.confObj.loader) {
1991 self.confObj.loader = $.Deferred();
1992 self.fm.loadScript([fm.options.cdns.tinymce + (fm.options.cdns.tinymce.match(/\.js/) ? '' : '/tinymce.min.js')], function () {
1993 self.confObj.loader.resolve();
1994 }, {
1995 loadType: 'tag'
1996 });
1997 }
1998 self.confObj.loader.done(init);
1999 return dfrd;
2000 },
2001 close: function (textarea, instance) {
2002 instance && tinymce.execCommand('mceRemoveEditor', false, textarea.id);
2003 },
2004 save: function (textarea, instance) {
2005 instance && instance.save();
2006 },
2007 focus: function (textarea, instance) {
2008 instance && instance.focus();
2009 },
2010 resize: function (textarea, instance, e, data) {
2011 // fit height to base node on dialog resize
2012 instance && textarea._setHeight();
2013 }
2014 },
2015 {
2016 info: {
2017 id: 'zohoeditor',
2018 name: 'Zoho Editor',
2019 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -32',
2020 cmdCheck: 'ZohoOffice',
2021 preventGet: true,
2022 hideButtons: true,
2023 syncInterval: 15000,
2024 canMakeEmpty: true,
2025 integrate: {
2026 title: 'Zoho Office API',
2027 link: 'https://www.zoho.com/officeapi/'
2028 }
2029 },
2030 mimes: [
2031 'application/msword',
2032 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
2033 //'application/pdf',
2034 'application/vnd.oasis.opendocument.text',
2035 'application/rtf',
2036 'text/html',
2037 'application/vnd.ms-excel',
2038 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
2039 'application/vnd.oasis.opendocument.spreadsheet',
2040 'application/vnd.sun.xml.calc',
2041 'text/csv',
2042 'text/tab-separated-values',
2043 'application/vnd.ms-powerpoint',
2044 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
2045 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
2046 'application/vnd.oasis.opendocument.presentation',
2047 'application/vnd.sun.xml.impress'
2048 ],
2049 html: '<iframe style="width:100%;max-height:100%;border:none;"></iframe>',
2050 // setup on elFinder bootup
2051 setup: function (opts, fm) {
2052 if (fm.UA.Mobile || fm.UA.ltIE8) {
2053 this.disabled = true;
2054 }
2055 },
2056 // Prepare on before show dialog
2057 prepare: function (base, dialogOpts, file) {
2058 var elfNode = base.editor.fm.getUI();
2059 $(base).height(elfNode.height());
2060 dialogOpts.width = Math.max(dialogOpts.width || 0, elfNode.width() * 0.8);
2061 },
2062 // Initialization of editing node (this: this editors HTML node)
2063 init: function (id, file, dum, fm) {
2064 var ta = this,
2065 ifm = $(this).hide(),
2066 uiToast = fm.getUI('toast'),
2067 spnr = $('<div class="elfinder-edit-spinner elfinder-edit-zohoeditor"/>')
2068 .html('<span class="elfinder-spinner-text">' + fm.i18n('nowLoading') + '</span><span class="elfinder-spinner"/>')
2069 .appendTo(ifm.parent()),
2070 cdata = function () {
2071 var data = '';
2072 $.each(fm.customData, function (key, val) {
2073 data += '&' + encodeURIComponent(key) + '=' + encodeURIComponent(val);
2074 });
2075 return data;
2076 };
2077
2078 $(ta).data('xhr', fm.request({
2079 data: {
2080 cmd: 'editor',
2081 name: ta.editor.confObj.info.cmdCheck,
2082 method: 'init',
2083 'args[target]': file.hash,
2084 'args[lang]': fm.lang,
2085 'args[cdata]': cdata()
2086 },
2087 preventDefault: true
2088 }).done(function (data) {
2089 var opts;
2090 if (data.zohourl) {
2091 opts = {
2092 css: {
2093 height: '100%'
2094 }
2095 };
2096 // trigger event 'editEditorPrepare'
2097 ta.editor.trigger('Prepare', {
2098 node: ta,
2099 editorObj: void (0),
2100 instance: ifm,
2101 opts: opts
2102 });
2103
2104 ifm.attr('src', data.zohourl).show().css(opts.css);
2105 if (data.warning) {
2106 uiToast.appendTo(ta.closest('.ui-dialog'));
2107 fm.toast({
2108 msg: fm.i18n(data.warning),
2109 mode: 'warning',
2110 timeOut: 0,
2111 onHidden: function () {
2112 uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
2113 },
2114 button: {
2115 text: 'btnYes'
2116 }
2117 });
2118 }
2119 } else {
2120 data.error && fm.error(data.error);
2121 ta.elfinderdialog('destroy');
2122 }
2123 }).fail(function (error) {
2124 error && fm.error(error);
2125 ta.elfinderdialog('destroy');
2126 }).always(function () {
2127 spnr.remove();
2128 }));
2129 },
2130 load: function () { },
2131 getContent: function () { },
2132 save: function () { },
2133 // Before dialog close
2134 beforeclose: iframeClose,
2135 // On dialog closed
2136 close: function (ta) {
2137 var fm = this.fm,
2138 xhr = $(ta).data('xhr');
2139 if (xhr.state() === 'pending') {
2140 xhr.reject();
2141 }
2142 }
2143 },
2144 {
2145 // Zip Archive with FlySystem
2146 info: {
2147 id: 'ziparchive',
2148 name: 'btnMount',
2149 iconImg: fm_menu_icon_base_url + 'img/toolbar.png 0 -416',
2150 cmdCheck: 'ZipArchive',
2151 edit: function (file, editor) {
2152 var fm = this,
2153 dfrd = $.Deferred();
2154 fm.request({
2155 data: {
2156 cmd: 'netmount',
2157 protocol: 'ziparchive',
2158 host: file.hash,
2159 path: file.phash
2160 },
2161 preventFail: true,
2162 notify: { type: 'netmount', cnt: 1, hideCnt: true }
2163 }).done(function (data) {
2164 var pdir;
2165 if (data.added && data.added.length) {
2166 if (data.added[0].phash) {
2167 if (pdir = fm.file(data.added[0].phash)) {
2168 if (!pdir.dirs) {
2169 pdir.dirs = 1;
2170 fm.change({ changed: [pdir] });
2171 }
2172 }
2173 }
2174 fm.one('netmountdone', function () {
2175 fm.exec('open', data.added[0].hash);
2176 fm.one('opendone', function () {
2177 data.toast && fm.toast(data.toast);
2178 });
2179 });
2180 }
2181 dfrd.resolve();
2182 })
2183 .fail(function (error) {
2184 dfrd.reject(error);
2185 });
2186 return dfrd;
2187 }
2188 },
2189 mimes: ['application/zip'],
2190 load: function () { },
2191 save: function () { }
2192 },
2193 {
2194 // Simple Text (basic textarea editor)
2195 info: {
2196 id: 'textarea',
2197 name: 'TextArea',
2198 useTextAreaEvent: true
2199 },
2200 load: function (textarea) {
2201 // trigger event 'editEditorPrepare'
2202 this.trigger('Prepare', {
2203 node: textarea,
2204 editorObj: void (0),
2205 instance: void (0),
2206 opts: {}
2207 });
2208 textarea.setSelectionRange && textarea.setSelectionRange(0, 0);
2209 $(textarea).trigger('focus').show();
2210 },
2211 save: function () { }
2212 },
2213 {
2214 // File converter with online-convert.com
2215 info: {
2216 id: 'onlineconvert',
2217 name: 'Online Convert',
2218 iconImg: fm_menu_icon_base_url + 'img/editor-icons.png 0 -144',
2219 cmdCheck: 'OnlineConvert',
2220 preventGet: true,
2221 hideButtons: true,
2222 single: true,
2223 converter: true,
2224 canMakeEmpty: false,
2225 integrate: {
2226 title: 'ONLINE-CONVERT.COM',
2227 link: 'https://online-convert.com'
2228 }
2229 },
2230 mimes: ['*'],
2231 html: '<div style="width:100%;max-height:100%;"></div>',
2232 // setup on elFinder bootup
2233 setup: function (opts, fm) {
2234 var mOpts = opts.extraOptions.onlineConvert || { maxSize: 100, showLink: true };
2235 if (mOpts.maxSize) {
2236 this.info.maxSize = mOpts.maxSize * 1048576;
2237 }
2238 this.set = Object.assign({
2239 url: 'https://%s.online-convert.com%s?external_url=',
2240 conv: {
2241 Archive: { '7Z': {}, 'BZ2': { ext: 'bz' }, 'GZ': {}, 'ZIP': {} },
2242 Audio: { 'MP3': {}, 'OGG': { ext: 'oga' }, 'WAV': {}, 'WMA': {}, 'AAC': {}, 'AIFF': { ext: 'aif' }, 'FLAC': {}, 'M4A': {}, 'MMF': {}, 'OPUS': { ext: 'oga' } },
2243 Document: { 'DOC': {}, 'DOCX': {}, 'HTML': {}, 'ODT': {}, 'PDF': {}, 'PPT': {}, 'PPTX': {}, 'RTF': {}, 'SWF': {}, 'TXT': {} },
2244 eBook: { 'AZW3': { ext: 'azw' }, 'ePub': {}, 'FB2': { ext: 'xml' }, 'LIT': {}, 'LRF': {}, 'MOBI': {}, 'PDB': {}, 'PDF': {}, 'PDF-eBook': { ext: 'pdf' }, 'TCR': {} },
2245 Hash: { 'Adler32': {}, 'Apache-htpasswd': {}, 'Blowfish': {}, 'CRC32': {}, 'CRC32B': {}, 'Gost': {}, 'Haval128': {}, 'MD4': {}, 'MD5': {}, 'RIPEMD128': {}, 'RIPEMD160': {}, 'SHA1': {}, 'SHA256': {}, 'SHA384': {}, 'SHA512': {}, 'Snefru': {}, 'Std-DES': {}, 'Tiger128': {}, 'Tiger128-calculator': {}, 'Tiger128-converter': {}, 'Tiger160': {}, 'Tiger192': {}, 'Whirlpool': {} },
2246 Image: { 'BMP': {}, 'EPS': { ext: 'ai' }, 'GIF': {}, 'EXR': {}, 'ICO': {}, 'JPG': {}, 'PNG': {}, 'SVG': {}, 'TGA': {}, 'TIFF': { ext: 'tif' }, 'WBMP': {}, 'WebP': {} },
2247 Video: { '3G2': {}, '3GP': {}, 'AVI': {}, 'FLV': {}, 'HLS': { ext: 'm3u8' }, 'MKV': {}, 'MOV': {}, 'MP4': {}, 'MPEG-1': { ext: 'mpeg' }, 'MPEG-2': { ext: 'mpeg' }, 'OGG': { ext: 'ogv' }, 'OGV': {}, 'WebM': {}, 'WMV': {}, 'Android': { link: '/convert-video-for-%s', ext: 'mp4' }, 'Blackberry': { link: '/convert-video-for-%s', ext: 'mp4' }, 'DPG': { link: '/convert-video-for-%s', ext: 'avi' }, 'iPad': { link: '/convert-video-for-%s', ext: 'mp4' }, 'iPhone': { link: '/convert-video-for-%s', ext: 'mp4' }, 'iPod': { link: '/convert-video-for-%s', ext: 'mp4' }, 'Nintendo-3DS': { link: '/convert-video-for-%s', ext: 'avi' }, 'Nintendo-DS': { link: '/convert-video-for-%s', ext: 'avi' }, 'PS3': { link: '/convert-video-for-%s', ext: 'mp4' }, 'Wii': { link: '/convert-video-for-%s', ext: 'avi' }, 'Xbox': { link: '/convert-video-for-%s', ext: 'wmv' } }
2248 },
2249 catExts: {
2250 Hash: 'txt'
2251 },
2252 link: '<div class="elfinder-edit-onlineconvert-link"><a href="https://www.online-convert.com" target="_blank"><span class="elfinder-button-icon"></span>ONLINE-CONVERT.COM</a></div>',
2253 useTabs: ($.fn.tabs && !fm.UA.iOS) ? true : false // Can't work on iOS, I don't know why.
2254 }, mOpts);
2255 },
2256 // Prepare on before show dialog
2257 prepare: function (base, dialogOpts, file) {
2258 var elfNode = base.editor.fm.getUI();
2259 $(base).height(elfNode.height());
2260 dialogOpts.width = Math.max(dialogOpts.width || 0, elfNode.width() * 0.8);
2261 },
2262 // Initialization of editing node (this: this editors HTML node)
2263 init: function (id, file, dum, fm) {
2264 var ta = this,
2265 confObj = ta.editor.confObj,
2266 set = confObj.set,
2267 uiToast = fm.getUI('toast'),
2268 idxs = {},
2269 allowZip = fm.uploadMimeCheck('application/zip', file.phash),
2270 selfUrl = $('base').length ? document.location.href.replace(/#.*$/, '') : '',
2271 getExt = function (cat, con) {
2272 var c;
2273 if (set.catExts[cat]) {
2274 return set.catExts[cat];
2275 }
2276 if (set.conv[cat] && (c = set.conv[cat][con])) {
2277 return (c.ext || con).toLowerCase();
2278 }
2279 return con.toLowerCase();
2280 },
2281 setOptions = function (cat, done) {
2282 var type, dfdInit, dfd;
2283 if (typeof confObj.api === 'undefined') {
2284 dfdInit = fm.request({
2285 data: {
2286 cmd: 'editor',
2287 name: 'OnlineConvert',
2288 method: 'init'
2289 },
2290 preventDefault: true
2291 });
2292 } else {
2293 dfdInit = $.Deferred().resolve({ api: confObj.api });
2294 }
2295 cat = cat.toLowerCase();
2296 dfdInit.done(function (data) {
2297 confObj.api = data.api;
2298 if (confObj.api) {
2299 if (cat) {
2300 type = '?category=' + cat;
2301 } else {
2302 type = '';
2303 cat = 'all';
2304 }
2305 if (!confObj.conversions) {
2306 confObj.conversions = {};
2307 }
2308 if (!confObj.conversions[cat]) {
2309 dfd = $.getJSON('https://api2.online-convert.com/conversions' + type);
2310 } else {
2311 dfd = $.Deferred().resolve(confObj.conversions[cat]);
2312 }
2313 dfd.done(function (d) {
2314 confObj.conversions[cat] = d;
2315 $.each(d, function (i, o) {
2316 btns[set.useTabs ? 'children' : 'find']('.onlineconvert-category-' + o.category).children('.onlineconvert-' + o.target).trigger('makeoption', o);
2317 });
2318 done && done();
2319 });
2320 }
2321 });
2322 },
2323 btns = (function () {
2324 var btns = $('<div/>').on('click', 'button', function () {
2325 var b = $(this),
2326 opts = b.data('opts') || null,
2327 cat = b.closest('.onlineconvert-category').data('cname'),
2328 con = b.data('conv');
2329 if (confObj.api === true) {
2330 api({
2331 category: cat,
2332 convert: con,
2333 options: opts
2334 });
2335 }
2336 }).on('change', function (e) {
2337 var t = $(e.target),
2338 p = t.parent(),
2339 b = t.closest('.elfinder-edit-onlineconvert-button').children('button:first'),
2340 o = b.data('opts') || {},
2341 v = p.data('type') === 'boolean' ? t.is(':checked') : t.val();
2342 e.stopPropagation();
2343 if (v) {
2344 if (p.data('type') === 'integer') {
2345 v = parseInt(v);
2346 }
2347 if (p.data('pattern')) {
2348 var reg = new RegExp(p.data('pattern'));
2349 if (!reg.test(v)) {
2350 requestAnimationFrame(function () {
2351 fm.error('"' + fm.escape(v) + '" is not match to "/' + fm.escape(p.data('pattern')) + '/"');
2352 });
2353 v = null;
2354 }
2355 }
2356 }
2357 if (v) {
2358 o[t.parent().data('optkey')] = v;
2359 } else {
2360 delete o[p.data('optkey')];
2361 }
2362 b.data('opts', o);
2363 }),
2364 ul = $('<ul/>'),
2365 oform = function (n, o) {
2366 var f = $('<p/>').data('optkey', n).data('type', o.type),
2367 checked = '',
2368 disabled = '',
2369 nozip = false,
2370 opts, btn, elm;
2371 if (o.description) {
2372 f.attr('title', fm.i18n(o.description));
2373 }
2374 if (o.pattern) {
2375 f.data('pattern', o.pattern);
2376 }
2377 f.append($('<span/>').text(fm.i18n(n) + ' : '));
2378 if (o.type === 'boolean') {
2379 if (o['default'] || (nozip = (n === 'allow_multiple_outputs' && !allowZip))) {
2380 checked = ' checked';
2381 if (nozip) {
2382 disabled = ' disabled';
2383 }
2384 btn = this.children('button:first');
2385 opts = btn.data('opts') || {};
2386 opts[n] = true;
2387 btn.data('opts', opts);
2388 }
2389 f.append($('<input type="checkbox" value="true"' + checked + disabled + '/>'));
2390 } else if (o['enum']) {
2391 elm = $('<select/>').append($('<option value=""/>').text('Select...'));
2392 $.each(o['enum'], function (i, v) {
2393 elm.append($('<option value="' + v + '"/>').text(v));
2394 });
2395 f.append(elm);
2396 } else {
2397 f.append($('<input type="text" value=""/>'));
2398 }
2399 return f;
2400 },
2401 makeOption = function (o) {
2402 var elm = this,
2403 b = $('<span class="elfinder-button-icon elfinder-button-icon-preference"/>').on('click', function () {
2404 f.toggle();
2405 }),
2406 f = $('<div class="elfinder-edit-onlinconvert-options"/>').hide();
2407 if (o.options) {
2408 $.each(o.options, function (k, v) {
2409 k !== 'download_password' && f.append(oform.call(elm, k, v));
2410 });
2411 }
2412 elm.append(b, f);
2413 },
2414 ts = (+new Date()),
2415 i = 0;
2416
2417 if (!confObj.ext2mime) {
2418 confObj.ext2mime = Object.assign(fm.arrayFlip(fm.mimeTypes), ext2mime);
2419 }
2420 $.each(set.conv, function (t, c) {
2421 var cname = t.toLowerCase(),
2422 id = 'elfinder-edit-onlineconvert-' + cname + ts,
2423 type = $('<div id="' + id + '" class="onlineconvert-category onlineconvert-category-' + cname + '"/>').data('cname', t),
2424 cext;
2425 $.each(c, function (n, o) {
2426 var nl = n.toLowerCase(),
2427 ext = getExt(t, n);
2428 if (!confObj.ext2mime[ext]) {
2429 if (cname === 'audio' || cname === 'image' || cname === 'video') {
2430 confObj.ext2mime[ext] = cname + '/x-' + nl;
2431 } else {
2432 confObj.ext2mime[ext] = 'application/octet-stream';
2433 }
2434 }
2435 if (fm.uploadMimeCheck(confObj.ext2mime[ext], file.phash)) {
2436 type.append($('<div class="elfinder-edit-onlineconvert-button onlineconvert-' + nl + '"/>').on('makeoption', function (e, data) {
2437 var elm = $(this);
2438 if (!elm.children('.elfinder-button-icon-preference').length) {
2439 makeOption.call(elm, data);
2440 }
2441 }).append($('<button/>').text(n).data('conv', n)));
2442 }
2443 });
2444 if (type.children().length) {
2445 ul.append($('<li/>').append($('<a/>').attr('href', selfUrl + '#' + id).text(t)));
2446 btns.append(type);
2447 idxs[cname] = i++;
2448 }
2449 });
2450 if (set.useTabs) {
2451 btns.prepend(ul).tabs({
2452 beforeActivate: function (e, ui) {
2453 setOptions(ui.newPanel.data('cname'));
2454 }
2455 });
2456 } else {
2457 $.each(set.conv, function (t) {
2458 var tl = t.toLowerCase();
2459 btns.append($('<fieldset class="onlineconvert-fieldset-' + tl + '"/>').append($('<legend/>').text(t)).append(btns.children('.onlineconvert-category-' + tl)));
2460 });
2461 }
2462 return btns;
2463 })(),
2464 select = $(this)
2465 .append(
2466 btns,
2467 (set.showLink ? $(set.link) : null)
2468 ),
2469 spnr = $('<div class="elfinder-edit-spinner elfinder-edit-onlineconvert"/>')
2470 .hide()
2471 .html('<span class="elfinder-spinner-text">' + fm.i18n('nowLoading') + '</span><span class="elfinder-spinner"/>')
2472 .appendTo(select.parent()),
2473 prog = $('<div class="elfinder-quicklook-info-progress"/>').appendTo(spnr),
2474 _url = null,
2475 url = function () {
2476 var onetime;
2477 if (_url) {
2478 return $.Deferred().resolve(_url);
2479 } else {
2480 spnr.show();
2481 return fm.forExternalUrl(file.hash, { progressBar: prog }).done(function (url) {
2482 _url = url;
2483 }).fail(function (error) {
2484 error && fm.error(error);
2485 ta.elfinderdialog('destroy');
2486 }).always(function () {
2487 spnr.hide();
2488 });
2489 }
2490 },
2491 api = function (opts) {
2492 $(ta).data('dfrd', url().done(function (url) {
2493 select.fadeOut();
2494 setStatus({ info: 'Start conversion request.' });
2495 fm.request({
2496 data: {
2497 cmd: 'editor',
2498 name: 'OnlineConvert',
2499 method: 'api',
2500 'args[category]': opts.category.toLowerCase(),
2501 'args[convert]': opts.convert.toLowerCase(),
2502 'args[options]': JSON.stringify(opts.options),
2503 'args[source]': fm.convAbsUrl(url),
2504 'args[filename]': fm.splitFileExtention(file.name)[0] + '.' + getExt(opts.category, opts.convert),
2505 'args[mime]': file.mime
2506 },
2507 preventDefault: true
2508 }).done(function (data) {
2509 checkRes(data.apires, opts.category, opts.convert);
2510 }).fail(function (error) {
2511 error && fm.error(error);
2512 ta.elfinderdialog('destroy');
2513 });
2514 }));
2515 },
2516 checkRes = function (res, cat, con) {
2517 var status, err = [];
2518 if (res && res.id) {
2519 status = res.status;
2520 if (status.code === 'failed') {
2521 spnr.hide();
2522 if (res.errors && res.errors.length) {
2523 $.each(res.errors, function (i, o) {
2524 o.message && err.push(o.message);
2525 });
2526 }
2527 fm.error(err.length ? err : status.info);
2528 select.fadeIn();
2529 } else if (status.code === 'completed') {
2530 upload(res);
2531 } else {
2532 setStatus(status);
2533 setTimeout(function () {
2534 polling(res.id);
2535 }, 1000);
2536 }
2537 } else {
2538 uiToast.appendTo(ta.closest('.ui-dialog'));
2539 if (res.message) {
2540 fm.toast({
2541 msg: fm.i18n(res.message),
2542 mode: 'error',
2543 timeOut: 5000,
2544 onHidden: function () {
2545 uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
2546 }
2547 });
2548 }
2549 fm.toast({
2550 msg: fm.i18n('editorConvNoApi'),
2551 mode: 'error',
2552 timeOut: 3000,
2553 onHidden: function () {
2554 uiToast.children().length === 1 && uiToast.appendTo(fm.getUI());
2555 }
2556 });
2557 spnr.hide();
2558 select.show();
2559 }
2560 },
2561 setStatus = function (status) {
2562 spnr.show().children('.elfinder-spinner-text').text(status.info);
2563 },
2564 polling = function (jobid) {
2565 fm.request({
2566 data: {
2567 cmd: 'editor',
2568 name: 'OnlineConvert',
2569 method: 'api',
2570 'args[jobid]': jobid
2571 },
2572 preventDefault: true
2573 }).done(function (data) {
2574 checkRes(data.apires);
2575 }).fail(function (error) {
2576 error && fm.error(error);
2577 ta.elfinderdialog('destroy');
2578 });
2579 },
2580 upload = function (res) {
2581 var output = res.output,
2582 id = res.id,
2583 url = '';
2584 spnr.hide();
2585 if (output && output.length) {
2586 ta.elfinderdialog('destroy');
2587 $.each(output, function (i, o) {
2588 if (o.uri) {
2589 url += o.uri + '\n';
2590 }
2591 });
2592 fm.upload({
2593 target: file.phash,
2594 files: [url],
2595 type: 'text',
2596 extraData: {
2597 contentSaveId: 'OnlineConvert-' + res.id
2598 }
2599 });
2600 }
2601 },
2602 mode = 'document',
2603 cl, m;
2604 select.parent().css({ overflow: 'auto' }).addClass('overflow-scrolling-touch');
2605 if (m = file.mime.match(/^(audio|image|video)/)) {
2606 mode = m[1];
2607 }
2608 if (set.useTabs) {
2609 if (idxs[mode]) {
2610 btns.tabs('option', 'active', idxs[mode]);
2611 }
2612 } else {
2613 cl = Object.keys(set.conv).length;
2614 $.each(set.conv, function (t) {
2615 if (t.toLowerCase() === mode) {
2616 setOptions(t, function () {
2617 $.each(set.conv, function (t0) {
2618 t0.toLowerCase() !== mode && setOptions(t0);
2619 });
2620 });
2621 return false;
2622 }
2623 cl--;
2624 });
2625 if (!cl) {
2626 $.each(set.conv, function (t) {
2627 setOptions(t);
2628 });
2629 }
2630 select.parent().scrollTop(btns.children('.onlineconvert-fieldset-' + mode).offset().top);
2631 }
2632 },
2633 load: function () { },
2634 getContent: function () { },
2635 save: function () { },
2636 // On dialog closed
2637 close: function (ta) {
2638 var fm = this.fm,
2639 dfrd = $(ta).data('dfrd');
2640 if (dfrd && dfrd.state() === 'pending') {
2641 dfrd.reject();
2642 }
2643 }
2644 }
2645 ];
2646 }, window.elFinder));
2647 })(jQuery)
2648