diff --git a/.gitignore b/.gitignore index c4dcc104ca873c7fec9f8939e07ecf4e14be73e2..93d0325152423212bdd5503e6bb8fae563f3b012 100755 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ errors/ private/ sessions/ uploads/ +web2py.plugin.dbui.w2p diff --git a/static/plugin_dbui/dbui-debug.js b/static/plugin_dbui/dbui-debug.js new file mode 100644 index 0000000000000000000000000000000000000000..69392cfc2cbb4e1bfb78903f98c446fe2c468428 --- /dev/null +++ b/static/plugin_dbui/dbui-debug.js @@ -0,0 +1,1892 @@ +/** + * appbase.js + * + * Constants and elementary functions to built an application + * + * $Id$ + * + */ +Ext.namespace('App'); + +/** + * @cfg {String} App.name + * Name of the web application. + * This constants is defined by the server. + */ + +/** + * @cfg {Array} App.storeCfgs + * Configuration option for all json stores + */ + +/** + * @param {String} App.version version of the library + */ +App.version = '0.4.3'; + +/** + * Helper function mimicking the encode_field function running on the server + * (table, field) → TableField + * @param {String} table + * @param {String} field + */ +App.encodeField = function (table, field) { + var t, f; + t = table[0].toUpperCase() + table.slice(1, table.length); + f = field[0].toUpperCase() + field.slice(1, field.length); + return t + f; +}; + +/** + * Helper function returning the App.data.DirectStore identifies by its id. + * If the store does not exit it is created and register in the store manager. + * + * NOTE: The storeId is defined by the server for each widget. + * The syntax of the storeId is "tableStore". + * If the store does not exist, its configuration is extract from + * the application constant App.storeCfgs.Then it is created and registered. + * In the current frame work a unique store is associated to a database table. + * This property guaranty that widget share the same store. + * + * @param {String} storeId + */ +App.getDirectStore = function (storeId) { + + var cfg, + store, + table; + + store = Ext.StoreMgr.lookup(storeId); + + if (!store && !Ext.isString(storeId)) { + throw new Error('Fail to instanciate the store: "' + storeId + '"'); + } + + if (!store && Ext.isString(storeId)) { + + table = storeId.slice(0, storeId.search('Store')); + cfg = App.storeCfgs[table]; + store = new App.data.DirectStore(cfg); + } + + return store; +}; + +/** + * Helper function returning the name of the database table + * associated to the store. works with store configured using the + * Dbui.getJsonStore method. + * @param {App.data.DirectStore} store + */ +App.getTableName = function (store) { + return store.baseParams.tableName; +}; + +/** + * Helper function to determine if a plugin is present + * in the plugins list of a component + * @param {Ext.Component} + * @param {String} ptype the ptype of the plugin + */ +App.isPlugin = function (component, ptype) { + + var i, + plugin; + + if (!component.hasOwnProperty('plugins')) { + return false; + } + + for (i = 0; i < component.plugins.length; i += 1) { + + plugin = component.plugins[i]; + + if (plugin === ptype) { + return true; + } + + if ((typeof (plugin) === 'object') && plugin.hasOwnProperty('ptype') && (plugin.ptype === ptype)) { + return true; + } + } + return false; +}; +/** + * A border layout with a panel and a selector. + * + * The selector is a collapsible panel appearing on the right side. + * It contains a set of Fields usually organized in field sets. + * By default the selector contains two buttons Go and Reset. + * + * The logic between the panel, the selector and the buttons + * is defined in inherited class. + * + * The type of this component is xpanelwithselector. + * + * @extend: Ext.Panel + * + */ +Ext.namespace('App'); + +App.BasePanelWithSelector = Ext.extend(Ext.Panel, { + + /** + * @param {Object} panel and selector configurations + */ + panelCfg: null, + selectorCfg: null, + + /** + * Private attribute for internationalization + */ + textGo: 'Go', + textReset: 'Reset', + + /** + * constructor + * @param {Object} config + */ + constructor: function(config){ + + var cfg, + panel, + selector; + + Ext.apply(this, config); + + panel = Ext.ComponentMgr.create(this.panelCfg); + selector = Ext.ComponentMgr.create(this.selectorCfg); + + // predefined configuration of the panel + cfg = { + layout: 'border', + items: [{ + border: false, + layout: 'fit', + itemId: 'mainPanel', + items: [panel], + region: 'center', + }, { + buttons: [{ + ref: '../../goButton', + text: this.textGo + }, { + ref: '../../resetButton', + text: this.textReset + }], + collapsible: true, + defaults: {anchor: '99%'}, + frame: true, + layout: 'form', + itemId: 'selectorPanel', + items: [selector], + region: 'east', + split: true, + width: 300, + }] + }; + + Ext.apply(this, cfg); + + // instanciate the panel + App.BasePanelWithSelector.superclass.constructor.call(this); + } +}); + +Ext.reg('xpanelwithselector', App.BasePanelWithSelector);/** + * A button to download file from the server + * + * @extends Ext.Button + * @version $Id$ + * + */ + +Ext.namespace('App'); + +App.ButtonDownload = Ext.extend(Ext.Button, { + + /** + * @cfg{String} url pointing to the server controller + * which will return the file + */ + url: undefined, + + /** + * private method requests by the component model of ExtJS + */ + initComponent: function () { + + // initialize the underlying class + App.ButtonDownload.superclass.initComponent.call(this); + + // download when the button is pressed + this.on('click', this.onDownload, this); + }, + + /** + * Handler to download the file + * The scope is the button. + * + * NOTE: this method add and iframe at the end of the DOM document + * the source of the iframe is the url pointing on the server conroller. + * The latter returun a document which is automatically loaded. + * + * Method suggested on the sencha forum: + * www.sencha.com/forum/showthread.php?26471-Download-File-on%28-click-%29 + */ + onDownload: function (button, event) { + + // remove existing iframe + try { + Ext.destroy(Ext.get('downloadIframe')); + } catch (err) {} + + // create a fresh iframe + Ext.DomHelper.append(document.body, { + tag: 'iframe', + id: 'downloadIframe', + frameBorder: 0, + width: 0, + height: 0, + css: 'display:none;visibility:hidden;height:0px;', + src: this.url + }); + } +}); + +Ext.reg('xbuttondownload', App.ButtonDownload); +/** + * The ComboBox is an Ext.form.ComboBox with an App.data.DirectStore. + * + * The type of this component is xcombobox. + * + * @extends Ext.form.ComboBox + * @version $Id$ + * + */ +Ext.namespace('App.form'); + +App.form.ComboBox = Ext.extend(Ext.form.ComboBox, { + + /** + * Predefined setting + * + */ + mode: 'remote', + editable: false, + selectOnFocus: true, + triggerAction: 'all', + typeAhead: true, + + /** + * private method require by the ExtJs component model + */ + initComponent: function () { + + // defined the empty text from the field label + if (this.fieldLabel && !this.emptyText) { + this.emptyText = "Select a " + this.fieldLabel + " ..."; + } + + // link the combobox to the data store + this.store = App.getDirectStore(this.store); + + // construct the underlying class. DON'T MOVE + App.form.ComboBox.superclass.initComponent.call(this); + + // load the store if not yet done + if (this.store.getCount() === 0) { + this.store.load(); + } + + // Set the default value when the comboBox is not yet expend + this.store.on('load', function () { + this.setValue(this.initialConfig.value); + }, this); + } +}); + +Ext.reg('xcombobox', App.form.ComboBox); +/** + * The FormPanel is an Ext.form.formPanel with an App.data.DirectStore. + * It comes with two button 'Action' and 'Reset' and a set of method to + * create, duplicate, destroy and update record in the direct store. + * + * This object can be used as a classic form, a row editor for a grid, + * a record browser, .... + * + * The action associated to the button is set via the setAction method. + * the create action is set by default. + * + * The type of this component is xform. + * + * @extends Ext.form.FormPanel + * @version $Id$ + * + */ +Ext.namespace('App.form'); + +App.form.FormPanel = Ext.extend(Ext.form.FormPanel, { + + /** + * @cfg {Ext.data.Store} store the Ext.data.Store that the form should use. + * The store has to be defined since the actions rely on it. + */ + store: null, + + /** + * Predefined configuration options + */ + autoScroll: true, + bodyStyle: 'padding:5px 5px 0', + buttons: [{ + formBind: true, + text: 'Action', + ref: '../buttonAction' + }, { + text: 'Reset', + ref: '../buttonReset' + }], + defaults: {anchor: '100%'}, + defaultType: 'textfield', + frame: true, + monitorValid: true, + + /** + * private attribute to keep track of current action parameters + */ + currentAction: null, + currentRecord: null, + + /** + * private attributes for internationalization + */ + textCreate: 'Create', + textDestroy: 'Delete', + textDuplicate: 'Duplicate', + textReset: 'Reset', + textUpdate: 'Update', + + /** + * private method require by the ExtJs component model + */ + initComponent: function () { + + // helper function to reset the form + // the scope is Ext.data.BasicStore + function resetForm() { + this.reset(); + } + + // construct the underlying class. DON'T MOVE + App.form.FormPanel.superclass.initComponent.call(this); + + // button events listeners + this.buttonAction.on('click', this.doAction, this); + this.buttonReset.on('click', resetForm, this.getForm()); + + // activate fields tooltip listeners + this.addFieldToolTipsListeners(); + + // link the form to the data store + this.store = App.getDirectStore(this.store); + this.store.on('exception', this.onStoreException, this); + this.store.on('write', resetForm, this.getForm()); + + // set the default action: create + this.setAction('create'); + this.buttonReset.setText(this.textReset); + }, + + /** + * Private method to register fields tooltip + * Require the configuration parameter Ext.form.Field.tipText. + */ + addFieldToolTipsListeners: function () { + var form; + + function createToolTip(c) { + //NOTE: don't remove the new keyword although JSlint require it + new Ext.ToolTip({ + target: c.getEl(), + title: c.fieldLabel, + anchor: 'left', + trackMouse: false, + html: Ext.util.Format.htmlEncode(c.tipText) + }); + } + + form = this.getForm(); + form.items.each(function (field) { + if (field.tipText) { + field.on('render', createToolTip); + } + }); + }, + + /** + * Private method to enable/disable all fields of the form + * @param {Object} boolean + */ + disableFields: function (bool) { + + var form = this.getForm(); + + form.items.each(function (field) { + field.setDisabled(bool); + }); + + }, + + /** + * Handler to perform the current action on the store. + * The current action is set by the setAction method. + * Should not be call directly, except for delete without confirmation. + * Raise an error if the store is not defined. + */ + doAction: function () { + + var form = this.getForm(), + newRecord; + + if (!form.isValid()) { + return; + } + + if (!this.store) { + throw new Error('the store is undefined !!!'); + } + + switch (this.currentAction) { + + case 'create': + newRecord = new this.store.recordType(); + this.updateRecord(newRecord); + this.store.add(newRecord); + break; + + case 'destroy': + this.store.remove(this.currentRecord); + break; + + case 'duplicate': + newRecord = new this.store.recordType(); + this.updateRecord(newRecord); + this.store.add(newRecord); + break; + + case 'update': + this.currentRecord.beginEdit(); + this.updateRecord(this.currentRecord); + this.currentRecord.endEdit(); + break; + } + if (this.store.autoSave === false) { + this.store.save(); + } + }, + + /** + * Private method to hardreset the form. + * + * The reset method resets the field value to the originally loaded. + * + * The hardreset erases the originally loaded values and reset the form. + * Hardreset is required to clean form in sequence like update, create,... + * It handle properly default value for combobox. + * + */ + hardReset: function () { + var form = this.getForm(); + + form.items.each(function (field) { + field.originalValue = Ext.value(field.initialConfig.value, ''); + field.setValue(field.originalValue); + }); + }, + + /** + * Handler to process store exception + * the scope of this function is App.form.FormPanel + * @param {Object} proxy + * @param {Object} type + * @param {Object} action + * @param {Object} options + * @param {Object} response + * @param {Object} arg + */ + onStoreException: function (proxy, type, action, options, response, arg) { + + var field, + fieldName, + form = this.getForm(), + record; + + // mark fields invalid + for (fieldName in response.errors) { + if (response.errors.hasOwnProperty(fieldName)) { + field = form.findField(fieldName); + field.markInvalid(response.errors[fieldName]); + } + } + + // remove the bad record from the store + // otherwise it will be send again in the next transaction + record = arg[0]; + this.store.remove(record); + }, + + /** + * Public method to set the action associated to the form. + * The record is load into the form and the text of the button is + * properly set. Understood action are: create, destroy, duplicate, + * update and view. + * @param {Object} action + * @param {Object} record + */ + setAction: function (action, record) { + + var form = this.getForm(), + table, + tableId; + + this.buttonReset.show(); + this.buttonAction.show(); + + this.disableFields(false); + + this.currentAction = action; + this.currentRecord = record; + + switch (action) { + + case 'create': + this.hardReset(); + this.buttonAction.setText(this.textCreate); + break; + + case 'destroy': + this.buttonAction.setText(this.textDestroy); + form.loadRecord(record); + break; + + case 'duplicate': + this.buttonAction.setText(this.textDuplicate); + this.hardReset(); + table = App.getTableName(this.store); + tableId = App.encodeField(table, 'id'); + delete record.data[tableId]; + form.loadRecord(record); + break; + + case 'update': + this.buttonAction.setText(this.textUpdate); + form.loadRecord(record); + break; + + case 'view': + this.buttonReset.hide(); + this.buttonAction.hide(); + form.loadRecord(record); + this.disableFields(true); + break; + } + }, + + /** + * Private method to update the selected record with the value of the form + * This method have been designed to handle foreign keys, date object, .... + * @param {Object} record + */ + updateRecord: function (record) { + + var combo, + field, + fields = [], + i, + items, + rec, + value; + + // get the list of dirty fields + items = this.findByType('field'); + for (i = 0; i < items.length; i += 1) { + field = items[i]; + if (field.getXType() !== 'compositefield') { + fields.push(field); + } + } + + // include dirty fields embedded in composite fields + items = this.findByType('compositefield'); + for (i = 0; i < items.length; i += 1) { + items[i].items.eachKey(function(key, subfield) { + fields.push(subfield); + }); + } + + // update the record + // take care of special treatment required by date and combobox + for (i = 0; i < fields.length; i += 1) { + + field = fields[i]; + value = field.getValue(); + + switch (field.getXType()) { + + // We convert the date object according to a string defined + // by the Ext.form.DateField property format. + // NOTE: by default in the json encoding, the date object + // is converted as string using iso format YYYY-MM-DDTHH:MM:SS. + // However, the string expected by the database depends on + // the date type: date, datetime or time. The format of the + // Ext.form.DateField, used by the interface, is defined in the + // property format. + + case 'datefield': + if (Ext.isDate(value)) { + value = value.format(field.format); + } + break; + + // For foreign key, the record contains the valueField + // as well as the displayField. The default action update + // the valueField but note the display field. + // The next lines append the displayField + + case 'xcombobox': + combo = field; + rec = combo.findRecord(combo.valueField, combo.getValue()); + record.set(combo.displayField, rec.get(combo.displayField)); + break; + } + + record.set(field.getName(), value); + } + + } + +}); + +Ext.reg('xform', App.form.FormPanel); +/** + * The GridFilter class + * + * Associate to grid, it contains all the logic to filter the content of a grid + * according to the value define in the form. + * + * @version $Id$ + * + */ + +Ext.namespace('App.grid'); + +App.grid.GridFilter = Ext.extend(Ext.form.FieldSet, { + + /** + * Private + * Dictionary with additional where clause + */ + filterConditions: {}, + + /** + * Private + * Initial where clause + */ + initialFilterConditions: [], + + /** + * Private + * Reference to the paging toolBar + */ + pagingToolbar: null, + + /** + * Private + * a reference to the grid store + */ + store: null, + + /** + * Predefined setting + * + * NOTE: + * the enableKeyEvents is mandatory for Ext.form.TextField + */ + defaults: { + anchor: '99%', + enableKeyEvents: true + }, + + /** + * private method requests by the component model of Ext JS + */ + initComponent: function () { + + var field, + fields, + i; + + // initialize the underlying class. + App.grid.GridFilter.superclass.initComponent.call(this); + + // associate handlers to fields + fields = this.findByType('field'); + for (i = 0; i < fields.length; i += 1) { + field = fields[i]; + + // catch a change of value for comboBox + if (field.xtype === 'xcombobox') { + field.on('select', this.onChange, this); + + } else { + // catch the key pressed enter for other fields + // wait that user finish to type to run the handler + field.on('keyup', this.onChange, this, {buffer: 500}); + } + } + }, + + /** + * Bind the grid filter to the grid + * @param {Ext.grid.GridPanel} grid + */ + bind: function (grid) { + + var baseParams, + i; + + this.store = grid.getStore(); + + // paging toolbar ? + if (grid.pagingInitialized) { + this.pagingToolbar = grid.getBottomToolbar(); + } + + // initial filter conditions + baseParams = this.store.baseParams; + for (i = 0; i < baseParams.where.length; i += 1) { + this.initialFilterConditions.push(baseParams.where[i]); + } + + }, + + /** + * Handler to catch a change in field value + */ + onChange: function (field) { + this.setupCondition(field); + }, + + /** + * Handler to reset the filter + */ + onReset: function () { + + var fields, + i; + + // reset the field value + fields = this.findByType('field'); + for (i = 0; i < fields.length; i += 1) { + fields[i].reset(); + } + + // reset filter conditions + this.filterConditions = {}; + this.store.baseParams.where = this.initialFilterConditions; + + // update the store + this.updateStore(); + }, + + /** + * Setup condition + * @param {Ext.form.Field} + */ + setupCondition: function (field) { + + var conditions = [], + i, + k, + value, + where; + + // get the field value and update the condition dictionary + value = field.getValue(); + where = field.name + " " + field.filterOperator + " '" + value + "'"; + + if (value === '') { + delete this.filterConditions[field.name]; + } else { + this.filterConditions[field.name] = where; + } + + // build the complete where clause + for (i = 0; i < this.initialFilterConditions.length; i += 1) { + conditions.push(this.initialFilterConditions[i]); + } + + for (k in this.filterConditions) { + if (this.filterConditions.hasOwnProperty(k)) { + conditions.push(this.filterConditions[k]); + } + } + + // push new condition in the where clause of the store + this.store.baseParams.where = conditions; + + // update the store + this.updateStore(); + }, + + /** + * private + * Helper function to load the store applying filter conditions + */ + updateStore: function () { + + if (this.pagingToolbar) { + this.pagingToolbar.doRefresh(); + } else { + this.store.load(); + } + } +}); + +Ext.reg('xgridfilter', App.grid.GridFilter);/** + * The Grid class is Ext.grid.GridPanel with a Ext.PagingToolbar + * Many functionality can be added through grid plugins like + * App.grid.RowEditor, .... + * + * The type of this component is xgrid. + * + * @extends Ext.gridPanel + * @version $Id$ + * + */ + +Ext.namespace('App.grid'); + +App.grid.Grid = Ext.extend(Ext.grid.GridPanel, { + + /** + * private attributes used by plugins + */ + rowEditor: null, + + /** + * private method requests by the component model of ExtJS + */ + initComponent: function () { + + // link the grid to the data store + this.store = App.getDirectStore(this.store); + + // empty Paging toolBar requires by the plugins + this.bbar = new Ext.PagingToolbar(); + this.bbar.hide(); + + // initialize the underlying class. DON'T MOVE + App.grid.Grid.superclass.initComponent.call(this); + + // load the store if not yet done + // if the grid paging is used load only the first 10 rows + if (!this.store.getTotalCount()) { + if (App.isPlugin(this, 'pGridPaging')) { + this.store.load({params: {start: 0, limit: 10}}); + } else { + this.store.load(); + } + } + } +}); + +Ext.reg('xgrid', App.grid.Grid);/** + * Plugin to render mathematics formula embedded in grid content. + * The processing is performed by MathJax. + * The MathJax library is loaded by the framework + * + * @version $Id$ + * + */ + +Ext.namespace('App.grid'); + +App.grid.MathJax = Ext.extend(Object, { + + init: function (grid) { + + function processMath(gridView) { + var domEl = Ext.getDom(gridView.el); + MathJax.Hub.Queue(["Typeset",MathJax.Hub, domEl]); + } + + grid.getView().on('refresh', processMath, grid); + grid.store.on('write', processMath, grid); + } +}); + +Ext.preg('pGridMathJax', App.grid.MathJax);/** + * The paging bottom bar plugin + * The ptype of this component is pGridPaging. + * + * + * NOTE: the number of row load in the grid at the first time + * is defined by the grid when loading the store. + * + * @version $Id$ + * + */ +Ext.namespace('App.grid'); + +App.grid.Paging = Ext.extend(Object, { + + /** + * Private parameter identifying the type of plugin + */ + ptype: 'pGridPaging', + + /** + * Private attributs for internationalization + */ + textExport: 'Export', + textSlider: 'Rows per page', + + /** + * Plugin initialization + */ + init: function (grid) { + + var bbar; + + // link the bbar and the grid store + bbar = grid.getBottomToolbar(); + bbar.bindStore(grid.store); + + // add the slider and the export button + bbar.add('-', + this.textSlider, + { + xtype: 'slider', + plugins: new Ext.slider.Tip(), + listeners: { + changecomplete: this.onChangePageSize, + scope: bbar + }, + minValue: 1, + width: 100 + }, + '->', + { + xtype: 'xbuttondownload', + text: this.textExport, + url: App.csvUrl + '?tableName=' + App.getTableName(grid.store) + } + ); + + bbar.show(); + + // Initialize the parameters of the paging and slider widgets. + // Depends on the status of the store. + // Initialization is only performed once. + if (grid.store.getTotalCount() > 0) { + + this.onInit.call(grid, grid.store); + + } else { + + grid.store.on('load', this.onInit, grid, {single: true}); + + } + + // update the slider parameter after a write action + // which destroy and create records + grid.store.on('write', this.onWrite, grid); + }, + + /** + * Handler to change the number of rows per page + * the scope is the bottom tool bar + */ + onChangePageSize: function (slider, newValue, thumb) { + var bbar = this; + bbar.pageSize = newValue; + bbar.moveFirst(); + }, + + /** + * Handler to initialize the number of rows per page and the number of page + * the scope is the grid + */ + onInit: function (store, records, options) { + + var bbar, + grid = this, + nRows = store.getCount(), + slider; + + bbar = grid.getBottomToolbar(); + bbar.pageSize = nRows; + + slider = bbar.findByType('slider')[0]; + slider.setMaxValue(store.getTotalCount()); + slider.setValue(nRows); + + }, + + /** + * Handler to update the slider/bottomToolBar parameters after a store write action. + * The latter create or destroy records in the store. + * The scope is the bottom grid + */ + onWrite: function () { + + var bbar, + grid = this, + slider; + + bbar = grid.getBottomToolbar(); + bbar.pageSize = grid.store.getCount(); + + slider = bbar.findByType('slider')[0]; + slider.setMaxValue(grid.store.getTotalCount()); + slider.setValue(grid.store.getCount()); + + } +}); + +Ext.preg('pGridPaging', App.grid.Paging); +/** + * The row editor context menu plugin + * + * Display the context menu when the user right click on a row. + * The content of the menu allows to manipulate the App.grid.RowEditor + * + * Requires the App.grid.GridRowEditor plugin. + * + * The ptype of this component is pGridRowEditorContextMenu. + * + * @extends Object + * @version $Id$ + * + */ + +Ext.namespace('App.grid'); + +App.grid.RowEditorContextMenu = Ext.extend(Object, { + + /** + * Private attribute identifying the type of plugin + */ + ptype: 'pGridRowEditorContextMenu', + + /** + * private attributes for internationalization + */ + textAdd: 'Add', + textCreate: 'Create', + textDestroy: 'Delete', + textDuplicate: 'Duplicate', + textUpdate: 'Update', + textView: 'View', + + /** + * Plugin initialization + * @param {Object} grid + */ + init: function (grid) { + + var menu = new Ext.menu.Menu(); + + // protection + if (!grid.rowEditor) { + throw new Error('no grid row editor !!!'); + } + + grid.addListener('containercontextmenu', this.onContainerContextMenu, menu); + grid.addListener('headercontextmenu', this.onHeaderContextMenu, menu); + grid.addListener('rowcontextmenu', this.onRowContextMenu, menu); + + menu.add({ + text: this.textAdd, + iconCls: 'xaction-create', + handler: grid.rowEditor.onAddRow, + scope: grid.rowEditor + }, '-', { + text: this.textDuplicate, + iconCls: 'xaction-duplicate', + handler: grid.rowEditor.onDuplicateRow, + scope: grid.rowEditor + }, { + text: this.textUpdate, + iconCls: 'xaction-update', + handler: grid.rowEditor.onEditRow, + scope: grid.rowEditor + }, { + text: this.textView, + iconCls: 'xaction-view', + handler: grid.rowEditor.onViewRow, + scope: grid.rowEditor + }, '-', { + text: this.textDestroy, + iconCls: 'xaction-destroy', + handler: grid.rowEditor.onDeleteRow, + scope: grid.rowEditor + }); + }, + + /** + * Prvate handler displaying the row context menu in an empty grid + */ + onContainerContextMenu: function (grid, event) { + var menu = this; + + event.stopEvent(); + menu.showAt(event.getXY()); + + }, + /** + * Private handler displaying the context menu in the header + * Nothing is display in that case. + */ + onHeaderContextMenu: function (grid, columIndex, event) { + event.stopEvent(); + }, + /** + * Private handler displaying the row context menu + * The scope use by the handler is the menu object. + */ + onRowContextMenu: function (grid, rowIndex, event) { + + var menu = this; + + event.stopEvent(); + grid.selModel.selectRow(rowIndex); + menu.showAt(event.getXY()); + + } + +}); + +Ext.preg('pGridRowEditorContextMenu', App.grid.RowEditorContextMenu); +/** + * The grid row editor plugin + * The gridRowEditor is a window containing a formPanel. + * It exposes series of handlers to delete, duplicate, update, view the + * selected row of the grid, as well as to create new row. + * + * This plugin limit the number of selected row to one. + * + * The ptype of this component is pgridroweditor. + * + * @extends Ext.Window + * @version $Id$ + * + */ + +Ext.namespace('App.grid'); + +App.grid.RowEditor = Ext.extend(Ext.Window, { + + /** + * Private attribute identifying the type of plugin + */ + ptype: 'pGridRowEditor', + formPanel: null, + grid: null, + + /** + * Private attributes for internationalization + */ + addTitle: "Create a new record...", + deleteTitle: "Delete the record...", + duplicateTitle: "Duplicate the record...", + editTitle: "Update the record...", + viewTitle: "View the record...", + textMsg: 'Select a row please', + + /** + * pre-defined configuration options for the window + */ + autoScroll: true, + closeAction: 'hide', + constrainHeader: true, + defaults: {autoScroll: true}, + modal: true, + plain: true, + + /** + * Plugin initialization + * + * @param {Object} grid GridPanel the grid instantiating the plugin + */ + init: function (grid) { + + var table; + + this.grid = grid; + + // The grid keeps track of the editor + // since it can be shared between different plugins + this.grid.rowEditor = this; + + // The user can only select one row of the grid + this.grid.selModel = new Ext.grid.RowSelectionModel({ + singleSelect: true + }); + + // Add a listener to hide the window when the transaction is successful + this.grid.store.on('write', this.onWrite, this); + + // get the formPanel configuration and instantiate the object + table = App.getTableName(this.grid.store); + Dbui.getForm(table, this.addFormPanel, this); + }, + + /** + * Private call-back method to instantiate the formPanel + * the scope is the gridRowEditor + * @param {Object} provider + * @param {Object} response + */ + addFormPanel: function (provider, response) { + var formCfg = response.result; + + // instantiate the form + this.formPanel = new App.form.FormPanel(formCfg); + this.add(this.formPanel); + + // add a listeners to catch the store exception + this.grid.store.on('exception', + this.formPanel.onStoreException, + this.formPanel); + }, + /** + * Private method to return the record + * associate to the row selected in the grid + * + * In absence of selected row a warning message is shown + * and the function return false. + */ + getSelected: function () { + + var record = this.grid.getSelectionModel().getSelected(); + + if (!record) { + Ext.MessageBox.alert('Warning', this.textMsg); + return false; + } + + return record; + }, + + /** + * Handler to add a new row. + * The scope use by the handler is the plugin object. + * + */ + onAddRow: function () { + + this.formPanel.setAction('create'); + this.setTitle(this.addTitle); + this.show(); + }, + + /** + * Handler to delete the selected row without confirmation + * The scope use by the handler is the plugin object. + * + */ + onDeleteRow: function () { + + var record = this.getSelected(); + if (!record) {return; } + + this.formPanel.setAction('destroy', record); + this.setTitle(this.deleteTitle); + this.formPanel.doAction(); + }, + + /** + * Handler to duplicate the selected row + * The scope use by the handler is plugin object. + * + */ + onDuplicateRow: function () { + + var record = this.getSelected(); + if (!record) {return; } + + this.formPanel.setAction('duplicate', record); + this.setTitle(this.duplicateTitle); + this.show(); + }, + + /** + * Handler to edit the selected row. + * The scope use by the handler is the plugin object. + * + */ + onEditRow: function () { + + var record = this.getSelected(); + if (!record) {return; } + + this.formPanel.setAction('update', record); + this.setTitle(this.editTitle); + this.show(); + }, + + /** + * Handler to view the selected row in the form. + * The scope use by the handler is the plugin object. + * + */ + onViewRow: function () { + + var record = this.getSelected(); + if (!record) {return; } + + this.formPanel.setAction('view', record); + this.setTitle(this.viewTitle); + this.show(); + }, + + /** + * Private handler call when the write transaction is successful. + * It aim is to hide the window. + * @param {Object} store + * @param {Object} action + * @param {Object} result + * @param {Object} transaction + * @param {Object} record + */ + onWrite: function (store, action, result, transaction, record) { + this.hide(); + } +}); + +Ext.preg('pGridRowEditor', App.grid.RowEditor);/** + * Add a toolbar with buttons to manipulate the App.grid.RowEditor. + * Requires the App.grid.GridRowEditor plugin. + * + * The ptype of this component is pGridRowEditorToolbar. + * + * @version $Id$ + * + */ +Ext.namespace('App.grid'); + +App.grid.RowEditorToolbar = Ext.extend(Object, { + + /** + * Private parameter identifying the type of plugin + */ + ptype: 'pGridRowEditorToolbar', + + /** + * Plugin intiaisation + * @param {Object} grid + */ + init: function (grid) { + + var toolbar; + + // protection + if (!grid.rowEditor) { + throw new Error('no grid row editor !!!'); + } + + toolbar = grid.getTopToolbar(); + + toolbar.add([{ + text: 'Add', + iconCls: 'silk-add', + handler: grid.rowEditor.onAddRow, + scope: grid.rowEditor + }, ' ', { + text: 'Delete', + iconCls: 'silk-delete', + handler: grid.rowEditor.onDeleteRow, + scope: grid.rowEditor + }, ' ', { + text: 'Duplicate', + iconCls: 'silk-clone', + handler: grid.rowEditor.onDuplicateRow, + scope: grid.rowEditor + }, ' ', { + text: 'Update', + iconCls: 'silk-update', + handler: grid.rowEditor.onEditRow, + scope: grid.rowEditor + }, ' ', { + text: 'View', + iconCls: 'silk-view', + handler: grid.rowEditor.onViewRow, + scope: grid.rowEditor + }, '-']); + } +}); + +Ext.preg('pGridRowEditorToolbar', App.grid.RowEditorToolbar);/** + * A border layout with a grid and its filter + * The type of this component is xgridwithfilter. + * + * @extend: Ext.Panel + * + */ +Ext.namespace('App.grid'); + +App.grid.GridWithFilter = Ext.extend(App.BasePanelWithSelector, { + + initComponent: function() { + + var filter, + grid; + + // collpase the grid filter panel + this.items[1].collapsed = true; + + App.grid.GridWithFilter.superclass.initComponent.call(this); + + // bind the filter to the grid + filter = this.findByType('xgridfilter')[0]; + grid = this.findByType('xgrid')[0]; + filter.bind(grid); + + // connect buttons + this.goButton.hide(); + this.resetButton.on('click', filter.onReset, filter); + } +}); + +Ext.reg('xgridwithfilter', App.grid.GridWithFilter); +/** + * A JsonStore to read/write in the database using the EXt.Direct protocol. + * Design to work with the class DbSvc on the server side. + * + * This class is an Ext.dat.DirectStore with an Ext.data.JsonWriter. + * + * Defaults configuration parameters of the writer are defined in the + * constructor. Changing them might break the decoding running on the + * server-side and break the behavior of the grid widget. + * + * The default API associated to this store is defined in the constructor + * + * The type of this component is xdirectstore. + * + * @extends Ext.data.DirectStore + * @version $Id$ + * * + */ +Ext.namespace('App.data'); + +App.data.DirectStore = Ext.extend(Ext.data.DirectStore, { + + /** + * @cfg(Boolean) encode JsonWiter property, by default false + * Changing this option might break the decoding running on the server-side + * NOTE: the default value is defined in the constructor + */ + /** + * @cfg(Boolean) listful JsonWiter property, by default true + * Changing this option might break the decoding running on the server-side + * NOTE: the default value is defined in the constructor + */ + /** + * @cfg(Boolean) writeAllFields JsonWiter property, by default false + * Changing this option might break the decoding running on the server-side + * NOTE: the default value is defined in the constructor + */ + /** + * pre-defined configuration option + * The autoSave parameter is helpful for gridRowEditor. + */ + autoLoad: true, + autoSave: true, + + /** + * constructor + */ + constructor: function (config) { + + var cfg, + cfgWrt; + + // configuration option with default value for + // the Ext.data.JsonWriter. Changing these options might break + // the decoding running on the server side. + cfg = Ext.apply({}, { + encode: false, + listful: true, + writeAllFields: false + }, config); + + // define the remote procedure for store action + cfg = Ext.apply(cfg, { + api: { + create: Dbui.create, + destroy: Dbui.destroy, + read: Dbui.read, + update: Dbui.update + } + }); + + // setup the writer + if (!Ext.isDefined(cfg.writer)) { + cfgWrt = Ext.copyTo({}, cfg, 'encode, listful, writeAllFields'); + cfg.writer = new Ext.data.JsonWriter(cfgWrt); + } + + // instantiate the store + App.data.DirectStore.superclass.constructor.call(this, cfg); + + // update the total number of records + this.on('write', this.onWrite); + }, + + /** + * Handler to update the total number of records after a write action + * @param {Object} store + * @param {Object} action + */ + onWrite: function (store, action) { + + switch (action) { + case 'create': + store.totalLength += 1; + break; + + case 'destroy': + store.totalLength -= 1; + break; + } + } +}); + +Ext.reg('xdirectstore', App.data.DirectStore);/** + * Plugin to render mathematics formula embedded in HTML content. + * The processing is performed by MathJax. + * The MathJax library is loaded by the framework + * + * @version $Id$ + * + */ + +Ext.namespace('App.panel'); + +App.panel.MathJax = Ext.extend(Object, { + + init: function (panel) { + + // register an handler which is activated only once + // when the panel is rendered for the first time + panel.on('render', this.onPanelRender, this, {single: true}); + }, + + /** + * Handler to activates the MathJax processing + * when the html content of the panel is loaded. + * + * @param {Ext.Panel} panel + */ + onPanelRender: function (panel) { + var updater = panel.body.getUpdater(); + updater.on('update', this.onProcess); + }, + + /** + * Handler to run the mathJax processing + * @param {Ext.Element} el + * @param {Object} o the response object + */ + onProcess: function (e, o) { + MathJax.Hub.PreProcess(); + MathJax.Hub.Process(); + } +}); + +Ext.preg('pPanelMathJax', App.panel.MathJax);/** + * A panel displaying an url content with an collpasible selector. + * the selector allow to setup the url parameters and to launch the + * request to the server. + * + * The type of this component is xpanelwithurlselector. + * + * @extend: App.PanelWithSelector + * + */ +Ext.namespace('App'); + +App.PanelWithUrlSelector = Ext.extend(App.BasePanelWithSelector, { + + /** + * @param {String} + */ + baseUrl: null, + + /** + * Require by the ExtJS model + */ + initComponent: function () { + + App.PanelWithUrlSelector.superclass.initComponent.call(this); + + // connect the buttons + this.goButton.on('click', this.onGo, this); + this.resetButton.on('click', this.onReset, this); + }, + + /** + * handle to build the URL and to load it in the panel + */ + onGo: function() { + + var fields, + i, + panel = this.getComponent('mainPanel'), + params = {}, + selector = this.getComponent('selectorPanel'); + + fields = selector.findByType('field'); + for (i = 0; i < fields.length; i += 1) { + params[fields[i].getName()] = fields[i].getValue(); + } + + panel.load({ + url: this.baseUrl, + params: params, + text: 'Loading...', + timeout: 30 + }); + }, + + /** + * Handler to reset the selector + */ + onReset: function () { + + var fields, + i, + selector = this.getComponent('selectorPanel'); + + + fields = selector.findByType('field'); + for (i = 0; i < fields.length; i += 1) { + fields[i].reset(); + } + } +}); + +Ext.reg('xpanelwithurlselector', App.PanelWithUrlSelector); +/** + * A pre-configured combobox displaying a set of predefined value. + * + * MANDATORY PROPERTIES: + * - model + * + * All configuration properties of Ext.form.ComboBox are understood. + * + * @extends Ext.form.ComboBox + * @author $Author$ + * @version $Id$ + * + */ +Ext.namespace('App.form'); + +App.form.SetBox = Ext.extend(Ext.form.ComboBox, { + + /** + * @cfg {Object} model Object to configure the widget. + * It is provided by the server and contains the following keys: + * { setData: [...]} + */ + model: null, + + /** + * Predefined setting + * + */ + editable: false, + emptyText: "Select...", + selectOnFocus: true, + triggerAction: 'all', + typeAhead: true, + + /** + * private method require by the ExtJS component model + */ + initComponent: function () { + + var i, + li = []; + + if (!this.model) { + throw new Error("the property model is missing !!!"); + } + + // setup the store + li = []; + for (i = 0; i < this.model.setData.length; i += 1) { + li.push([this.model.setData[i]]); + } + + this.store = new Ext.data.ArrayStore({ + fields: [this.model.name], + data : li + }); + + // display field an mode + this.displayField = this.model.name; + this.mode = 'local'; + + // construct the underlying class. DON'T MOVE + App.form.SetBox.superclass.initComponent.call(this); + + } +}); + +Ext.reg('xsetbox', App.form.SetBox);/** + * The Viewport for the application. + * + * The type of this component is xviewport. + * + * @extend Ext.Viexport + * + */ +Ext.namespace('App'); + +App.Viewport = Ext.extend(Ext.Viewport, { + + /** + * private shortcuts + */ + tabPanel: null, + treePanel: null, + + /** + * constructor + * @param {Object} config + */ + constructor: function (config) { + + var cgf, + loader, + root, + treePanel, + viewport; + + Ext.apply(this, config); + + // define the root node + root = new Ext.tree.AsyncTreeNode({ + id: 'root', + nodeType: 'async', + expanded: true, + text: App.name + }); + + // define nodes loader + loader = new Ext.tree.TreeLoader({ + directFn: Dbui.getTree + }); + + // predefined configuration of the view port + cfg = { + layout: 'border', + title: 'Ext Layout Browser', + items: [{ + autoScroll: true, + collapsible: true, + itemId: 'treePanel', + loader: loader, + region: 'west', + root: root, + rootVisible: false, + split: true, + title: App.name, + width: 200, + xtype: 'treepanel' + }, { + autoScroll: true, + defaults: {layout: 'fit'}, + itemId: 'tabPanel', + region: 'center', + xtype: 'tabpanel' + }] + }; + + // apply the user configuration if any + Ext.apply(this, cfg); + + // instanciate the viewport + App.Viewport.superclass.constructor.call(this); + + // define shortcuts + this.tabPanel = this.getComponent('tabPanel'); + this.treePanel = this.getComponent('treePanel'); + + // Add handler to create tab and their widget + this.treePanel.on('click', this.onCreateTab, this); + + // disable contextmenu in the treepanel + this.treePanel.on('contextmenu', function (node, event) { + event.stopEvent(); + }); + }, + + /** + * Handler to create / activate a tab + * The scope is the viewport + * + * @param {Object} node + * @param {Object} event + */ + onCreateTab: function(node, event){ + + var cfg, + parent = node.parentNode, + panel, + tabId, + viewport = this, + wdgcfg = node.attributes.cfg, + wdgtype = node.attributes.cfg.xtype; + + // protection + if(!node.isLeaf()){ + return; + } + + // unique tab identifier + tabId = node.attributes.text + '/' + wdgtype; + + // activate an existing tab + if (this.tabPanel.getComponent(tabId)) { + this.tabPanel.setActiveTab(tabId); + return; + } + + // tab configuration + // the title is defined as 'Form tablename', 'Grid tablename', ... + cfg = { + closable: true, + itemId: tabId, + title: parent.attributes.text + " " + node.attributes.text + }; + + // embed single panel in the tab panel + // other widget as children + if( wdgtype === 'panel') { + delete wdgcfg.xtype; + Ext.apply(cfg, wdgcfg); + + } else { + cfg.items = [node.attributes.cfg]; + } + + // create a new tab and activate it + panel = this.tabPanel.add(cfg); + this.tabPanel.setActiveTab(tabId); + } +}); diff --git a/static/plugin_dbui/dbui-min.js b/static/plugin_dbui/dbui-min.js new file mode 100644 index 0000000000000000000000000000000000000000..337c4ca42cbbfed123f4176fb1d4227860006c6b --- /dev/null +++ b/static/plugin_dbui/dbui-min.js @@ -0,0 +1 @@ +Ext.namespace("App");App.version="0.4.3";App.encodeField=function(b,d){var a,c;a=b[0].toUpperCase()+b.slice(1,b.length);c=d[0].toUpperCase()+d.slice(1,d.length);return a+c};App.getDirectStore=function(b){var a,c,d;c=Ext.StoreMgr.lookup(b);if(!c&&!Ext.isString(b)){throw new Error('Fail to instanciate the store: "'+b+'"')}if(!c&&Ext.isString(b)){d=b.slice(0,b.search("Store"));a=App.storeCfgs[d];c=new App.data.DirectStore(a)}return c};App.getTableName=function(a){return a.baseParams.tableName};App.isPlugin=function(a,d){var b,c;if(!a.hasOwnProperty("plugins")){return false}for(b=0;b<a.plugins.length;b+=1){c=a.plugins[b];if(c===d){return true}if((typeof(c)==="object")&&c.hasOwnProperty("ptype")&&(c.ptype===d)){return true}}return false};Ext.namespace("App");App.BasePanelWithSelector=Ext.extend(Ext.Panel,{panelCfg:null,selectorCfg:null,textGo:"Go",textReset:"Reset",constructor:function(d){var c,b,a;Ext.apply(this,d);b=Ext.ComponentMgr.create(this.panelCfg);a=Ext.ComponentMgr.create(this.selectorCfg);c={layout:"border",items:[{border:false,layout:"fit",itemId:"mainPanel",items:[b],region:"center",},{buttons:[{ref:"../../goButton",text:this.textGo},{ref:"../../resetButton",text:this.textReset}],collapsible:true,defaults:{anchor:"99%"},frame:true,layout:"form",itemId:"selectorPanel",items:[a],region:"east",split:true,width:300,}]};Ext.apply(this,c);App.BasePanelWithSelector.superclass.constructor.call(this)}});Ext.reg("xpanelwithselector",App.BasePanelWithSelector);Ext.namespace("App");App.ButtonDownload=Ext.extend(Ext.Button,{url:undefined,initComponent:function(){App.ButtonDownload.superclass.initComponent.call(this);this.on("click",this.onDownload,this)},onDownload:function(a,c){try{Ext.destroy(Ext.get("downloadIframe"))}catch(b){}Ext.DomHelper.append(document.body,{tag:"iframe",id:"downloadIframe",frameBorder:0,width:0,height:0,css:"display:none;visibility:hidden;height:0px;",src:this.url})}});Ext.reg("xbuttondownload",App.ButtonDownload);Ext.namespace("App.form");App.form.ComboBox=Ext.extend(Ext.form.ComboBox,{mode:"remote",editable:false,selectOnFocus:true,triggerAction:"all",typeAhead:true,initComponent:function(){if(this.fieldLabel&&!this.emptyText){this.emptyText="Select a "+this.fieldLabel+" ..."}this.store=App.getDirectStore(this.store);App.form.ComboBox.superclass.initComponent.call(this);if(this.store.getCount()===0){this.store.load()}this.store.on("load",function(){this.setValue(this.initialConfig.value)},this)}});Ext.reg("xcombobox",App.form.ComboBox);Ext.namespace("App.form");App.form.FormPanel=Ext.extend(Ext.form.FormPanel,{store:null,autoScroll:true,bodyStyle:"padding:5px 5px 0",buttons:[{formBind:true,text:"Action",ref:"../buttonAction"},{text:"Reset",ref:"../buttonReset"}],defaults:{anchor:"100%"},defaultType:"textfield",frame:true,monitorValid:true,currentAction:null,currentRecord:null,textCreate:"Create",textDestroy:"Delete",textDuplicate:"Duplicate",textReset:"Reset",textUpdate:"Update",initComponent:function(){function a(){this.reset()}App.form.FormPanel.superclass.initComponent.call(this);this.buttonAction.on("click",this.doAction,this);this.buttonReset.on("click",a,this.getForm());this.addFieldToolTipsListeners();this.store=App.getDirectStore(this.store);this.store.on("exception",this.onStoreException,this);this.store.on("write",a,this.getForm());this.setAction("create");this.buttonReset.setText(this.textReset)},addFieldToolTipsListeners:function(){var b;function a(d){new Ext.ToolTip({target:d.getEl(),title:d.fieldLabel,anchor:"left",trackMouse:false,html:Ext.util.Format.htmlEncode(d.tipText)})}b=this.getForm();b.items.each(function(c){if(c.tipText){c.on("render",a)}})},disableFields:function(a){var b=this.getForm();b.items.each(function(c){c.setDisabled(a)})},doAction:function(){var b=this.getForm(),a;if(!b.isValid()){return}if(!this.store){throw new Error("the store is undefined !!!")}switch(this.currentAction){case"create":a=new this.store.recordType();this.updateRecord(a);this.store.add(a);break;case"destroy":this.store.remove(this.currentRecord);break;case"duplicate":a=new this.store.recordType();this.updateRecord(a);this.store.add(a);break;case"update":this.currentRecord.beginEdit();this.updateRecord(this.currentRecord);this.currentRecord.endEdit();break}if(this.store.autoSave===false){this.store.save()}},hardReset:function(){var a=this.getForm();a.items.each(function(b){b.originalValue=Ext.value(b.initialConfig.value,"");b.setValue(b.originalValue)})},onStoreException:function(e,f,b,j,c,i){var g,h,a=this.getForm(),d;for(h in c.errors){if(c.errors.hasOwnProperty(h)){g=a.findField(h);g.markInvalid(c.errors[h])}}d=i[0];this.store.remove(d)},setAction:function(e,a){var d=this.getForm(),c,b;this.buttonReset.show();this.buttonAction.show();this.disableFields(false);this.currentAction=e;this.currentRecord=a;switch(e){case"create":this.hardReset();this.buttonAction.setText(this.textCreate);break;case"destroy":this.buttonAction.setText(this.textDestroy);d.loadRecord(a);break;case"duplicate":this.buttonAction.setText(this.textDuplicate);this.hardReset();c=App.getTableName(this.store);b=App.encodeField(c,"id");delete a.data[b];d.loadRecord(a);break;case"update":this.buttonAction.setText(this.textUpdate);d.loadRecord(a);break;case"view":this.buttonReset.hide();this.buttonAction.hide();d.loadRecord(a);this.disableFields(true);break}},updateRecord:function(b){var g,f,a=[],d,c,h,e;c=this.findByType("field");for(d=0;d<c.length;d+=1){f=c[d];if(f.getXType()!=="compositefield"){a.push(f)}}c=this.findByType("compositefield");for(d=0;d<c.length;d+=1){c[d].items.eachKey(function(j,i){a.push(i)})}for(d=0;d<a.length;d+=1){f=a[d];e=f.getValue();switch(f.getXType()){case"datefield":if(Ext.isDate(e)){e=e.format(f.format)}break;case"xcombobox":g=f;h=g.findRecord(g.valueField,g.getValue());b.set(g.displayField,h.get(g.displayField));break}b.set(f.getName(),e)}}});Ext.reg("xform",App.form.FormPanel);Ext.namespace("App.grid");App.grid.GridFilter=Ext.extend(Ext.form.FieldSet,{filterConditions:{},initialFilterConditions:[],pagingToolbar:null,store:null,defaults:{anchor:"99%",enableKeyEvents:true},initComponent:function(){var c,a,b;App.grid.GridFilter.superclass.initComponent.call(this);a=this.findByType("field");for(b=0;b<a.length;b+=1){c=a[b];if(c.xtype==="xcombobox"){c.on("select",this.onChange,this)}else{c.on("keyup",this.onChange,this,{buffer:500})}}},bind:function(b){var c,a;this.store=b.getStore();if(b.pagingInitialized){this.pagingToolbar=b.getBottomToolbar()}c=this.store.baseParams;for(a=0;a<c.where.length;a+=1){this.initialFilterConditions.push(c.where[a])}},onChange:function(a){this.setupCondition(a)},onReset:function(){var a,b;a=this.findByType("field");for(b=0;b<a.length;b+=1){a[b].reset()}this.filterConditions={};this.store.baseParams.where=this.initialFilterConditions;this.updateStore()},setupCondition:function(f){var e=[],c,a,d,b;d=f.getValue();b=f.name+" "+f.filterOperator+" '"+d+"'";if(d===""){delete this.filterConditions[f.name]}else{this.filterConditions[f.name]=b}for(c=0;c<this.initialFilterConditions.length;c+=1){e.push(this.initialFilterConditions[c])}for(a in this.filterConditions){if(this.filterConditions.hasOwnProperty(a)){e.push(this.filterConditions[a])}}this.store.baseParams.where=e;this.updateStore()},updateStore:function(){if(this.pagingToolbar){this.pagingToolbar.doRefresh()}else{this.store.load()}}});Ext.reg("xgridfilter",App.grid.GridFilter);Ext.namespace("App.grid");App.grid.Grid=Ext.extend(Ext.grid.GridPanel,{rowEditor:null,initComponent:function(){this.store=App.getDirectStore(this.store);this.bbar=new Ext.PagingToolbar();this.bbar.hide();App.grid.Grid.superclass.initComponent.call(this);if(!this.store.getTotalCount()){if(App.isPlugin(this,"pGridPaging")){this.store.load({params:{start:0,limit:10}})}else{this.store.load()}}}});Ext.reg("xgrid",App.grid.Grid);Ext.namespace("App.grid");App.grid.MathJax=Ext.extend(Object,{init:function(a){function b(d){var c=Ext.getDom(d.el);MathJax.Hub.Queue(["Typeset",MathJax.Hub,c])}a.getView().on("refresh",b,a);a.store.on("write",b,a)}});Ext.preg("pGridMathJax",App.grid.MathJax);Ext.namespace("App.grid");App.grid.Paging=Ext.extend(Object,{ptype:"pGridPaging",textExport:"Export",textSlider:"Rows per page",init:function(a){var b;b=a.getBottomToolbar();b.bindStore(a.store);b.add("-",this.textSlider,{xtype:"slider",plugins:new Ext.slider.Tip(),listeners:{changecomplete:this.onChangePageSize,scope:b},minValue:1,width:100},"->",{xtype:"xbuttondownload",text:this.textExport,url:App.csvUrl+"?tableName="+App.getTableName(a.store)});b.show();if(a.store.getTotalCount()>0){this.onInit.call(a,a.store)}else{a.store.on("load",this.onInit,a,{single:true})}a.store.on("write",this.onWrite,a)},onChangePageSize:function(b,c,a){var d=this;d.pageSize=c;d.moveFirst()},onInit:function(b,a,c){var g,e=this,d=b.getCount(),f;g=e.getBottomToolbar();g.pageSize=d;f=g.findByType("slider")[0];f.setMaxValue(b.getTotalCount());f.setValue(d)},onWrite:function(){var c,a=this,b;c=a.getBottomToolbar();c.pageSize=a.store.getCount();b=c.findByType("slider")[0];b.setMaxValue(a.store.getTotalCount());b.setValue(a.store.getCount())}});Ext.preg("pGridPaging",App.grid.Paging);Ext.namespace("App.grid");App.grid.RowEditorContextMenu=Ext.extend(Object,{ptype:"pGridRowEditorContextMenu",textAdd:"Add",textCreate:"Create",textDestroy:"Delete",textDuplicate:"Duplicate",textUpdate:"Update",textView:"View",init:function(a){var b=new Ext.menu.Menu();if(!a.rowEditor){throw new Error("no grid row editor !!!")}a.addListener("containercontextmenu",this.onContainerContextMenu,b);a.addListener("headercontextmenu",this.onHeaderContextMenu,b);a.addListener("rowcontextmenu",this.onRowContextMenu,b);b.add({text:this.textAdd,iconCls:"xaction-create",handler:a.rowEditor.onAddRow,scope:a.rowEditor},"-",{text:this.textDuplicate,iconCls:"xaction-duplicate",handler:a.rowEditor.onDuplicateRow,scope:a.rowEditor},{text:this.textUpdate,iconCls:"xaction-update",handler:a.rowEditor.onEditRow,scope:a.rowEditor},{text:this.textView,iconCls:"xaction-view",handler:a.rowEditor.onViewRow,scope:a.rowEditor},"-",{text:this.textDestroy,iconCls:"xaction-destroy",handler:a.rowEditor.onDeleteRow,scope:a.rowEditor})},onContainerContextMenu:function(a,b){var c=this;b.stopEvent();c.showAt(b.getXY())},onHeaderContextMenu:function(b,a,c){c.stopEvent()},onRowContextMenu:function(a,d,b){var c=this;b.stopEvent();a.selModel.selectRow(d);c.showAt(b.getXY())}});Ext.preg("pGridRowEditorContextMenu",App.grid.RowEditorContextMenu);Ext.namespace("App.grid");App.grid.RowEditor=Ext.extend(Ext.Window,{ptype:"pGridRowEditor",formPanel:null,grid:null,addTitle:"Create a new record...",deleteTitle:"Delete the record...",duplicateTitle:"Duplicate the record...",editTitle:"Update the record...",viewTitle:"View the record...",textMsg:"Select a row please",autoScroll:true,closeAction:"hide",constrainHeader:true,defaults:{autoScroll:true},modal:true,plain:true,init:function(a){var b;this.grid=a;this.grid.rowEditor=this;this.grid.selModel=new Ext.grid.RowSelectionModel({singleSelect:true});this.grid.store.on("write",this.onWrite,this);b=App.getTableName(this.grid.store);Dbui.getForm(b,this.addFormPanel,this)},addFormPanel:function(c,a){var b=a.result;this.formPanel=new App.form.FormPanel(b);this.add(this.formPanel);this.grid.store.on("exception",this.formPanel.onStoreException,this.formPanel)},getSelected:function(){var a=this.grid.getSelectionModel().getSelected();if(!a){Ext.MessageBox.alert("Warning",this.textMsg);return false}return a},onAddRow:function(){this.formPanel.setAction("create");this.setTitle(this.addTitle);this.show()},onDeleteRow:function(){var a=this.getSelected();if(!a){return}this.formPanel.setAction("destroy",a);this.setTitle(this.deleteTitle);this.formPanel.doAction()},onDuplicateRow:function(){var a=this.getSelected();if(!a){return}this.formPanel.setAction("duplicate",a);this.setTitle(this.duplicateTitle);this.show()},onEditRow:function(){var a=this.getSelected();if(!a){return}this.formPanel.setAction("update",a);this.setTitle(this.editTitle);this.show()},onViewRow:function(){var a=this.getSelected();if(!a){return}this.formPanel.setAction("view",a);this.setTitle(this.viewTitle);this.show()},onWrite:function(c,d,a,e,b){this.hide()}});Ext.preg("pGridRowEditor",App.grid.RowEditor);Ext.namespace("App.grid");App.grid.RowEditorToolbar=Ext.extend(Object,{ptype:"pGridRowEditorToolbar",init:function(a){var b;if(!a.rowEditor){throw new Error("no grid row editor !!!")}b=a.getTopToolbar();b.add([{text:"Add",iconCls:"silk-add",handler:a.rowEditor.onAddRow,scope:a.rowEditor}," ",{text:"Delete",iconCls:"silk-delete",handler:a.rowEditor.onDeleteRow,scope:a.rowEditor}," ",{text:"Duplicate",iconCls:"silk-clone",handler:a.rowEditor.onDuplicateRow,scope:a.rowEditor}," ",{text:"Update",iconCls:"silk-update",handler:a.rowEditor.onEditRow,scope:a.rowEditor}," ",{text:"View",iconCls:"silk-view",handler:a.rowEditor.onViewRow,scope:a.rowEditor},"-"])}});Ext.preg("pGridRowEditorToolbar",App.grid.RowEditorToolbar);Ext.namespace("App.grid");App.grid.GridWithFilter=Ext.extend(App.BasePanelWithSelector,{initComponent:function(){var b,a;this.items[1].collapsed=true;App.grid.GridWithFilter.superclass.initComponent.call(this);b=this.findByType("xgridfilter")[0];a=this.findByType("xgrid")[0];b.bind(a);this.goButton.hide();this.resetButton.on("click",b.onReset,b)}});Ext.reg("xgridwithfilter",App.grid.GridWithFilter);Ext.namespace("App.data");App.data.DirectStore=Ext.extend(Ext.data.DirectStore,{autoLoad:true,autoSave:true,constructor:function(c){var a,b;a=Ext.apply({},{encode:false,listful:true,writeAllFields:false},c);a=Ext.apply(a,{api:{create:Dbui.create,destroy:Dbui.destroy,read:Dbui.read,update:Dbui.update}});if(!Ext.isDefined(a.writer)){b=Ext.copyTo({},a,"encode, listful, writeAllFields");a.writer=new Ext.data.JsonWriter(b)}App.data.DirectStore.superclass.constructor.call(this,a);this.on("write",this.onWrite)},onWrite:function(a,b){switch(b){case"create":a.totalLength+=1;break;case"destroy":a.totalLength-=1;break}}});Ext.reg("xdirectstore",App.data.DirectStore);Ext.namespace("App.panel");App.panel.MathJax=Ext.extend(Object,{init:function(a){a.on("render",this.onPanelRender,this,{single:true})},onPanelRender:function(a){var b=a.body.getUpdater();b.on("update",this.onProcess)},onProcess:function(a,b){MathJax.Hub.PreProcess();MathJax.Hub.Process()}});Ext.preg("pPanelMathJax",App.panel.MathJax);Ext.namespace("App");App.PanelWithUrlSelector=Ext.extend(App.BasePanelWithSelector,{baseUrl:null,initComponent:function(){App.PanelWithUrlSelector.superclass.initComponent.call(this);this.goButton.on("click",this.onGo,this);this.resetButton.on("click",this.onReset,this)},onGo:function(){var b,d,c=this.getComponent("mainPanel"),e={},a=this.getComponent("selectorPanel");b=a.findByType("field");for(d=0;d<b.length;d+=1){e[b[d].getName()]=b[d].getValue()}c.load({url:this.baseUrl,params:e,text:"Loading...",timeout:30})},onReset:function(){var b,c,a=this.getComponent("selectorPanel");b=a.findByType("field");for(c=0;c<b.length;c+=1){b[c].reset()}}});Ext.reg("xpanelwithurlselector",App.PanelWithUrlSelector);Ext.namespace("App.form");App.form.SetBox=Ext.extend(Ext.form.ComboBox,{model:null,editable:false,emptyText:"Select...",selectOnFocus:true,triggerAction:"all",typeAhead:true,initComponent:function(){var b,a=[];if(!this.model){throw new Error("the property model is missing !!!")}a=[];for(b=0;b<this.model.setData.length;b+=1){a.push([this.model.setData[b]])}this.store=new Ext.data.ArrayStore({fields:[this.model.name],data:a});this.displayField=this.model.name;this.mode="local";App.form.SetBox.superclass.initComponent.call(this)}});Ext.reg("xsetbox",App.form.SetBox);Ext.namespace("App");App.Viewport=Ext.extend(Ext.Viewport,{tabPanel:null,treePanel:null,constructor:function(d){var e,b,c,f,a;Ext.apply(this,d);c=new Ext.tree.AsyncTreeNode({id:"root",nodeType:"async",expanded:true,text:App.name});b=new Ext.tree.TreeLoader({directFn:Dbui.getTree});cfg={layout:"border",title:"Ext Layout Browser",items:[{autoScroll:true,collapsible:true,itemId:"treePanel",loader:b,region:"west",root:c,rootVisible:false,split:true,title:App.name,width:200,xtype:"treepanel"},{autoScroll:true,defaults:{layout:"fit"},itemId:"tabPanel",region:"center",xtype:"tabpanel"}]};Ext.apply(this,cfg);App.Viewport.superclass.constructor.call(this);this.tabPanel=this.getComponent("tabPanel");this.treePanel=this.getComponent("treePanel");this.treePanel.on("click",this.onCreateTab,this);this.treePanel.on("contextmenu",function(h,g){g.stopEvent()})},onCreateTab:function(d,b){var f,h=d.parentNode,a,c,g=this,e=d.attributes.cfg,i=d.attributes.cfg.xtype;if(!d.isLeaf()){return}c=d.attributes.text+"/"+i;if(this.tabPanel.getComponent(c)){this.tabPanel.setActiveTab(c);return}f={closable:true,itemId:c,title:h.attributes.text+" "+d.attributes.text};if(i==="panel"){delete e.xtype;Ext.apply(f,e)}else{f.items=[d.attributes.cfg]}a=this.tabPanel.add(f);this.tabPanel.setActiveTab(c)}}); \ No newline at end of file diff --git a/static/plugin_dbui/dbui.min.js b/static/plugin_dbui/dbui.min.js deleted file mode 100644 index a1e34ca572f8285c081d53390ff381f8d3e72754..0000000000000000000000000000000000000000 --- a/static/plugin_dbui/dbui.min.js +++ /dev/null @@ -1 +0,0 @@ -Ext.namespace("App.grid");App.grid.MathJax=Ext.extend(Object,{init:function(a){function b(d){var c=Ext.getDom(d.el);MathJax.Hub.Queue(["Typeset",MathJax.Hub,c])}a.getView().on("refresh",b,a);a.store.on("write",b,a)}});Ext.preg("pGridMathJax",App.grid.MathJax);Ext.namespace("App.grid");App.grid.Grid=Ext.extend(Ext.grid.GridPanel,{rowEditor:null,initComponent:function(){this.store=App.getDirectStore(this.store);this.bbar=new Ext.PagingToolbar();this.bbar.hide();App.grid.Grid.superclass.initComponent.call(this);if(!this.store.getTotalCount()){if(App.isPlugin(this,"pGridPaging")){this.store.load({params:{start:0,limit:10}})}else{this.store.load()}}}});Ext.reg("xgrid",App.grid.Grid);Ext.namespace("App");App.version="0.4.3";App.encodeField=function(b,d){var a,c;a=b[0].toUpperCase()+b.slice(1,b.length);c=d[0].toUpperCase()+d.slice(1,d.length);return a+c};App.getDirectStore=function(b){var a,c,d;c=Ext.StoreMgr.lookup(b);if(!c&&!Ext.isString(b)){throw new Error('Fail to instanciate the store: "'+b+'"')}if(!c&&Ext.isString(b)){d=b.slice(0,b.search("Store"));a=App.storeCfgs[d];c=new App.data.DirectStore(a)}return c};App.getTableName=function(a){return a.baseParams.tableName};App.isPlugin=function(a,d){var b,c;if(!a.hasOwnProperty("plugins")){return false}for(b=0;b<a.plugins.length;b+=1){c=a.plugins[b];if(c===d){return true}if((typeof(c)==="object")&&c.hasOwnProperty("ptype")&&(c.ptype===d)){return true}}return false};Ext.namespace("App.grid");App.grid.Paging=Ext.extend(Object,{ptype:"pGridPaging",textExport:"Export",textSlider:"Rows per page",init:function(a){var b;b=a.getBottomToolbar();b.bindStore(a.store);b.add("-",this.textSlider,{xtype:"slider",plugins:new Ext.slider.Tip(),listeners:{changecomplete:this.onChangePageSize,scope:b},minValue:1,width:100},"->",{xtype:"xbuttondownload",text:this.textExport,url:App.csvUrl+"?tableName="+App.getTableName(a.store)});b.show();if(a.store.getTotalCount()>0){this.onInit.call(a,a.store)}else{a.store.on("load",this.onInit,a,{single:true})}a.store.on("write",this.onWrite,a)},onChangePageSize:function(b,c,a){var d=this;d.pageSize=c;d.moveFirst()},onInit:function(b,a,c){var g,e=this,d=b.getCount(),f;g=e.getBottomToolbar();g.pageSize=d;f=g.findByType("slider")[0];f.setMaxValue(b.getTotalCount());f.setValue(d)},onWrite:function(){var c,a=this,b;c=a.getBottomToolbar();c.pageSize=a.store.getCount();b=c.findByType("slider")[0];b.setMaxValue(a.store.getTotalCount());b.setValue(a.store.getCount())}});Ext.preg("pGridPaging",App.grid.Paging);Ext.namespace("App.grid");App.grid.RowEditor=Ext.extend(Ext.Window,{ptype:"pGridRowEditor",formPanel:null,grid:null,addTitle:"Create a new record...",deleteTitle:"Delete the record...",duplicateTitle:"Duplicate the record...",editTitle:"Update the record...",viewTitle:"View the record...",textMsg:"Select a row please",autoScroll:true,closeAction:"hide",constrainHeader:true,defaults:{autoScroll:true},modal:true,plain:true,init:function(a){var b;this.grid=a;this.grid.rowEditor=this;this.grid.selModel=new Ext.grid.RowSelectionModel({singleSelect:true});this.grid.store.on("write",this.onWrite,this);b=App.getTableName(this.grid.store);Dbui.getForm(b,this.addFormPanel,this)},addFormPanel:function(c,a){var b=a.result;this.formPanel=new App.form.FormPanel(b);this.add(this.formPanel);this.grid.store.on("exception",this.formPanel.onStoreException,this.formPanel)},getSelected:function(){var a=this.grid.getSelectionModel().getSelected();if(!a){Ext.MessageBox.alert("Warning",this.textMsg);return false}return a},onAddRow:function(){this.formPanel.setAction("create");this.setTitle(this.addTitle);this.show()},onDeleteRow:function(){var a=this.getSelected();if(!a){return}this.formPanel.setAction("destroy",a);this.setTitle(this.deleteTitle);this.formPanel.doAction()},onDuplicateRow:function(){var a=this.getSelected();if(!a){return}this.formPanel.setAction("duplicate",a);this.setTitle(this.duplicateTitle);this.show()},onEditRow:function(){var a=this.getSelected();if(!a){return}this.formPanel.setAction("update",a);this.setTitle(this.editTitle);this.show()},onViewRow:function(){var a=this.getSelected();if(!a){return}this.formPanel.setAction("view",a);this.setTitle(this.viewTitle);this.show()},onWrite:function(c,d,a,e,b){this.hide()}});Ext.preg("pGridRowEditor",App.grid.RowEditor);Ext.namespace("App");App.ButtonDownload=Ext.extend(Ext.Button,{url:undefined,initComponent:function(){App.ButtonDownload.superclass.initComponent.call(this);this.on("click",this.onDownload,this)},onDownload:function(a,c){try{Ext.destroy(Ext.get("downloadIframe"))}catch(b){}Ext.DomHelper.append(document.body,{tag:"iframe",id:"downloadIframe",frameBorder:0,width:0,height:0,css:"display:none;visibility:hidden;height:0px;",src:this.url})}});Ext.reg("xbuttondownload",App.ButtonDownload);Ext.namespace("App.form");App.form.FormPanel=Ext.extend(Ext.form.FormPanel,{store:null,autoScroll:true,bodyStyle:"padding:5px 5px 0",buttons:[{formBind:true,text:"Action",ref:"../buttonAction"},{text:"Reset",ref:"../buttonReset"}],defaults:{anchor:"100%"},defaultType:"textfield",frame:true,monitorValid:true,currentAction:null,currentRecord:null,textCreate:"Create",textDestroy:"Delete",textDuplicate:"Duplicate",textReset:"Reset",textUpdate:"Update",initComponent:function(){function a(){this.reset()}App.form.FormPanel.superclass.initComponent.call(this);this.buttonAction.on("click",this.doAction,this);this.buttonReset.on("click",a,this.getForm());this.addFieldToolTipsListeners();this.store=App.getDirectStore(this.store);this.store.on("exception",this.onStoreException,this);this.store.on("write",a,this.getForm());this.setAction("create");this.buttonReset.setText(this.textReset)},addFieldToolTipsListeners:function(){var b;function a(d){new Ext.ToolTip({target:d.getEl(),title:d.fieldLabel,anchor:"left",trackMouse:false,html:Ext.util.Format.htmlEncode(d.tipText)})}b=this.getForm();b.items.each(function(c){if(c.tipText){c.on("render",a)}})},disableFields:function(a){var b=this.getForm();b.items.each(function(c){c.setDisabled(a)})},doAction:function(){var b=this.getForm(),a;if(!b.isValid()){return}if(!this.store){throw new Error("the store is undefined !!!")}switch(this.currentAction){case"create":a=new this.store.recordType();this.updateRecord(a);this.store.add(a);break;case"destroy":this.store.remove(this.currentRecord);break;case"duplicate":a=new this.store.recordType();this.updateRecord(a);this.store.add(a);break;case"update":this.currentRecord.beginEdit();this.updateRecord(this.currentRecord);this.currentRecord.endEdit();break}if(this.store.autoSave===false){this.store.save()}},hardReset:function(){var a=this.getForm();a.items.each(function(b){b.originalValue=Ext.value(b.initialConfig.value,"");b.setValue(b.originalValue)})},onStoreException:function(e,f,b,j,c,i){var g,h,a=this.getForm(),d;for(h in c.errors){if(c.errors.hasOwnProperty(h)){g=a.findField(h);g.markInvalid(c.errors[h])}}d=i[0];this.store.remove(d)},setAction:function(e,a){var d=this.getForm(),c,b;this.buttonReset.show();this.buttonAction.show();this.disableFields(false);this.currentAction=e;this.currentRecord=a;switch(e){case"create":this.hardReset();this.buttonAction.setText(this.textCreate);break;case"destroy":this.buttonAction.setText(this.textDestroy);d.loadRecord(a);break;case"duplicate":this.buttonAction.setText(this.textDuplicate);this.hardReset();c=App.getTableName(this.store);b=App.encodeField(c,"id");delete a.data[b];d.loadRecord(a);break;case"update":this.buttonAction.setText(this.textUpdate);d.loadRecord(a);break;case"view":this.buttonReset.hide();this.buttonAction.hide();d.loadRecord(a);this.disableFields(true);break}},updateRecord:function(b){var g,f,a=[],d,c,h,e;c=this.findByType("field");for(d=0;d<c.length;d+=1){f=c[d];if(f.getXType()!=="compositefield"){a.push(f)}}c=this.findByType("compositefield");for(d=0;d<c.length;d+=1){c[d].items.eachKey(function(j,i){a.push(i)})}for(d=0;d<a.length;d+=1){f=a[d];e=f.getValue();switch(f.getXType()){case"datefield":if(Ext.isDate(e)){e=e.format(f.format)}break;case"xcombobox":g=f;h=g.findRecord(g.valueField,g.getValue());b.set(g.displayField,h.get(g.displayField));break}b.set(f.getName(),e)}}});Ext.reg("xform",App.form.FormPanel);Ext.namespace("App");App.Viewport=Ext.extend(Ext.Viewport,{tabPanel:null,treePanel:null,constructor:function(d){var e,b,c,f,a;Ext.apply(this,d);c=new Ext.tree.AsyncTreeNode({id:"root",nodeType:"async",expanded:true,text:App.name});b=new Ext.tree.TreeLoader({directFn:Dbui.getTree});cfg={layout:"border",title:"Ext Layout Browser",items:[{autoScroll:true,collapsible:true,itemId:"treePanel",loader:b,region:"west",root:c,rootVisible:false,split:true,title:App.name,width:200,xtype:"treepanel"},{autoScroll:true,defaults:{layout:"fit"},itemId:"tabPanel",region:"center",xtype:"tabpanel"}]};Ext.apply(this,cfg);App.Viewport.superclass.constructor.call(this);this.tabPanel=this.getComponent("tabPanel");this.treePanel=this.getComponent("treePanel");this.treePanel.on("click",this.onCreateTab,this);this.treePanel.on("contextmenu",function(h,g){g.stopEvent()})},onCreateTab:function(d,b){var f,h=d.parentNode,a,c,g=this,e=d.attributes.cfg,i=d.attributes.cfg.xtype;if(!d.isLeaf()){return}c=d.attributes.text+"/"+i;if(this.tabPanel.getComponent(c)){this.tabPanel.setActiveTab(c);return}f={closable:true,itemId:c,title:h.attributes.text+" "+d.attributes.text};if(i==="panel"){delete e.xtype;Ext.apply(f,e)}else{f.items=[d.attributes.cfg]}a=this.tabPanel.add(f);this.tabPanel.setActiveTab(c)}});Ext.namespace("App");App.BasePanelWithSelector=Ext.extend(Ext.Panel,{panelCfg:null,selectorCfg:null,textGo:"Go",textReset:"Reset",constructor:function(d){var c,b,a;Ext.apply(this,d);b=Ext.ComponentMgr.create(this.panelCfg);a=Ext.ComponentMgr.create(this.selectorCfg);c={layout:"border",items:[{border:false,layout:"fit",itemId:"mainPanel",items:[b],region:"center",},{buttons:[{ref:"../../goButton",text:this.textGo},{ref:"../../resetButton",text:this.textReset}],collapsible:true,defaults:{anchor:"99%"},frame:true,layout:"form",itemId:"selectorPanel",items:[a],region:"east",split:true,width:300,}]};Ext.apply(this,c);App.BasePanelWithSelector.superclass.constructor.call(this)}});Ext.reg("xpanelwithselector",App.BasePanelWithSelector);Ext.namespace("App.form");App.form.ComboBox=Ext.extend(Ext.form.ComboBox,{mode:"remote",editable:false,selectOnFocus:true,triggerAction:"all",typeAhead:true,initComponent:function(){if(this.fieldLabel&&!this.emptyText){this.emptyText="Select a "+this.fieldLabel+" ..."}this.store=App.getDirectStore(this.store);App.form.ComboBox.superclass.initComponent.call(this);if(this.store.getCount()===0){this.store.load()}this.store.on("load",function(){this.setValue(this.initialConfig.value)},this)}});Ext.reg("xcombobox",App.form.ComboBox);Ext.namespace("App.grid");App.grid.GridWithFilter=Ext.extend(App.BasePanelWithSelector,{initComponent:function(){var b,a;this.items[1].collapsed=true;App.grid.GridWithFilter.superclass.initComponent.call(this);b=this.findByType("xgridfilter")[0];a=this.findByType("xgrid")[0];b.bind(a);this.goButton.hide();this.resetButton.on("click",b.onReset,b)}});Ext.reg("xgridwithfilter",App.grid.GridWithFilter);Ext.namespace("App.form");App.form.SetBox=Ext.extend(Ext.form.ComboBox,{model:null,editable:false,emptyText:"Select...",selectOnFocus:true,triggerAction:"all",typeAhead:true,initComponent:function(){var b,a=[];if(!this.model){throw new Error("the property model is missing !!!")}a=[];for(b=0;b<this.model.setData.length;b+=1){a.push([this.model.setData[b]])}this.store=new Ext.data.ArrayStore({fields:[this.model.name],data:a});this.displayField=this.model.name;this.mode="local";App.form.SetBox.superclass.initComponent.call(this)}});Ext.reg("xsetbox",App.form.SetBox);Ext.namespace("App.grid");App.grid.RowEditorToolbar=Ext.extend(Object,{ptype:"pGridRowEditorToolbar",init:function(a){var b;if(!a.rowEditor){throw new Error("no grid row editor !!!")}b=a.getTopToolbar();b.add([{text:"Add",iconCls:"silk-add",handler:a.rowEditor.onAddRow,scope:a.rowEditor}," ",{text:"Delete",iconCls:"silk-delete",handler:a.rowEditor.onDeleteRow,scope:a.rowEditor}," ",{text:"Duplicate",iconCls:"silk-clone",handler:a.rowEditor.onDuplicateRow,scope:a.rowEditor}," ",{text:"Update",iconCls:"silk-update",handler:a.rowEditor.onEditRow,scope:a.rowEditor}," ",{text:"View",iconCls:"silk-view",handler:a.rowEditor.onViewRow,scope:a.rowEditor},"-"])}});Ext.preg("pGridRowEditorToolbar",App.grid.RowEditorToolbar);Ext.namespace("App.grid");App.grid.RowEditorContextMenu=Ext.extend(Object,{ptype:"pGridRowEditorContextMenu",textAdd:"Add",textCreate:"Create",textDestroy:"Delete",textDuplicate:"Duplicate",textUpdate:"Update",textView:"View",init:function(a){var b=new Ext.menu.Menu();if(!a.rowEditor){throw new Error("no grid row editor !!!")}a.addListener("containercontextmenu",this.onContainerContextMenu,b);a.addListener("headercontextmenu",this.onHeaderContextMenu,b);a.addListener("rowcontextmenu",this.onRowContextMenu,b);b.add({text:this.textAdd,iconCls:"xaction-create",handler:a.rowEditor.onAddRow,scope:a.rowEditor},"-",{text:this.textDuplicate,iconCls:"xaction-duplicate",handler:a.rowEditor.onDuplicateRow,scope:a.rowEditor},{text:this.textUpdate,iconCls:"xaction-update",handler:a.rowEditor.onEditRow,scope:a.rowEditor},{text:this.textView,iconCls:"xaction-view",handler:a.rowEditor.onViewRow,scope:a.rowEditor},"-",{text:this.textDestroy,iconCls:"xaction-destroy",handler:a.rowEditor.onDeleteRow,scope:a.rowEditor})},onContainerContextMenu:function(a,b){var c=this;b.stopEvent();c.showAt(b.getXY())},onHeaderContextMenu:function(b,a,c){c.stopEvent()},onRowContextMenu:function(a,d,b){var c=this;b.stopEvent();a.selModel.selectRow(d);c.showAt(b.getXY())}});Ext.preg("pGridRowEditorContextMenu",App.grid.RowEditorContextMenu);Ext.namespace("App.grid");App.grid.GridFilter=Ext.extend(Ext.form.FieldSet,{filterConditions:{},initialFilterConditions:[],pagingToolbar:null,store:null,defaults:{anchor:"99%",enableKeyEvents:true},initComponent:function(){var c,a,b;App.grid.GridFilter.superclass.initComponent.call(this);a=this.findByType("field");for(b=0;b<a.length;b+=1){c=a[b];if(c.xtype==="xcombobox"){c.on("select",this.onChange,this)}else{c.on("keyup",this.onChange,this,{buffer:500})}}},bind:function(b){var c,a;this.store=b.getStore();if(b.pagingInitialized){this.pagingToolbar=b.getBottomToolbar()}c=this.store.baseParams;for(a=0;a<c.where.length;a+=1){this.initialFilterConditions.push(c.where[a])}},onChange:function(a){this.setupCondition(a)},onReset:function(){var a,b;a=this.findByType("field");for(b=0;b<a.length;b+=1){a[b].reset()}this.filterConditions={};this.store.baseParams.where=this.initialFilterConditions;this.updateStore()},setupCondition:function(f){var e=[],c,a,d,b;d=f.getValue();b=f.name+" "+f.filterOperator+" '"+d+"'";if(d===""){delete this.filterConditions[f.name]}else{this.filterConditions[f.name]=b}for(c=0;c<this.initialFilterConditions.length;c+=1){e.push(this.initialFilterConditions[c])}for(a in this.filterConditions){if(this.filterConditions.hasOwnProperty(a)){e.push(this.filterConditions[a])}}this.store.baseParams.where=e;this.updateStore()},updateStore:function(){if(this.pagingToolbar){this.pagingToolbar.doRefresh()}else{this.store.load()}}});Ext.reg("xgridfilter",App.grid.GridFilter);Ext.namespace("App.panel");App.panel.MathJax=Ext.extend(Object,{init:function(a){a.on("render",this.onPanelRender,this,{single:true})},onPanelRender:function(a){var b=a.body.getUpdater();b.on("update",this.onProcess)},onProcess:function(a,b){MathJax.Hub.PreProcess();MathJax.Hub.Process()}});Ext.preg("pPanelMathJax",App.panel.MathJax);Ext.namespace("App.data");App.data.DirectStore=Ext.extend(Ext.data.DirectStore,{autoLoad:true,autoSave:true,constructor:function(c){var a,b;a=Ext.apply({},{encode:false,listful:true,writeAllFields:false},c);a=Ext.apply(a,{api:{create:Dbui.create,destroy:Dbui.destroy,read:Dbui.read,update:Dbui.update}});if(!Ext.isDefined(a.writer)){b=Ext.copyTo({},a,"encode, listful, writeAllFields");a.writer=new Ext.data.JsonWriter(b)}App.data.DirectStore.superclass.constructor.call(this,a);this.on("write",this.onWrite)},onWrite:function(a,b){switch(b){case"create":a.totalLength+=1;break;case"destroy":a.totalLength-=1;break}}});Ext.reg("xdirectstore",App.data.DirectStore);Ext.namespace("App");App.PanelWithUrlSelector=Ext.extend(App.BasePanelWithSelector,{baseUrl:null,initComponent:function(){App.PanelWithUrlSelector.superclass.initComponent.call(this);this.goButton.on("click",this.onGo,this);this.resetButton.on("click",this.onReset,this)},onGo:function(){var b,d,c=this.getComponent("mainPanel"),e={},a=this.getComponent("selectorPanel");b=a.findByType("field");for(d=0;d<b.length;d+=1){e[b[d].getName()]=b[d].getValue()}c.load({url:this.baseUrl,params:e,text:"Loading...",timeout:30})},onReset:function(){var b,c,a=this.getComponent("selectorPanel");b=a.findByType("field");for(c=0;c<b.length;c+=1){b[c].reset()}}});Ext.reg("xpanelwithurlselector",App.PanelWithUrlSelector); \ No newline at end of file diff --git a/views/plugin_dbui/index.html b/views/plugin_dbui/index.html index 2ee502a6b094d0c263ec8d00c36fe4df36bd8e6a..da0645a6a51875370a87e3104c982689f86152da 100644 --- a/views/plugin_dbui/index.html +++ b/views/plugin_dbui/index.html @@ -36,7 +36,7 @@ <!-- dbui + user javascript library and main script --> <script type="text/javascript" src="/{{=request.application}}/plugin_dbui/get_api"></script> - <script type="text/javascript" src="/{{=request.application}}/static/plugin_dbui/dbui.min.js"></script> + <script type="text/javascript" src="/{{=request.application}}/static/plugin_dbui/dbui-min.js"></script> <script type="text/javascript" src="/{{=request.application}}/static/plugin_dbui/locale/app-lang-{{=lg}}.js"></script> {{for el in response.files:}}<script type="text/javascript" src="{{=el}}"></script>{{pass}}