editors.default.js 78 KB

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