modx.grid.js 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154
  1. Ext.namespace('MODx.grid');
  2. MODx.grid.Grid = function(config) {
  3. config = config || {};
  4. this.config = config;
  5. this._loadStore();
  6. this._loadColumnModel();
  7. Ext.applyIf(config,{
  8. store: this.store
  9. ,cm: this.cm
  10. ,sm: new Ext.grid.RowSelectionModel({singleSelect:true})
  11. ,paging: (config.bbar ? true : false)
  12. ,loadMask: true
  13. ,autoHeight: true
  14. ,collapsible: true
  15. ,stripeRows: true
  16. ,header: false
  17. ,cls: 'modx-grid'
  18. ,preventRender: true
  19. ,preventSaveRefresh: true
  20. ,showPerPage: true
  21. ,stateful: false
  22. ,menuConfig: {
  23. defaultAlign: 'tl-b?'
  24. ,enableScrolling: false
  25. }
  26. ,viewConfig: {
  27. forceFit: true
  28. ,enableRowBody: true
  29. ,autoFill: true
  30. ,showPreview: true
  31. ,scrollOffset: 0
  32. ,emptyText: config.emptyText || _('ext_emptymsg')
  33. }
  34. ,groupingConfig: {
  35. enableGroupingMenu: true
  36. }
  37. });
  38. if (config.paging) {
  39. var pgItms = config.showPerPage ? [_('per_page')+':',{
  40. xtype: 'textfield'
  41. ,cls: 'x-tbar-page-size'
  42. ,value: config.pageSize || (parseInt(MODx.config.default_per_page) || 20)
  43. ,listeners: {
  44. 'change': {fn:this.onChangePerPage,scope:this}
  45. ,'render': {fn: function(cmp) {
  46. new Ext.KeyMap(cmp.getEl(), {
  47. key: Ext.EventObject.ENTER
  48. ,fn: this.blur
  49. ,scope: cmp
  50. });
  51. },scope:this}
  52. }
  53. }] : [];
  54. if (config.pagingItems) {
  55. for (var i=0;i<config.pagingItems.length;i++) {
  56. pgItms.push(config.pagingItems[i]);
  57. }
  58. }
  59. Ext.applyIf(config,{
  60. bbar: new Ext.PagingToolbar({
  61. pageSize: config.pageSize || (parseInt(MODx.config.default_per_page) || 20)
  62. ,store: this.getStore()
  63. ,displayInfo: true
  64. ,items: pgItms
  65. })
  66. });
  67. }
  68. if (config.grouping) {
  69. var groupingConfig = {
  70. forceFit: true
  71. ,scrollOffset: 0
  72. ,groupTextTpl: '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "'
  73. + (config.pluralText || _('records')) + '" : "'
  74. + (config.singleText || _('record')) + '"]})'
  75. };
  76. Ext.applyIf(config.groupingConfig, groupingConfig);
  77. Ext.applyIf(config,{
  78. view: new Ext.grid.GroupingView(config.groupingConfig)
  79. });
  80. }
  81. if (config.tbar) {
  82. for (var ix = 0;ix<config.tbar.length;ix++) {
  83. var itm = config.tbar[ix];
  84. if (itm.handler && typeof(itm.handler) == 'object' && itm.handler.xtype) {
  85. itm.handler = this.loadWindow.createDelegate(this,[itm.handler],true);
  86. }
  87. if (!itm.scope) { itm.scope = this; }
  88. }
  89. }
  90. MODx.grid.Grid.superclass.constructor.call(this,config);
  91. this._loadMenu(config);
  92. this.addEvents('beforeRemoveRow','afterRemoveRow','afterAutoSave');
  93. if (this.autosave) {
  94. this.on('afterAutoSave', this.onAfterAutoSave, this);
  95. }
  96. if (!config.preventRender) { this.render(); }
  97. this.on('rowcontextmenu',this._showMenu,this);
  98. if (config.autosave) {
  99. this.on('afteredit',this.saveRecord,this);
  100. }
  101. if (config.paging && config.grouping) {
  102. this.getBottomToolbar().bind(this.store);
  103. }
  104. if (!config.paging && !config.hasOwnProperty('pageSize')) {
  105. config.pageSize = 0;
  106. }
  107. this.getStore().load({
  108. params: {
  109. start: config.pageStart || 0
  110. ,limit: config.hasOwnProperty('pageSize') ? config.pageSize : (parseInt(MODx.config.default_per_page) || 20)
  111. }
  112. });
  113. this.getStore().on('exception',this.onStoreException,this);
  114. this.config = config;
  115. };
  116. Ext.extend(MODx.grid.Grid,Ext.grid.EditorGridPanel,{
  117. windows: {}
  118. ,onStoreException: function(dp,type,act,opt,resp){
  119. var r = Ext.decode(resp.responseText);
  120. if (r.message) {
  121. this.getView().emptyText = r.message;
  122. this.getView().refresh(false);
  123. }
  124. }
  125. ,saveRecord: function(e) {
  126. e.record.data.menu = null;
  127. var p = this.config.saveParams || {};
  128. Ext.apply(e.record.data,p);
  129. var d = Ext.util.JSON.encode(e.record.data);
  130. var url = this.config.saveUrl || (this.config.url || this.config.connector);
  131. MODx.Ajax.request({
  132. url: url
  133. ,params: {
  134. action: this.config.save_action || 'updateFromGrid'
  135. ,data: d
  136. }
  137. ,listeners: {
  138. success: {
  139. fn: function(r) {
  140. if (this.config.save_callback) {
  141. Ext.callback(this.config.save_callback,this.config.scope || this,[r]);
  142. }
  143. e.record.commit();
  144. if (!this.config.preventSaveRefresh) {
  145. this.refresh();
  146. }
  147. this.fireEvent('afterAutoSave',r);
  148. }
  149. ,scope: this
  150. }
  151. ,failure: {
  152. fn: function(r) {
  153. e.record.reject();
  154. this.fireEvent('afterAutoSave', r);
  155. }
  156. ,scope: this
  157. }
  158. }
  159. });
  160. }
  161. /**
  162. * Method executed after a record has been edited/saved inline from within the grid
  163. *
  164. * @param {Object} response - The processor save response object. See modConnectorResponse::outputContent (PHP)
  165. */
  166. ,onAfterAutoSave: function(response) {
  167. if (!response.success && response.message === '') {
  168. var msg = '';
  169. if (response.data.length) {
  170. // We get some data for specific field(s) error but not regular error message
  171. Ext.each(response.data, function(data, index, list) {
  172. msg += (msg != '' ? '<br/>' : '') + data.msg;
  173. }, this);
  174. }
  175. if (Ext.isEmpty(msg)) {
  176. // Still no valid message so far, let's use some fallback
  177. msg = this.autosaveErrorMsg || _('error');
  178. }
  179. MODx.msg.alert(_('error'), msg);
  180. }
  181. }
  182. ,onChangePerPage: function(tf,nv) {
  183. if (Ext.isEmpty(nv)) return false;
  184. nv = parseInt(nv);
  185. this.getBottomToolbar().pageSize = nv;
  186. this.store.load({params:{
  187. start:0
  188. ,limit: nv
  189. }});
  190. }
  191. ,loadWindow: function(btn,e,win,or) {
  192. var r = this.menu.record;
  193. if (!this.windows[win.xtype] || win.force) {
  194. Ext.applyIf(win,{
  195. record: win.blankValues ? {} : r
  196. ,grid: this
  197. ,listeners: {
  198. 'success': {fn:win.success || this.refresh,scope:win.scope || this}
  199. }
  200. });
  201. if (or) {
  202. Ext.apply(win,or);
  203. }
  204. this.windows[win.xtype] = Ext.ComponentMgr.create(win);
  205. }
  206. if (this.windows[win.xtype].setValues && win.blankValues !== true && r != undefined) {
  207. this.windows[win.xtype].setValues(r);
  208. }
  209. this.windows[win.xtype].show(e.target);
  210. }
  211. ,confirm: function(type,text) {
  212. var p = { action: type };
  213. var k = this.config.primaryKey || 'id';
  214. p[k] = this.menu.record[k];
  215. MODx.msg.confirm({
  216. title: _(type)
  217. ,text: _(text) || _('confirm_remove')
  218. ,url: this.config.url
  219. ,params: p
  220. ,listeners: {
  221. 'success': {fn:this.refresh,scope:this}
  222. }
  223. });
  224. }
  225. ,remove: function(text, action) {
  226. if (this.destroying) {
  227. return MODx.grid.Grid.superclass.remove.apply(this, arguments);
  228. }
  229. var r = this.menu.record;
  230. text = text || 'confirm_remove';
  231. var p = this.config.saveParams || {};
  232. Ext.apply(p,{ action: action || 'remove' });
  233. //console.log(action, p);
  234. var k = this.config.primaryKey || 'id';
  235. p[k] = r[k];
  236. if (this.fireEvent('beforeRemoveRow',r)) {
  237. MODx.msg.confirm({
  238. title: _('warning')
  239. ,text: _(text, r)
  240. ,url: this.config.url
  241. ,params: p
  242. ,listeners: {
  243. 'success': {fn:function() {
  244. this.removeActiveRow(r);
  245. },scope:this}
  246. }
  247. });
  248. }
  249. }
  250. ,removeActiveRow: function(r) {
  251. if (this.fireEvent('afterRemoveRow',r)) {
  252. var rx = this.getSelectionModel().getSelected();
  253. this.getStore().remove(rx);
  254. }
  255. }
  256. ,_loadMenu: function() {
  257. this.menu = new Ext.menu.Menu(this.config.menuConfig);
  258. }
  259. ,_showMenu: function(g,ri,e) {
  260. e.stopEvent();
  261. e.preventDefault();
  262. this.menu.record = this.getStore().getAt(ri).data;
  263. if (!this.getSelectionModel().isSelected(ri)) {
  264. this.getSelectionModel().selectRow(ri);
  265. }
  266. this.menu.removeAll();
  267. if (this.getMenu) {
  268. var m = this.getMenu(g,ri,e);
  269. if (m && m.length && m.length > 0) {
  270. this.addContextMenuItem(m);
  271. }
  272. }
  273. if ((!m || m.length <= 0) && this.menu.record.menu) {
  274. this.addContextMenuItem(this.menu.record.menu);
  275. }
  276. if (this.menu.items.length > 0) {
  277. this.menu.showAt(e.xy);
  278. }
  279. }
  280. ,_loadStore: function() {
  281. if (this.config.grouping) {
  282. this.store = new Ext.data.GroupingStore({
  283. url: this.config.url
  284. ,baseParams: this.config.baseParams || { action: this.config.action || 'getList'}
  285. ,reader: new Ext.data.JsonReader({
  286. totalProperty: 'total'
  287. ,root: 'results'
  288. ,fields: this.config.fields
  289. })
  290. ,sortInfo:{
  291. field: this.config.sortBy || 'id'
  292. ,direction: this.config.sortDir || 'ASC'
  293. }
  294. ,remoteSort: this.config.remoteSort || false
  295. ,groupField: this.config.groupBy || 'name'
  296. ,storeId: this.config.storeId || Ext.id()
  297. ,autoDestroy: true
  298. ,listeners:{
  299. load: function(){
  300. var cmp = Ext.getCmp('modx-content');
  301. if(typeof cmp !== "undefined") {
  302. cmp.doLayout(); /* Fix layout bug with absolute positioning */
  303. }
  304. }
  305. }
  306. });
  307. } else {
  308. this.store = new Ext.data.JsonStore({
  309. url: this.config.url
  310. ,baseParams: this.config.baseParams || { action: this.config.action || 'getList' }
  311. ,fields: this.config.fields
  312. ,root: 'results'
  313. ,totalProperty: 'total'
  314. ,remoteSort: this.config.remoteSort || false
  315. ,storeId: this.config.storeId || Ext.id()
  316. ,autoDestroy: true
  317. ,listeners:{
  318. load: function(){
  319. var cmp = Ext.getCmp('modx-content');
  320. if(typeof cmp !== "undefined") {
  321. cmp.doLayout(); /* Fix layout bug with absolute positioning */
  322. }
  323. }
  324. }
  325. });
  326. }
  327. }
  328. ,_loadColumnModel: function() {
  329. if (this.config.columns) {
  330. var c = this.config.columns;
  331. for (var i=0;i<c.length;i++) {
  332. // if specifying custom editor/renderer
  333. if (typeof(c[i].editor) == 'string') {
  334. c[i].editor = eval(c[i].editor);
  335. }
  336. if (typeof(c[i].renderer) == 'string') {
  337. c[i].renderer = eval(c[i].renderer);
  338. }
  339. if (typeof(c[i].editor) == 'object' && c[i].editor.xtype) {
  340. var r = c[i].editor.renderer;
  341. if (Ext.isEmpty(c[i].editor.id)) { c[i].editor.id = Ext.id(); }
  342. c[i].editor = Ext.ComponentMgr.create(c[i].editor);
  343. if (r === true) {
  344. if (c[i].editor && c[i].editor.store && !c[i].editor.store.isLoaded && c[i].editor.config.mode != 'local') {
  345. c[i].editor.store.load();
  346. c[i].editor.store.isLoaded = true;
  347. }
  348. c[i].renderer = Ext.util.Format.comboRenderer(c[i].editor);
  349. } else if (c[i].editor.initialConfig.xtype === 'datefield') {
  350. c[i].renderer = Ext.util.Format.dateRenderer(c[i].editor.initialConfig.format || 'Y-m-d');
  351. } else if (r === 'boolean') {
  352. c[i].renderer = this.rendYesNo;
  353. } else if (r === 'password') {
  354. c[i].renderer = this.rendPassword;
  355. } else if (r === 'local' && typeof(c[i].renderer) == 'string') {
  356. c[i].renderer = eval(c[i].renderer);
  357. }
  358. }
  359. }
  360. this.cm = new Ext.grid.ColumnModel(c);
  361. }
  362. }
  363. ,addContextMenuItem: function(items) {
  364. var l = items.length;
  365. for(var i = 0; i < l; i++) {
  366. var options = items[i];
  367. if (options == '-') {
  368. this.menu.add('-');
  369. continue;
  370. }
  371. var h = Ext.emptyFn;
  372. if (options.handler) {
  373. h = eval(options.handler);
  374. if (h && typeof(h) == 'object' && h.xtype) {
  375. h = this.loadWindow.createDelegate(this,[h],true);
  376. }
  377. } else {
  378. h = function(itm) {
  379. var o = itm.options;
  380. var id = this.menu.record.id;
  381. if (o.confirm) {
  382. Ext.Msg.confirm('',o.confirm,function(e) {
  383. if (e == 'yes') {
  384. var act = Ext.urlEncode(o.params || {action: o.action});
  385. location.href = '?id='+id+'&'+act;
  386. }
  387. },this);
  388. } else {
  389. var act = Ext.urlEncode(o.params || {action: o.action});
  390. location.href = '?id='+id+'&'+act;
  391. }
  392. };
  393. }
  394. this.menu.add({
  395. id: options.id || Ext.id()
  396. ,text: options.text
  397. ,scope: options.scope || this
  398. ,options: options
  399. ,handler: h
  400. });
  401. }
  402. }
  403. ,refresh: function() {
  404. this.getStore().reload();
  405. }
  406. ,rendPassword: function(v) {
  407. var z = '';
  408. for (var i=0;i<v.length;i++) {
  409. z = z+'*';
  410. }
  411. return z;
  412. }
  413. ,rendYesNo: function(v,md) {
  414. if (v === 1 || v == '1') { v = true; }
  415. if (v === 0 || v == '0') { v = false; }
  416. switch (v) {
  417. case true:
  418. case 'true':
  419. case 1:
  420. md.css = 'green';
  421. return _('yes');
  422. case false:
  423. case 'false':
  424. case '':
  425. case 0:
  426. md.css = 'red';
  427. return _('no');
  428. }
  429. }
  430. ,getSelectedAsList: function() {
  431. var sels = this.getSelectionModel().getSelections();
  432. if (sels.length <= 0) return false;
  433. var cs = '';
  434. for (var i=0;i<sels.length;i++) {
  435. cs += ','+sels[i].data[this.config.primaryKey || 'id'];
  436. }
  437. if (cs[0] == ',') {
  438. cs = cs.substr(1);
  439. }
  440. return cs;
  441. }
  442. ,editorYesNo: function(r) {
  443. r = r || {};
  444. Ext.applyIf(r,{
  445. store: new Ext.data.SimpleStore({
  446. fields: ['d','v']
  447. ,data: [[_('yes'),true],[_('no'),false]]
  448. })
  449. ,displayField: 'd'
  450. ,valueField: 'v'
  451. ,mode: 'local'
  452. ,triggerAction: 'all'
  453. ,editable: false
  454. ,selectOnFocus: false
  455. });
  456. return new Ext.form.ComboBox(r);
  457. }
  458. ,encodeModified: function() {
  459. var p = this.getStore().getModifiedRecords();
  460. var rs = {};
  461. for (var i=0;i<p.length;i++) {
  462. rs[p[i].data[this.config.primaryKey || 'id']] = p[i].data;
  463. }
  464. return Ext.encode(rs);
  465. }
  466. ,encode: function() {
  467. var p = this.getStore().getRange();
  468. var rs = {};
  469. for (var i=0;i<p.length;i++) {
  470. rs[p[i].data[this.config.primaryKey || 'id']] = p[i].data;
  471. }
  472. return Ext.encode(rs);
  473. }
  474. ,expandAll: function() {
  475. var expander = this.findExpanderPlugin(this.config.plugins);
  476. if (!expander) {
  477. return false;
  478. }
  479. var rows = this.getView().getRows();
  480. for (var i = 0; i < rows.length; i++) {
  481. expander.expandRow(rows[i]);
  482. }
  483. if (this.tools['plus'] !== undefined) {
  484. this.tools['plus'].hide();
  485. }
  486. if (this.tools['minus'] !== undefined) {
  487. this.tools['minus'].show();
  488. }
  489. return true;
  490. }
  491. ,collapseAll: function() {
  492. var expander = this.findExpanderPlugin(this.config.plugins);
  493. if (!expander) {
  494. return false;
  495. }
  496. var rows = this.getView().getRows();
  497. for (var i = 0; i < rows.length; i++) {
  498. expander.collapseRow(rows[i]);
  499. }
  500. if (this.tools['minus'] !== undefined) {
  501. this.tools['minus'].hide();
  502. }
  503. if (this.tools['plus'] !== undefined) {
  504. this.tools['plus'].show();
  505. }
  506. return true;
  507. }
  508. /**
  509. * Returns first found expander plugin
  510. * @param plugins
  511. */
  512. ,findExpanderPlugin: function (plugins) {
  513. if (Ext.isObject(plugins)) {
  514. plugins = [plugins];
  515. }
  516. var index = Ext.each(plugins, function (item) {
  517. if (item.id !== undefined && item.id === 'expander') {
  518. return false;
  519. }
  520. });
  521. return plugins[index];
  522. }
  523. });
  524. /* local grid */
  525. MODx.grid.LocalGrid = function(config) {
  526. config = config || {};
  527. if (config.grouping) {
  528. Ext.applyIf(config,{
  529. view: new Ext.grid.GroupingView({
  530. forceFit: true
  531. ,scrollOffset: 0
  532. ,hideGroupedColumn: config.hideGroupedColumn ? true : false
  533. ,groupTextTpl: config.groupTextTpl || ('{text} ({[values.rs.length]} {[values.rs.length > 1 ? "'
  534. +(config.pluralText || _('records')) + '" : "'
  535. +(config.singleText || _('record'))+'"]})' )
  536. })
  537. });
  538. }
  539. if (config.tbar) {
  540. for (var i = 0;i<config.tbar.length;i++) {
  541. var itm = config.tbar[i];
  542. if (itm.handler && typeof(itm.handler) == 'object' && itm.handler.xtype) {
  543. itm.handler = this.loadWindow.createDelegate(this,[itm.handler],true);
  544. }
  545. if (!itm.scope) { itm.scope = this; }
  546. }
  547. }
  548. Ext.applyIf(config,{
  549. title: ''
  550. ,store: this._loadStore(config)
  551. ,sm: new Ext.grid.RowSelectionModel({singleSelect:false})
  552. ,loadMask: true
  553. ,collapsible: true
  554. ,stripeRows: true
  555. ,enableColumnMove: true
  556. ,header: false
  557. ,cls: 'modx-grid'
  558. ,viewConfig: {
  559. forceFit: true
  560. ,enableRowBody: true
  561. ,autoFill: true
  562. ,showPreview: true
  563. ,scrollOffset: 0
  564. ,emptyText: config.emptyText || _('ext_emptymsg')
  565. }
  566. ,menuConfig: { defaultAlign: 'tl-b?' ,enableScrolling: false }
  567. });
  568. this.menu = new Ext.menu.Menu(config.menuConfig);
  569. this.config = config;
  570. this._loadColumnModel();
  571. MODx.grid.LocalGrid.superclass.constructor.call(this,config);
  572. this.addEvents({
  573. beforeRemoveRow: true
  574. ,afterRemoveRow: true
  575. });
  576. this.on('rowcontextmenu',this._showMenu,this);
  577. };
  578. Ext.extend(MODx.grid.LocalGrid,Ext.grid.EditorGridPanel,{
  579. windows: {}
  580. ,_loadStore: function(config) {
  581. if (config.grouping) {
  582. this.store = new Ext.data.GroupingStore({
  583. data: config.data || []
  584. ,reader: new Ext.data.ArrayReader({},config.fields || [])
  585. ,sortInfo: config.sortInfo || {
  586. field: config.sortBy || 'name'
  587. ,direction: config.sortDir || 'ASC'
  588. }
  589. ,groupField: config.groupBy || 'name'
  590. });
  591. } else {
  592. this.store = new Ext.data.SimpleStore({
  593. fields: config.fields
  594. ,data: config.data || []
  595. })
  596. }
  597. return this.store;
  598. }
  599. ,loadWindow: function(btn,e,win,or) {
  600. var r = this.menu.record;
  601. if (!this.windows[win.xtype]) {
  602. Ext.applyIf(win,{
  603. scope: this
  604. ,success: this.refresh
  605. ,record: win.blankValues ? {} : r
  606. });
  607. if (or) {
  608. Ext.apply(win,or);
  609. }
  610. this.windows[win.xtype] = Ext.ComponentMgr.create(win);
  611. }
  612. if (this.windows[win.xtype].setValues && win.blankValues !== true && r != undefined) {
  613. this.windows[win.xtype].setValues(r);
  614. }
  615. this.windows[win.xtype].show(e.target);
  616. }
  617. ,_loadColumnModel: function() {
  618. if (this.config.columns) {
  619. var c = this.config.columns;
  620. for (var i=0;i<c.length;i++) {
  621. if (typeof(c[i].editor) == 'string') {
  622. c[i].editor = eval(c[i].editor);
  623. }
  624. if (typeof(c[i].renderer) == 'string') {
  625. c[i].renderer = eval(c[i].renderer);
  626. }
  627. if (typeof(c[i].editor) == 'object' && c[i].editor.xtype) {
  628. var r = c[i].editor.renderer;
  629. c[i].editor = Ext.ComponentMgr.create(c[i].editor);
  630. if (r === true) {
  631. if (c[i].editor && c[i].editor.store && !c[i].editor.store.isLoaded && c[i].editor.config.mode != 'local') {
  632. c[i].editor.store.load();
  633. c[i].editor.store.isLoaded = true;
  634. }
  635. c[i].renderer = Ext.util.Format.comboRenderer(c[i].editor);
  636. } else if (c[i].editor.initialConfig.xtype === 'datefield') {
  637. c[i].renderer = Ext.util.Format.dateRenderer(c[i].editor.initialConfig.format || 'Y-m-d');
  638. } else if (r === 'boolean') {
  639. c[i].renderer = this.rendYesNo;
  640. } else if (r === 'password') {
  641. c[i].renderer = this.rendPassword;
  642. } else if (r === 'local' && typeof(c[i].renderer) == 'string') {
  643. c[i].renderer = eval(c[i].renderer);
  644. }
  645. }
  646. }
  647. this.cm = new Ext.grid.ColumnModel(c);
  648. }
  649. }
  650. ,_showMenu: function(g,ri,e) {
  651. e.stopEvent();
  652. e.preventDefault();
  653. this.menu.recordIndex = ri;
  654. this.menu.record = this.getStore().getAt(ri).data;
  655. if (!this.getSelectionModel().isSelected(ri)) {
  656. this.getSelectionModel().selectRow(ri);
  657. }
  658. this.menu.removeAll();
  659. var m = this.getMenu(g,ri);
  660. if (m) {
  661. this.addContextMenuItem(m);
  662. this.menu.showAt(e.xy);
  663. }
  664. }
  665. ,getMenu: function() {
  666. return this.menu.record.menu;
  667. }
  668. ,addContextMenuItem: function(items) {
  669. var l = items.length;
  670. for(var i = 0; i < l; i++) {
  671. var options = items[i];
  672. if (options == '-') {
  673. this.menu.add('-');
  674. continue;
  675. }
  676. var h = Ext.emptyFn;
  677. if (options.handler) {
  678. h = eval(options.handler);
  679. if (h && typeof(h) == 'object' && h.xtype) {
  680. h = this.loadWindow.createDelegate(this,[h],true);
  681. }
  682. } else {
  683. h = function(itm) {
  684. var o = itm.options;
  685. var id = this.menu.record.id;
  686. var w = Ext.get('modx_content');
  687. if (o.confirm) {
  688. Ext.Msg.confirm('',o.confirm,function(e) {
  689. if (e == 'yes') {
  690. var a = Ext.urlEncode(o.params || {action: o.action});
  691. var s = '?id='+id+'&'+a;
  692. if (w === null) {
  693. location.href = s;
  694. } else { w.dom.src = s; }
  695. }
  696. },this);
  697. } else {
  698. var a = Ext.urlEncode(o.params || {action: o.action});
  699. var s = '?id='+id+'&'+a;
  700. if (w === null) {
  701. location.href = s;
  702. } else { w.dom.src = s; }
  703. }
  704. };
  705. }
  706. this.menu.add({
  707. id: options.id || Ext.id()
  708. ,text: options.text
  709. ,scope: this
  710. ,options: options
  711. ,handler: h
  712. });
  713. }
  714. }
  715. ,remove: function(config) {
  716. if (this.destroying) {
  717. return MODx.grid.LocalGrid.superclass.remove.apply(this, arguments);
  718. }
  719. var r = this.getSelectionModel().getSelected();
  720. if (this.fireEvent('beforeRemoveRow',r)) {
  721. Ext.Msg.confirm(config.title || '',config.text || '',function(e) {
  722. if (e == 'yes') {
  723. this.getStore().remove(r);
  724. this.fireEvent('afterRemoveRow',r);
  725. }
  726. },this);
  727. }
  728. }
  729. ,encode: function() {
  730. var s = this.getStore();
  731. var ct = s.getCount();
  732. var rs = this.config.encodeByPk ? {} : [];
  733. var r;
  734. for (var j=0;j<ct;j++) {
  735. r = s.getAt(j).data;
  736. r.menu = null;
  737. if (this.config.encodeAssoc) {
  738. rs[r[this.config.encodeByPk || 'id']] = r;
  739. } else {
  740. rs.push(r);
  741. }
  742. }
  743. return Ext.encode(rs);
  744. }
  745. ,expandAll: function() {
  746. var expander = this.findExpanderPlugin(this.config.plugins);
  747. if (!expander) {
  748. return false;
  749. }
  750. var rows = this.getView().getRows();
  751. for (var i = 0; i < rows.length; i++) {
  752. expander.expandRow(rows[i]);
  753. }
  754. if (this.tools['plus'] !== undefined) {
  755. this.tools['plus'].hide();
  756. }
  757. if (this.tools['minus'] !== undefined) {
  758. this.tools['minus'].show();
  759. }
  760. return true;
  761. }
  762. ,collapseAll: function() {
  763. var expander = this.findExpanderPlugin(this.config.plugins);
  764. if (!expander) {
  765. return false;
  766. }
  767. var rows = this.getView().getRows();
  768. for (var i = 0; i < rows.length; i++) {
  769. expander.collapseRow(rows[i]);
  770. }
  771. if (this.tools['minus'] !== undefined) {
  772. this.tools['minus'].hide();
  773. }
  774. if (this.tools['plus'] !== undefined) {
  775. this.tools['plus'].show();
  776. }
  777. return true;
  778. }
  779. /**
  780. * Returns first found expander plugin
  781. * @param plugins
  782. */
  783. ,findExpanderPlugin: function (plugins) {
  784. if (Ext.isObject(plugins)) {
  785. plugins = [plugins];
  786. }
  787. var index = Ext.each(plugins, function (item) {
  788. if (item.id !== undefined && item.id === 'expander') {
  789. return false;
  790. }
  791. });
  792. return plugins[index];
  793. }
  794. ,rendYesNo: function(d,c) {
  795. switch(d) {
  796. case '':
  797. return '-';
  798. case false:
  799. c.css = 'red';
  800. return _('no');
  801. case true:
  802. c.css = 'green';
  803. return _('yes');
  804. }
  805. }
  806. ,rendPassword: function(v) {
  807. var z = '';
  808. for (var i=0;i<v.length;i++) {
  809. z = z+'*';
  810. }
  811. return z;
  812. }
  813. });
  814. Ext.reg('grid-local',MODx.grid.LocalGrid);
  815. Ext.reg('modx-grid-local',MODx.grid.LocalGrid);
  816. /* grid extensions */
  817. /*!
  818. * Ext JS Library 3.4.0
  819. * Copyright(c) 2006-2011 Sencha Inc.
  820. * licensing@sencha.com
  821. * http://www.sencha.com/license
  822. */
  823. Ext.ns('Ext.ux.grid');
  824. /**
  825. * @class Ext.ux.grid.RowExpander
  826. * @extends Ext.util.Observable
  827. * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables
  828. * a second row body which expands/contracts. The expand/contract behavior is configurable to react
  829. * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.
  830. *
  831. * @ptype rowexpander
  832. */
  833. Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, {
  834. /**
  835. * @cfg {Boolean} expandOnEnter
  836. * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter
  837. * key is pressed (defaults to <tt>true</tt>).
  838. */
  839. expandOnEnter : true,
  840. /**
  841. * @cfg {Boolean} expandOnDblClick
  842. * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked
  843. * (defaults to <tt>true</tt>).
  844. */
  845. expandOnDblClick : true,
  846. header : '',
  847. width : 20,
  848. sortable : false,
  849. fixed : true,
  850. hideable: false,
  851. menuDisabled : true,
  852. dataIndex : '',
  853. id : 'expander',
  854. lazyRender : true,
  855. enableCaching : true,
  856. constructor: function(config){
  857. Ext.apply(this, config);
  858. this.addEvents({
  859. /**
  860. * @event beforeexpand
  861. * Fires before the row expands. Have the listener return false to prevent the row from expanding.
  862. * @param {Object} this RowExpander object.
  863. * @param {Object} Ext.data.Record Record for the selected row.
  864. * @param {Object} body body element for the secondary row.
  865. * @param {Number} rowIndex The current row index.
  866. */
  867. beforeexpand: true,
  868. /**
  869. * @event expand
  870. * Fires after the row expands.
  871. * @param {Object} this RowExpander object.
  872. * @param {Object} Ext.data.Record Record for the selected row.
  873. * @param {Object} body body element for the secondary row.
  874. * @param {Number} rowIndex The current row index.
  875. */
  876. expand: true,
  877. /**
  878. * @event beforecollapse
  879. * Fires before the row collapses. Have the listener return false to prevent the row from collapsing.
  880. * @param {Object} this RowExpander object.
  881. * @param {Object} Ext.data.Record Record for the selected row.
  882. * @param {Object} body body element for the secondary row.
  883. * @param {Number} rowIndex The current row index.
  884. */
  885. beforecollapse: true,
  886. /**
  887. * @event collapse
  888. * Fires after the row collapses.
  889. * @param {Object} this RowExpander object.
  890. * @param {Object} Ext.data.Record Record for the selected row.
  891. * @param {Object} body body element for the secondary row.
  892. * @param {Number} rowIndex The current row index.
  893. */
  894. collapse: true
  895. });
  896. Ext.ux.grid.RowExpander.superclass.constructor.call(this);
  897. if(this.tpl){
  898. if(typeof this.tpl == 'string'){
  899. this.tpl = new Ext.Template(this.tpl);
  900. }
  901. this.tpl.compile();
  902. }
  903. this.state = {};
  904. this.bodyContent = {};
  905. },
  906. getRowClass : function(record, rowIndex, p, ds){
  907. p.cols = p.cols-1;
  908. var content = this.bodyContent[record.id];
  909. if(!content && !this.lazyRender){
  910. content = this.getBodyContent(record, rowIndex);
  911. }
  912. if(content){
  913. p.body = content;
  914. }
  915. return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
  916. },
  917. init : function(grid){
  918. this.grid = grid;
  919. var view = grid.getView();
  920. view.getRowClass = this.getRowClass.createDelegate(this);
  921. view.enableRowBody = true;
  922. grid.on('render', this.onRender, this);
  923. grid.on('destroy', this.onDestroy, this);
  924. },
  925. // @private
  926. onRender: function() {
  927. var grid = this.grid;
  928. var mainBody = grid.getView().mainBody;
  929. mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.x-grid3-row-expander'});
  930. if (this.expandOnEnter) {
  931. this.keyNav = new Ext.KeyNav(this.grid.getGridEl(), {
  932. 'enter' : this.onEnter,
  933. scope: this
  934. });
  935. }
  936. if (this.expandOnDblClick) {
  937. grid.on('rowdblclick', this.onRowDblClick, this);
  938. }
  939. },
  940. // @private
  941. onDestroy: function() {
  942. if(this.keyNav){
  943. this.keyNav.disable();
  944. delete this.keyNav;
  945. }
  946. /*
  947. * A majority of the time, the plugin will be destroyed along with the grid,
  948. * which means the mainBody won't be available. On the off chance that the plugin
  949. * isn't destroyed with the grid, take care of removing the listener.
  950. */
  951. var mainBody = this.grid.getView().mainBody;
  952. if(mainBody){
  953. mainBody.un('mousedown', this.onMouseDown, this);
  954. }
  955. },
  956. // @private
  957. onRowDblClick: function(grid, rowIdx, e) {
  958. this.toggleRow(rowIdx);
  959. },
  960. onEnter: function(e) {
  961. var g = this.grid;
  962. var sm = g.getSelectionModel();
  963. var sels = sm.getSelections();
  964. for (var i = 0, len = sels.length; i < len; i++) {
  965. var rowIdx = g.getStore().indexOf(sels[i]);
  966. this.toggleRow(rowIdx);
  967. }
  968. },
  969. getBodyContent : function(record, index){
  970. if(!this.enableCaching){
  971. return this.tpl.apply(record.data);
  972. }
  973. var content = this.bodyContent[record.id];
  974. if(!content){
  975. content = this.tpl.apply(record.data);
  976. this.bodyContent[record.id] = content;
  977. }
  978. return content;
  979. },
  980. onMouseDown : function(e, t){
  981. e.stopEvent();
  982. var row = e.getTarget('.x-grid3-row');
  983. this.toggleRow(row);
  984. },
  985. renderer : function(v, p, record){
  986. p.cellAttr = 'rowspan="2"';
  987. return '<div class="x-grid3-row-expander">&#160;</div>';
  988. },
  989. beforeExpand : function(record, body, rowIndex){
  990. if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){
  991. if(this.tpl && this.lazyRender){
  992. body.innerHTML = this.getBodyContent(record, rowIndex);
  993. }
  994. return true;
  995. }else{
  996. return false;
  997. }
  998. },
  999. toggleRow : function(row){
  1000. if(typeof row == 'number'){
  1001. row = this.grid.view.getRow(row);
  1002. }
  1003. this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
  1004. },
  1005. expandRow : function(row){
  1006. if(typeof row == 'number'){
  1007. row = this.grid.view.getRow(row);
  1008. }
  1009. var record = this.grid.store.getAt(row.rowIndex);
  1010. var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
  1011. if(this.beforeExpand(record, body, row.rowIndex)){
  1012. this.state[record.id] = true;
  1013. Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
  1014. this.fireEvent('expand', this, record, body, row.rowIndex);
  1015. }
  1016. },
  1017. collapseRow : function(row){
  1018. if(typeof row == 'number'){
  1019. row = this.grid.view.getRow(row);
  1020. }
  1021. var record = this.grid.store.getAt(row.rowIndex);
  1022. var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
  1023. if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){
  1024. this.state[record.id] = false;
  1025. Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
  1026. this.fireEvent('collapse', this, record, body, row.rowIndex);
  1027. }
  1028. }
  1029. });
  1030. Ext.preg('rowexpander', Ext.ux.grid.RowExpander);
  1031. //backwards compat
  1032. Ext.grid.RowExpander = Ext.ux.grid.RowExpander;
  1033. Ext.ns('Ext.ux.grid');Ext.ux.grid.CheckColumn=function(a){Ext.apply(this,a);if(!this.id){this.id=Ext.id()}this.renderer=this.renderer.createDelegate(this)};Ext.ux.grid.CheckColumn.prototype={init:function(b){this.grid=b;this.grid.on('render',function(){var a=this.grid.getView();a.mainBody.on('mousedown',this.onMouseDown,this)},this);this.grid.on('destroy',this.onDestroy,this)},onMouseDown:function(e,t){this.grid.fireEvent('rowclick');if(t.className&&t.className.indexOf('x-grid3-cc-'+this.id)!=-1){e.stopEvent();var a=this.grid.getView().findRowIndex(t);var b=this.grid.store.getAt(a);b.set(this.dataIndex,!b.data[this.dataIndex]);this.grid.fireEvent('afteredit')}},renderer:function(v,p,a){p.css+=' x-grid3-check-col-td';return'<div class="x-grid3-check-col'+(v?'-on':'')+' x-grid3-cc-'+this.id+'">&#160;</div>'},onDestroy:function(){var mainBody = this.grid.getView().mainBody;
  1034. if(mainBody){
  1035. mainBody.un('mousedown', this.onMouseDown, this);
  1036. }}};Ext.preg('checkcolumn',Ext.ux.grid.CheckColumn);Ext.grid.CheckColumn=Ext.ux.grid.CheckColumn;
  1037. Ext.grid.PropertyColumnModel=function(a,b){var g=Ext.grid,f=Ext.form;this.grid=a;g.PropertyColumnModel.superclass.constructor.call(this,[{header:this.nameText,width:50,sortable:true,dataIndex:'name',id:'name',menuDisabled:true},{header:this.valueText,width:50,resizable:false,dataIndex:'value',id:'value',menuDisabled:true}]);this.store=b;var c=new f.Field({autoCreate:{tag:'select',children:[{tag:'option',value:'true',html:'true'},{tag:'option',value:'false',html:'false'}]},getValue:function(){return this.el.dom.value=='true'}});this.editors={'date':new g.GridEditor(new f.DateField({selectOnFocus:true})),'string':new g.GridEditor(new f.TextField({selectOnFocus:true})),'number':new g.GridEditor(new f.NumberField({selectOnFocus:true,style:'text-align:left;'})),'boolean':new g.GridEditor(c)};this.renderCellDelegate=this.renderCell.createDelegate(this);this.renderPropDelegate=this.renderProp.createDelegate(this)};Ext.extend(Ext.grid.PropertyColumnModel,Ext.grid.ColumnModel,{nameText:'Name',valueText:'Value',dateFormat:'m/j/Y',renderDate:function(a){return a.dateFormat(this.dateFormat)},renderBool:function(a){return a?'true':'false'},isCellEditable:function(a,b){return a==1},getRenderer:function(a){return a==1?this.renderCellDelegate:this.renderPropDelegate},renderProp:function(v){return this.getPropertyName(v)},renderCell:function(a){var b=a;if(Ext.isDate(a)){b=this.renderDate(a)}else if(typeof a=='boolean'){b=this.renderBool(a)}return Ext.util.Format.htmlEncode(b)},getPropertyName:function(a){var b=this.grid.propertyNames;return b&&b[a]?b[a]:a},getCellEditor:function(a,b){var p=this.store.getProperty(b),n=p.data.name,val=p.data.value;if(this.grid.customEditors[n]){return this.grid.customEditors[n]}if(Ext.isDate(val)){return this.editors.date}else if(typeof val=='number'){return this.editors.number}else if(typeof val=='boolean'){return this.editors['boolean']}else{return this.editors.string}},destroy:function(){Ext.grid.PropertyColumnModel.superclass.destroy.call(this);for(var a in this.editors){Ext.destroy(a)}}});