Subversion Repositories eFlore/Applications.cel

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

// vim: ts=4:sw=4:nu:fdc=4:nospell
/**
 * CellActions plugin for Ext grid
 *
 * Contains renderer for an icon and fires events when icon is clicked
 *
 * @author    Ing. Jozef Sakáloš
 * @date      22. March 2008
 * @version   $Id: Ext.ux.grid.CellActions.js 253 2008-05-11 09:31:48Z jozo $
 *
 * @license Ext.ux.grid.CellActions is licensed under the terms of
 * the Open Source LGPL 3.0 license.  Commercial use is permitted to the extent
 * that the code/component(s) do NOT become part of another Open Source or Commercially
 * licensed development library or toolkit without explicit permission.
 * 
 * License details: http://www.gnu.org/licenses/lgpl.html
 */

/**
 * The following css is required:
 *
 * .ux-cell-value {
 *      position:relative;
 *      zoom:1;
 * }
 * .ux-cell-actions {
 *      position:absolute;
 *      right:0;
 *      top:-2px;
 * }
 * .ux-cell-actions-left {
 *      left:0;
 *      top:-2px;
 * }
 * .ux-cell-action {
 *      width:16px;
 *      height:16px;
 *      float:left;
 *      cursor:pointer;
 *      margin: 0 0 0 4px;
 * }
 * .ux-cell-actions-left .ux-cell-action {
 *      margin: 0 4px 0 0;
 * }
 */

/*global Ext */

Ext.ns('Ext.ux.grid');

// constructor and cellActions documentation
// {{{
/**
 * @class Ext.ux.grid.CellActions
 * @extends Ext.util.Observable
 * @constructor
 *
 * CellActions plugin causes that column model recognizes the config property cellAcions
 * that is the array of configuration objects for that column. The documentationi follows.
 *
 * THE FOLLOWING CONFIG OPTIONS ARE FOR COLUMN MODEL COLUMN, NOT FOR CellActions ITSELF.
 *
 * @cfg {Array} cellActions Mandatory. Array of action configuration objects. The following
 * configuration options of action are recognized:
 *
 * - @cfg {Function} callback Optional. Function to call if the action icon is clicked.
 *   This function is called with same signature as action event and in its original scope.
 *   If you need to call it in different scope or with another signature use 
 *   createCallback or createDelegate functions. Works for statically defined actions. Use
 *   callbacks configuration options for store bound actions.
 *
 * - @cfg {Function} cb Shortcut for callback.
 *
 * - @cfg {String} iconIndex Optional, however either iconIndex or iconCls must be
 *   configured. Field name of the field of the grid store record that contains
 *   css class of the icon to show. If configured, shown icons can vary depending
 *   of the value of this field.
 *
 * - @cfg {String} iconCls. css class of the icon to show. It is ignored if iconIndex is
 *   configured. Use this if you want static icons that are not base on the values in the record.
 *
 * - @cfg {String} qtipIndex Optional. Field name of the field of the grid store record that 
 *   contains tooltip text. If configured, the tooltip texts are taken from the store.
 *
 * - @cfg {String} tooltip Optional. Tooltip text to use as icon tooltip. It is ignored if 
 *   qtipIndex is configured. Use this if you want static tooltips that are not taken from the store.
 *
 * - @cfg {String} qtip Synonym for tooltip
 *
 * - @cfg {String} style Optional. Style to apply to action icon container.
 */
