modx.browser.js 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535
  1. Ext.namespace('MODx.browser');
  2. MODx.Browser = function(config) {
  3. if (MODx.browserOpen && !config.multiple) return false;
  4. if (!config.multiple) MODx.browserOpen = true;
  5. config = config || {};
  6. Ext.applyIf(config,{
  7. onSelect: function(data) {}
  8. ,scope: this
  9. ,source: config.source || 1
  10. ,cls: 'modx-browser'
  11. ,closeAction: 'hide'
  12. });
  13. MODx.Browser.superclass.constructor.call(this,config);
  14. this.config = config;
  15. this.win = new MODx.browser.Window(config);
  16. this.win.reset();
  17. };
  18. Ext.extend(MODx.Browser,Ext.Component,{
  19. show: function(el) { if (this.win) { this.win.show(el); } }
  20. ,hide: function() { if (this.win) { this.win.hide(); } }
  21. ,setSource: function(source) {
  22. this.config.source = source;
  23. this.win.tree.config.baseParams.source = source;
  24. this.win.view.config.baseParams.source = source;
  25. }
  26. });
  27. Ext.reg('modx-browser',MODx.Browser);
  28. MODx.browser.View = function(config) {
  29. config = config || {};
  30. this.ident = config.ident+'-view' || 'modx-browser-'+Ext.id()+'-view';
  31. this._initTemplates();
  32. Ext.applyIf(config,{
  33. url: MODx.config.connector_url
  34. ,id: this.ident
  35. ,fields: [
  36. {name: 'name', sortType: Ext.data.SortTypes.asUCString}
  37. ,'cls','url','relativeUrl','fullRelativeUrl','image','image_width','image_height','thumb','thumb_width','thumb_height','pathname','pathRelative','ext','disabled','preview'
  38. ,{name: 'size', type: 'float'}
  39. ,{name: 'lastmod', type: 'date', dateFormat: 'timestamp'}
  40. ,'menu'
  41. ]
  42. ,baseParams: {
  43. action: 'browser/directory/getfiles'
  44. ,prependPath: config.prependPath || null
  45. ,prependUrl: config.prependUrl || null
  46. ,source: config.source || 1
  47. // @todo: this overrides the media source configuration
  48. ,allowedFileTypes: config.allowedFileTypes || ''
  49. ,wctx: config.wctx || 'web'
  50. ,dir: config.openTo || ''
  51. }
  52. ,tpl: MODx.config.modx_browser_default_viewmode === 'list' ? this.templates.list : this.templates.thumb
  53. ,itemSelector: MODx.config.modx_browser_default_viewmode === 'list' ? 'div.modx-browser-list-item' : 'div.modx-browser-thumb-wrap'
  54. ,thumbnails: []
  55. ,lazyLoad: function() {
  56. var height = this.getEl().parent().getHeight() + 100;
  57. for (var i = 0; i < this.thumbnails.length; i++) {
  58. var image = this.thumbnails[i];
  59. if (image !== undefined) {
  60. var rect = image.getBoundingClientRect();
  61. if (rect.top >= 0 && rect.left >= 0 && rect.top <= height) {
  62. image.src = image.getAttribute('data-src');
  63. delete(this.thumbnails[i]);
  64. }
  65. }
  66. }
  67. }
  68. ,refresh: function() {
  69. MODx.DataView.prototype.refresh.call(this);
  70. this.thumbnails = Array.prototype.slice.call(document.querySelectorAll('img[data-src]'));
  71. this.lazyLoad();
  72. }
  73. ,listeners: {
  74. 'selectionchange': {fn:this.showDetails, scope:this, buffer:100}
  75. ,'dblclick': config.onSelect || {fn:Ext.emptyFn,scope:this}
  76. ,'render': {fn:this.sortStore, scope:this}
  77. ,'afterrender': {
  78. fn: function() {
  79. this.getEl().parent().on('scroll', function() {
  80. this.lazyLoad();
  81. }, this);
  82. }, scope:this
  83. }
  84. }
  85. ,prepareData: this.formatData.createDelegate(this)
  86. });
  87. MODx.browser.View.superclass.constructor.call(this,config);
  88. };
  89. Ext.extend(MODx.browser.View,MODx.DataView,{
  90. templates: {}
  91. ,run: function(p) {
  92. p = p || {};
  93. if (p.dir) { this.dir = p.dir; }
  94. Ext.applyIf(p,{
  95. action: 'browser/directory/getFiles'
  96. ,dir: this.dir
  97. ,source: this.config.source || MODx.config.default_media_source
  98. });
  99. this.store.load({
  100. params: p
  101. ,callback: function() {
  102. this.refresh();
  103. // reset the bottom filepath bar
  104. Ext.getCmp(this.ident+'-filepath').setValue('');
  105. this.select(0);
  106. }
  107. ,scope: this
  108. });
  109. }
  110. ,editFile: function(item,e) {
  111. var node = this.cm.activeNode;
  112. var data = this.lookup[node.id];
  113. MODx.loadPage('system/file/edit', 'file='+data.pathRelative+'&source='+this.config.source);
  114. }
  115. ,quickUpdateFile: function(item,e) {
  116. var node = this.cm.activeNode;
  117. var data = this.lookup[node.id];
  118. MODx.Ajax.request({
  119. url: MODx.config.connector_url
  120. ,params: {
  121. action: 'browser/file/get'
  122. ,file: data.pathRelative
  123. ,wctx: MODx.ctx || ''
  124. ,source: this.config.source
  125. }
  126. ,listeners: {
  127. 'success': {fn:function(response) {
  128. var r = {
  129. file: data.pathRelative
  130. ,name: data.name
  131. ,path: data.pathRelative
  132. ,source: this.config.source
  133. ,content: response.object.content
  134. };
  135. var w = MODx.load({
  136. xtype: 'modx-window-file-quick-update'
  137. ,record: r
  138. ,listeners: {
  139. 'hide':{fn:function() {this.destroy();}}
  140. }
  141. });
  142. w.show(e.target);
  143. },scope:this}
  144. }
  145. });
  146. }
  147. ,renameFile: function(item,e) {
  148. var node = this.cm.activeNode;
  149. var data = this.lookup[node.id];
  150. var r = {
  151. old_name: data.name
  152. ,name: data.name
  153. ,path: data.pathRelative
  154. ,source: this.config.source
  155. };
  156. var w = MODx.load({
  157. xtype: 'modx-window-file-rename'
  158. ,record: r
  159. ,listeners: {
  160. 'success':{fn:function(r) {
  161. this.config.tree.refreshParentNode();
  162. this.run();
  163. },scope:this}
  164. ,'hide':{fn:function() {
  165. this.destroy();}
  166. }
  167. }
  168. });
  169. w.show(e.target);
  170. }
  171. ,downloadFile: function(item,e) {
  172. var node = this.cm.activeNode;
  173. var data = this.lookup[node.id];
  174. MODx.Ajax.request({
  175. url: MODx.config.connector_url
  176. ,params: {
  177. action: 'browser/file/download'
  178. ,file: data.pathRelative
  179. ,wctx: MODx.ctx || ''
  180. ,source: this.config.source
  181. }
  182. ,listeners: {
  183. 'success':{fn:function(r) {
  184. if (!Ext.isEmpty(r.object.url)) {
  185. location.href = MODx.config.connector_url+'?action=browser/file/download&download=1&file='+data.pathRelative+'&HTTP_MODAUTH='+MODx.siteId+'&source='+this.config.source+'&wctx='+MODx.ctx;
  186. }
  187. },scope:this}
  188. }
  189. });
  190. }
  191. ,copyRelativePath: function(item,e) {
  192. var node = this.cm.activeNode;
  193. var data = this.lookup[node.id];
  194. var dummyRelativePathInput = document.createElement("input");
  195. document.body.appendChild(dummyRelativePathInput);
  196. dummyRelativePathInput.setAttribute('value', data.pathRelative);
  197. dummyRelativePathInput.select();
  198. document.execCommand("copy");
  199. document.body.removeChild(dummyRelativePathInput);
  200. }
  201. ,removeFile: function(item,e) {
  202. var node = this.cm.activeNode;
  203. var data = this.lookup[node.id];
  204. // var d = '';
  205. // if (typeof(this.dir) != 'object' && typeof(this.dir) != 'undefined') { d = this.dir; }
  206. MODx.msg.confirm({
  207. text: _('file_remove_confirm')
  208. ,url: MODx.config.connector_url
  209. ,params: {
  210. action: 'browser/file/remove'
  211. ,file: data.pathRelative
  212. ,source: this.config.source
  213. ,wctx: this.config.wctx || 'web'
  214. }
  215. ,listeners: {
  216. 'success': {fn:function(r) {
  217. this.config.tree.refreshParentNode();
  218. this.run();
  219. },scope:this}
  220. }
  221. });
  222. }
  223. ,setTemplate: function(tpl) {
  224. if (tpl === 'list') {
  225. this.tpl = this.templates.list;
  226. this.itemSelector = 'div.modx-browser-list-item';
  227. } else {
  228. this.tpl = this.templates.thumb;
  229. this.itemSelector = 'div.modx-browser-thumb-wrap';
  230. }
  231. this.refresh();
  232. this.select(0);
  233. }
  234. ,sortStore: function() {
  235. var v = MODx.config.modx_browser_default_sort || 'name'
  236. this.store.sort(v, v == 'name' ? 'ASC' : 'DESC');
  237. this.select(0);
  238. }
  239. ,showDetails: function() {
  240. var node = this.getSelectedNodes();
  241. var detailPanel = Ext.getCmp(this.config.ident+'-img-detail-panel').body;
  242. var okBtn = Ext.getCmp(this.ident+'-ok-btn');
  243. if (node && node.length > 0) {
  244. node = node[0];
  245. if (okBtn) {
  246. okBtn.enable();
  247. }
  248. var data = this.lookup[node.id];
  249. // sync the selected file in browser view and tree
  250. // we have to take care of the tree loosing sync after a file is deleted
  251. // and this.config.tree.getNodeById(data.pathRelative) being undefined
  252. if (this.config.tree.getNodeById(data.pathRelative)) {
  253. // this is necessary to prevent the whole tree from refreshing
  254. // e.g. like this we set the correct activeNode which is then used to determine the parent node
  255. this.config.tree.cm.activeNode = this.config.tree.getNodeById(data.pathRelative);
  256. // and this to have the visual syncing of selected items in browser view and tree
  257. this.config.tree.getSelectionModel().select(this.config.tree.getNodeById(data.pathRelative));
  258. }
  259. // keeps the bottom filepath bar in sync with the selected file
  260. Ext.getCmp(this.ident+'-filepath').setValue((data.fullRelativeUrl.indexOf('http') === -1 ? '/' : '')+data.fullRelativeUrl);
  261. detailPanel.hide();
  262. this.templates.details.overwrite(detailPanel, data);
  263. detailPanel.slideIn('l', {stopFx:true,duration:'.2'});
  264. } else {
  265. if (okBtn) {
  266. okBtn.disable();
  267. }
  268. detailPanel.update('');
  269. }
  270. }
  271. ,showFullView: function(name,ident) {
  272. var data = this.lookup[name];
  273. if (!data) return;
  274. if (!this.fvWin) {
  275. this.fvWin = new Ext.Window({
  276. layout:'fit'
  277. ,width: 600
  278. ,height: 450
  279. ,bodyStyle: 'padding: 0;'
  280. ,closeAction: 'hide'
  281. ,plain: true
  282. ,items: [{
  283. id: this.ident+'modx-view-item-full'
  284. ,cls: 'modx-browser-fullview'
  285. ,html: ''
  286. }]
  287. ,buttons: [{
  288. text: _('close')
  289. ,cls: 'primary-button'
  290. ,handler: function() { this.fvWin.hide(); }
  291. ,scope: this
  292. }]
  293. });
  294. }
  295. this.fvWin.show();
  296. var ratio = data.image_width > 800 ? 800/data.image_width : 1;
  297. var w = data.image_width < 250 ? 250 : (data.image_width > 800 ? 800 : data.image_width);
  298. var hfit = (data.image_height*ratio)+this.fvWin.footer.dom.clientHeight+1+this.fvWin.header.dom.clientHeight+1; // +1 for the borders
  299. var h = data.image_height < 200 ? 200 : (data.image_height > 600 ? (hfit > 600 ? 600 : hfit) : data.image_height);
  300. this.fvWin.setSize(w,h);
  301. this.fvWin.center();
  302. this.fvWin.setTitle(data.name);
  303. Ext.get(this.ident+'modx-view-item-full').update('<img src="'+data.image+'" width="'+data.image_width+'" height="'+data.image_height+'" alt="'+data.name+'" title="'+data.name+'" class="modx-browser-fullview-img" onclick="Ext.getCmp(\''+ident+'\').fvWin.hide();" />');
  304. }
  305. ,formatData: function(data) {
  306. var formatSize = function(size){
  307. if(size < 1024) {
  308. return size + " bytes";
  309. } else {
  310. return (Math.round(((size*10) / 1024))/10) + " KB";
  311. }
  312. };
  313. data.shortName = Ext.util.Format.ellipsis(data.name,18);
  314. data.sizeString = data.size != 0 ? formatSize(data.size) : 0;
  315. data.imageSizeString = data.preview != 0 ? data.image_width + "x" + data.image_height + "px": 0;
  316. data.imageSizeString = data.imageSizeString === "xpx" ? 0 : data.imageSizeString;
  317. data.dateString = !Ext.isEmpty(data.lastmod) ? new Date(data.lastmod).format(MODx.config.manager_date_format + " " + MODx.config.manager_time_format) : 0;
  318. this.lookup[data.name] = data;
  319. return data;
  320. }
  321. ,_initTemplates: function() {
  322. this.templates.thumb = new Ext.XTemplate(
  323. '<tpl for=".">'
  324. ,'<div class="modx-browser-thumb-wrap" id="{name}" title="{name}">'
  325. ,' <div class="modx-browser-thumb">'
  326. ,' <img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" ' +
  327. 'data-src="{thumb}" width="{thumb_width}" height="{thumb_height}" alt="{name}" title="{name}" />'
  328. ,' </div>'
  329. ,' <span>{shortName}</span>'
  330. ,'</div>'
  331. ,'</tpl>'
  332. );
  333. this.templates.thumb.compile();
  334. this.templates.list = new Ext.XTemplate(
  335. '<tpl for=".">'
  336. ,'<div class="modx-browser-list-item" id="{name}">'
  337. ,' <span class="icon icon-file {cls}">'
  338. ,' <span class="file-name">{name}</span>'
  339. ,' <tpl if="sizeString !== 0">'
  340. ,' <span class="file-size">{sizeString}</span>'
  341. ,' </tpl>'
  342. ,' <tpl if="imageSizeString !== 0">'
  343. ,' <span class="image-size">{imageSizeString}</span>'
  344. ,' </tpl>'
  345. ,' </span>'
  346. ,'</div>'
  347. ,'</tpl>'
  348. );
  349. this.templates.list.compile();
  350. this.templates.details = new Ext.XTemplate(
  351. '<div class="details">'
  352. ,' <tpl for=".">'
  353. ,' <tpl if="preview === 1">'
  354. ,' <div class="modx-browser-detail-thumb preview" onclick="Ext.getCmp(\''+this.ident+'\').showFullView(\'{name}\',\''+this.ident+'\'); return false;">'
  355. ,' <img src="{image}" width="{image_width}" height="{image_height}" alt="{name}" title="{name}" />'
  356. ,' </div>'
  357. ,' </tpl>'
  358. ,' <tpl if="preview === 0">'
  359. ,' <div class="modx-browser-detail-thumb">'
  360. ,' <img src="{image}" alt="" />'
  361. ,' </div>'
  362. ,' </tpl>'
  363. ,' <div class="modx-browser-details-info">'
  364. ,' <b>'+_('file_name')+':</b>'
  365. ,' <span>{name}</span>'
  366. ,' <tpl if="sizeString !== 0">'
  367. ,' <b>'+_('file_size')+':</b>'
  368. ,' <span>{sizeString}</span>'
  369. ,' </tpl>'
  370. ,' <tpl if="imageSizeString !== 0">'
  371. ,' <b>'+_('image_size')+':</b>'
  372. ,' <span>{imageSizeString}</span>'
  373. ,' </tpl>'
  374. ,' <tpl if="dateString !== 0">'
  375. ,' <b>'+_('last_modified')+':</b>'
  376. ,' <span>{dateString}</span>'
  377. ,' </tpl>'
  378. ,' </div>'
  379. ,' </tpl>'
  380. ,'</div>'
  381. );
  382. this.templates.details.compile();
  383. }
  384. });
  385. Ext.reg('modx-browser-view',MODx.browser.View);
  386. /**
  387. * This is the regular media browser window that opens when clicking on an image or file TV for example
  388. */
  389. MODx.browser.Window = function(config) {
  390. config = config || {};
  391. this.ident = Ext.id();
  392. // Hide the "MODX Browser" toolbar button
  393. MODx.browserOpen = true;
  394. // Tree navigation
  395. this.tree = MODx.load({
  396. xtype: 'modx-tree-directory'
  397. ,onUpload: function() {
  398. this.view.run();
  399. }
  400. ,scope: this
  401. ,source: config.source || MODx.config.default_media_source
  402. ,hideFiles: config.hideFiles || MODx.config.modx_browser_tree_hide_files
  403. ,hideTooltips: config.hideTooltips || MODx.config.modx_browser_tree_hide_tooltips || true // by default do not request image preview tooltips in the media browser
  404. ,openTo: config.openTo || ''
  405. ,ident: this.ident
  406. ,rootId: config.rootId || '/'
  407. ,rootName: _('files')
  408. ,rootVisible: config.rootVisible == undefined || !Ext.isEmpty(config.rootId)
  409. ,id: this.ident+'-tree'
  410. ,hideSourceCombo: config.hideSourceCombo || false
  411. ,useDefaultToolbar: false
  412. ,listeners: {
  413. 'afterUpload': {
  414. fn: function() {
  415. this.view.run();
  416. }
  417. ,scope: this
  418. }
  419. ,'afterQuickCreate': {
  420. fn: function() {
  421. this.view.run();
  422. }
  423. ,scope: this
  424. }
  425. ,'afterRename': {
  426. fn: function() {
  427. this.view.run();
  428. }
  429. ,scope: this
  430. }
  431. ,'afterRemove': {
  432. fn: function() {
  433. this.view.run();
  434. }
  435. ,scope: this
  436. }
  437. ,'changeSource': {
  438. fn: function(s) {
  439. this.config.source = s;
  440. this.view.config.source = s;
  441. this.view.baseParams.source = s;
  442. this.view.dir = '/';
  443. this.view.run();
  444. }
  445. ,scope: this
  446. }
  447. ,'afterrender': {
  448. fn: function(tree) {
  449. tree.root.expand();
  450. }
  451. ,scope: this
  452. }
  453. ,'beforeclick': {
  454. fn: function(node, e) {
  455. // load the node/folder that is clicked on but prevent unnecessary requests when a file is clicked
  456. if (!node.leaf) {
  457. this.load(node.id);
  458. } else {
  459. // sync the selected item in the tree with the one in browser view
  460. // the id of a browser view node in the store is the full absolute URL
  461. // but there is a bug with urlAbsolute, see #11821 that's why we prepend a slash
  462. this.view.select(this.view.store.indexOfId('/' + node.attributes.url));
  463. // but instead load the container the file resides in if not already displayed
  464. if (this.view.dir !== node.parentNode.id) {
  465. this.load(node.parentNode.id);
  466. }
  467. return false;
  468. }
  469. }
  470. ,scope: this
  471. }
  472. }
  473. });
  474. // DataView
  475. this.view = MODx.load({
  476. xtype: 'modx-browser-view'
  477. ,onSelect: {
  478. fn: this.onSelect
  479. ,scope: this
  480. }
  481. ,source: config.source || MODx.config.default_media_source
  482. ,allowedFileTypes: config.allowedFileTypes || ''
  483. ,wctx: config.wctx || 'web'
  484. ,openTo: config.openTo || ''
  485. ,ident: this.ident
  486. ,id: this.ident+'-view'
  487. ,tree: this.tree
  488. });
  489. Ext.applyIf(config,{
  490. title: _('modx_browser')+' ('+(MODx.ctx ? MODx.ctx : 'web')+')'
  491. ,cls: 'modx-browser modx-browser-window'
  492. ,layout: 'border'
  493. ,minWidth: 500
  494. ,minHeight: 300
  495. ,width: '90%'
  496. ,height: Ext.getBody().getViewSize().height * 0.9
  497. ,modal: false
  498. ,closeAction: 'hide'
  499. ,border: false
  500. ,items: [{
  501. id: this.ident+'-browser-tree'
  502. ,cls: 'modx-browser-tree'
  503. ,region: 'west'
  504. ,width: 250
  505. ,height: '100%'
  506. ,items: this.tree
  507. ,autoScroll: true
  508. ,split: true
  509. ,border: false
  510. },{
  511. id: this.ident+'-browser-view'
  512. ,cls: 'modx-browser-view-ct'
  513. ,region: 'center'
  514. ,autoScroll: true
  515. //,width: 635
  516. ,border: false
  517. ,items: this.view
  518. ,tbar: this.getToolbar()
  519. ,bbar: this.getPathbar()
  520. },{
  521. id: this.ident+'-img-detail-panel'
  522. ,cls: 'modx-browser-details-ct'
  523. ,region: 'east'
  524. ,split: true
  525. ,border: false
  526. ,width: 250
  527. }]
  528. ,buttons: [{
  529. id: this.ident+'-cancel-btn'
  530. ,text: _('cancel')
  531. ,handler: this.close
  532. ,scope: this
  533. },{
  534. id: this.ident+'-ok-btn'
  535. ,text: _('ok')
  536. ,cls: 'primary-button'
  537. ,handler: this.onSelect
  538. ,scope: this
  539. }]
  540. ,keys: {
  541. key: 27
  542. ,handler: this.hide
  543. ,scope: this
  544. }
  545. });
  546. MODx.browser.Window.superclass.constructor.call(this,config);
  547. this.config = config;
  548. this.addEvents({
  549. 'select': true
  550. });
  551. };
  552. Ext.extend(MODx.browser.Window,Ext.Window,{
  553. returnEl: null
  554. /**
  555. * Filter the DataView results
  556. */
  557. ,filter : function() {
  558. var filter = Ext.getCmp(this.ident+'filter');
  559. this.view.store.filter('name', filter.getValue(), true);
  560. this.view.select(0);
  561. }
  562. /**
  563. * Load the given directory in the DataView
  564. *
  565. * @param {String} dir
  566. */
  567. ,load: function(dir) {
  568. dir = dir || (Ext.isEmpty(this.config.openTo) ? '' : this.config.openTo);
  569. this.view.run({
  570. dir: dir
  571. ,source: this.config.source
  572. ,allowedFileTypes: this.config.allowedFileTypes || ''
  573. ,wctx: this.config.wctx || 'web'
  574. });
  575. this.sortStore();
  576. }
  577. /**
  578. * Sort the DataView results
  579. */
  580. ,sortStore: function(){
  581. var v = Ext.getCmp(this.ident+'sortSelect').getValue();
  582. this.view.store.sort(v, v == 'name' ? 'ASC' : 'DESC');
  583. this.view.select(0);
  584. }
  585. /**
  586. * Switch viewmode from grid to list and vice versa
  587. */
  588. ,changeViewmode: function() {
  589. var v = Ext.getCmp(this.ident+'viewSelect').getValue();
  590. this.view.setTemplate(v);
  591. this.view.select(0);
  592. }
  593. /**
  594. * Remove any filter applied to the DataView
  595. */
  596. ,reset: function() {
  597. if (this.rendered) {
  598. Ext.getCmp(this.ident+'filter').reset();
  599. this.view.getEl().dom.scrollTop = 0;
  600. }
  601. this.view.store.clearFilter();
  602. this.view.select(0);
  603. }
  604. /**
  605. * Get the browser view toolbar configuration
  606. *
  607. * @returns {Array}
  608. */
  609. ,getToolbar: function() {
  610. return [{
  611. text: _('filter')+':'
  612. ,xtype: 'label'
  613. },{
  614. xtype: 'textfield'
  615. ,id: this.ident+'filter'
  616. ,selectOnFocus: true
  617. ,width: 200
  618. ,listeners: {
  619. 'render': {
  620. fn: function() {
  621. Ext.getCmp(this.ident+'filter').getEl().on('keyup', function() {
  622. this.filter();
  623. }, this, {buffer: 500});
  624. }
  625. ,scope: this
  626. }
  627. }
  628. },{
  629. text: _('sort_by')+':'
  630. ,xtype: 'label'
  631. },{
  632. id: this.ident+'sortSelect'
  633. ,xtype: 'combo'
  634. ,typeAhead: true
  635. ,triggerAction: 'all'
  636. ,width: 130
  637. ,editable: false
  638. ,mode: 'local'
  639. ,displayField: 'desc'
  640. ,valueField: 'name'
  641. ,lazyInit: false
  642. ,value: MODx.config.modx_browser_default_sort || 'name'
  643. ,store: new Ext.data.SimpleStore({
  644. fields: ['name', 'desc'],
  645. data : [
  646. ['name', _('name')]
  647. ,['size', _('file_size')]
  648. ,['lastmod', _('last_modified')]
  649. ]
  650. })
  651. ,listeners: {
  652. 'select': {
  653. fn: this.sortStore
  654. ,scope: this
  655. }
  656. }
  657. }, '-', {
  658. text: _('files_viewmode')+':'
  659. ,xtype: 'label'
  660. }, '-', {
  661. id: this.ident+'viewSelect'
  662. ,xtype: 'combo'
  663. ,typeAhead: false
  664. ,triggerAction: 'all'
  665. ,width: 100
  666. ,editable: false
  667. ,mode: 'local'
  668. ,displayField: 'desc'
  669. ,valueField: 'type'
  670. ,lazyInit: false
  671. ,value: MODx.config.modx_browser_default_viewmode || 'grid'
  672. ,store: new Ext.data.SimpleStore({
  673. fields: ['type', 'desc'],
  674. data : [
  675. ['grid', _('files_viewmode_grid')]
  676. ,['list', _('files_viewmode_list')]
  677. ]
  678. })
  679. ,listeners: {
  680. 'select': {
  681. fn: this.changeViewmode
  682. ,scope: this
  683. }
  684. }
  685. }];
  686. }
  687. /**
  688. * Get the bottom filepath textfield in the browser view
  689. *
  690. * @returns {Array}
  691. */
  692. ,getPathbar: function() {
  693. return {
  694. cls: 'modx-browser-pathbbar'
  695. ,items: [{
  696. xtype: 'textfield'
  697. ,id: this.ident+'-filepath'
  698. ,cls: 'modx-browser-filepath'
  699. ,listeners: {
  700. 'focus': {
  701. // select the filepath on focus
  702. fn: function(el) {
  703. // let the focus event stick first, needed for webkit primarily
  704. setTimeout(function () {
  705. var field = el.getEl().dom;
  706. if (field.createTextRange) {
  707. var selRange = field.createTextRange();
  708. selRange.collapse(true);
  709. selRange.moveStart('character', 0);
  710. selRange.moveEnd('character', field.value.length);
  711. selRange.select();
  712. } else if (field.setSelectionRange) {
  713. field.setSelectionRange(0, field.value.length);
  714. } else if (field.selectionStart) {
  715. field.selectionStart = 0;
  716. field.selectionEnd = field.value.length;
  717. }
  718. }, 50);
  719. }
  720. ,scope: this
  721. }
  722. }
  723. }]
  724. };
  725. }
  726. ,setReturn: function(el) {
  727. this.returnEl = el;
  728. }
  729. ,onSelect: function(data) {
  730. var selNode = this.view.getSelectedNodes()[0];
  731. var callback = this.config.onSelect || this.onSelectHandler;
  732. var lookup = this.view.lookup;
  733. var scope = this.config.scope;
  734. this.hide(this.config.animEl || null,function(){
  735. if(selNode && callback){
  736. var data = lookup[selNode.id];
  737. Ext.callback(callback,scope || this,[data]);
  738. this.fireEvent('select',data);
  739. }
  740. },scope);
  741. }
  742. ,onSelectHandler: function(data) {
  743. Ext.get(this.returnEl).dom.value = unescape(data.url);
  744. }
  745. });
  746. Ext.reg('modx-browser-window',MODx.browser.Window);
  747. /**
  748. * This is an attempt to extract the MODx.Browser.Window as a whole "component/page" found under Media > Media Browser
  749. *
  750. * @param {Object} config
  751. *
  752. * @extends Ext.Container
  753. * @xtype modx-media-view
  754. */
  755. MODx.Media = function(config) {
  756. config = config || {};
  757. this.ident = config.ident || Ext.id();
  758. // Hide the "MODX Browser" toolbar button
  759. MODx.browserOpen = true;
  760. // Tree navigation
  761. this.tree = MODx.load({
  762. xtype: 'modx-tree-directory'
  763. ,onUpload: function() {
  764. this.view.run();
  765. }
  766. ,scope: this
  767. ,source: config.source || MODx.config.default_media_source
  768. ,hideFiles: config.hideFiles || MODx.config.modx_browser_tree_hide_files
  769. ,hideTooltips: config.hideTooltips || MODx.config.modx_browser_tree_hide_tooltips || true // by default do not request image preview tooltips in the media browser
  770. ,openTo: config.openTo || ''
  771. ,ident: this.ident
  772. ,rootId: config.rootId || '/'
  773. ,rootName: _('files')
  774. ,rootVisible: config.rootVisible == undefined || !Ext.isEmpty(config.rootId)
  775. ,id: this.ident+'-tree'
  776. ,hideSourceCombo: config.hideSourceCombo || false
  777. ,useDefaultToolbar: false
  778. ,listeners: {
  779. 'afterUpload': {
  780. fn: function() {
  781. this.view.run();
  782. }
  783. ,scope: this
  784. }
  785. ,'afterQuickCreate': {
  786. fn: function() {
  787. this.view.run();
  788. }
  789. ,scope: this
  790. }
  791. ,'afterRename': {
  792. fn: function() {
  793. this.view.run();
  794. }
  795. ,scope: this
  796. }
  797. ,'afterRemove': {
  798. fn: function() {
  799. this.view.run();
  800. }
  801. ,scope: this
  802. }
  803. ,'changeSource': {
  804. fn: function(s) {
  805. this.config.source = s;
  806. this.view.config.source = s;
  807. this.view.baseParams.source = s;
  808. this.view.dir = '/';
  809. this.view.run();
  810. }
  811. ,scope: this
  812. }
  813. ,'afterrender': {
  814. fn: function(tree) {
  815. tree.root.expand();
  816. }
  817. ,scope: this
  818. }
  819. ,'beforeclick': {
  820. fn: function(node, e) {
  821. // load the node/folder that is clicked on but prevent unnecessary requests when a file is clicked
  822. if (!node.leaf) {
  823. this.load(node.id);
  824. } else {
  825. // sync the selected item in the tree with the one in browser view
  826. // the id of a browser view node in the store is the full absolute URL
  827. // but there is a bug with urlAbsolute, see #11821 that's why we prepend a slash
  828. this.view.select(this.view.store.indexOfId('/' + node.attributes.url));
  829. // but instead load the container the file resides in if not already displayed
  830. if (this.view.dir !== node.parentNode.id) {
  831. this.load(node.parentNode.id);
  832. }
  833. return false;
  834. }
  835. }
  836. ,scope: this
  837. }
  838. }
  839. });
  840. // DataView
  841. this.view = MODx.load({
  842. xtype: 'modx-browser-view'
  843. ,onSelect: {
  844. fn: this.onSelect
  845. ,scope: this
  846. }
  847. ,source: config.source || MODx.config.default_media_source
  848. ,allowedFileTypes: config.allowedFileTypes || ''
  849. ,wctx: config.wctx || 'web'
  850. ,openTo: config.openTo || ''
  851. ,ident: this.ident
  852. ,id: this.ident+'-view'
  853. ,tree: this.tree
  854. });
  855. Ext.applyIf(config, {
  856. cls: 'modx-browser modx-browser-panel container'
  857. ,layout: 'border'
  858. ,width: '98%'
  859. ,height: '95%'
  860. ,items: [{
  861. region: 'west'
  862. ,width: 250
  863. ,items: this.tree
  864. ,id: this.ident+'-browser-tree'
  865. ,cls: 'modx-browser-tree'
  866. ,autoScroll: true
  867. ,split: true
  868. },{
  869. region: 'center'
  870. ,layout: 'fit'
  871. ,items: this.view
  872. ,id: this.ident+'-browser-view'
  873. ,cls: 'modx-browser-view-ct'
  874. ,autoScroll: true
  875. ,border: false
  876. ,tbar: this.getToolbar()
  877. ,bbar: this.getPathbar()
  878. },{
  879. region: 'east'
  880. ,width: 250
  881. ,id: this.ident+'-img-detail-panel'
  882. ,cls: 'modx-browser-details-ct'
  883. ,split: true
  884. //,collapsed: true
  885. }]
  886. });
  887. MODx.Media.superclass.constructor.call(this, config);
  888. this.config = config;
  889. };
  890. Ext.extend(MODx.Media, Ext.Container, {
  891. returnEl: null
  892. /**
  893. * Filter the DataView results
  894. */
  895. ,filter : function() {
  896. var filter = Ext.getCmp(this.ident+'filter');
  897. this.view.store.filter('name', filter.getValue(), true);
  898. this.view.select(0);
  899. }
  900. /**
  901. * Load the given directory in the DataView
  902. *
  903. * @param {String} dir
  904. */
  905. ,load: function(dir) {
  906. dir = dir || (Ext.isEmpty(this.config.openTo) ? '' : this.config.openTo);
  907. this.view.run({
  908. dir: dir
  909. ,source: this.config.source
  910. ,allowedFileTypes: this.config.allowedFileTypes || ''
  911. ,wctx: this.config.wctx || 'web'
  912. });
  913. this.sortStore();
  914. }
  915. /**
  916. * Sort the DataView results
  917. */
  918. ,sortStore: function(){
  919. var v = Ext.getCmp(this.ident+'sortSelect').getValue();
  920. this.view.store.sort(v, v == 'name' ? 'ASC' : 'DESC');
  921. this.view.select(0);
  922. }
  923. /**
  924. * Switch viewmode from grid to list and vice versa
  925. */
  926. ,changeViewmode: function() {
  927. var v = Ext.getCmp(this.ident+'viewSelect').getValue();
  928. this.view.setTemplate(v);
  929. this.view.select(0);
  930. }
  931. /**
  932. * Remove any filter applied to the DataView
  933. */
  934. ,reset: function() {
  935. if (this.rendered) {
  936. Ext.getCmp(this.ident+'filter').reset();
  937. this.view.getEl().dom.scrollTop = 0;
  938. }
  939. this.view.store.clearFilter();
  940. this.view.select(0);
  941. }
  942. /**
  943. * Get the browser view toolbar configuration
  944. *
  945. * @returns {Array}
  946. */
  947. ,getToolbar: function() {
  948. return [{
  949. text: _('filter')+':'
  950. ,xtype: 'label'
  951. },{
  952. xtype: 'textfield'
  953. ,id: this.ident+'filter'
  954. ,selectOnFocus: true
  955. ,width: 200
  956. ,listeners: {
  957. 'render': {
  958. fn: function() {
  959. Ext.getCmp(this.ident+'filter').getEl().on('keyup', function() {
  960. this.filter();
  961. }, this, {buffer: 500});
  962. }
  963. ,scope: this
  964. }
  965. }
  966. },{
  967. text: _('sort_by')+':'
  968. ,xtype: 'label'
  969. },{
  970. id: this.ident+'sortSelect'
  971. ,xtype: 'combo'
  972. ,typeAhead: true
  973. ,triggerAction: 'all'
  974. ,width: 130
  975. ,editable: false
  976. ,mode: 'local'
  977. ,displayField: 'desc'
  978. ,valueField: 'name'
  979. ,lazyInit: false
  980. ,value: MODx.config.modx_browser_default_sort || 'name'
  981. ,store: new Ext.data.SimpleStore({
  982. fields: ['name', 'desc'],
  983. data : [
  984. ['name', _('name')]
  985. ,['size', _('file_size')]
  986. ,['lastmod', _('last_modified')]
  987. ]
  988. })
  989. ,listeners: {
  990. 'select': {
  991. fn: this.sortStore
  992. ,scope: this
  993. }
  994. }
  995. }, '-', {
  996. text: _('files_viewmode')+':'
  997. ,xtype: 'label'
  998. }, '-', {
  999. id: this.ident+'viewSelect'
  1000. ,xtype: 'combo'
  1001. ,typeAhead: false
  1002. ,triggerAction: 'all'
  1003. ,width: 100
  1004. ,editable: false
  1005. ,mode: 'local'
  1006. ,displayField: 'desc'
  1007. ,valueField: 'type'
  1008. ,lazyInit: false
  1009. ,value: MODx.config.modx_browser_default_viewmode || 'grid'
  1010. ,store: new Ext.data.SimpleStore({
  1011. fields: ['type', 'desc'],
  1012. data : [
  1013. ['grid', _('files_viewmode_grid')]
  1014. ,['list', _('files_viewmode_list')]
  1015. ]
  1016. })
  1017. ,listeners: {
  1018. 'select': {
  1019. fn: this.changeViewmode
  1020. ,scope: this
  1021. }
  1022. }
  1023. }];
  1024. }
  1025. /**
  1026. * Get the bottom filepath textfield in the browser view
  1027. *
  1028. * @returns {Array}
  1029. */
  1030. ,getPathbar: function() {
  1031. return {
  1032. cls: 'modx-browser-pathbbar'
  1033. ,items: [{
  1034. xtype: 'textfield'
  1035. ,id: this.ident+'-filepath'
  1036. ,cls: 'modx-browser-filepath'
  1037. ,listeners: {
  1038. 'focus': {
  1039. // select the filepath on focus
  1040. fn: function(el) {
  1041. // let the focus event stick first, needed for webkit primarily
  1042. setTimeout(function () {
  1043. var field = el.getEl().dom;
  1044. if (field.createTextRange) {
  1045. var selRange = field.createTextRange();
  1046. selRange.collapse(true);
  1047. selRange.moveStart('character', 0);
  1048. selRange.moveEnd('character', field.value.length);
  1049. selRange.select();
  1050. } else if (field.setSelectionRange) {
  1051. field.setSelectionRange(0, field.value.length);
  1052. } else if (field.selectionStart) {
  1053. field.selectionStart = 0;
  1054. field.selectionEnd = field.value.length;
  1055. }
  1056. }, 50);
  1057. }
  1058. ,scope: this
  1059. }
  1060. }
  1061. }]
  1062. };
  1063. }
  1064. ,setReturn: function(el) {
  1065. this.returnEl = el;
  1066. }
  1067. ,onSelect: function(data) {
  1068. return;
  1069. }
  1070. ,onSelectHandler: function(data) {
  1071. Ext.get(this.returnEl).dom.value = unescape(data.url);
  1072. }
  1073. });
  1074. Ext.reg('modx-media-view', MODx.Media);
  1075. /**
  1076. * This is the popup window (not Ext.Window!) that opens when triggered from an RTE
  1077. */
  1078. MODx.browser.RTE = function(config) {
  1079. config = config || {};
  1080. this.ident = config.ident || Ext.id();
  1081. // Hide the "MODX Browser" toolbar button
  1082. MODx.browserOpen = true;
  1083. Ext.Ajax.defaultHeaders = {
  1084. 'modAuth': config.auth
  1085. };
  1086. Ext.Ajax.extraParams = {
  1087. 'HTTP_MODAUTH': config.auth
  1088. };
  1089. // Tree navigation
  1090. this.tree = MODx.load({
  1091. xtype: 'modx-tree-directory'
  1092. ,onUpload: function() {
  1093. this.view.run();
  1094. }
  1095. ,scope: this
  1096. ,source: config.source || MODx.config.default_media_source
  1097. ,hideFiles: config.hideFiles || MODx.config.modx_browser_tree_hide_files
  1098. ,hideTooltips: config.hideTooltips || MODx.config.modx_browser_tree_hide_tooltips || true // by default do not request image preview tooltips in the media browser
  1099. ,openTo: config.openTo || ''
  1100. ,ident: this.ident
  1101. ,rootId: config.rootId || '/'
  1102. ,rootName: _('files')
  1103. ,rootVisible: config.rootVisible == undefined || !Ext.isEmpty(config.rootId)
  1104. ,id: this.ident+'-tree'
  1105. ,hideSourceCombo: config.hideSourceCombo || false
  1106. ,useDefaultToolbar: false
  1107. ,listeners: {
  1108. 'afterUpload': {
  1109. fn: function() {
  1110. this.view.run();
  1111. }
  1112. ,scope: this
  1113. }
  1114. ,'afterQuickCreate': {
  1115. fn: function() {
  1116. this.view.run();
  1117. }
  1118. ,scope: this
  1119. }
  1120. ,'afterRename': {
  1121. fn: function() {
  1122. this.view.run();
  1123. }
  1124. ,scope: this
  1125. }
  1126. ,'afterRemove': {
  1127. fn: function() {
  1128. this.view.run();
  1129. }
  1130. ,scope: this
  1131. }
  1132. ,'changeSource': {
  1133. fn: function(s) {
  1134. this.config.source = s;
  1135. this.view.config.source = s;
  1136. this.view.baseParams.source = s;
  1137. this.view.dir = '/';
  1138. this.view.run();
  1139. }
  1140. ,scope: this
  1141. }
  1142. ,'afterrender': {
  1143. fn: function(tree) {
  1144. tree.root.expand();
  1145. }
  1146. ,scope: this
  1147. }
  1148. ,'beforeclick': {
  1149. fn: function(node, e) {
  1150. // load the node/folder that is clicked on but prevent unnecessary requests when a file is clicked
  1151. if (!node.leaf) {
  1152. this.load(node.id);
  1153. } else {
  1154. // sync the selected item in the tree with the one in browser view
  1155. // the id of a browser view node in the store is the full absolute URL
  1156. // but there is a bug with urlAbsolute, see #11821 that's why we prepend a slash
  1157. this.view.select(this.view.store.indexOfId('/' + node.attributes.url));
  1158. // but instead load the container the file resides in if not already displayed
  1159. if (this.view.dir !== node.parentNode.id) {
  1160. this.load(node.parentNode.id);
  1161. }
  1162. return false;
  1163. }
  1164. }
  1165. ,scope: this
  1166. }
  1167. }
  1168. });
  1169. // DataView
  1170. this.view = MODx.load({
  1171. xtype: 'modx-browser-view'
  1172. ,onSelect: {
  1173. fn: this.onSelect
  1174. ,scope: this
  1175. }
  1176. ,source: config.source || MODx.config.default_media_source
  1177. ,allowedFileTypes: config.allowedFileTypes || ''
  1178. ,wctx: config.wctx || 'web'
  1179. ,openTo: config.openTo || ''
  1180. ,ident: this.ident
  1181. ,id: this.ident+'-view'
  1182. ,tree: this.tree
  1183. });
  1184. Ext.applyIf(config,{
  1185. title: _('modx_browser')
  1186. ,cls: 'modx-browser modx-browser-rte'
  1187. ,layout: 'border'
  1188. ,renderTo: document.body
  1189. ,id: this.ident+'-viewport'
  1190. ,onSelect: MODx.onBrowserReturn || function(data) {}
  1191. ,items: [{
  1192. id: this.ident+'-browser-tree'
  1193. ,cls: 'modx-browser-tree'
  1194. ,region: 'west'
  1195. ,width: 250
  1196. ,height: '100%'
  1197. ,split: true
  1198. ,items: this.tree
  1199. ,autoScroll: true
  1200. },{
  1201. id: this.ident+'-browser-view'
  1202. ,cls: 'modx-browser-view-ct'
  1203. ,region: 'center'
  1204. ,autoScroll: true
  1205. ,width: 450
  1206. ,items: this.view
  1207. ,tbar: this.getToolbar()
  1208. ,bbar: this.getPathbar()
  1209. },{
  1210. id: this.ident+'-img-detail-panel'
  1211. ,cls: 'modx-browser-details-ct'
  1212. ,region: 'east'
  1213. ,split: true
  1214. ,width: 200
  1215. ,minWidth: 200
  1216. ,maxWidth: 300
  1217. },{
  1218. id: this.ident+'-south'
  1219. ,cls: 'modx-browser-rte-buttons'
  1220. ,region: 'south'
  1221. ,split: false
  1222. ,bbar: ['->',{
  1223. xtype: 'button'
  1224. ,id: this.ident+'-cancel-btn'
  1225. ,text: _('cancel')
  1226. ,minWidth: 75
  1227. ,handler: this.onCancel
  1228. ,scope: this
  1229. // ,width: 200
  1230. },{
  1231. xtype: 'button'
  1232. ,id: this.ident+'-ok-btn'
  1233. ,text: _('ok')
  1234. ,cls: 'primary-button'
  1235. ,minWidth: 75
  1236. ,handler: this.onSelect
  1237. ,scope: this
  1238. // ,width: 200
  1239. }]
  1240. }]
  1241. });
  1242. MODx.browser.RTE.superclass.constructor.call(this,config);
  1243. this.config = config;
  1244. };
  1245. Ext.extend(MODx.browser.RTE,Ext.Viewport,{
  1246. returnEl: null
  1247. /**
  1248. * Filter the DataView results
  1249. */
  1250. ,filter : function() {
  1251. var filter = Ext.getCmp(this.ident+'filter');
  1252. this.view.store.filter('name', filter.getValue(), true);
  1253. this.view.select(0);
  1254. }
  1255. /**
  1256. * Load the given directory in the DataView
  1257. *
  1258. * @param {String} dir
  1259. */
  1260. ,load: function(dir) {
  1261. dir = dir || (Ext.isEmpty(this.config.openTo) ? '' : this.config.openTo);
  1262. this.view.run({
  1263. dir: dir
  1264. ,source: this.config.source
  1265. ,allowedFileTypes: this.config.allowedFileTypes || ''
  1266. ,wctx: this.config.wctx || 'web'
  1267. });
  1268. this.sortStore();
  1269. }
  1270. /**
  1271. * Sort the DataView results
  1272. */
  1273. ,sortStore: function(){
  1274. var v = Ext.getCmp(this.ident+'sortSelect').getValue();
  1275. this.view.store.sort(v, v == 'name' ? 'ASC' : 'DESC');
  1276. this.view.select(0);
  1277. }
  1278. /**
  1279. * Switch viewmode from grid to list and vice versa
  1280. */
  1281. ,changeViewmode: function() {
  1282. var v = Ext.getCmp(this.ident+'viewSelect').getValue();
  1283. this.view.setTemplate(v);
  1284. this.view.select(0);
  1285. }
  1286. /**
  1287. * Remove any filter applied to the DataView
  1288. */
  1289. ,reset: function() {
  1290. if (this.rendered) {
  1291. Ext.getCmp(this.ident+'filter').reset();
  1292. this.view.getEl().dom.scrollTop = 0;
  1293. }
  1294. this.view.store.clearFilter();
  1295. this.view.select(0);
  1296. }
  1297. /**
  1298. * Get the browser view toolbar configuration
  1299. *
  1300. * @returns {Array}
  1301. */
  1302. ,getToolbar: function() {
  1303. return [{
  1304. text: _('filter')+':'
  1305. ,xtype: 'label'
  1306. },{
  1307. xtype: 'textfield'
  1308. ,id: this.ident+'filter'
  1309. ,selectOnFocus: true
  1310. ,width: 200
  1311. ,listeners: {
  1312. 'render': {
  1313. fn: function() {
  1314. Ext.getCmp(this.ident+'filter').getEl().on('keyup', function() {
  1315. this.filter();
  1316. }, this, {buffer: 500});
  1317. }
  1318. ,scope: this
  1319. }
  1320. }
  1321. },{
  1322. text: _('sort_by')+':'
  1323. ,xtype: 'label'
  1324. },{
  1325. id: this.ident+'sortSelect'
  1326. ,xtype: 'combo'
  1327. ,typeAhead: true
  1328. ,triggerAction: 'all'
  1329. ,width: 130
  1330. ,editable: false
  1331. ,mode: 'local'
  1332. ,displayField: 'desc'
  1333. ,valueField: 'name'
  1334. ,lazyInit: false
  1335. ,value: MODx.config.modx_browser_default_sort || 'name'
  1336. ,store: new Ext.data.SimpleStore({
  1337. fields: ['name', 'desc'],
  1338. data : [
  1339. ['name', _('name')]
  1340. ,['size', _('file_size')]
  1341. ,['lastmod', _('last_modified')]
  1342. ]
  1343. })
  1344. ,listeners: {
  1345. 'select': {
  1346. fn: this.sortStore
  1347. ,scope: this
  1348. }
  1349. }
  1350. }, '-', {
  1351. text: _('files_viewmode')+':'
  1352. ,xtype: 'label'
  1353. }, '-', {
  1354. id: this.ident+'viewSelect'
  1355. ,xtype: 'combo'
  1356. ,typeAhead: false
  1357. ,triggerAction: 'all'
  1358. ,width: 100
  1359. ,editable: false
  1360. ,mode: 'local'
  1361. ,displayField: 'desc'
  1362. ,valueField: 'type'
  1363. ,lazyInit: false
  1364. ,value: MODx.config.modx_browser_default_viewmode || 'grid'
  1365. ,store: new Ext.data.SimpleStore({
  1366. fields: ['type', 'desc'],
  1367. data : [
  1368. ['grid', _('files_viewmode_grid')]
  1369. ,['list', _('files_viewmode_list')]
  1370. ]
  1371. })
  1372. ,listeners: {
  1373. 'select': {
  1374. fn: this.changeViewmode
  1375. ,scope: this
  1376. }
  1377. }
  1378. }];
  1379. }
  1380. /**
  1381. * Get the bottom filepath textfield in the browser view
  1382. *
  1383. * @returns {Array}
  1384. */
  1385. ,getPathbar: function() {
  1386. return {
  1387. cls: 'modx-browser-pathbbar'
  1388. ,items: [{
  1389. xtype: 'textfield'
  1390. ,id: this.ident+'-filepath'
  1391. ,cls: 'modx-browser-filepath'
  1392. ,listeners: {
  1393. 'focus': {
  1394. // select the filepath on focus
  1395. fn: function(el) {
  1396. // let the focus event stick first, needed for webkit primarily
  1397. setTimeout(function () {
  1398. var field = el.getEl().dom;
  1399. if (field.createTextRange) {
  1400. var selRange = field.createTextRange();
  1401. selRange.collapse(true);
  1402. selRange.moveStart('character', 0);
  1403. selRange.moveEnd('character', field.value.length);
  1404. selRange.select();
  1405. } else if (field.setSelectionRange) {
  1406. field.setSelectionRange(0, field.value.length);
  1407. } else if (field.selectionStart) {
  1408. field.selectionStart = 0;
  1409. field.selectionEnd = field.value.length;
  1410. }
  1411. }, 50);
  1412. }
  1413. ,scope: this
  1414. }
  1415. }
  1416. }]
  1417. };
  1418. }
  1419. ,setReturn: function(el) {
  1420. this.returnEl = el;
  1421. }
  1422. ,onSelect: function(data) {
  1423. var selNode = this.view.getSelectedNodes()[0];
  1424. var callback = this.config.onSelect || this.onSelectHandler;
  1425. var lookup = this.view.lookup;
  1426. var scope = this.config.scope;
  1427. if (callback) {
  1428. data = (selNode) ? lookup[selNode.id] : null;
  1429. Ext.callback(callback, scope || this, [data]);
  1430. this.fireEvent('select', data);
  1431. if (window.top.opener) {
  1432. window.top.close();
  1433. window.top.opener.focus();
  1434. }
  1435. }
  1436. }
  1437. ,onCancel: function() {
  1438. var callback = this.config.onSelect || this.onSelectHandler;
  1439. var scope = this.config.scope;
  1440. Ext.callback(callback, scope || this, [null]);
  1441. this.fireEvent('select', null);
  1442. if (window.top.opener) {
  1443. window.top.close();
  1444. window.top.opener.focus();
  1445. }
  1446. }
  1447. ,onSelectHandler: function(data) {
  1448. Ext.get(this.returnEl).dom.value = unescape(data.url);
  1449. }
  1450. });
  1451. Ext.reg('modx-browser-rte',MODx.browser.RTE);