modx.panel.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633
  1. Ext.namespace('MODx.panel');
  2. MODx.Panel = function(config) {
  3. config = config || {};
  4. Ext.applyIf(config,{
  5. cls: 'modx-panel'
  6. ,title: ''
  7. });
  8. MODx.Panel.superclass.constructor.call(this,config);
  9. this.config = config;
  10. };
  11. Ext.extend(MODx.Panel,Ext.Panel);
  12. Ext.reg('modx-panel',MODx.Panel);
  13. MODx.FormPanel = function(config) {
  14. config = config || {};
  15. Ext.applyIf(config,{
  16. autoHeight: true
  17. ,collapsible: true
  18. ,bodyStyle: ''
  19. ,layout: 'anchor'
  20. ,border: false
  21. ,header: false
  22. ,method: 'POST'
  23. ,cls: 'modx-form'
  24. ,allowDrop: true
  25. ,errorReader: MODx.util.JSONReader
  26. ,checkDirty: true
  27. ,useLoadingMask: false
  28. ,defaults: { collapsible: false ,autoHeight: true, border: false }
  29. });
  30. if (config.items) { this.addChangeEvent(config.items); }
  31. MODx.FormPanel.superclass.constructor.call(this,config);
  32. this.config = config;
  33. this.addEvents({
  34. setup: true
  35. ,fieldChange: true
  36. ,ready: true
  37. ,beforeSubmit: true
  38. ,success: true
  39. ,failure: true
  40. ,save: true
  41. ,actionNew: true
  42. ,actionContinue: true
  43. ,actionClose: true
  44. ,postReady: true
  45. });
  46. this.getForm().addEvents({
  47. success: true
  48. ,failure: true
  49. });
  50. this.dropTargets = [];
  51. this.on('ready',this.onReady);
  52. if (this.config.useLoadingMask) {
  53. this.on('render', function() {
  54. this.mask = new Ext.LoadMask(this.getEl());
  55. this.mask.show();
  56. });
  57. }
  58. if (this.fireEvent('setup',config)) {
  59. this.clearDirty();
  60. }
  61. this.focusFirstField();
  62. };
  63. Ext.extend(MODx.FormPanel,Ext.FormPanel,{
  64. isReady: false
  65. ,defaultValues: []
  66. ,initialized: false
  67. ,submit: function(o) {
  68. var fm = this.getForm();
  69. if (fm.isValid() || o.bypassValidCheck) {
  70. o = o || {};
  71. o.headers = {
  72. 'Powered-By': 'MODx'
  73. ,'modAuth': MODx.siteId
  74. };
  75. if (this.fireEvent('beforeSubmit',{
  76. form: fm
  77. ,options: o
  78. ,config: this.config
  79. })) {
  80. fm.submit({
  81. waitMsg: this.config.saveMsg || _('saving')
  82. ,scope: this
  83. ,headers: o.headers
  84. ,clientValidation: (o.bypassValidCheck ? false : true)
  85. ,failure: function(f,a) {
  86. if (this.fireEvent('failure',{
  87. form: f
  88. ,result: a.result
  89. ,options: o
  90. ,config: this.config
  91. })) {
  92. MODx.form.Handler.errorExt(a.result,f);
  93. }
  94. }
  95. ,success: function(f,a) {
  96. if (this.config.success) {
  97. Ext.callback(this.config.success,this.config.scope || this,[f,a]);
  98. }
  99. this.fireEvent('success',{
  100. form:f
  101. ,result:a.result
  102. ,options:o
  103. ,config:this.config
  104. });
  105. this.clearDirty();
  106. this.fireEvent('setup',this.config);
  107. //get our Active input value and keep focus
  108. var lastActiveEle = Ext.state.Manager.get('curFocus');
  109. if (lastActiveEle && lastActiveEle != '') {
  110. Ext.state.Manager.clear('curFocus');
  111. var initFocus = document.getElementById(lastActiveEle);
  112. if(initFocus) initFocus.focus();
  113. }
  114. }
  115. });
  116. }
  117. } else {
  118. return false;
  119. }
  120. return true;
  121. }
  122. ,focusFirstField: function() {
  123. if (this.getForm().items.getCount() > 0) {
  124. var fld = this.findFirstTextField();
  125. if (fld) { fld.focus(false,200); }
  126. }
  127. }
  128. ,findFirstTextField: function(i) {
  129. i = i || 0;
  130. var fld = this.getForm().items.itemAt(i);
  131. if (!fld) return false;
  132. if (fld.isXType('combo') || fld.isXType('checkbox') || fld.isXType('radio') || fld.isXType('displayfield') || fld.isXType('statictextfield') || fld.isXType('hidden')) {
  133. i = i+1;
  134. fld = this.findFirstTextField(i);
  135. }
  136. return fld;
  137. }
  138. ,addChangeEvent: function(items) {
  139. if (!items) { return false; }
  140. if (typeof(items) == 'object' && items.items) {
  141. items = items.items;
  142. }
  143. for (var f=0;f<items.length;f++) {
  144. var cmp = items[f];
  145. if (cmp.items) {
  146. this.addChangeEvent(cmp.items);
  147. } else if (cmp.xtype) {
  148. if (!cmp.listeners) { cmp.listeners = {}; }
  149. var ctypes = ['change'];
  150. cmp.enableKeyEvents = true;
  151. switch (cmp.xtype) {
  152. case 'numberfield':
  153. case 'textfield':
  154. case 'textarea':
  155. ctypes = ['keydown', 'change'];
  156. break;
  157. case 'checkbox':
  158. case 'xcheckbox':
  159. case 'radio':
  160. ctypes = ['check'];
  161. break;
  162. }
  163. if (cmp.xtype && cmp.xtype.indexOf('modx-combo') == 0) {
  164. ctypes = ['select'];
  165. }
  166. var that = this;
  167. Ext.iterate(ctypes, function(ctype) {
  168. if (cmp.listeners[ctype] && cmp.listeners[ctype].fn) {
  169. cmp.listeners[ctype] = {fn:that.fieldChangeEvent.createSequence(cmp.listeners[ctype].fn,cmp.listeners[ctype].scope),scope:that}
  170. } else {
  171. cmp.listeners[ctype] = {fn:that.fieldChangeEvent,scope:that};
  172. }
  173. });
  174. }
  175. }
  176. }
  177. ,fieldChangeEvent: function(fld,nv,ov,f) {
  178. if (!this.isReady) { return false; }
  179. var f = this.config.onDirtyForm ? Ext.getCmp(this.config.onDirtyForm) : this.getForm();
  180. this.fireEvent('fieldChange',{
  181. field: fld
  182. ,nv: nv
  183. ,ov: ov
  184. ,form: f
  185. });
  186. }
  187. ,markDirty: function() {
  188. this.fireEvent('fieldChange');
  189. }
  190. ,isDirty: function() {
  191. var f = this.config.onDirtyForm ? Ext.getCmp(this.config.onDirtyForm) : this.getForm();
  192. return f.isDirty();
  193. }
  194. ,clearDirty: function() {
  195. var f = this.config.onDirtyForm ? Ext.getCmp(this.config.onDirtyForm) : this.getForm();
  196. return f.clearDirty();
  197. }
  198. ,onReady: function(r) {
  199. this.isReady = true;
  200. if (this.config.allowDrop) { this.loadDropZones(); }
  201. if (this.config.useLoadingMask && this.mask) {
  202. this.mask.hide();
  203. }
  204. this.fireEvent('postReady');
  205. }
  206. ,loadDropZones: function() {
  207. var dropTargets = this.dropTargets;
  208. var flds = this.getForm().items;
  209. flds.each(function(fld) {
  210. if (fld.isFormField && (
  211. fld.isXType('textfield') || fld.isXType('textarea')
  212. ) && !fld.isXType('combo')) {
  213. var el = fld.getEl();
  214. if (el) {
  215. var target = new MODx.load({
  216. xtype: 'modx-treedrop'
  217. ,target: fld
  218. ,targetEl: el.dom
  219. });
  220. dropTargets.push(target);
  221. }
  222. }
  223. });
  224. }
  225. ,getField: function(f) {
  226. var fld = false;
  227. if (typeof f == 'string') {
  228. fld = this.getForm().findField(f);
  229. if (!fld) { fld = Ext.getCmp(f); }
  230. }
  231. return fld;
  232. }
  233. ,hideField: function(flds) {
  234. if (!Ext.isArray(flds)) { flds = flds[flds]; }
  235. var f;
  236. for (var i=0;i<flds.length;i++) {
  237. f = this.getField(flds[i]);
  238. if (!f) return;
  239. f.hide();
  240. var d = f.getEl().up('.x-form-item');
  241. if (d) { d.setDisplayed(false); }
  242. }
  243. }
  244. ,showField: function(flds) {
  245. if (!Ext.isArray(flds)) { flds = flds[flds]; }
  246. var f;
  247. for (var i=0;i<flds.length;i++) {
  248. f = this.getField(flds[i]);
  249. if (!f) return;
  250. f.enable();
  251. f.show();
  252. var d = f.getEl().up('.x-form-item');
  253. if (d) { d.setDisplayed(true); }
  254. }
  255. }
  256. ,setLabel: function(flds,vals,bp){
  257. if (!Ext.isArray(flds)) { flds = flds[flds]; }
  258. if (!Ext.isArray(vals)) { vals = valss[vals]; }
  259. var f,v;
  260. for (var i=0;i<flds.length;i++) {
  261. f = this.getField(flds[i]);
  262. if (!f) return;
  263. v = String.format('{0}',vals[i]);
  264. if (f.xtype == 'checkbox' || f.xtype == 'xcheckbox' || f.xtype == 'radio') {
  265. f.setBoxLabel(v);
  266. } else if (f.label) {
  267. f.label.update(v);
  268. }
  269. }
  270. }
  271. ,destroy: function() {
  272. for (var i = 0; i < this.dropTargets.length; i++) {
  273. this.dropTargets[i].destroy();
  274. }
  275. MODx.FormPanel.superclass.destroy.call(this);
  276. }
  277. /**
  278. * Find errored field in the panel and activates the tab where the first error was found.
  279. *
  280. * @param {Array} detectingForms - array of forms where we should find errors
  281. * @param {String} tabsId - id of tab component for a given panel
  282. */
  283. ,showErroredTab: function(detectingForms, tabsId) {
  284. var tab = null, index = null;
  285. for (var i = 0; i < detectingForms.length; i++) {
  286. var component = Ext.getCmp(detectingForms[i]);
  287. if (component && component.el && component.el.dom) {
  288. if (this.detectErrors(component.el.dom)) {
  289. tab = component.itemId ? component.itemId : detectingForms[i];
  290. break;
  291. }
  292. }
  293. }
  294. if (tab === null) {
  295. return;
  296. }
  297. var tabs = Ext.getCmp(tabsId);
  298. if (tabs && tabs.items && tabs.items.keys) {
  299. index = tabs.items.keys.indexOf(tab);
  300. }
  301. if (!tabs.items.items[index].hidden) {
  302. return;
  303. }
  304. tabs.activate(tab);
  305. }
  306. ,detectErrors: function(node) {
  307. if (typeof node.classList !== 'undefined' && node.classList.contains('x-form-invalid')) {
  308. return true;
  309. }
  310. if (typeof node.children == 'undefined') {
  311. return false;
  312. }
  313. for (var i = 0; i < node.children.length; i++) {
  314. if (this.detectErrors(node.children[i])) {
  315. return true;
  316. }
  317. }
  318. return false;
  319. }
  320. });
  321. Ext.reg('modx-formpanel',MODx.FormPanel);
  322. MODx.panel.Wizard = function(config) {
  323. config = config || {};
  324. Ext.applyIf(config,{
  325. layout: 'card'
  326. ,activeItem: 0
  327. ,resizable: true
  328. ,collapsible: true
  329. ,maximizable: true
  330. ,autoHeight: true
  331. ,width: 750
  332. ,firstPanel: ''
  333. ,lastPanel: ''
  334. ,defaults: { border: false }
  335. ,modal: true
  336. ,txtFinish: _('finish')
  337. ,txtNext: _('next')
  338. ,txtBack: _('back')
  339. ,bbar: [{
  340. id: 'pi-btn-bck'
  341. ,itemId: 'btn-back'
  342. ,text: config.txtBack || _('back')
  343. ,handler: this.navHandler.createDelegate(this,[-1])
  344. ,scope: this
  345. ,disabled: true
  346. },{
  347. id: 'pi-btn-fwd'
  348. ,itemId: 'btn-fwd'
  349. ,text: config.txtNext || _('next')
  350. ,handler: this.navHandler.createDelegate(this,[1])
  351. ,scope: this
  352. }]
  353. });
  354. MODx.panel.Wizard.superclass.constructor.call(this,config);
  355. this.config = config;
  356. this.lastActiveItem = this.config.firstPanel;
  357. this._go();
  358. };
  359. Ext.extend(MODx.panel.Wizard,Ext.Panel,{
  360. windows: {}
  361. ,_go: function() {
  362. this.getBottomToolbar().items.item(1).setText(this.config.txtNext);
  363. this.proceed(this.config.firstPanel);
  364. }
  365. ,navHandler: function(dir) {
  366. this.doLayout();
  367. var a = this.getLayout().activeItem;
  368. if (dir == -1) {
  369. this.proceed(a.config.back || a.config.id);
  370. } else {
  371. a.submit({
  372. scope: this
  373. ,proceed: this.proceed
  374. });
  375. }
  376. }
  377. ,proceed: function(id) {
  378. this.doLayout();
  379. this.getLayout().setActiveItem(id);
  380. if (id == this.config.firstPanel) {
  381. this.getBottomToolbar().items.item(0).setDisabled(true);
  382. this.getBottomToolbar().items.item(1).setText(this.config.txtNext);
  383. } else if (id == this.config.lastPanel) {
  384. this.getBottomToolbar().items.item(1).setText(this.config.txtFinish);
  385. } else {
  386. this.getBottomToolbar().items.item(0).setDisabled(false);
  387. this.getBottomToolbar().items.item(1).setText(this.config.txtNext);
  388. }
  389. }
  390. });
  391. Ext.reg('modx-panel-wizard',MODx.panel.Wizard);
  392. MODx.panel.WizardPanel = function(config) {
  393. config = config || {};
  394. Ext.applyIf(config,{
  395. wizard: null
  396. ,checkDirty: false
  397. ,bodyStyle: 'padding: 3em 3em'
  398. ,hideMode: 'offsets'
  399. });
  400. MODx.panel.WizardPanel.superclass.constructor.call(this,config);
  401. };
  402. Ext.extend(MODx.panel.WizardPanel,MODx.FormPanel);
  403. Ext.reg('modx-wizard-panel',MODx.panel.WizardPanel);
  404. MODx.PanelSpacer = {
  405. html: '<br />'
  406. ,border: false
  407. };
  408. /**
  409. * A template panel base class
  410. *
  411. * @class MODx.TemplatePanel
  412. * @extends Ext.Panel
  413. * @param {Object} config An object of options.
  414. * @xtype modx-template-panel
  415. */
  416. MODx.TemplatePanel = function(config) {
  417. config = config || {};
  418. Ext.applyIf(config,{
  419. frame:false
  420. ,startingMarkup: '<tpl for=".">'
  421. +'<div class="empty-text-wrapper"><p>{text}</p></div>'
  422. +'</tpl>'
  423. ,startingText: 'Loading...'
  424. ,markup: null
  425. ,plain:true
  426. ,border: false
  427. });
  428. MODx.TemplatePanel.superclass.constructor.call(this,config);
  429. this.on('render', this.init, this);
  430. }
  431. Ext.extend(MODx.TemplatePanel,Ext.Panel,{
  432. init: function(){
  433. this.defaultMarkup = new Ext.XTemplate(this.startingMarkup, { compiled: true });
  434. this.reset();
  435. this.tpl = new Ext.XTemplate(this.markup, { compiled: true });
  436. }
  437. ,reset: function(){
  438. this.body.hide();
  439. this.defaultMarkup.overwrite(this.body, {text: this.startingText});
  440. this.body.slideIn('r', {stopFx:true, duration:.2});
  441. setTimeout(function(){
  442. Ext.getCmp('modx-content').doLayout();
  443. }, 500);
  444. }
  445. ,updateDetail: function(data) {
  446. this.body.hide();
  447. this.tpl.overwrite(this.body, data);
  448. this.body.slideIn('r', {stopFx:true, duration:.2});
  449. setTimeout(function(){
  450. Ext.getCmp('modx-content').doLayout();
  451. }, 500);
  452. }
  453. });
  454. Ext.reg('modx-template-panel',MODx.TemplatePanel);
  455. /**
  456. * A breacrumb builder + the panel desc if necessary
  457. *
  458. * @class MODx.BreadcrumbsPanel
  459. * @extends Ext.Panel
  460. * @param {Object} config An object of options.
  461. * @xtype modx-breadcrumbs-panel
  462. */
  463. MODx.BreadcrumbsPanel = function(config) {
  464. config = config || {};
  465. Ext.applyIf(config,{
  466. frame:false
  467. ,plain:true
  468. ,border: false
  469. ,desc: 'This the description part of this panel'
  470. ,bdMarkup: '<tpl if="typeof(trail) != &quot;undefined&quot;">'
  471. +'<div class="crumb_wrapper"><ul class="crumbs">'
  472. +'<tpl for="trail">'
  473. +'<li{[values.className != undefined ? \' class="\'+values.className+\'"\' : \'\' ]}>'
  474. +'<tpl if="typeof pnl != \'undefined\'">'
  475. +'<button type="button" class="controlBtn {pnl}{[values.root ? \' root\' : \'\' ]}">{text}</button>'
  476. +'</tpl>'
  477. +'<tpl if="typeof install != \'undefined\'">'
  478. +'<button type="button" class="controlBtn install{[values.root ? \' root\' : \'\' ]}">{text}</button>'
  479. +'</tpl>'
  480. +'<tpl if="typeof pnl == \'undefined\' && typeof install == \'undefined\'"><span class="text{[values.root ? \' root\' : \'\' ]}">{text}</span></tpl>'
  481. +'</li>'
  482. +'</tpl>'
  483. +'</ul></div>'
  484. +'</tpl>'
  485. +'<tpl if="typeof(text) != &quot;undefined&quot;">'
  486. +'<div class="panel-desc{[values.className != undefined ? \' \'+values.className+\'"\' : \'\' ]}"><p>{text}</p></div>'
  487. +'</tpl>'
  488. ,root : {
  489. text : 'Home'
  490. ,className: 'first'
  491. ,root: true
  492. ,pnl: ''
  493. }
  494. ,bodyCssClass: 'breadcrumbs'
  495. });
  496. MODx.BreadcrumbsPanel.superclass.constructor.call(this,config);
  497. this.on('render', this.init, this);
  498. }
  499. Ext.extend(MODx.BreadcrumbsPanel,Ext.Panel,{
  500. data: {trail: []}
  501. ,init: function(){
  502. this.tpl = new Ext.XTemplate(this.bdMarkup, { compiled: true });
  503. this.reset(this.desc);
  504. this.body.on('click', this.onClick, this);
  505. }
  506. ,getResetText: function(srcInstance){
  507. if(typeof(srcInstance) != 'object' || srcInstance == null){
  508. return srcInstance;
  509. }
  510. var newInstance = srcInstance.constructor();
  511. for(var i in srcInstance){
  512. newInstance[i] = this.getResetText(srcInstance[i]);
  513. }
  514. //The trail is not a link
  515. if(newInstance.hasOwnProperty('pnl')){
  516. delete newInstance['pnl'];
  517. }
  518. return newInstance;
  519. }
  520. ,updateDetail: function(data){
  521. this.data = data;
  522. // Automagically the trail root
  523. if(data.hasOwnProperty('trail')){
  524. var trail = data.trail;
  525. trail.unshift(this.root);
  526. }
  527. this._updatePanel(data);
  528. }
  529. ,getData: function() {
  530. return this.data;
  531. }
  532. ,reset: function(msg){
  533. if(typeof(this.resetText) == "undefined"){
  534. this.resetText = this.getResetText(this.root);
  535. }
  536. this.data = { text : msg ,trail : [this.resetText] };
  537. this._updatePanel(this.data);
  538. }
  539. ,onClick: function(e){
  540. var target = e.getTarget();
  541. var index = 1;
  542. var parent = target.parentElement;
  543. while ((parent = parent.previousSibling) != null) {
  544. index += 1;
  545. }
  546. var remove = this.data.trail.length - index;
  547. while (remove > 0) {
  548. this.data.trail.pop();
  549. remove -= 1;
  550. }
  551. elm = target.className.split(' ')[0];
  552. if(elm != "" && elm == 'controlBtn'){
  553. // Don't use "pnl" shorthand, it make the breadcrumb fail
  554. var panel = target.className.split(' ')[1];
  555. if (panel == 'install') {
  556. var last = this.data.trail[this.data.trail.length - 1];
  557. if (last != undefined && last.rec != undefined) {
  558. this.data.trail.pop();
  559. var grid = Ext.getCmp('modx-package-grid');
  560. grid.install(last.rec);
  561. return;
  562. }
  563. } else {
  564. Ext.getCmp(panel).activate();
  565. }
  566. }
  567. }
  568. ,_updatePanel: function(data){
  569. this.body.hide();
  570. this.tpl.overwrite(this.body, data);
  571. this.body.slideIn('r', {stopFx:true, duration:.2});
  572. setTimeout(function(){
  573. Ext.getCmp('modx-content').doLayout();
  574. }, 500);
  575. }
  576. });
  577. Ext.reg('modx-breadcrumbs-panel',MODx.BreadcrumbsPanel);