Ext.ux.grid.CellActions = function(config) {
        Ext.apply(this, config);

        this.addEvents(
                /**
                 * @event action
                 * Fires when user clicks a cell action
                 * @param {Ext.grid.GridPanel} grid
                 * @param {Ext.data.Record} record Record containing data of clicked cell
                 * @param {String} action Action clicked (equals iconCls);
                 * @param {Mixed} value Value of the clicke cell
                 * @param {String} dataIndex as specified in column model
                 * @param {Number} rowIndex Index of row clicked
                 * @param {Number} colIndex Incex of col clicked
                 */
                'action'
                /**
                 * @event beforeaction
                 * Fires when user clicks a cell action but before action event is fired. Return false to cancel the action;
                 * @param {Ext.grid.GridPanel} grid
                 * @param {Ext.data.Record} record Record containing data of clicked cell
                 * @param {String} action Action clicked (equals iconCls);
                 * @param {Mixed} value Value of the clicke cell
                 * @param {String} dataIndex as specified in column model
                 * @param {Number} rowIndex Index of row clicked
                 * @param {Number} colIndex Incex of col clicked
                 */
                ,'beforeaction'
        );
        // call parent
        Ext.ux.grid.CellActions.superclass.constructor.call(this);

}; // eo constructor
// }}}

Ext.extend(Ext.ux.grid.CellActions, Ext.util.Observable, {

        /**
         * @cfg {String} actionEvnet Event to trigger actions, e.g. click, dblclick, mouseover (defaults to 'click')
         */
         actionEvent:'click'

        /**
         * @cfg {Number} actionWidth Width of action icon in pixels. Has effect only if align:'left'
         */
        ,actionWidth:20

        /**
         * @cfg {String} align Set to 'left' to put action icons before the cell text. (defaults to undefined, meaning right)
         */

        /**
         * @private
         * @cfg {String} tpl Template for cell with actions
         */
        ,tpl:'<div class="ux-cell-value" style="padding-left:{padding}px">'
                        +'<tpl if="\'left\'!==align">{value}</tpl>'
                        +'<div class="ux-cell-actions<tpl if="\'left\'===align"> ux-cell-actions-left</tpl>" style="width:{width}px">'
                                +'<tpl for="actions"><div class="ux-cell-action {cls}" qtip="{qtip}" style="{style}">&#160;</div></tpl>'
                        +'</div>'
                        +'<tpl if="\'left\'===align">{value}</tpl>'
                +'<div>'
                
        /**
         * Called at the end of processActions. Override this if you need it.
         * @param {Object} c Column model configuration object
         * @param {Object} data See this.processActions method for details
         */
        ,userProcessing:Ext.emptyFn

        // {{{
        /**
         * Init function
         * @param {Ext.grid.GridPanel} grid Grid this plugin is in
         */
        ,init:function(grid) {
                this.grid = grid;
//              grid.on({scope:this, render:this.onRenderGrid});
                grid.afterRender = grid.afterRender.createSequence(this.onRenderGrid, this);

                var cm = this.grid.getColumnModel();
                Ext.each(cm.config, function(c, idx) {
                        if('object' === typeof c.cellActions) {
                                c.origRenderer = cm.getRenderer(idx);
                                c.renderer = this.renderActions.createDelegate(this);
                        }
                }, this);


        } // eo function init
        // }}}
        // {{{
        /**
         * grid render event handler, install actionEvent handler on view.mainBody
         * @private
         */
        ,onRenderGrid:function() {

                // install click event handler on view mainBody
                this.view = this.grid.getView();
                var cfg = {scope:this};
                cfg[this.actionEvent] = this.onClick;
                this.view.mainBody.on(cfg);

        } // eo function onRender
        // }}}
        // {{{
        /**
         * Returns data to apply to template. Override this if needed
         * @param {Mixed} value 
         * @param {Object} cell object to set some attributes of the grid cell
         * @param {Ext.data.Record} record from which the data is extracted
         * @param {Number} row row index
         * @param {Number} col col index
         * @param {Ext.data.Store} store object from which the record is extracted
         * @returns {Object} data to apply to template
         */
        ,getData:function(value, cell, record, row, col, store) {
                return record.data || {};
        }
        // }}}
        // {{{
        /**
         * replaces (but calls) the original renderer from column model
         * @private
         * @param {Mixed} value 
         * @param {Object} cell object to set some attributes of the grid cell
         * @param {Ext.data.Record} record from which the data is extracted
         * @param {Number} row row index
         * @param {Number} col col index
         * @param {Ext.data.Store} store object from which the record is extracted
         * @returns {String} markup of cell content
         */
        ,renderActions:function(value, cell, record, row, col, store) {

                // get column config from column model
                var c = this.grid.getColumnModel().config[col];

                // get output of the original renderer
                var val = c.origRenderer(value, cell, record, row, col, store);

                // get actions template if we need but don't have one
                if(c.cellActions && !c.actionsTpl) {
                        c.actionsTpl = this.processActions(c);
                        c.actionsTpl.compile();
                }
                // return original renderer output if we don't have actions
                else if(!c.cellActions) {
                        return val;
                }

                // get and return final markup
                var data = this.getData.apply(this, arguments);
                data.value = val;
                return c.actionsTpl.apply(data);

        } // eo function renderActions
        // }}}
        // {{{
        /**
         * processes the actions configs from column model column, saves callbacks and creates template
         * @param {Object} c column model config of one column
         * @private
         */
        ,processActions:function(c) {

                // callbacks holder
                this.callbacks = this.callbacks || {};

                // data for intermediate template
                var data = {
                         align:this.align || 'right'
                        ,width:this.actionWidth * c.cellActions.length
                        ,padding:'left' === this.align ? this.actionWidth * c.cellActions.length : 0
                        ,value:'{value}'
                        ,actions:[]
                };

                // cellActions loop
                Ext.each(c.cellActions, function(a, i) {

                        // save callback
                        if(a.iconCls && 'function' === typeof (a.callback || a.cb)) {
                                this.callbacks[a.iconCls] = a.callback || a.cb;
                        }

                        // data for intermediate xtemplate action
                        var o = {
                                 cls:a.iconIndex ? '{' + a.iconIndex + '}' : (a.iconCls ? a.iconCls : '')
                                ,qtip:a.qtipIndex ? '{' + a.qtipIndex + '}' : (a.tooltip || a.qtip ? a.tooltip || a.qtip : '')
                                ,style:a.style ? a.style : ''
                        };
                        data.actions.push(o);

                }, this); // eo cellActions loop

                this.userProcessing(c, data);

                // get and return final template
                var xt = new Ext.XTemplate(this.tpl);
                return new Ext.Template(xt.apply(data));

        } // eo function processActions
        // }}}
        // {{{
        /**
         * Grid body actionEvent event handler
         * @private
         */
        ,onClick:function(e, target) {

                // collect all variables for callback and/or events
                var t = e.getTarget('div.ux-cell-action');
                var row = e.getTarget('.x-grid3-row');
                var col = this.view.findCellIndex(target.parentNode.parentNode);
                var c = this.grid.getColumnModel().config[col];
                var record, dataIndex, value, action;
                if(t) {
                        record = this.grid.store.getAt(row.rowIndex);
                        dataIndex = c.dataIndex;
                        value = record.get(dataIndex);
                        action = t.className.replace(/ux-cell-action /, '');
                }

                // check if we've collected all necessary variables
                if(false !== row && false !== col && record && dataIndex && action) {

                        // call callback if any
                        if(this.callbacks && 'function' === typeof this.callbacks[action]) {
                                this.callbacks[action](this.grid, record, action, value, row.rowIndex, col);
                        }

                        // fire events
                        if(true !== this.eventsSuspended && false === this.fireEvent('beforeaction', this.grid, record, action, value, dataIndex, row.rowIndex, col)) {
                                return;
                        }
                        else if(true !== this.eventsSuspended) {
                                this.fireEvent('action', this.grid, record, action, value, dataIndex, row.rowIndex, col);
                        }

                }
        } // eo function onClick
        // }}}

});

// register xtype
Ext.reg('cellactions', Ext.ux.grid.CellActions);

// eof