* Fork - LGPL
* <script type="text/javascript">
*/
-
+ /**
+ * @extends Roo.dd.DDProxy
+ * @class Roo.grid.SplitDragZone
+ * Support for Column Header resizing
+ * @constructor
+ * @param {Object} config
+ */
+// private
+// This is a support class used internally by the Grid components
+Roo.grid.SplitDragZone = function(grid, hd, hd2){
+ this.grid = grid;
+ this.view = grid.getView();
+ this.proxy = this.view.resizeProxy;
+ Roo.grid.SplitDragZone.superclass.constructor.call(
+ this,
+ hd, // ID
+ "gridSplitters" + this.grid.getGridEl().id, // SGROUP
+ { // CONFIG
+ dragElId : Roo.id(this.proxy.dom),
+ resizeFrame:false
+ }
+ );
+
+ this.setHandleElId(Roo.id(hd));
+ if (hd2 !== false) {
+ this.setOuterHandleElId(Roo.id(hd2));
+ }
+
+ this.scroll = false;
+};
+Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
+ fly: Roo.Element.fly,
+
+ b4StartDrag : function(x, y){
+ this.view.headersDisabled = true;
+ var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
+ this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
+ );
+ this.proxy.setHeight(h);
+
+ // for old system colWidth really stored the actual width?
+ // in bootstrap we tried using xs/ms/etc.. to do % sizing?
+ // which in reality did not work.. - it worked only for fixed sizes
+ // for resizable we need to use actual sizes.
+ var w = this.cm.getColumnWidth(this.cellIndex);
+ if (!this.view.mainWrap) {
+ // bootstrap.
+ w = this.view.getHeaderIndex(this.cellIndex).getWidth();
+ }
+
+
+
+ // this was w-this.grid.minColumnWidth;
+ // doesnt really make sense? - w = thie curren width or the rendered one?
+ var minw = Math.max(w-this.grid.minColumnWidth, 0);
+ this.resetConstraints();
+ this.setXConstraint(minw, 1000);
+ this.setYConstraint(0, 0);
+ this.minX = x - minw;
+ this.maxX = x + 1000;
+ this.startPos = x;
+ if (!this.view.mainWrap) { // this is Bootstrap code..
+ this.getDragEl().style.display='block';
+ }
+
+ Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
+ },
+
+
+ handleMouseDown : function(e){
+ ev = Roo.EventObject.setEvent(e);
+ var t = this.fly(ev.getTarget());
+ if(t.hasClass("x-grid-split")){
+ this.cellIndex = this.view.getCellIndex(t.dom);
+ this.split = t.dom;
+ this.cm = this.grid.colModel;
+ if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
+ Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
+ }
+ }
+ },
+
+ endDrag : function(e){
+ this.view.headersDisabled = false;
+ var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
+ var diff = endX - this.startPos;
+ //
+ var w = this.cm.getColumnWidth(this.cellIndex);
+ if (!this.view.mainWrap) {
+ w = 0;
+ }
+ this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
+ },
+
+ autoOffset : function(){
+ this.setDelta(0,0);
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
/**
- * @class Roo.grid.ColumnModel
+ * @class Roo.grid.AbstractSelectionModel
* @extends Roo.util.Observable
- * This is the default implementation of a ColumnModel used by the Grid. It defines
- * the columns in the grid.
- * <br>Usage:<br>
- <pre><code>
- var colModel = new Roo.grid.ColumnModel([
- {header: "Ticker", width: 60, sortable: true, locked: true},
- {header: "Company Name", width: 150, sortable: true},
- {header: "Market Cap.", width: 100, sortable: true},
- {header: "$ Sales", width: 100, sortable: true, renderer: money},
- {header: "Employees", width: 100, sortable: true, resizable: false}
- ]);
- </code></pre>
- * <p>
-
- * The config options listed for this class are options which may appear in each
- * individual column definition.
- * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
+ * Abstract base class for grid SelectionModels. It provides the interface that should be
+ * implemented by descendant classes. This class should not be directly instantiated.
* @constructor
- * @param {Object} config An Array of column config objects. See this class's
- * config objects for details.
-*/
-Roo.grid.ColumnModel = function(config){
- /**
- * The config passed into the constructor
- */
- this.config = []; //config;
- this.lookup = {};
+ */
+Roo.grid.AbstractSelectionModel = function(){
+ this.locked = false;
+ Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
+};
- // if no id, create one
- // if the column does not have a dataIndex mapping,
- // map it to the order it is in the config
- for(var i = 0, len = config.length; i < len; i++){
- this.addColumn(config[i]);
-
- }
+Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
+ /** @ignore Called by the grid automatically. Do not call directly. */
+ init : function(grid){
+ this.grid = grid;
+ this.initEvents();
+ },
/**
- * The width of columns which have no width specified (defaults to 100)
- * @type Number
+ * Locks the selections.
*/
- this.defaultWidth = 100;
+ lock : function(){
+ this.locked = true;
+ },
/**
- * Default sortable of columns which have no sortable specified (defaults to false)
- * @type Boolean
+ * Unlocks the selections.
*/
- this.defaultSortable = false;
+ unlock : function(){
+ this.locked = false;
+ },
+
+ /**
+ * Returns true if the selections are locked.
+ * @return {Boolean}
+ */
+ isLocked : function(){
+ return this.locked;
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @extends Roo.grid.AbstractSelectionModel
+ * @class Roo.grid.RowSelectionModel
+ * The default SelectionModel used by {@link Roo.grid.Grid}.
+ * It supports multiple selections and keyboard selection/navigation.
+ * @constructor
+ * @param {Object} config
+ */
+Roo.grid.RowSelectionModel = function(config){
+ Roo.apply(this, config);
+ this.selections = new Roo.util.MixedCollection(false, function(o){
+ return o.id;
+ });
+
+ this.last = false;
+ this.lastActive = false;
this.addEvents({
/**
- * @event widthchange
- * Fires when the width of a column changes.
- * @param {ColumnModel} this
- * @param {Number} columnIndex The column index
- * @param {Number} newWidth The new width
- */
- "widthchange": true,
- /**
- * @event headerchange
- * Fires when the text of a header changes.
- * @param {ColumnModel} this
- * @param {Number} columnIndex The column index
- * @param {Number} newText The new header text
- */
- "headerchange": true,
- /**
- * @event hiddenchange
- * Fires when a column is hidden or "unhidden".
- * @param {ColumnModel} this
- * @param {Number} columnIndex The column index
- * @param {Boolean} hidden true if hidden, false otherwise
- */
- "hiddenchange": true,
- /**
- * @event columnmoved
- * Fires when a column is moved.
- * @param {ColumnModel} this
- * @param {Number} oldIndex
- * @param {Number} newIndex
- */
- "columnmoved" : true,
- /**
- * @event columlockchange
- * Fires when a column's locked state is changed
- * @param {ColumnModel} this
- * @param {Number} colIndex
- * @param {Boolean} locked true if locked
- */
- "columnlockchange" : true
+ * @event selectionchange
+ * Fires when the selection changes
+ * @param {SelectionModel} this
+ */
+ "selectionchange" : true,
+ /**
+ * @event afterselectionchange
+ * Fires after the selection changes (eg. by key press or clicking)
+ * @param {SelectionModel} this
+ */
+ "afterselectionchange" : true,
+ /**
+ * @event beforerowselect
+ * Fires when a row is selected being selected, return false to cancel.
+ * @param {SelectionModel} this
+ * @param {Number} rowIndex The selected index
+ * @param {Boolean} keepExisting False if other selections will be cleared
+ */
+ "beforerowselect" : true,
+ /**
+ * @event rowselect
+ * Fires when a row is selected.
+ * @param {SelectionModel} this
+ * @param {Number} rowIndex The selected index
+ * @param {Roo.data.Record} r The record
+ */
+ "rowselect" : true,
+ /**
+ * @event rowdeselect
+ * Fires when a row is deselected.
+ * @param {SelectionModel} this
+ * @param {Number} rowIndex The selected index
+ */
+ "rowdeselect" : true
});
- Roo.grid.ColumnModel.superclass.constructor.call(this);
+ Roo.grid.RowSelectionModel.superclass.constructor.call(this);
+ this.locked = false;
};
-Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
- /**
- * @cfg {String} header The header text to display in the Grid view.
- */
- /**
- * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
- * {@link Roo.data.Record} definition from which to draw the column's value. If not
- * specified, the column's index is used as an index into the Record's data Array.
- */
- /**
- * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
- * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
- */
- /**
- * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
- * Defaults to the value of the {@link #defaultSortable} property.
- * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
- */
- /**
- * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
- */
- /**
- * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
- */
- /**
- * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
- */
- /**
- * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
- */
- /**
- * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
- * given the cell's data value. See {@link #setRenderer}. If not specified, the
- * default renderer returns the escaped data value. If an object is returned (bootstrap only)
- * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
- */
- /**
- * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
- */
- /**
- * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
- */
- /**
- * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
- */
- /**
- * @cfg {String} cursor (Optional)
- */
- /**
- * @cfg {String} tooltip (Optional)
- */
- /**
- * @cfg {Number} xs (Optional)
- */
- /**
- * @cfg {Number} sm (Optional)
- */
- /**
- * @cfg {Number} md (Optional)
- */
- /**
- * @cfg {Number} lg (Optional)
- */
- /**
- * Returns the id of the column at the specified index.
- * @param {Number} index The column index
- * @return {String} the id
- */
- getColumnId : function(index){
- return this.config[index].id;
- },
+Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
/**
- * Returns the column for a specified id.
- * @param {String} id The column id
- * @return {Object} the column
+ * @cfg {Boolean} singleSelect
+ * True to allow selection of only one row at a time (defaults to false)
*/
- getColumnById : function(id){
- return this.lookup[id];
- },
+ singleSelect : false,
-
- /**
- * Returns the column Object for a specified dataIndex.
- * @param {String} dataIndex The column dataIndex
- * @return {Object|Boolean} the column or false if not found
- */
- getColumnByDataIndex: function(dataIndex){
- var index = this.findColumnIndex(dataIndex);
- return index > -1 ? this.config[index] : false;
- },
-
- /**
- * Returns the index for a specified column id.
- * @param {String} id The column id
- * @return {Number} the index, or -1 if not found
- */
- getIndexById : function(id){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(this.config[i].id == id){
- return i;
- }
+ // private
+ initEvents : function(){
+
+ if(!this.grid.enableDragDrop && !this.grid.enableDrag){
+ this.grid.on("mousedown", this.handleMouseDown, this);
+ }else{ // allow click to work like normal
+ this.grid.on("rowclick", this.handleDragableRowClick, this);
}
- return -1;
+ // bootstrap does not have a view..
+ var view = this.grid.view ? this.grid.view : this.grid;
+ this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
+ "up" : function(e){
+ if(!e.shiftKey){
+ this.selectPrevious(e.shiftKey);
+ }else if(this.last !== false && this.lastActive !== false){
+ var last = this.last;
+ this.selectRange(this.last, this.lastActive-1);
+ view.focusRow(this.lastActive);
+ if(last !== false){
+ this.last = last;
+ }
+ }else{
+ this.selectFirstRow();
+ }
+ this.fireEvent("afterselectionchange", this);
+ },
+ "down" : function(e){
+ if(!e.shiftKey){
+ this.selectNext(e.shiftKey);
+ }else if(this.last !== false && this.lastActive !== false){
+ var last = this.last;
+ this.selectRange(this.last, this.lastActive+1);
+ view.focusRow(this.lastActive);
+ if(last !== false){
+ this.last = last;
+ }
+ }else{
+ this.selectFirstRow();
+ }
+ this.fireEvent("afterselectionchange", this);
+ },
+ scope: this
+ });
+
+
+ view.on("refresh", this.onRefresh, this);
+ view.on("rowupdated", this.onRowUpdated, this);
+ view.on("rowremoved", this.onRemove, this);
},
-
- /**
- * Returns the index for a specified column dataIndex.
- * @param {String} dataIndex The column dataIndex
- * @return {Number} the index, or -1 if not found
- */
-
- findColumnIndex : function(dataIndex){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(this.config[i].dataIndex == dataIndex){
- return i;
+
+ // private
+ onRefresh : function(){
+ var ds = this.grid.ds, i, v = this.grid.view;
+ var s = this.selections;
+ s.each(function(r){
+ if((i = ds.indexOfId(r.id)) != -1){
+ v.onRowSelect(i);
+ s.add(ds.getAt(i)); // updating the selection relate data
+ }else{
+ s.remove(r);
}
- }
- return -1;
- },
-
-
- moveColumn : function(oldIndex, newIndex){
- var c = this.config[oldIndex];
- this.config.splice(oldIndex, 1);
- this.config.splice(newIndex, 0, c);
- this.dataMap = null;
- this.fireEvent("columnmoved", this, oldIndex, newIndex);
+ });
},
- isLocked : function(colIndex){
- return this.config[colIndex].locked === true;
+ // private
+ onRemove : function(v, index, r){
+ this.selections.remove(r);
},
- setLocked : function(colIndex, value, suppressEvent){
- if(this.isLocked(colIndex) == value){
- return;
- }
- this.config[colIndex].locked = value;
- if(!suppressEvent){
- this.fireEvent("columnlockchange", this, colIndex, value);
+ // private
+ onRowUpdated : function(v, index, r){
+ if(this.isSelected(r)){
+ v.onRowSelect(index);
}
},
- getTotalLockedWidth : function(){
- var totalWidth = 0;
- for(var i = 0; i < this.config.length; i++){
- if(this.isLocked(i) && !this.isHidden(i)){
- this.totalWidth += this.getColumnWidth(i);
- }
+ /**
+ * Select records.
+ * @param {Array} records The records to select
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
+ */
+ selectRecords : function(records, keepExisting){
+ if(!keepExisting){
+ this.clearSelections();
}
- return totalWidth;
- },
-
- getLockedCount : function(){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(!this.isLocked(i)){
- return i;
- }
+ var ds = this.grid.ds;
+ for(var i = 0, len = records.length; i < len; i++){
+ this.selectRow(ds.indexOf(records[i]), true);
}
-
- return this.config.length;
},
/**
- * Returns the number of columns.
+ * Gets the number of selected rows.
* @return {Number}
*/
- getColumnCount : function(visibleOnly){
- if(visibleOnly === true){
- var c = 0;
- for(var i = 0, len = this.config.length; i < len; i++){
- if(!this.isHidden(i)){
- c++;
- }
- }
- return c;
- }
- return this.config.length;
+ getCount : function(){
+ return this.selections.length;
},
/**
- * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
- * @param {Function} fn
- * @param {Object} scope (optional)
- * @return {Array} result
+ * Selects the first row in the grid.
*/
- getColumnsBy : function(fn, scope){
- var r = [];
- for(var i = 0, len = this.config.length; i < len; i++){
- var c = this.config[i];
- if(fn.call(scope||this, c, i) === true){
- r[r.length] = c;
- }
- }
- return r;
+ selectFirstRow : function(){
+ this.selectRow(0);
},
/**
- * Returns true if the specified column is sortable.
- * @param {Number} col The column index
- * @return {Boolean}
+ * Select the last row.
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
*/
- isSortable : function(col){
- if(typeof this.config[col].sortable == "undefined"){
- return this.defaultSortable;
- }
- return this.config[col].sortable;
+ selectLastRow : function(keepExisting){
+ this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
},
/**
- * Returns the rendering (formatting) function defined for the column.
- * @param {Number} col The column index.
- * @return {Function} The function used to render the cell. See {@link #setRenderer}.
+ * Selects the row immediately following the last selected row.
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
*/
- getRenderer : function(col){
- if(!this.config[col].renderer){
- return Roo.grid.ColumnModel.defaultRenderer;
+ selectNext : function(keepExisting){
+ if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
+ this.selectRow(this.last+1, keepExisting);
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.focusRow(this.last);
}
- return this.config[col].renderer;
},
/**
- * Sets the rendering (formatting) function for a column.
- * @param {Number} col The column index
- * @param {Function} fn The function to use to process the cell's raw data
- * to return HTML markup for the grid view. The render function is called with
- * the following parameters:<ul>
- * <li>Data value.</li>
- * <li>Cell metadata. An object in which you may set the following attributes:<ul>
- * <li>css A CSS style string to apply to the table cell.</li>
- * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
- * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
- * <li>Row index</li>
- * <li>Column index</li>
- * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
+ * Selects the row that precedes the last selected row.
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
*/
- setRenderer : function(col, fn){
- this.config[col].renderer = fn;
+ selectPrevious : function(keepExisting){
+ if(this.last){
+ this.selectRow(this.last-1, keepExisting);
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.focusRow(this.last);
+ }
},
/**
- * Returns the width for the specified column.
- * @param {Number} col The column index
- * @return {Number}
+ * Returns the selected records
+ * @return {Array} Array of selected records
*/
- getColumnWidth : function(col){
- return this.config[col].width * 1 || this.defaultWidth;
+ getSelections : function(){
+ return [].concat(this.selections.items);
},
/**
- * Sets the width for a column.
- * @param {Number} col The column index
- * @param {Number} width The new width
+ * Returns the first selected record.
+ * @return {Record}
*/
- setColumnWidth : function(col, width, suppressEvent){
- this.config[col].width = width;
- this.totalWidth = null;
- if(!suppressEvent){
- this.fireEvent("widthchange", this, col, width);
- }
+ getSelected : function(){
+ return this.selections.itemAt(0);
},
+
/**
- * Returns the total width of all columns.
- * @param {Boolean} includeHidden True to include hidden column widths
- * @return {Number}
+ * Clears all selections.
*/
- getTotalWidth : function(includeHidden){
- if(!this.totalWidth){
- this.totalWidth = 0;
- for(var i = 0, len = this.config.length; i < len; i++){
- if(includeHidden || !this.isHidden(i)){
- this.totalWidth += this.getColumnWidth(i);
- }
- }
+ clearSelections : function(fast){
+ if(this.locked) {
+ return;
}
- return this.totalWidth;
+ if(fast !== true){
+ var ds = this.grid.ds;
+ var s = this.selections;
+ s.each(function(r){
+ this.deselectRow(ds.indexOfId(r.id));
+ }, this);
+ s.clear();
+ }else{
+ this.selections.clear();
+ }
+ this.last = false;
},
+
/**
- * Returns the header for the specified column.
- * @param {Number} col The column index
- * @return {String}
+ * Selects all rows.
*/
- getColumnHeader : function(col){
- return this.config[col].header;
- },
-
- /**
- * Sets the header for a column.
- * @param {Number} col The column index
- * @param {String} header The new header
- */
- setColumnHeader : function(col, header){
- this.config[col].header = header;
- this.fireEvent("headerchange", this, col, header);
- },
-
- /**
- * Returns the tooltip for the specified column.
- * @param {Number} col The column index
- * @return {String}
- */
- getColumnTooltip : function(col){
- return this.config[col].tooltip;
- },
- /**
- * Sets the tooltip for a column.
- * @param {Number} col The column index
- * @param {String} tooltip The new tooltip
- */
- setColumnTooltip : function(col, tooltip){
- this.config[col].tooltip = tooltip;
+ selectAll : function(){
+ if(this.locked) {
+ return;
+ }
+ this.selections.clear();
+ for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
+ this.selectRow(i, true);
+ }
},
/**
- * Returns the dataIndex for the specified column.
- * @param {Number} col The column index
- * @return {Number}
+ * Returns True if there is a selection.
+ * @return {Boolean}
*/
- getDataIndex : function(col){
- return this.config[col].dataIndex;
+ hasSelection : function(){
+ return this.selections.length > 0;
},
/**
- * Sets the dataIndex for a column.
- * @param {Number} col The column index
- * @param {Number} dataIndex The new dataIndex
+ * Returns True if the specified row is selected.
+ * @param {Number/Record} record The record or index of the record to check
+ * @return {Boolean}
*/
- setDataIndex : function(col, dataIndex){
- this.config[col].dataIndex = dataIndex;
+ isSelected : function(index){
+ var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
+ return (r && this.selections.key(r.id) ? true : false);
},
-
-
/**
- * Returns true if the cell is editable.
- * @param {Number} colIndex The column index
- * @param {Number} rowIndex The row index - this is nto actually used..?
+ * Returns True if the specified record id is selected.
+ * @param {String} id The id of record to check
* @return {Boolean}
*/
- isCellEditable : function(colIndex, rowIndex){
- return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
+ isIdSelected : function(id){
+ return (this.selections.key(id) ? true : false);
},
- /**
- * Returns the editor defined for the cell/column.
- * return false or null to disable editing.
- * @param {Number} colIndex The column index
- * @param {Number} rowIndex The row index
- * @return {Object}
- */
- getCellEditor : function(colIndex, rowIndex){
- return this.config[colIndex].editor;
+ // private
+ handleMouseDown : function(e, t)
+ {
+ var view = this.grid.view ? this.grid.view : this.grid;
+ var rowIndex;
+ if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
+ return;
+ };
+ if(e.shiftKey && this.last !== false){
+ var last = this.last;
+ this.selectRange(last, rowIndex, e.ctrlKey);
+ this.last = last; // reset the last
+ view.focusRow(rowIndex);
+ }else{
+ var isSelected = this.isSelected(rowIndex);
+ if(e.button !== 0 && isSelected){
+ view.focusRow(rowIndex);
+ }else if(e.ctrlKey && isSelected){
+ this.deselectRow(rowIndex);
+ }else if(!isSelected){
+ this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
+ view.focusRow(rowIndex);
+ }
+ }
+ this.fireEvent("afterselectionchange", this);
},
-
- /**
- * Sets if a column is editable.
- * @param {Number} col The column index
- * @param {Boolean} editable True if the column is editable
- */
- setEditable : function(col, editable){
- this.config[col].editable = editable;
+ // private
+ handleDragableRowClick : function(grid, rowIndex, e)
+ {
+ if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
+ this.selectRow(rowIndex, false);
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.focusRow(rowIndex);
+ this.fireEvent("afterselectionchange", this);
+ }
},
-
-
+
/**
- * Returns true if the column is hidden.
- * @param {Number} colIndex The column index
- * @return {Boolean}
+ * Selects multiple rows.
+ * @param {Array} rows Array of the indexes of the row to select
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
*/
- isHidden : function(colIndex){
- return this.config[colIndex].hidden;
+ selectRows : function(rows, keepExisting){
+ if(!keepExisting){
+ this.clearSelections();
+ }
+ for(var i = 0, len = rows.length; i < len; i++){
+ this.selectRow(rows[i], true);
+ }
},
-
/**
- * Returns true if the column width cannot be changed
+ * Selects a range of rows. All rows in between startRow and endRow are also selected.
+ * @param {Number} startRow The index of the first row in the range
+ * @param {Number} endRow The index of the last row in the range
+ * @param {Boolean} keepExisting (optional) True to retain existing selections
*/
- isFixed : function(colIndex){
- return this.config[colIndex].fixed;
+ selectRange : function(startRow, endRow, keepExisting){
+ if(this.locked) {
+ return;
+ }
+ if(!keepExisting){
+ this.clearSelections();
+ }
+ if(startRow <= endRow){
+ for(var i = startRow; i <= endRow; i++){
+ this.selectRow(i, true);
+ }
+ }else{
+ for(var i = startRow; i >= endRow; i--){
+ this.selectRow(i, true);
+ }
+ }
},
/**
- * Returns true if the column can be resized
- * @return {Boolean}
+ * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
+ * @param {Number} startRow The index of the first row in the range
+ * @param {Number} endRow The index of the last row in the range
*/
- isResizable : function(colIndex){
- return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
+ deselectRange : function(startRow, endRow, preventViewNotify){
+ if(this.locked) {
+ return;
+ }
+ for(var i = startRow; i <= endRow; i++){
+ this.deselectRow(i, preventViewNotify);
+ }
},
+
/**
- * Sets if a column is hidden.
- * @param {Number} colIndex The column index
- * @param {Boolean} hidden True if the column is hidden
+ * Selects a row.
+ * @param {Number} row The index of the row to select
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
*/
- setHidden : function(colIndex, hidden){
- this.config[colIndex].hidden = hidden;
- this.totalWidth = null;
- this.fireEvent("hiddenchange", this, colIndex, hidden);
+ selectRow : function(index, keepExisting, preventViewNotify){
+ if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
+ return;
+ }
+ if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
+ if(!keepExisting || this.singleSelect){
+ this.clearSelections();
+ }
+ var r = this.grid.ds.getAt(index);
+ this.selections.add(r);
+ this.last = this.lastActive = index;
+ if(!preventViewNotify){
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.onRowSelect(index);
+ }
+ this.fireEvent("rowselect", this, index, r);
+ this.fireEvent("selectionchange", this);
+ }
},
/**
- * Sets the editor for a column.
- * @param {Number} col The column index
- * @param {Object} editor The editor object
+ * Deselects a row.
+ * @param {Number} row The index of the row to deselect
*/
- setEditor : function(col, editor){
- this.config[col].editor = editor;
- },
- /**
- * Add a column (experimental...) - defaults to adding to the end..
- * @param {Object} config
- */
- addColumn : function(c)
- {
-
- var i = this.config.length;
- this.config[i] = c;
-
- if(typeof c.dataIndex == "undefined"){
- c.dataIndex = i;
+ deselectRow : function(index, preventViewNotify){
+ if(this.locked) {
+ return;
}
- if(typeof c.renderer == "string"){
- c.renderer = Roo.util.Format[c.renderer];
+ if(this.last == index){
+ this.last = false;
}
- if(typeof c.id == "undefined"){
- c.id = Roo.id();
+ if(this.lastActive == index){
+ this.lastActive = false;
}
- if(c.editor && c.editor.xtype){
- c.editor = Roo.factory(c.editor, Roo.grid);
+ var r = this.grid.ds.getAt(index);
+ this.selections.remove(r);
+ if(!preventViewNotify){
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.onRowDeselect(index);
}
- if(c.editor && c.editor.isFormField){
- c.editor = new Roo.grid.GridEditor(c.editor);
+ this.fireEvent("rowdeselect", this, index);
+ this.fireEvent("selectionchange", this);
+ },
+
+ // private
+ restoreLast : function(){
+ if(this._last){
+ this.last = this._last;
}
- this.lookup[c.id] = c;
- }
-
-});
+ },
-Roo.grid.ColumnModel.defaultRenderer = function(value)
-{
- if(typeof value == "object") {
- return value;
- }
- if(typeof value == "string" && value.length < 1){
- return " ";
- }
-
- return String.format("{0}", value);
-};
+ // private
+ acceptsNav : function(row, col, cm){
+ return !cm.isHidden(col) && cm.isCellEditable(col, row);
+ },
-// Alias for backwards compatibility
-Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
-/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
+ // private
+ onEditorKey : function(field, e){
+ var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
+ if(k == e.TAB){
+ e.stopEvent();
+ ed.completeEdit();
+ if(e.shiftKey){
+ newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
+ }else{
+ newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
+ }
+ }else if(k == e.ENTER && !e.ctrlKey){
+ e.stopEvent();
+ ed.completeEdit();
+ if(e.shiftKey){
+ newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
+ }else{
+ newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
+ }
+ }else if(k == e.ESC){
+ ed.cancelEdit();
+ }
+ if(newCell){
+ g.startEditing(newCell[0], newCell[1]);
+ }
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
*
* Fork - LGPL
* <script type="text/javascript">
*/
+
/**
- * @class Roo.LoadMask
- * A simple utility class for generically masking elements while loading data. If the element being masked has
- * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
- * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
- * element's UpdateManager load indicator and will be destroyed after the initial load.
+ * @class Roo.grid.ColumnModel
+ * @extends Roo.util.Observable
+ * This is the default implementation of a ColumnModel used by the Grid. It defines
+ * the columns in the grid.
+ * <br>Usage:<br>
+ <pre><code>
+ var colModel = new Roo.grid.ColumnModel([
+ {header: "Ticker", width: 60, sortable: true, locked: true},
+ {header: "Company Name", width: 150, sortable: true},
+ {header: "Market Cap.", width: 100, sortable: true},
+ {header: "$ Sales", width: 100, sortable: true, renderer: money},
+ {header: "Employees", width: 100, sortable: true, resizable: false}
+ ]);
+ </code></pre>
+ * <p>
+
+ * The config options listed for this class are options which may appear in each
+ * individual column definition.
+ * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
* @constructor
- * Create a new LoadMask
- * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
- * @param {Object} config The config object
- */
-Roo.LoadMask = function(el, config){
- this.el = Roo.get(el);
- Roo.apply(this, config);
- if(this.store){
- this.store.on('beforeload', this.onBeforeLoad, this);
- this.store.on('load', this.onLoad, this);
- this.store.on('loadexception', this.onLoadException, this);
- this.removeMask = false;
- }else{
- var um = this.el.getUpdateManager();
- um.showLoadIndicator = false; // disable the default indicator
- um.on('beforeupdate', this.onBeforeLoad, this);
- um.on('update', this.onLoad, this);
- um.on('failure', this.onLoad, this);
- this.removeMask = true;
+ * @param {Object} config An Array of column config objects. See this class's
+ * config objects for details.
+*/
+Roo.grid.ColumnModel = function(config){
+ /**
+ * The config passed into the constructor
+ */
+ this.config = []; //config;
+ this.lookup = {};
+
+ // if no id, create one
+ // if the column does not have a dataIndex mapping,
+ // map it to the order it is in the config
+ for(var i = 0, len = config.length; i < len; i++){
+ this.addColumn(config[i]);
+
}
-};
-Roo.LoadMask.prototype = {
/**
- * @cfg {Boolean} removeMask
- * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
- * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
+ * The width of columns which have no width specified (defaults to 100)
+ * @type Number
*/
+ this.defaultWidth = 100;
+
/**
- * @cfg {String} msg
- * The text to display in a centered loading message box (defaults to 'Loading...')
+ * Default sortable of columns which have no sortable specified (defaults to false)
+ * @type Boolean
*/
- msg : 'Loading...',
+ this.defaultSortable = false;
+
+ this.addEvents({
+ /**
+ * @event widthchange
+ * Fires when the width of a column changes.
+ * @param {ColumnModel} this
+ * @param {Number} columnIndex The column index
+ * @param {Number} newWidth The new width
+ */
+ "widthchange": true,
+ /**
+ * @event headerchange
+ * Fires when the text of a header changes.
+ * @param {ColumnModel} this
+ * @param {Number} columnIndex The column index
+ * @param {Number} newText The new header text
+ */
+ "headerchange": true,
+ /**
+ * @event hiddenchange
+ * Fires when a column is hidden or "unhidden".
+ * @param {ColumnModel} this
+ * @param {Number} columnIndex The column index
+ * @param {Boolean} hidden true if hidden, false otherwise
+ */
+ "hiddenchange": true,
+ /**
+ * @event columnmoved
+ * Fires when a column is moved.
+ * @param {ColumnModel} this
+ * @param {Number} oldIndex
+ * @param {Number} newIndex
+ */
+ "columnmoved" : true,
+ /**
+ * @event columlockchange
+ * Fires when a column's locked state is changed
+ * @param {ColumnModel} this
+ * @param {Number} colIndex
+ * @param {Boolean} locked true if locked
+ */
+ "columnlockchange" : true
+ });
+ Roo.grid.ColumnModel.superclass.constructor.call(this);
+};
+Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
/**
- * @cfg {String} msgCls
- * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
+ * @cfg {String} header The header text to display in the Grid view.
+ */
+ /**
+ * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
+ */
+ /**
+ * @cfg {String} smHeader Header at Bootsrap Small width
+ */
+ /**
+ * @cfg {String} mdHeader Header at Bootsrap Medium width
+ */
+ /**
+ * @cfg {String} lgHeader Header at Bootsrap Large width
+ */
+ /**
+ * @cfg {String} xlHeader Header at Bootsrap extra Large width
*/
- msgCls : 'x-mask-loading',
-
/**
- * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
- * @type Boolean
+ * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
+ * {@link Roo.data.Record} definition from which to draw the column's value. If not
+ * specified, the column's index is used as an index into the Record's data Array.
*/
- disabled: false,
+ /**
+ * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
+ * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
+ */
+ /**
+ * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
+ * Defaults to the value of the {@link #defaultSortable} property.
+ * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
+ */
+ /**
+ * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
+ */
+ /**
+ * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
+ */
+ /**
+ * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
+ */
+ /**
+ * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
+ */
+ /**
+ * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
+ * given the cell's data value. See {@link #setRenderer}. If not specified, the
+ * default renderer returns the escaped data value. If an object is returned (bootstrap only)
+ * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
+ */
+ /**
+ * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
+ */
+ /**
+ * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
+ */
+ /**
+ * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
+ */
+ /**
+ * @cfg {String} cursor (Optional)
+ */
+ /**
+ * @cfg {String} tooltip (Optional)
+ */
+ /**
+ * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * Returns the id of the column at the specified index.
+ * @param {Number} index The column index
+ * @return {String} the id
+ */
+ getColumnId : function(index){
+ return this.config[index].id;
+ },
/**
- * Disables the mask to prevent it from being displayed
+ * Returns the column for a specified id.
+ * @param {String} id The column id
+ * @return {Object} the column
*/
- disable : function(){
- this.disabled = true;
+ getColumnById : function(id){
+ return this.lookup[id];
},
+
/**
- * Enables the mask so that it can be displayed
+ * Returns the column Object for a specified dataIndex.
+ * @param {String} dataIndex The column dataIndex
+ * @return {Object|Boolean} the column or false if not found
*/
- enable : function(){
- this.disabled = false;
+ getColumnByDataIndex: function(dataIndex){
+ var index = this.findColumnIndex(dataIndex);
+ return index > -1 ? this.config[index] : false;
},
- onLoadException : function()
- {
- Roo.log(arguments);
-
- if (typeof(arguments[3]) != 'undefined') {
- Roo.MessageBox.alert("Error loading",arguments[3]);
- }
- /*
- try {
- if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
- Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
- }
- } catch(e) {
-
+ /**
+ * Returns the index for a specified column id.
+ * @param {String} id The column id
+ * @return {Number} the index, or -1 if not found
+ */
+ getIndexById : function(id){
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(this.config[i].id == id){
+ return i;
+ }
}
- */
+ return -1;
+ },
- (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+ /**
+ * Returns the index for a specified column dataIndex.
+ * @param {String} dataIndex The column dataIndex
+ * @return {Number} the index, or -1 if not found
+ */
+
+ findColumnIndex : function(dataIndex){
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(this.config[i].dataIndex == dataIndex){
+ return i;
+ }
+ }
+ return -1;
},
- // private
- onLoad : function()
- {
- (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+
+
+ moveColumn : function(oldIndex, newIndex){
+ var c = this.config[oldIndex];
+ this.config.splice(oldIndex, 1);
+ this.config.splice(newIndex, 0, c);
+ this.dataMap = null;
+ this.fireEvent("columnmoved", this, oldIndex, newIndex);
},
- // private
- onBeforeLoad : function(){
- if(!this.disabled){
- (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
+ isLocked : function(colIndex){
+ return this.config[colIndex].locked === true;
+ },
+
+ setLocked : function(colIndex, value, suppressEvent){
+ if(this.isLocked(colIndex) == value){
+ return;
+ }
+ this.config[colIndex].locked = value;
+ if(!suppressEvent){
+ this.fireEvent("columnlockchange", this, colIndex, value);
}
},
- // private
- destroy : function(){
- if(this.store){
- this.store.un('beforeload', this.onBeforeLoad, this);
- this.store.un('load', this.onLoad, this);
- this.store.un('loadexception', this.onLoadException, this);
- }else{
- var um = this.el.getUpdateManager();
- um.un('beforeupdate', this.onBeforeLoad, this);
- um.un('update', this.onLoad, this);
- um.un('failure', this.onLoad, this);
+ getTotalLockedWidth : function(){
+ var totalWidth = 0;
+ for(var i = 0; i < this.config.length; i++){
+ if(this.isLocked(i) && !this.isHidden(i)){
+ this.totalWidth += this.getColumnWidth(i);
+ }
}
- }
-};/*
- * - LGPL
- *
- * table
- *
- */
+ return totalWidth;
+ },
-/**
- * @class Roo.bootstrap.Table
- * @extends Roo.bootstrap.Component
- * Bootstrap Table class
- * @cfg {String} cls table class
- * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
- * @cfg {String} bgcolor Specifies the background color for a table
- * @cfg {Number} border Specifies whether the table cells should have borders or not
- * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
- * @cfg {Number} cellspacing Specifies the space between cells
- * @cfg {String} frame Specifies which parts of the outside borders that should be visible
- * @cfg {String} rules Specifies which parts of the inside borders that should be visible
- * @cfg {String} sortable Specifies that the table should be sortable
- * @cfg {String} summary Specifies a summary of the content of a table
- * @cfg {Number} width Specifies the width of a table
- * @cfg {String} layout table layout (auto | fixed | initial | inherit)
- *
- * @cfg {boolean} striped Should the rows be alternative striped
- * @cfg {boolean} bordered Add borders to the table
- * @cfg {boolean} hover Add hover highlighting
- * @cfg {boolean} condensed Format condensed
- * @cfg {boolean} responsive Format condensed
- * @cfg {Boolean} loadMask (true|false) default false
- * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
- * @cfg {Boolean} headerShow (true|false) generate thead, default true
- * @cfg {Boolean} rowSelection (true|false) default false
- * @cfg {Boolean} cellSelection (true|false) default false
- * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
- * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
- * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
- * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
-
- *
- * @constructor
- * Create a new Table
- * @param {Object} config The config object
- */
+ getLockedCount : function(){
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(!this.isLocked(i)){
+ return i;
+ }
+ }
+
+ return this.config.length;
+ },
-Roo.bootstrap.Table = function(config){
- Roo.bootstrap.Table.superclass.constructor.call(this, config);
-
-
-
- // BC...
- this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
- this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
- this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
- this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
-
- this.sm = this.sm || {xtype: 'RowSelectionModel'};
- if (this.sm) {
- this.sm.grid = this;
- this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
- this.sm = this.selModel;
- this.sm.xmodule = this.xmodule || false;
- }
-
- if (this.cm && typeof(this.cm.config) == 'undefined') {
- this.colModel = new Roo.grid.ColumnModel(this.cm);
- this.cm = this.colModel;
- this.cm.xmodule = this.xmodule || false;
- }
- if (this.store) {
- this.store= Roo.factory(this.store, Roo.data);
- this.ds = this.store;
- this.ds.xmodule = this.xmodule || false;
-
- }
- if (this.footer && this.store) {
- this.footer.dataSource = this.ds;
- this.footer = Roo.factory(this.footer);
- }
-
- /** @private */
- this.addEvents({
- /**
- * @event cellclick
- * Fires when a cell is clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "cellclick" : true,
- /**
- * @event celldblclick
- * Fires when a cell is double clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "celldblclick" : true,
- /**
- * @event rowclick
- * Fires when a row is clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Roo.EventObject} e
- */
- "rowclick" : true,
- /**
- * @event rowdblclick
- * Fires when a row is double clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Roo.EventObject} e
- */
- "rowdblclick" : true,
- /**
- * @event mouseover
- * Fires when a mouseover occur
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "mouseover" : true,
- /**
- * @event mouseout
- * Fires when a mouseout occur
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "mouseout" : true,
- /**
- * @event rowclass
- * Fires when a row is rendered, so you can change add a style to it.
- * @param {Roo.bootstrap.Table} this
- * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
- */
- 'rowclass' : true,
- /**
- * @event rowsrendered
- * Fires when all the rows have been rendered
- * @param {Roo.bootstrap.Table} this
- */
- 'rowsrendered' : true,
- /**
- * @event contextmenu
- * The raw contextmenu event for the entire grid.
- * @param {Roo.EventObject} e
- */
- "contextmenu" : true,
- /**
- * @event rowcontextmenu
- * Fires when a row is right clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Number} rowIndex
- * @param {Roo.EventObject} e
- */
- "rowcontextmenu" : true,
- /**
- * @event cellcontextmenu
- * Fires when a cell is right clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Number} rowIndex
- * @param {Number} cellIndex
- * @param {Roo.EventObject} e
- */
- "cellcontextmenu" : true,
- /**
- * @event headercontextmenu
- * Fires when a header is right clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "headercontextmenu" : true
- });
-};
-
-Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
-
- cls: false,
- align: false,
- bgcolor: false,
- border: false,
- cellpadding: false,
- cellspacing: false,
- frame: false,
- rules: false,
- sortable: false,
- summary: false,
- width: false,
- striped : false,
- scrollBody : false,
- bordered: false,
- hover: false,
- condensed : false,
- responsive : false,
- sm : false,
- cm : false,
- store : false,
- loadMask : false,
- footerShow : true,
- headerShow : true,
-
- rowSelection : false,
- cellSelection : false,
- layout : false,
-
- // Roo.Element - the tbody
- mainBody: false,
- // Roo.Element - thead element
- mainHead: false,
-
- container: false, // used by gridpanel...
-
- lazyLoad : false,
-
- CSS : Roo.util.CSS,
-
- auto_hide_footer : false,
-
- getAutoCreate : function()
- {
- var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag: 'table',
- cls : 'table',
- cn : []
- };
- if (this.scrollBody) {
- cfg.cls += ' table-body-fixed';
- }
- if (this.striped) {
- cfg.cls += ' table-striped';
- }
-
- if (this.hover) {
- cfg.cls += ' table-hover';
- }
- if (this.bordered) {
- cfg.cls += ' table-bordered';
- }
- if (this.condensed) {
- cfg.cls += ' table-condensed';
- }
- if (this.responsive) {
- cfg.cls += ' table-responsive';
- }
-
- if (this.cls) {
- cfg.cls+= ' ' +this.cls;
- }
-
- // this lot should be simplifed...
- var _t = this;
- var cp = [
- 'align',
- 'bgcolor',
- 'border',
- 'cellpadding',
- 'cellspacing',
- 'frame',
- 'rules',
- 'sortable',
- 'summary',
- 'width'
- ].forEach(function(k) {
- if (_t[k]) {
- cfg[k] = _t[k];
+ /**
+ * Returns the number of columns.
+ * @return {Number}
+ */
+ getColumnCount : function(visibleOnly){
+ if(visibleOnly === true){
+ var c = 0;
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(!this.isHidden(i)){
+ c++;
+ }
}
- });
-
-
- if (this.layout) {
- cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
+ return c;
}
-
- if(this.store || this.cm){
- if(this.headerShow){
- cfg.cn.push(this.renderHeader());
- }
-
- cfg.cn.push(this.renderBody());
-
- if(this.footerShow){
- cfg.cn.push(this.renderFooter());
+ return this.config.length;
+ },
+
+ /**
+ * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
+ * @param {Function} fn
+ * @param {Object} scope (optional)
+ * @return {Array} result
+ */
+ getColumnsBy : function(fn, scope){
+ var r = [];
+ for(var i = 0, len = this.config.length; i < len; i++){
+ var c = this.config[i];
+ if(fn.call(scope||this, c, i) === true){
+ r[r.length] = c;
}
- // where does this come from?
- //cfg.cls+= ' TableGrid';
}
-
- return { cn : [ cfg ] };
+ return r;
},
-
- initEvents : function()
- {
- if(!this.store || !this.cm){
- return;
- }
- if (this.selModel) {
- this.selModel.initEvents();
+
+ /**
+ * Returns true if the specified column is sortable.
+ * @param {Number} col The column index
+ * @return {Boolean}
+ */
+ isSortable : function(col){
+ if(typeof this.config[col].sortable == "undefined"){
+ return this.defaultSortable;
}
-
-
- //Roo.log('initEvents with ds!!!!');
-
- this.mainBody = this.el.select('tbody', true).first();
- this.mainHead = this.el.select('thead', true).first();
- this.mainFoot = this.el.select('tfoot', true).first();
-
-
-
-
- Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
- e.on('click', this.sort, this);
- }, this);
-
- this.mainBody.on("click", this.onClick, this);
- this.mainBody.on("dblclick", this.onDblClick, this);
-
- // why is this done????? = it breaks dialogs??
- //this.parent().el.setStyle('position', 'relative');
-
-
- if (this.footer) {
- this.footer.parentId = this.id;
- this.footer.onRender(this.el.select('tfoot tr td').first(), null);
-
- if(this.lazyLoad){
- this.el.select('tfoot tr td').first().addClass('hide');
- }
- }
-
- if(this.loadMask) {
- this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
+ return this.config[col].sortable;
+ },
+
+ /**
+ * Returns the rendering (formatting) function defined for the column.
+ * @param {Number} col The column index.
+ * @return {Function} The function used to render the cell. See {@link #setRenderer}.
+ */
+ getRenderer : function(col){
+ if(!this.config[col].renderer){
+ return Roo.grid.ColumnModel.defaultRenderer;
}
-
- this.store.on('load', this.onLoad, this);
- this.store.on('beforeload', this.onBeforeLoad, this);
- this.store.on('update', this.onUpdate, this);
- this.store.on('add', this.onAdd, this);
- this.store.on("clear", this.clear, this);
-
- this.el.on("contextmenu", this.onContextMenu, this);
-
- this.mainBody.on('scroll', this.onBodyScroll, this);
-
- this.cm.on("headerchange", this.onHeaderChange, this);
-
- this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
-
+ return this.config[col].renderer;
},
-
- onContextMenu : function(e, t)
- {
- this.processEvent("contextmenu", e);
+
+ /**
+ * Sets the rendering (formatting) function for a column.
+ * @param {Number} col The column index
+ * @param {Function} fn The function to use to process the cell's raw data
+ * to return HTML markup for the grid view. The render function is called with
+ * the following parameters:<ul>
+ * <li>Data value.</li>
+ * <li>Cell metadata. An object in which you may set the following attributes:<ul>
+ * <li>css A CSS style string to apply to the table cell.</li>
+ * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
+ * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
+ * <li>Row index</li>
+ * <li>Column index</li>
+ * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
+ */
+ setRenderer : function(col, fn){
+ this.config[col].renderer = fn;
},
-
- processEvent : function(name, e)
- {
- if (name != 'touchstart' ) {
- this.fireEvent(name, e);
- }
-
- var t = e.getTarget();
-
- var cell = Roo.get(t);
-
- if(!cell){
- return;
- }
-
- if(cell.findParent('tfoot', false, true)){
- return;
+
+ /**
+ * Returns the width for the specified column.
+ * @param {Number} col The column index
+ * @param (optional) {String} gridSize bootstrap width size.
+ * @return {Number}
+ */
+ getColumnWidth : function(col, gridSize)
+ {
+ var cfg = this.config[col];
+
+ if (typeof(gridSize) == 'undefined') {
+ return cfg.width * 1 || this.defaultWidth;
+ }
+ if (gridSize === false) { // if we set it..
+ return cfg.width || false;
+ }
+ var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
+
+ for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
+ if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
+ continue;
+ }
+ return cfg[ sizes[i] ];
+ }
+ return 1;
+
+ },
+
+ /**
+ * Sets the width for a column.
+ * @param {Number} col The column index
+ * @param {Number} width The new width
+ */
+ setColumnWidth : function(col, width, suppressEvent){
+ this.config[col].width = width;
+ this.totalWidth = null;
+ if(!suppressEvent){
+ this.fireEvent("widthchange", this, col, width);
}
-
- if(cell.findParent('thead', false, true)){
-
- if(e.getTarget().nodeName.toLowerCase() != 'th'){
- cell = Roo.get(t).findParent('th', false, true);
- if (!cell) {
- Roo.log("failed to find th in thead?");
- Roo.log(e.getTarget());
- return;
+ },
+
+ /**
+ * Returns the total width of all columns.
+ * @param {Boolean} includeHidden True to include hidden column widths
+ * @return {Number}
+ */
+ getTotalWidth : function(includeHidden){
+ if(!this.totalWidth){
+ this.totalWidth = 0;
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(includeHidden || !this.isHidden(i)){
+ this.totalWidth += this.getColumnWidth(i);
}
}
-
- var cellIndex = cell.dom.cellIndex;
-
- var ename = name == 'touchstart' ? 'click' : name;
- this.fireEvent("header" + ename, this, cellIndex, e);
-
- return;
- }
-
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = Roo.get(t).findParent('td', false, true);
- if (!cell) {
- Roo.log("failed to find th in tbody?");
- Roo.log(e.getTarget());
- return;
- }
- }
-
- var row = cell.findParent('tr', false, true);
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = row.dom.rowIndex - 1;
-
- if(row !== false){
-
- this.fireEvent("row" + name, this, rowIndex, e);
-
- if(cell !== false){
-
- this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
- }
}
-
+ return this.totalWidth;
},
-
- onMouseover : function(e, el)
- {
- var cell = Roo.get(el);
-
- if(!cell){
- return;
- }
-
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = cell.findParent('td', false, true);
- }
-
- var row = cell.findParent('tr', false, true);
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = row.dom.rowIndex - 1; // start from 0
-
- this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
-
+
+ /**
+ * Returns the header for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnHeader : function(col){
+ return this.config[col].header;
},
-
- onMouseout : function(e, el)
- {
- var cell = Roo.get(el);
-
- if(!cell){
- return;
- }
-
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = cell.findParent('td', false, true);
- }
-
- var row = cell.findParent('tr', false, true);
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = row.dom.rowIndex - 1; // start from 0
-
- this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
-
+
+ /**
+ * Sets the header for a column.
+ * @param {Number} col The column index
+ * @param {String} header The new header
+ */
+ setColumnHeader : function(col, header){
+ this.config[col].header = header;
+ this.fireEvent("headerchange", this, col, header);
+ },
+
+ /**
+ * Returns the tooltip for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnTooltip : function(col){
+ return this.config[col].tooltip;
+ },
+ /**
+ * Sets the tooltip for a column.
+ * @param {Number} col The column index
+ * @param {String} tooltip The new tooltip
+ */
+ setColumnTooltip : function(col, tooltip){
+ this.config[col].tooltip = tooltip;
+ },
+
+ /**
+ * Returns the dataIndex for the specified column.
+ * @param {Number} col The column index
+ * @return {Number}
+ */
+ getDataIndex : function(col){
+ return this.config[col].dataIndex;
+ },
+
+ /**
+ * Sets the dataIndex for a column.
+ * @param {Number} col The column index
+ * @param {Number} dataIndex The new dataIndex
+ */
+ setDataIndex : function(col, dataIndex){
+ this.config[col].dataIndex = dataIndex;
},
+
- onClick : function(e, el)
+
+ /**
+ * Returns true if the cell is editable.
+ * @param {Number} colIndex The column index
+ * @param {Number} rowIndex The row index - this is nto actually used..?
+ * @return {Boolean}
+ */
+ isCellEditable : function(colIndex, rowIndex){
+ return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
+ },
+
+ /**
+ * Returns the editor defined for the cell/column.
+ * return false or null to disable editing.
+ * @param {Number} colIndex The column index
+ * @param {Number} rowIndex The row index
+ * @return {Object}
+ */
+ getCellEditor : function(colIndex, rowIndex){
+ return this.config[colIndex].editor;
+ },
+
+ /**
+ * Sets if a column is editable.
+ * @param {Number} col The column index
+ * @param {Boolean} editable True if the column is editable
+ */
+ setEditable : function(col, editable){
+ this.config[col].editable = editable;
+ },
+
+
+ /**
+ * Returns true if the column is hidden.
+ * @param {Number} colIndex The column index
+ * @return {Boolean}
+ */
+ isHidden : function(colIndex){
+ return this.config[colIndex].hidden;
+ },
+
+
+ /**
+ * Returns true if the column width cannot be changed
+ */
+ isFixed : function(colIndex){
+ return this.config[colIndex].fixed;
+ },
+
+ /**
+ * Returns true if the column can be resized
+ * @return {Boolean}
+ */
+ isResizable : function(colIndex){
+ return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
+ },
+ /**
+ * Sets if a column is hidden.
+ * @param {Number} colIndex The column index
+ * @param {Boolean} hidden True if the column is hidden
+ */
+ setHidden : function(colIndex, hidden){
+ this.config[colIndex].hidden = hidden;
+ this.totalWidth = null;
+ this.fireEvent("hiddenchange", this, colIndex, hidden);
+ },
+
+ /**
+ * Sets the editor for a column.
+ * @param {Number} col The column index
+ * @param {Object} editor The editor object
+ */
+ setEditor : function(col, editor){
+ this.config[col].editor = editor;
+ },
+ /**
+ * Add a column (experimental...) - defaults to adding to the end..
+ * @param {Object} config
+ */
+ addColumn : function(c)
{
- var cell = Roo.get(el);
-
- if(!cell || (!this.cellSelection && !this.rowSelection)){
- return;
+
+ var i = this.config.length;
+ this.config[i] = c;
+
+ if(typeof c.dataIndex == "undefined"){
+ c.dataIndex = i;
}
-
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = cell.findParent('td', false, true);
+ if(typeof c.renderer == "string"){
+ c.renderer = Roo.util.Format[c.renderer];
}
-
- if(!cell || typeof(cell) == 'undefined'){
- return;
+ if(typeof c.id == "undefined"){
+ c.id = Roo.id();
}
-
- var row = cell.findParent('tr', false, true);
-
- if(!row || typeof(row) == 'undefined'){
- return;
+ if(c.editor && c.editor.xtype){
+ c.editor = Roo.factory(c.editor, Roo.grid);
}
-
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = this.getRowIndex(row);
-
- // why??? - should these not be based on SelectionModel?
- //if(this.cellSelection){
- this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
- //}
-
- //if(this.rowSelection){
- this.fireEvent('rowclick', this, row, rowIndex, e);
- //}
-
+ if(c.editor && c.editor.isFormField){
+ c.editor = new Roo.grid.GridEditor(c.editor);
+ }
+ this.lookup[c.id] = c;
+ }
+
+});
+
+Roo.grid.ColumnModel.defaultRenderer = function(value)
+{
+ if(typeof value == "object") {
+ return value;
+ }
+ if(typeof value == "string" && value.length < 1){
+ return " ";
+ }
+
+ return String.format("{0}", value);
+};
+
+// Alias for backwards compatibility
+Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.LoadMask
+ * A simple utility class for generically masking elements while loading data. If the element being masked has
+ * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
+ * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
+ * element's UpdateManager load indicator and will be destroyed after the initial load.
+ * @constructor
+ * Create a new LoadMask
+ * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
+ * @param {Object} config The config object
+ */
+Roo.LoadMask = function(el, config){
+ this.el = Roo.get(el);
+ Roo.apply(this, config);
+ if(this.store){
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('loadexception', this.onLoadException, this);
+ this.removeMask = false;
+ }else{
+ var um = this.el.getUpdateManager();
+ um.showLoadIndicator = false; // disable the default indicator
+ um.on('beforeupdate', this.onBeforeLoad, this);
+ um.on('update', this.onLoad, this);
+ um.on('failure', this.onLoad, this);
+ this.removeMask = true;
+ }
+};
+
+Roo.LoadMask.prototype = {
+ /**
+ * @cfg {Boolean} removeMask
+ * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
+ * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
+ */
+ /**
+ * @cfg {String} msg
+ * The text to display in a centered loading message box (defaults to 'Loading...')
+ */
+ msg : 'Loading...',
+ /**
+ * @cfg {String} msgCls
+ * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
+ */
+ msgCls : 'x-mask-loading',
+
+ /**
+ * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
+ * @type Boolean
+ */
+ disabled: false,
+
+ /**
+ * Disables the mask to prevent it from being displayed
+ */
+ disable : function(){
+ this.disabled = true;
},
-
- onDblClick : function(e,el)
+
+ /**
+ * Enables the mask so that it can be displayed
+ */
+ enable : function(){
+ this.disabled = false;
+ },
+
+ onLoadException : function()
{
- var cell = Roo.get(el);
-
- if(!cell || (!this.cellSelection && !this.rowSelection)){
- return;
- }
-
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = cell.findParent('td', false, true);
- }
-
- if(!cell || typeof(cell) == 'undefined'){
- return;
- }
-
- var row = cell.findParent('tr', false, true);
-
- if(!row || typeof(row) == 'undefined'){
- return;
- }
-
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = this.getRowIndex(row);
-
- if(this.cellSelection){
- this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
- }
+ Roo.log(arguments);
- if(this.rowSelection){
- this.fireEvent('rowdblclick', this, row, rowIndex, e);
+ if (typeof(arguments[3]) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",arguments[3]);
+ }
+ /*
+ try {
+ if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+ }
+ } catch(e) {
+
}
- },
+ */
- sort : function(e,el)
+ (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+ },
+ // private
+ onLoad : function()
{
- var col = Roo.get(el);
-
- if(!col.hasClass('sortable')){
- return;
+ (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+ },
+
+ // private
+ onBeforeLoad : function(){
+ if(!this.disabled){
+ (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
}
-
- var sort = col.attr('sort');
- var dir = 'ASC';
-
- if(col.select('i', true).first().hasClass('fa-arrow-up')){
- dir = 'DESC';
+ },
+
+ // private
+ destroy : function(){
+ if(this.store){
+ this.store.un('beforeload', this.onBeforeLoad, this);
+ this.store.un('load', this.onLoad, this);
+ this.store.un('loadexception', this.onLoadException, this);
+ }else{
+ var um = this.el.getUpdateManager();
+ um.un('beforeupdate', this.onBeforeLoad, this);
+ um.un('update', this.onLoad, this);
+ um.un('failure', this.onLoad, this);
}
-
- this.store.sortInfo = {field : sort, direction : dir};
-
- if (this.footer) {
- Roo.log("calling footer first");
- this.footer.onClick('first');
- } else {
-
- this.store.load({ params : { start : 0 } });
+ }
+};/**
+ * @class Roo.bootstrap.Table
+ * @licence LGBL
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Table class. This class represents the primary interface of a component based grid control.
+ * Similar to Roo.grid.Grid
+ * <pre><code>
+ var table = Roo.factory({
+ xtype : 'Table',
+ xns : Roo.bootstrap,
+ autoSizeColumns: true,
+
+
+ store : {
+ xtype : 'Store',
+ xns : Roo.data,
+ remoteSort : true,
+ sortInfo : { direction : 'ASC', field: 'name' },
+ proxy : {
+ xtype : 'HttpProxy',
+ xns : Roo.data,
+ method : 'GET',
+ url : 'https://example.com/some.data.url.json'
+ },
+ reader : {
+ xtype : 'JsonReader',
+ xns : Roo.data,
+ fields : [ 'id', 'name', whatever' ],
+ id : 'id',
+ root : 'data'
}
},
+ cm : [
+ {
+ xtype : 'ColumnModel',
+ xns : Roo.grid,
+ align : 'center',
+ cursor : 'pointer',
+ dataIndex : 'is_in_group',
+ header : "Name",
+ sortable : true,
+ renderer : function(v, x , r) {
+
+ return String.format("{0}", v)
+ }
+ width : 3
+ } // more columns..
+ ],
+ selModel : {
+ xtype : 'RowSelectionModel',
+ xns : Roo.bootstrap.Table
+ // you can add listeners to catch selection change here....
+ }
+
+
+ });
+ // set any options
+ grid.render(Roo.get("some-div"));
+</code></pre>
+
+Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
+
+
+
+ *
+ * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
+ * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
+ * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
+ *
+ * @cfg {String} cls table class
+ *
+ *
+ * @cfg {boolean} striped Should the rows be alternative striped
+ * @cfg {boolean} bordered Add borders to the table
+ * @cfg {boolean} hover Add hover highlighting
+ * @cfg {boolean} condensed Format condensed
+ * @cfg {boolean} responsive Format condensed
+ * @cfg {Boolean} loadMask (true|false) default false
+ * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
+ * @cfg {Boolean} headerShow (true|false) generate thead, default true
+ * @cfg {Boolean} rowSelection (true|false) default false
+ * @cfg {Boolean} cellSelection (true|false) default false
+ * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
+ * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
+ * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
+ * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
+ * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
+ * @cfg {Number} minColumnWidth default 50 pixels minimum column width
+ *
+ * @constructor
+ * Create a new Table
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Table = function(config)
+{
+ Roo.bootstrap.Table.superclass.constructor.call(this, config);
+
+ // BC...
+ this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
+ this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
+ this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
+ this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
- renderHeader : function()
- {
- var header = {
- tag: 'thead',
- cn : []
- };
-
- var cm = this.cm;
- this.totalWidth = 0;
-
- for(var i = 0, len = cm.getColumnCount(); i < len; i++){
-
- var config = cm.config[i];
-
- var c = {
- tag: 'th',
- cls : 'x-hcol-' + i,
- style : '',
-
- html: cm.getColumnHeader(i)
- };
-
- var tooltip = cm.getColumnTooltip(i);
- if (tooltip) {
- c.tooltip = tooltip;
- }
-
-
- var hh = '';
-
- if(typeof(config.sortable) != 'undefined' && config.sortable){
- c.cls = 'sortable';
- c.html = '<i class="fa"></i>' + c.html;
- }
-
- // could use BS4 hidden-..-down
-
- if(typeof(config.lgHeader) != 'undefined'){
- hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
- }
-
- if(typeof(config.mdHeader) != 'undefined'){
- hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
- }
-
- if(typeof(config.smHeader) != 'undefined'){
- hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
- }
-
- if(typeof(config.xsHeader) != 'undefined'){
- hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
+ this.view = this; // compat with grid.
+
+ this.sm = this.sm || {xtype: 'RowSelectionModel'};
+ if (this.sm) {
+ this.sm.grid = this;
+ this.selModel = Roo.factory(this.sm, Roo.grid);
+ this.sm = this.selModel;
+ this.sm.xmodule = this.xmodule || false;
+ }
+
+ if (this.cm && typeof(this.cm.config) == 'undefined') {
+ this.colModel = new Roo.grid.ColumnModel(this.cm);
+ this.cm = this.colModel;
+ this.cm.xmodule = this.xmodule || false;
+ }
+ if (this.store) {
+ this.store= Roo.factory(this.store, Roo.data);
+ this.ds = this.store;
+ this.ds.xmodule = this.xmodule || false;
+
+ }
+ if (this.footer && this.store) {
+ this.footer.dataSource = this.ds;
+ this.footer = Roo.factory(this.footer);
+ }
+
+ /** @private */
+ this.addEvents({
+ /**
+ * @event cellclick
+ * Fires when a cell is clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ * @param {Roo.EventObject} e
+ */
+ "cellclick" : true,
+ /**
+ * @event celldblclick
+ * Fires when a cell is double clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ * @param {Roo.EventObject} e
+ */
+ "celldblclick" : true,
+ /**
+ * @event rowclick
+ * Fires when a row is clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Roo.EventObject} e
+ */
+ "rowclick" : true,
+ /**
+ * @event rowdblclick
+ * Fires when a row is double clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Roo.EventObject} e
+ */
+ "rowdblclick" : true,
+ /**
+ * @event mouseover
+ * Fires when a mouseover occur
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ * @param {Roo.EventObject} e
+ */
+ "mouseover" : true,
+ /**
+ * @event mouseout
+ * Fires when a mouseout occur
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ * @param {Roo.EventObject} e
+ */
+ "mouseout" : true,
+ /**
+ * @event rowclass
+ * Fires when a row is rendered, so you can change add a style to it.
+ * @param {Roo.bootstrap.Table} this
+ * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
+ */
+ 'rowclass' : true,
+ /**
+ * @event rowsrendered
+ * Fires when all the rows have been rendered
+ * @param {Roo.bootstrap.Table} this
+ */
+ 'rowsrendered' : true,
+ /**
+ * @event contextmenu
+ * The raw contextmenu event for the entire grid.
+ * @param {Roo.EventObject} e
+ */
+ "contextmenu" : true,
+ /**
+ * @event rowcontextmenu
+ * Fires when a row is right clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Number} rowIndex
+ * @param {Roo.EventObject} e
+ */
+ "rowcontextmenu" : true,
+ /**
+ * @event cellcontextmenu
+ * Fires when a cell is right clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Number} rowIndex
+ * @param {Number} cellIndex
+ * @param {Roo.EventObject} e
+ */
+ "cellcontextmenu" : true,
+ /**
+ * @event headercontextmenu
+ * Fires when a header is right clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Number} columnIndex
+ * @param {Roo.EventObject} e
+ */
+ "headercontextmenu" : true,
+ /**
+ * @event mousedown
+ * The raw mousedown event for the entire grid.
+ * @param {Roo.EventObject} e
+ */
+ "mousedown" : true
+
+ });
+};
+
+Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
+
+ cls: false,
+
+ striped : false,
+ scrollBody : false,
+ bordered: false,
+ hover: false,
+ condensed : false,
+ responsive : false,
+ sm : false,
+ cm : false,
+ store : false,
+ loadMask : false,
+ footerShow : true,
+ headerShow : true,
+ enableColumnResize: true,
+
+ rowSelection : false,
+ cellSelection : false,
+ layout : false,
+
+ minColumnWidth : 50,
+
+ // Roo.Element - the tbody
+ bodyEl: false, // <tbody> Roo.Element - thead element
+ headEl: false, // <thead> Roo.Element - thead element
+ resizeProxy : false, // proxy element for dragging?
+
+
+
+ container: false, // used by gridpanel...
+
+ lazyLoad : false,
+
+ CSS : Roo.util.CSS,
+
+ auto_hide_footer : false,
+
+ view: false, // actually points to this..
+
+ getAutoCreate : function()
+ {
+ var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
+
+ cfg = {
+ tag: 'table',
+ cls : 'table',
+ cn : []
+ };
+ // this get's auto added by panel.Grid
+ if (this.scrollBody) {
+ cfg.cls += ' table-body-fixed';
+ }
+ if (this.striped) {
+ cfg.cls += ' table-striped';
+ }
+
+ if (this.hover) {
+ cfg.cls += ' table-hover';
+ }
+ if (this.bordered) {
+ cfg.cls += ' table-bordered';
+ }
+ if (this.condensed) {
+ cfg.cls += ' table-condensed';
+ }
+
+ if (this.responsive) {
+ cfg.cls += ' table-responsive';
+ }
+
+ if (this.cls) {
+ cfg.cls+= ' ' +this.cls;
+ }
+
+
+
+ if (this.layout) {
+ cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
+ }
+
+ if(this.store || this.cm){
+ if(this.headerShow){
+ cfg.cn.push(this.renderHeader());
}
- if(hh.length){
- c.html = hh;
- }
+ cfg.cn.push(this.renderBody());
- if(typeof(config.tooltip) != 'undefined'){
- c.tooltip = config.tooltip;
- }
-
- if(typeof(config.colspan) != 'undefined'){
- c.colspan = config.colspan;
- }
-
- if(typeof(config.hidden) != 'undefined' && config.hidden){
- c.style += ' display:none;';
+ if(this.footerShow){
+ cfg.cn.push(this.renderFooter());
}
+ // where does this come from?
+ //cfg.cls+= ' TableGrid';
+ }
+
+ return { cn : [ cfg ] };
+ },
+
+ initEvents : function()
+ {
+ if(!this.store || !this.cm){
+ return;
+ }
+ if (this.selModel) {
+ this.selModel.initEvents();
+ }
+
+
+ //Roo.log('initEvents with ds!!!!');
+
+ this.bodyEl = this.el.select('tbody', true).first();
+ this.headEl = this.el.select('thead', true).first();
+ this.mainFoot = this.el.select('tfoot', true).first();
+
+
+
+
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.on('click', this.sort, this);
+ }, this);
+
+
+ // why is this done????? = it breaks dialogs??
+ //this.parent().el.setStyle('position', 'relative');
+
+
+ if (this.footer) {
+ this.footer.parentId = this.id;
+ this.footer.onRender(this.el.select('tfoot tr td').first(), null);
- if(typeof(config.dataIndex) != 'undefined'){
- c.sort = config.dataIndex;
+ if(this.lazyLoad){
+ this.el.select('tfoot tr td').first().addClass('hide');
}
-
-
-
- if(typeof(config.align) != 'undefined' && config.align.length){
- c.style += ' text-align:' + config.align + ';';
+ }
+
+ if(this.loadMask) {
+ this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
+ }
+
+ this.store.on('load', this.onLoad, this);
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('update', this.onUpdate, this);
+ this.store.on('add', this.onAdd, this);
+ this.store.on("clear", this.clear, this);
+
+ this.el.on("contextmenu", this.onContextMenu, this);
+
+
+ this.cm.on("headerchange", this.onHeaderChange, this);
+ this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
+
+ //?? does bodyEl get replaced on render?
+ this.bodyEl.on("click", this.onClick, this);
+ this.bodyEl.on("dblclick", this.onDblClick, this);
+ this.bodyEl.on('scroll', this.onBodyScroll, this);
+
+ // guessing mainbody will work - this relays usually caught by selmodel at present.
+ this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
+
+
+ this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
+
+
+ if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
+ new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
+ }
+
+ this.initCSS();
+ },
+ // Compatibility with grid - we implement all the view features at present.
+ getView : function()
+ {
+ return this;
+ },
+
+ initCSS : function()
+ {
+
+
+ var cm = this.cm, styles = [];
+ this.CSS.removeStyleSheet(this.id + '-cssrules');
+ var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
+ // we can honour xs/sm/md/xl as widths...
+ // we first have to decide what widht we are currently at...
+ var sz = Roo.getGridSize();
+
+ var total = 0;
+ var last = -1;
+ var cols = []; // visable cols.
+ var total_abs = 0;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
+ var w = cm.getColumnWidth(i, false);
+ if(cm.isHidden(i)){
+ cols.push( { rel : false, abs : 0 });
+ continue;
}
-
- if(typeof(config.width) != 'undefined'){
- c.style += ' width:' + config.width + 'px;';
- this.totalWidth += config.width;
- } else {
- this.totalWidth += 100; // assume minimum of 100 per column?
+ if (w !== false) {
+ cols.push( { rel : false, abs : w });
+ total_abs += w;
+ last = i; // not really..
+ continue;
}
-
- if(typeof(config.cls) != 'undefined'){
- c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
+ var w = cm.getColumnWidth(i, sz);
+ if (w > 0) {
+ last = i
}
+ total += w;
+ cols.push( { rel : w, abs : false });
+ }
+
+ var avail = this.bodyEl.dom.clientWidth - total_abs;
+
+ var unitWidth = Math.floor(avail / total);
+ var rem = avail - (unitWidth * total);
+
+ var hidden, width, pos = 0 , splithide , left;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
- ['xs','sm','md','lg'].map(function(size){
+ hidden = 'display:none;';
+ left = '';
+ width = 'width:0px;';
+ splithide = '';
+ if(!cm.isHidden(i)){
+ hidden = '';
- if(typeof(config[size]) == 'undefined'){
- return;
+
+ // we can honour xs/sm/md/xl ?
+ var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
+ if (w===0) {
+ hidden = 'display:none;';
}
-
- if (!config[size]) { // 0 = hidden
- // BS 4 '0' is treated as hide that column and below.
- c.cls += ' hidden-' + size + ' hidden' + size + '-down';
- return;
+ // width should return a small number...
+ if (i == last) {
+ w+=rem; // add the remaining with..
}
+ pos += w;
+ left = "left:" + (pos -4) + "px;";
+ width = "width:" + w+ "px;";
- c.cls += ' col-' + size + '-' + config[size] + (
- size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
- );
-
+ }
+
+ styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
+ if (this.headEl) {
+ if (i == last) {
+ splithide = 'display:none;';
+ }
- });
+ styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
+ '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
+ );
+ }
- header.cn.push(c)
}
+ Roo.log(styles.join(''));
+ this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
- return header;
},
- renderBody : function()
- {
- var body = {
- tag: 'tbody',
- cn : [
- {
- tag: 'tr',
- cn : [
- {
- tag : 'td',
- colspan : this.cm.getColumnCount()
- }
- ]
- }
- ]
- };
-
- return body;
- },
- renderFooter : function()
+
+ onContextMenu : function(e, t)
{
- var footer = {
- tag: 'tfoot',
- cn : [
- {
- tag: 'tr',
- cn : [
- {
- tag : 'td',
- colspan : this.cm.getColumnCount()
- }
- ]
- }
- ]
- };
-
- return footer;
+ this.processEvent("contextmenu", e);
},
-
-
- onLoad : function()
+ processEvent : function(name, e)
{
-// Roo.log('ds onload');
- this.clear();
+ if (name != 'touchstart' ) {
+ this.fireEvent(name, e);
+ }
- var _this = this;
- var cm = this.cm;
- var ds = this.store;
+ var t = e.getTarget();
- Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
- e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
- if (_this.store.sortInfo) {
-
- if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
- e.select('i', true).addClass(['fa-arrow-up']);
- }
-
- if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
- e.select('i', true).addClass(['fa-arrow-down']);
- }
- }
- });
+ var cell = Roo.get(t);
- var tbody = this.mainBody;
-
- if(ds.getCount() > 0){
- ds.data.each(function(d,rowIndex){
- var row = this.renderRow(cm, ds, rowIndex);
-
- tbody.createChild(row);
-
- var _this = this;
-
- if(row.cellObjects.length){
- Roo.each(row.cellObjects, function(r){
- _this.renderCellObject(r);
- })
- }
-
- }, this);
+ if(!cell){
+ return;
}
- var tfoot = this.el.select('tfoot', true).first();
+ if(cell.findParent('tfoot', false, true)){
+ return;
+ }
- if(this.footerShow && this.auto_hide_footer && this.mainFoot){
+ if(cell.findParent('thead', false, true)){
- this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
+ if(e.getTarget().nodeName.toLowerCase() != 'th'){
+ cell = Roo.get(t).findParent('th', false, true);
+ if (!cell) {
+ Roo.log("failed to find th in thead?");
+ Roo.log(e.getTarget());
+ return;
+ }
+ }
- var total = this.ds.getTotalCount();
+ var cellIndex = cell.dom.cellIndex;
- if(this.footer.pageSize < total){
- this.mainFoot.show();
+ var ename = name == 'touchstart' ? 'click' : name;
+ this.fireEvent("header" + ename, this, cellIndex, e);
+
+ return;
+ }
+
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = Roo.get(t).findParent('td', false, true);
+ if (!cell) {
+ Roo.log("failed to find th in tbody?");
+ Roo.log(e.getTarget());
+ return;
}
}
- Roo.each(this.el.select('tbody td', true).elements, function(e){
- e.on('mouseover', _this.onMouseover, _this);
- });
+ var row = cell.findParent('tr', false, true);
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = row.dom.rowIndex - 1;
- Roo.each(this.el.select('tbody td', true).elements, function(e){
- e.on('mouseout', _this.onMouseout, _this);
- });
- this.fireEvent('rowsrendered', this);
+ if(row !== false){
+
+ this.fireEvent("row" + name, this, rowIndex, e);
+
+ if(cell !== false){
+
+ this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
+ }
+ }
- this.autoSize();
},
-
- onUpdate : function(ds,record)
+ onMouseover : function(e, el)
{
- this.refreshRow(record);
- this.autoSize();
- },
-
- onRemove : function(ds, record, index, isUpdate){
- if(isUpdate !== true){
- this.fireEvent("beforerowremoved", this, index, record);
- }
- var bt = this.mainBody.dom;
+ var cell = Roo.get(el);
- var rows = this.el.select('tbody > tr', true).elements;
+ if(!cell){
+ return;
+ }
- if(typeof(rows[index]) != 'undefined'){
- bt.removeChild(rows[index].dom);
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = cell.findParent('td', false, true);
}
-// if(bt.rows[index]){
-// bt.removeChild(bt.rows[index]);
-// }
+ var row = cell.findParent('tr', false, true);
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = row.dom.rowIndex - 1; // start from 0
+
+ this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
- if(isUpdate !== true){
- //this.stripeRows(index);
- //this.syncRowHeights(index, index);
- //this.layout();
- this.fireEvent("rowremoved", this, index, record);
- }
},
- onAdd : function(ds, records, rowIndex)
+ onMouseout : function(e, el)
{
- //Roo.log('on Add called');
- // - note this does not handle multiple adding very well..
- var bt = this.mainBody.dom;
- for (var i =0 ; i < records.length;i++) {
- //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
- //Roo.log(records[i]);
- //Roo.log(this.store.getAt(rowIndex+i));
- this.insertRow(this.store, rowIndex + i, false);
+ var cell = Roo.get(el);
+
+ if(!cell){
return;
}
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = cell.findParent('td', false, true);
+ }
+
+ var row = cell.findParent('tr', false, true);
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = row.dom.rowIndex - 1; // start from 0
+
+ this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
+
},
-
- refreshRow : function(record){
- var ds = this.store, index;
- if(typeof record == 'number'){
- index = record;
- record = ds.getAt(index);
- }else{
- index = ds.indexOf(record);
- if (index < 0) {
- return; // should not happen - but seems to
- }
+ onClick : function(e, el)
+ {
+ var cell = Roo.get(el);
+
+ if(!cell || (!this.cellSelection && !this.rowSelection)){
+ return;
}
- this.insertRow(ds, index, true);
- this.autoSize();
- this.onRemove(ds, record, index+1, true);
- this.autoSize();
- //this.syncRowHeights(index, index);
- //this.layout();
- this.fireEvent("rowupdated", this, index, record);
+
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = cell.findParent('td', false, true);
+ }
+
+ if(!cell || typeof(cell) == 'undefined'){
+ return;
+ }
+
+ var row = cell.findParent('tr', false, true);
+
+ if(!row || typeof(row) == 'undefined'){
+ return;
+ }
+
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = this.getRowIndex(row);
+
+ // why??? - should these not be based on SelectionModel?
+ //if(this.cellSelection){
+ this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
+ //}
+
+ //if(this.rowSelection){
+ this.fireEvent('rowclick', this, row, rowIndex, e);
+ //}
+
},
-
- insertRow : function(dm, rowIndex, isUpdate){
- if(!isUpdate){
- this.fireEvent("beforerowsinserted", this, rowIndex);
+ onDblClick : function(e,el)
+ {
+ var cell = Roo.get(el);
+
+ if(!cell || (!this.cellSelection && !this.rowSelection)){
+ return;
}
- //var s = this.getScrollState();
- var row = this.renderRow(this.cm, this.store, rowIndex);
- // insert before rowIndex..
- var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
- var _this = this;
-
- if(row.cellObjects.length){
- Roo.each(row.cellObjects, function(r){
- _this.renderCellObject(r);
- })
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = cell.findParent('td', false, true);
}
-
- if(!isUpdate){
- this.fireEvent("rowsinserted", this, rowIndex);
- //this.syncRowHeights(firstRow, lastRow);
- //this.stripeRows(firstRow);
- //this.layout();
+
+ if(!cell || typeof(cell) == 'undefined'){
+ return;
+ }
+
+ var row = cell.findParent('tr', false, true);
+
+ if(!row || typeof(row) == 'undefined'){
+ return;
+ }
+
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = this.getRowIndex(row);
+
+ if(this.cellSelection){
+ this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
}
+ if(this.rowSelection){
+ this.fireEvent('rowdblclick', this, row, rowIndex, e);
+ }
},
-
-
- getRowDom : function(rowIndex)
+ findRowIndex : function(el)
{
- var rows = this.el.select('tbody > tr', true).elements;
+ var cell = Roo.get(el);
+ if(!cell) {
+ return false;
+ }
+ var row = cell.findParent('tr', false, true);
- return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
+ if(!row || typeof(row) == 'undefined'){
+ return false;
+ }
+ return this.getRowIndex(row);
+ },
+ sort : function(e,el)
+ {
+ var col = Roo.get(el);
+
+ if(!col.hasClass('sortable')){
+ return;
+ }
+
+ var sort = col.attr('sort');
+ var dir = 'ASC';
+
+ if(col.select('i', true).first().hasClass('fa-arrow-up')){
+ dir = 'DESC';
+ }
+
+ this.store.sortInfo = {field : sort, direction : dir};
+
+ if (this.footer) {
+ Roo.log("calling footer first");
+ this.footer.onClick('first');
+ } else {
+ this.store.load({ params : { start : 0 } });
+ }
},
- // returns the object tree for a tr..
-
- renderRow : function(cm, ds, rowIndex)
+ renderHeader : function()
{
- var d = ds.getAt(rowIndex);
-
- var row = {
- tag : 'tr',
- cls : 'x-row-' + rowIndex,
+ var header = {
+ tag: 'thead',
cn : []
};
-
- var cellObjects = [];
+
+ var cm = this.cm;
+ this.totalWidth = 0;
for(var i = 0, len = cm.getColumnCount(); i < len; i++){
- var config = cm.config[i];
- var renderer = cm.getRenderer(i);
- var value = '';
- var id = false;
+ var config = cm.config[i];
- if(typeof(renderer) !== 'undefined'){
- value = renderer(d.data[cm.getDataIndex(i)], false, d);
- }
- // if object are returned, then they are expected to be Roo.bootstrap.Component instances
- // and are rendered into the cells after the row is rendered - using the id for the element.
+ var c = {
+ tag: 'th',
+ cls : 'x-hcol-' + i,
+ style : '',
+
+ html: cm.getColumnHeader(i)
+ };
- if(typeof(value) === 'object'){
- id = Roo.id();
- cellObjects.push({
- container : id,
- cfg : value
- })
+ var tooltip = cm.getColumnTooltip(i);
+ if (tooltip) {
+ c.tooltip = tooltip;
}
- var rowcfg = {
- record: d,
- rowIndex : rowIndex,
- colIndex : i,
- rowClass : ''
- };
-
- this.fireEvent('rowclass', this, rowcfg);
- var td = {
- tag: 'td',
- // this might end up displaying HTML?
- // this is too messy... - better to only do it on columsn you know are going to be too long
- //tooltip : (typeof(value) === 'object') ? '' : value,
- cls : rowcfg.rowClass + ' x-col-' + i,
- style: '',
- html: (typeof(value) === 'object') ? '' : value
- };
+ var hh = '';
- if (id) {
- td.id = id;
+ if(typeof(config.sortable) != 'undefined' && config.sortable){
+ c.cls += ' sortable';
+ c.html = '<i class="fa"></i>' + c.html;
}
- if(typeof(config.colspan) != 'undefined'){
- td.colspan = config.colspan;
- }
+ // could use BS4 hidden-..-down
- if(typeof(config.hidden) != 'undefined' && config.hidden){
- td.style += ' display:none;';
+ if(typeof(config.lgHeader) != 'undefined'){
+ hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
}
- if(typeof(config.align) != 'undefined' && config.align.length){
- td.style += ' text-align:' + config.align + ';';
+ if(typeof(config.mdHeader) != 'undefined'){
+ hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
}
- if(typeof(config.valign) != 'undefined' && config.valign.length){
- td.style += ' vertical-align:' + config.valign + ';';
+
+ if(typeof(config.smHeader) != 'undefined'){
+ hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
}
- if(typeof(config.width) != 'undefined'){
- td.style += ' width:' + config.width + 'px;';
+ if(typeof(config.xsHeader) != 'undefined'){
+ hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
}
- if(typeof(config.cursor) != 'undefined'){
- td.style += ' cursor:' + config.cursor + ';';
+ if(hh.length){
+ c.html = hh;
+ }
+
+ if(typeof(config.tooltip) != 'undefined'){
+ c.tooltip = config.tooltip;
+ }
+
+ if(typeof(config.colspan) != 'undefined'){
+ c.colspan = config.colspan;
+ }
+
+ // hidden is handled by CSS now
+
+ if(typeof(config.dataIndex) != 'undefined'){
+ c.sort = config.dataIndex;
+ }
+
+
+
+ if(typeof(config.align) != 'undefined' && config.align.length){
+ c.style += ' text-align:' + config.align + ';';
+ }
+
+ /* width is done in CSS
+ *if(typeof(config.width) != 'undefined'){
+ c.style += ' width:' + config.width + 'px;';
+ this.totalWidth += config.width;
+ } else {
+ this.totalWidth += 100; // assume minimum of 100 per column?
}
+ */
if(typeof(config.cls) != 'undefined'){
- td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
+ c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
}
+ // this is the bit that doesnt reall work at all...
+
+ /*
['xs','sm','md','lg'].map(function(size){
if(typeof(config[size]) == 'undefined'){
return;
}
-
-
-
+
if (!config[size]) { // 0 = hidden
// BS 4 '0' is treated as hide that column and below.
- td.cls += ' hidden-' + size + ' hidden' + size + '-down';
+ c.cls += ' hidden-' + size + ' hidden' + size + '-down';
return;
}
- td.cls += ' col-' + size + '-' + config[size] + (
- size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
+ c.cls += ' col-' + size + '-' + config[size] + (
+ size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
);
-
-
+
+
});
+ */
+ // at the end?
- row.cn.push(td);
-
+ c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
+
+
+
+
+ header.cn.push(c)
}
- row.cellObjects = cellObjects;
-
- return row;
-
+ return header;
},
-
-
- onBeforeLoad : function()
+ renderBody : function()
{
+ var body = {
+ tag: 'tbody',
+ cn : [
+ {
+ tag: 'tr',
+ cn : [
+ {
+ tag : 'td',
+ colspan : this.cm.getColumnCount()
+ }
+ ]
+ }
+ ]
+ };
+ return body;
},
- /**
- * Remove all rows
- */
- clear : function()
- {
- this.el.select('tbody', true).first().dom.innerHTML = '';
- },
- /**
- * Show or hide a row.
- * @param {Number} rowIndex to show or hide
- * @param {Boolean} state hide
- */
- setRowVisibility : function(rowIndex, state)
+
+ renderFooter : function()
{
- var bt = this.mainBody.dom;
-
- var rows = this.el.select('tbody > tr', true).elements;
+ var footer = {
+ tag: 'tfoot',
+ cn : [
+ {
+ tag: 'tr',
+ cn : [
+ {
+ tag : 'td',
+ colspan : this.cm.getColumnCount()
+ }
+ ]
+ }
+ ]
+ };
- if(typeof(rows[rowIndex]) == 'undefined'){
- return;
- }
- rows[rowIndex].dom.style.display = state ? '' : 'none';
+ return footer;
},
- getSelectionModel : function(){
- if(!this.selModel){
- this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
- }
- return this.selModel;
- },
- /*
- * Render the Roo.bootstrap object from renderder
- */
- renderCellObject : function(r)
- {
- var _this = this;
-
- r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
-
- var t = r.cfg.render(r.container);
-
- if(r.cfg.cn){
- Roo.each(r.cfg.cn, function(c){
- var child = {
- container: t.getChildContainer(),
- cfg: c
- };
- _this.renderCellObject(child);
- })
- }
- },
- getRowIndex : function(row)
+ onLoad : function()
{
- var rowIndex = -1;
+// Roo.log('ds onload');
+ this.clear();
- Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
- if(el != row){
- return;
+ var _this = this;
+ var cm = this.cm;
+ var ds = this.store;
+
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
+ if (_this.store.sortInfo) {
+
+ if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
+ e.select('i', true).addClass(['fa-arrow-up']);
+ }
+
+ if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
+ e.select('i', true).addClass(['fa-arrow-down']);
+ }
}
-
- rowIndex = index;
});
- return rowIndex;
- },
- /**
- * Returns the grid's underlying element = used by panel.Grid
- * @return {Element} The element
- */
- getGridEl : function(){
- return this.el;
- },
- /**
- * Forces a resize - used by panel.Grid
- * @return {Element} The element
- */
- autoSize : function()
- {
- //var ctr = Roo.get(this.container.dom.parentElement);
- var ctr = Roo.get(this.el.dom);
-
- var thd = this.getGridEl().select('thead',true).first();
- var tbd = this.getGridEl().select('tbody', true).first();
- var tfd = this.getGridEl().select('tfoot', true).first();
+ var tbody = this.bodyEl;
+
+ if(ds.getCount() > 0){
+ ds.data.each(function(d,rowIndex){
+ var row = this.renderRow(cm, ds, rowIndex);
+
+ tbody.createChild(row);
+
+ var _this = this;
+
+ if(row.cellObjects.length){
+ Roo.each(row.cellObjects, function(r){
+ _this.renderCellObject(r);
+ })
+ }
+
+ }, this);
+ }
- var cw = ctr.getWidth();
- this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
+ var tfoot = this.el.select('tfoot', true).first();
- if (tbd) {
+ if(this.footerShow && this.auto_hide_footer && this.mainFoot){
- tbd.setWidth(ctr.getWidth());
- // if the body has a max height - and then scrolls - we should perhaps set up the height here
- // this needs fixing for various usage - currently only hydra job advers I think..
- //tdb.setHeight(
- // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
- //);
- var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
- cw -= barsize;
+ this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
+
+ var total = this.ds.getTotalCount();
+
+ if(this.footer.pageSize < total){
+ this.mainFoot.show();
+ }
}
- cw = Math.max(cw, this.totalWidth);
- this.getGridEl().select('tbody tr',true).setWidth(cw);
- // resize 'expandable coloumn?
+ Roo.each(this.el.select('tbody td', true).elements, function(e){
+ e.on('mouseover', _this.onMouseover, _this);
+ });
- return; // we doe not have a view in this design..
+ Roo.each(this.el.select('tbody td', true).elements, function(e){
+ e.on('mouseout', _this.onMouseout, _this);
+ });
+ this.fireEvent('rowsrendered', this);
+
+ this.autoSize();
+
+ this.initCSS(); /// resize cols
+
},
- onBodyScroll: function()
+
+
+ onUpdate : function(ds,record)
{
- //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
- if(this.mainHead){
- this.mainHead.setStyle({
- 'position' : 'relative',
- 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
- });
+ this.refreshRow(record);
+ this.autoSize();
+ },
+
+ onRemove : function(ds, record, index, isUpdate){
+ if(isUpdate !== true){
+ this.fireEvent("beforerowremoved", this, index, record);
}
+ var bt = this.bodyEl.dom;
- if(this.lazyLoad){
-
- var scrollHeight = this.mainBody.dom.scrollHeight;
-
- var scrollTop = Math.ceil(this.mainBody.getScroll().top);
-
- var height = this.mainBody.getHeight();
-
- if(scrollHeight - height == scrollTop) {
-
- var total = this.ds.getTotalCount();
-
- if(this.footer.cursor + this.footer.pageSize < total){
-
- this.footer.ds.load({
- params : {
- start : this.footer.cursor + this.footer.pageSize,
- limit : this.footer.pageSize
- },
- add : true
- });
- }
- }
-
+ var rows = this.el.select('tbody > tr', true).elements;
+
+ if(typeof(rows[index]) != 'undefined'){
+ bt.removeChild(rows[index].dom);
+ }
+
+// if(bt.rows[index]){
+// bt.removeChild(bt.rows[index]);
+// }
+
+ if(isUpdate !== true){
+ //this.stripeRows(index);
+ //this.syncRowHeights(index, index);
+ //this.layout();
+ this.fireEvent("rowremoved", this, index, record);
}
},
- onHeaderChange : function()
+ onAdd : function(ds, records, rowIndex)
{
- var header = this.renderHeader();
- var table = this.el.select('table', true).first();
-
- this.mainHead.remove();
- this.mainHead = table.createChild(header, this.mainBody, false);
-
- Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
- e.on('click', this.sort, this);
- }, this);
-
+ //Roo.log('on Add called');
+ // - note this does not handle multiple adding very well..
+ var bt = this.bodyEl.dom;
+ for (var i =0 ; i < records.length;i++) {
+ //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
+ //Roo.log(records[i]);
+ //Roo.log(this.store.getAt(rowIndex+i));
+ this.insertRow(this.store, rowIndex + i, false);
+ return;
+ }
},
- onHiddenChange : function(colModel, colIndex, hidden)
+
+ refreshRow : function(record){
+ var ds = this.store, index;
+ if(typeof record == 'number'){
+ index = record;
+ record = ds.getAt(index);
+ }else{
+ index = ds.indexOf(record);
+ if (index < 0) {
+ return; // should not happen - but seems to
+ }
+ }
+ this.insertRow(ds, index, true);
+ this.autoSize();
+ this.onRemove(ds, record, index+1, true);
+ this.autoSize();
+ //this.syncRowHeights(index, index);
+ //this.layout();
+ this.fireEvent("rowupdated", this, index, record);
+ },
+ // private - called by RowSelection
+ onRowSelect : function(rowIndex){
+ var row = this.getRowDom(rowIndex);
+ row.addClass(['bg-info','info']);
+ },
+ // private - called by RowSelection
+ onRowDeselect : function(rowIndex)
{
- var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
- var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
+ if (rowIndex < 0) {
+ return;
+ }
+ var row = this.getRowDom(rowIndex);
+ row.removeClass(['bg-info','info']);
+ },
+ /**
+ * Focuses the specified row.
+ * @param {Number} row The row index
+ */
+ focusRow : function(row)
+ {
+ //Roo.log('GridView.focusRow');
+ var x = this.bodyEl.dom.scrollLeft;
+ this.focusCell(row, 0, false);
+ this.bodyEl.dom.scrollLeft = x;
+
+ },
+ /**
+ * Focuses the specified cell.
+ * @param {Number} row The row index
+ * @param {Number} col The column index
+ * @param {Boolean} hscroll false to disable horizontal scrolling
+ */
+ focusCell : function(row, col, hscroll)
+ {
+ //Roo.log('GridView.focusCell');
+ var el = this.ensureVisible(row, col, hscroll);
+ // not sure what focusEL achives = it's a <a> pos relative
+ //this.focusEl.alignTo(el, "tl-tl");
+ //if(Roo.isGecko){
+ // this.focusEl.focus();
+ //}else{
+ // this.focusEl.focus.defer(1, this.focusEl);
+ //}
+ },
+
+ /**
+ * Scrolls the specified cell into view
+ * @param {Number} row The row index
+ * @param {Number} col The column index
+ * @param {Boolean} hscroll false to disable horizontal scrolling
+ */
+ ensureVisible : function(row, col, hscroll)
+ {
+ //Roo.log('GridView.ensureVisible,' + row + ',' + col);
+ //return null; //disable for testing.
+ if(typeof row != "number"){
+ row = row.rowIndex;
+ }
+ if(row < 0 && row >= this.ds.getCount()){
+ return null;
+ }
+ col = (col !== undefined ? col : 0);
+ var cm = this.cm;
+ while(cm.isHidden(col)){
+ col++;
+ }
+
+ var el = this.getCellDom(row, col);
+ if(!el){
+ return null;
+ }
+ var c = this.bodyEl.dom;
+
+ var ctop = parseInt(el.offsetTop, 10);
+ var cleft = parseInt(el.offsetLeft, 10);
+ var cbot = ctop + el.offsetHeight;
+ var cright = cleft + el.offsetWidth;
+
+ //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
+ var ch = 0; //?? header is not withing the area?
+ var stop = parseInt(c.scrollTop, 10);
+ var sleft = parseInt(c.scrollLeft, 10);
+ var sbot = stop + ch;
+ var sright = sleft + c.clientWidth;
+ /*
+ Roo.log('GridView.ensureVisible:' +
+ ' ctop:' + ctop +
+ ' c.clientHeight:' + c.clientHeight +
+ ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
+ ' stop:' + stop +
+ ' cbot:' + cbot +
+ ' sbot:' + sbot +
+ ' ch:' + ch
+ );
+ */
+ if(ctop < stop){
+ c.scrollTop = ctop;
+ //Roo.log("set scrolltop to ctop DISABLE?");
+ }else if(cbot > sbot){
+ //Roo.log("set scrolltop to cbot-ch");
+ c.scrollTop = cbot-ch;
+ }
+
+ if(hscroll !== false){
+ if(cleft < sleft){
+ c.scrollLeft = cleft;
+ }else if(cright > sright){
+ c.scrollLeft = cright-c.clientWidth;
+ }
+ }
+
+ return el;
+ },
+
+
+ insertRow : function(dm, rowIndex, isUpdate){
- this.CSS.updateRule(thSelector, "display", "");
- this.CSS.updateRule(tdSelector, "display", "");
+ if(!isUpdate){
+ this.fireEvent("beforerowsinserted", this, rowIndex);
+ }
+ //var s = this.getScrollState();
+ var row = this.renderRow(this.cm, this.store, rowIndex);
+ // insert before rowIndex..
+ var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
- if(hidden){
- this.CSS.updateRule(thSelector, "display", "none");
- this.CSS.updateRule(tdSelector, "display", "none");
+ var _this = this;
+
+ if(row.cellObjects.length){
+ Roo.each(row.cellObjects, function(r){
+ _this.renderCellObject(r);
+ })
+ }
+
+ if(!isUpdate){
+ this.fireEvent("rowsinserted", this, rowIndex);
+ //this.syncRowHeights(firstRow, lastRow);
+ //this.stripeRows(firstRow);
+ //this.layout();
}
- this.onHeaderChange();
- this.onLoad();
},
- setColumnWidth: function(col_index, width)
+
+ getRowDom : function(rowIndex)
{
- // width = "md-2 xs-2..."
- if(!this.colModel.config[col_index]) {
- return;
- }
+ var rows = this.el.select('tbody > tr', true).elements;
- var w = width.split(" ");
+ return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
- var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
+ },
+ getCellDom : function(rowIndex, colIndex)
+ {
+ var row = this.getRowDom(rowIndex);
+ if (row === false) {
+ return false;
+ }
+ var cols = row.select('td', true).elements;
+ return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
- var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
+ },
+
+ // returns the object tree for a tr..
+
+
+ renderRow : function(cm, ds, rowIndex)
+ {
+ var d = ds.getAt(rowIndex);
+ var row = {
+ tag : 'tr',
+ cls : 'x-row-' + rowIndex,
+ cn : []
+ };
+
+ var cellObjects = [];
- for(var j = 0; j < w.length; j++) {
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+ var config = cm.config[i];
- if(!w[j]) {
- continue;
+ var renderer = cm.getRenderer(i);
+ var value = '';
+ var id = false;
+
+ if(typeof(renderer) !== 'undefined'){
+ value = renderer(d.data[cm.getDataIndex(i)], false, d);
}
+ // if object are returned, then they are expected to be Roo.bootstrap.Component instances
+ // and are rendered into the cells after the row is rendered - using the id for the element.
- var size_cls = w[j].split("-");
+ if(typeof(value) === 'object'){
+ id = Roo.id();
+ cellObjects.push({
+ container : id,
+ cfg : value
+ })
+ }
- if(!Number.isInteger(size_cls[1] * 1)) {
- continue;
+ var rowcfg = {
+ record: d,
+ rowIndex : rowIndex,
+ colIndex : i,
+ rowClass : ''
+ };
+
+ this.fireEvent('rowclass', this, rowcfg);
+
+ var td = {
+ tag: 'td',
+ // this might end up displaying HTML?
+ // this is too messy... - better to only do it on columsn you know are going to be too long
+ //tooltip : (typeof(value) === 'object') ? '' : value,
+ cls : rowcfg.rowClass + ' x-col-' + i,
+ style: '',
+ html: (typeof(value) === 'object') ? '' : value
+ };
+
+ if (id) {
+ td.id = id;
}
- if(!this.colModel.config[col_index][size_cls[0]]) {
- continue;
+ if(typeof(config.colspan) != 'undefined'){
+ td.colspan = config.colspan;
}
- if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
- continue;
+
+
+ if(typeof(config.align) != 'undefined' && config.align.length){
+ td.style += ' text-align:' + config.align + ';';
+ }
+ if(typeof(config.valign) != 'undefined' && config.valign.length){
+ td.style += ' vertical-align:' + config.valign + ';';
+ }
+ /*
+ if(typeof(config.width) != 'undefined'){
+ td.style += ' width:' + config.width + 'px;';
}
+ */
- h_row[0].classList.replace(
- "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
- "col-"+size_cls[0]+"-"+size_cls[1]
- );
+ if(typeof(config.cursor) != 'undefined'){
+ td.style += ' cursor:' + config.cursor + ';';
+ }
- for(var i = 0; i < rows.length; i++) {
-
- var size_cls = w[j].split("-");
+ if(typeof(config.cls) != 'undefined'){
+ td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
+ }
+ /*
+ ['xs','sm','md','lg'].map(function(size){
- if(!Number.isInteger(size_cls[1] * 1)) {
- continue;
+ if(typeof(config[size]) == 'undefined'){
+ return;
}
- if(!this.colModel.config[col_index][size_cls[0]]) {
- continue;
- }
- if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
- continue;
+
+ if (!config[size]) { // 0 = hidden
+ // BS 4 '0' is treated as hide that column and below.
+ td.cls += ' hidden-' + size + ' hidden' + size + '-down';
+ return;
}
- rows[i].classList.replace(
- "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
- "col-"+size_cls[0]+"-"+size_cls[1]
+ td.cls += ' col-' + size + '-' + config[size] + (
+ size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
);
- }
-
- this.colModel.config[col_index][size_cls[0]] = size_cls[1];
- }
- }
-});
-
-
-
- /*
- * - LGPL
- *
- * table cell
- *
- */
-
-/**
- * @class Roo.bootstrap.TableCell
- * @extends Roo.bootstrap.Component
- * Bootstrap TableCell class
- * @cfg {String} html cell contain text
- * @cfg {String} cls cell class
- * @cfg {String} tag cell tag (td|th) default td
- * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
- * @cfg {String} align Aligns the content in a cell
- * @cfg {String} axis Categorizes cells
- * @cfg {String} bgcolor Specifies the background color of a cell
- * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
- * @cfg {Number} colspan Specifies the number of columns a cell should span
- * @cfg {String} headers Specifies one or more header cells a cell is related to
- * @cfg {Number} height Sets the height of a cell
- * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
- * @cfg {Number} rowspan Sets the number of rows a cell should span
- * @cfg {String} scope Defines a way to associate header cells and data cells in a table
- * @cfg {String} valign Vertical aligns the content in a cell
- * @cfg {Number} width Specifies the width of a cell
- *
- * @constructor
- * Create a new TableCell
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TableCell = function(config){
- Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
-};
+
-Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
+ });
+ */
+ row.cn.push(td);
+
+ }
+
+ row.cellObjects = cellObjects;
+
+ return row;
+
+ },
- html: false,
- cls: false,
- tag: false,
- abbr: false,
- align: false,
- axis: false,
- bgcolor: false,
- charoff: false,
- colspan: false,
- headers: false,
- height: false,
- nowrap: false,
- rowspan: false,
- scope: false,
- valign: false,
- width: false,
- getAutoCreate : function(){
- var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag: 'td'
- };
+ onBeforeLoad : function()
+ {
- if(this.tag){
- cfg.tag = this.tag;
- }
+ },
+ /**
+ * Remove all rows
+ */
+ clear : function()
+ {
+ this.el.select('tbody', true).first().dom.innerHTML = '';
+ },
+ /**
+ * Show or hide a row.
+ * @param {Number} rowIndex to show or hide
+ * @param {Boolean} state hide
+ */
+ setRowVisibility : function(rowIndex, state)
+ {
+ var bt = this.bodyEl.dom;
- if (this.html) {
- cfg.html=this.html
- }
- if (this.cls) {
- cfg.cls=this.cls
- }
- if (this.abbr) {
- cfg.abbr=this.abbr
- }
- if (this.align) {
- cfg.align=this.align
- }
- if (this.axis) {
- cfg.axis=this.axis
- }
- if (this.bgcolor) {
- cfg.bgcolor=this.bgcolor
- }
- if (this.charoff) {
- cfg.charoff=this.charoff
- }
- if (this.colspan) {
- cfg.colspan=this.colspan
- }
- if (this.headers) {
- cfg.headers=this.headers
- }
- if (this.height) {
- cfg.height=this.height
- }
- if (this.nowrap) {
- cfg.nowrap=this.nowrap
- }
- if (this.rowspan) {
- cfg.rowspan=this.rowspan
- }
- if (this.scope) {
- cfg.scope=this.scope
- }
- if (this.valign) {
- cfg.valign=this.valign
- }
- if (this.width) {
- cfg.width=this.width
+ var rows = this.el.select('tbody > tr', true).elements;
+
+ if(typeof(rows[rowIndex]) == 'undefined'){
+ return;
}
+ rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
-
- return cfg;
- }
-
-});
-
-
-
- /*
- * - LGPL
- *
- * table row
- *
- */
-
-/**
- * @class Roo.bootstrap.TableRow
- * @extends Roo.bootstrap.Component
- * Bootstrap TableRow class
- * @cfg {String} cls row class
- * @cfg {String} align Aligns the content in a table row
- * @cfg {String} bgcolor Specifies a background color for a table row
- * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
- * @cfg {String} valign Vertical aligns the content in a table row
- *
- * @constructor
- * Create a new TableRow
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TableRow = function(config){
- Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
+ },
- cls: false,
- align: false,
- bgcolor: false,
- charoff: false,
- valign: false,
- getAutoCreate : function(){
- var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag: 'tr'
- };
-
- if(this.cls){
- cfg.cls = this.cls;
- }
- if(this.align){
- cfg.align = this.align;
+ getSelectionModel : function(){
+ if(!this.selModel){
+ this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
}
- if(this.bgcolor){
- cfg.bgcolor = this.bgcolor;
+ return this.selModel;
+ },
+ /*
+ * Render the Roo.bootstrap object from renderder
+ */
+ renderCellObject : function(r)
+ {
+ var _this = this;
+
+ r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
+
+ var t = r.cfg.render(r.container);
+
+ if(r.cfg.cn){
+ Roo.each(r.cfg.cn, function(c){
+ var child = {
+ container: t.getChildContainer(),
+ cfg: c
+ };
+ _this.renderCellObject(child);
+ })
}
- if(this.charoff){
- cfg.charoff = this.charoff;
+ },
+ /**
+ * get the Row Index from a dom element.
+ * @param {Roo.Element} row The row to look for
+ * @returns {Number} the row
+ */
+ getRowIndex : function(row)
+ {
+ var rowIndex = -1;
+
+ Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
+ if(el != row){
+ return;
+ }
+
+ rowIndex = index;
+ });
+
+ return rowIndex;
+ },
+ /**
+ * get the header TH element for columnIndex
+ * @param {Number} columnIndex
+ * @returns {Roo.Element}
+ */
+ getHeaderIndex: function(colIndex)
+ {
+ var cols = this.headEl.select('th', true).elements;
+ return cols[colIndex];
+ },
+ /**
+ * get the Column Index from a dom element. (using regex on x-hcol-{colid})
+ * @param {domElement} cell to look for
+ * @returns {Number} the column
+ */
+ getCellIndex : function(cell)
+ {
+ var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
+ if(id){
+ return parseInt(id[1], 10);
}
- if(this.valign){
- cfg.valign = this.valign;
+ return 0;
+ },
+ /**
+ * Returns the grid's underlying element = used by panel.Grid
+ * @return {Element} The element
+ */
+ getGridEl : function(){
+ return this.el;
+ },
+ /**
+ * Forces a resize - used by panel.Grid
+ * @return {Element} The element
+ */
+ autoSize : function()
+ {
+ //var ctr = Roo.get(this.container.dom.parentElement);
+ var ctr = Roo.get(this.el.dom);
+
+ var thd = this.getGridEl().select('thead',true).first();
+ var tbd = this.getGridEl().select('tbody', true).first();
+ var tfd = this.getGridEl().select('tfoot', true).first();
+
+ var cw = ctr.getWidth();
+ this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
+
+ if (tbd) {
+
+ tbd.setWidth(ctr.getWidth());
+ // if the body has a max height - and then scrolls - we should perhaps set up the height here
+ // this needs fixing for various usage - currently only hydra job advers I think..
+ //tdb.setHeight(
+ // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
+ //);
+ var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
+ cw -= barsize;
+ }
+ cw = Math.max(cw, this.totalWidth);
+ this.getGridEl().select('tbody tr',true).setWidth(cw);
+ this.initCSS();
+
+ // resize 'expandable coloumn?
+
+ return; // we doe not have a view in this design..
+
+ },
+ onBodyScroll: function()
+ {
+ //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
+ if(this.headEl){
+ this.headEl.setStyle({
+ 'position' : 'relative',
+ 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
+ });
+ }
+
+ if(this.lazyLoad){
+
+ var scrollHeight = this.bodyEl.dom.scrollHeight;
+
+ var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
+
+ var height = this.bodyEl.getHeight();
+
+ if(scrollHeight - height == scrollTop) {
+
+ var total = this.ds.getTotalCount();
+
+ if(this.footer.cursor + this.footer.pageSize < total){
+
+ this.footer.ds.load({
+ params : {
+ start : this.footer.cursor + this.footer.pageSize,
+ limit : this.footer.pageSize
+ },
+ add : true
+ });
+ }
+ }
+
+ }
+ },
+ onColumnSplitterMoved : function(i, diff)
+ {
+ this.userResized = true;
+
+ var cm = this.colModel;
+
+ var w = this.getHeaderIndex(i).getWidth() + diff;
+
+
+ cm.setColumnWidth(i, w, true);
+ this.initCSS();
+ //var cid = cm.getColumnId(i); << not used in this version?
+ /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
+
+ this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
+ this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
+ this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
+*/
+ //this.updateSplitters();
+ //this.layout(); << ??
+ this.fireEvent("columnresize", i, w);
+ },
+ onHeaderChange : function()
+ {
+ var header = this.renderHeader();
+ var table = this.el.select('table', true).first();
+
+ this.headEl.remove();
+ this.headEl = table.createChild(header, this.bodyEl, false);
+
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.on('click', this.sort, this);
+ }, this);
+
+ if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
+ new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
+ }
+
+ },
+
+ onHiddenChange : function(colModel, colIndex, hidden)
+ {
+ var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
+ var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
+
+ this.CSS.updateRule(thSelector, "display", "");
+ this.CSS.updateRule(tdSelector, "display", "");
+
+ if(hidden){
+ this.CSS.updateRule(thSelector, "display", "none");
+ this.CSS.updateRule(tdSelector, "display", "none");
+ }
+
+ this.onHeaderChange();
+ this.onLoad();
+ },
+
+ setColumnWidth: function(col_index, width)
+ {
+ // width = "md-2 xs-2..."
+ if(!this.colModel.config[col_index]) {
+ return;
+ }
+
+ var w = width.split(" ");
+
+ var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
+
+ var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
+
+
+ for(var j = 0; j < w.length; j++) {
+
+ if(!w[j]) {
+ continue;
+ }
+
+ var size_cls = w[j].split("-");
+
+ if(!Number.isInteger(size_cls[1] * 1)) {
+ continue;
+ }
+
+ if(!this.colModel.config[col_index][size_cls[0]]) {
+ continue;
+ }
+
+ if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
+ continue;
+ }
+
+ h_row[0].classList.replace(
+ "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
+ "col-"+size_cls[0]+"-"+size_cls[1]
+ );
+
+ for(var i = 0; i < rows.length; i++) {
+
+ var size_cls = w[j].split("-");
+
+ if(!Number.isInteger(size_cls[1] * 1)) {
+ continue;
+ }
+
+ if(!this.colModel.config[col_index][size_cls[0]]) {
+ continue;
+ }
+
+ if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
+ continue;
+ }
+
+ rows[i].classList.replace(
+ "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
+ "col-"+size_cls[0]+"-"+size_cls[1]
+ );
+ }
+
+ this.colModel.config[col_index][size_cls[0]] = size_cls[1];
}
-
- return cfg;
}
-
});
-
+// currently only used to find the split on drag..
+Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
- /*
+/**
+ * @depricated
+*/
+Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
+Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
+/*
* - LGPL
*
- * table body
+ * table cell
*
*/
/**
- * @class Roo.bootstrap.TableBody
+ * @class Roo.bootstrap.TableCell
* @extends Roo.bootstrap.Component
- * Bootstrap TableBody class
- * @cfg {String} cls element class
- * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
- * @cfg {String} align Aligns the content inside the element
- * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
- * @cfg {String} valign Vertical aligns the content inside the <tbody> element
+ * Bootstrap TableCell class
+ * @cfg {String} html cell contain text
+ * @cfg {String} cls cell class
+ * @cfg {String} tag cell tag (td|th) default td
+ * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
+ * @cfg {String} align Aligns the content in a cell
+ * @cfg {String} axis Categorizes cells
+ * @cfg {String} bgcolor Specifies the background color of a cell
+ * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
+ * @cfg {Number} colspan Specifies the number of columns a cell should span
+ * @cfg {String} headers Specifies one or more header cells a cell is related to
+ * @cfg {Number} height Sets the height of a cell
+ * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
+ * @cfg {Number} rowspan Sets the number of rows a cell should span
+ * @cfg {String} scope Defines a way to associate header cells and data cells in a table
+ * @cfg {String} valign Vertical aligns the content in a cell
+ * @cfg {Number} width Specifies the width of a cell
*
* @constructor
- * Create a new TableBody
+ * Create a new TableCell
* @param {Object} config The config object
*/
-Roo.bootstrap.TableBody = function(config){
- Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+Roo.bootstrap.TableCell = function(config){
+ Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
+ html: false,
cls: false,
tag: false,
+ abbr: false,
align: false,
+ axis: false,
+ bgcolor: false,
charoff: false,
+ colspan: false,
+ headers: false,
+ height: false,
+ nowrap: false,
+ rowspan: false,
+ scope: false,
valign: false,
+ width: false,
+
getAutoCreate : function(){
- var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
+ var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
+
+ cfg = {
+ tag: 'td'
+ };
+
+ if(this.tag){
+ cfg.tag = this.tag;
+ }
+
+ if (this.html) {
+ cfg.html=this.html
+ }
+ if (this.cls) {
+ cfg.cls=this.cls
+ }
+ if (this.abbr) {
+ cfg.abbr=this.abbr
+ }
+ if (this.align) {
+ cfg.align=this.align
+ }
+ if (this.axis) {
+ cfg.axis=this.axis
+ }
+ if (this.bgcolor) {
+ cfg.bgcolor=this.bgcolor
+ }
+ if (this.charoff) {
+ cfg.charoff=this.charoff
+ }
+ if (this.colspan) {
+ cfg.colspan=this.colspan
+ }
+ if (this.headers) {
+ cfg.headers=this.headers
+ }
+ if (this.height) {
+ cfg.height=this.height
+ }
+ if (this.nowrap) {
+ cfg.nowrap=this.nowrap
+ }
+ if (this.rowspan) {
+ cfg.rowspan=this.rowspan
+ }
+ if (this.scope) {
+ cfg.scope=this.scope
+ }
+ if (this.valign) {
+ cfg.valign=this.valign
+ }
+ if (this.width) {
+ cfg.width=this.width
+ }
+
+
+ return cfg;
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * table row
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.TableRow
+ * @extends Roo.bootstrap.Component
+ * Bootstrap TableRow class
+ * @cfg {String} cls row class
+ * @cfg {String} align Aligns the content in a table row
+ * @cfg {String} bgcolor Specifies a background color for a table row
+ * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
+ * @cfg {String} valign Vertical aligns the content in a table row
+ *
+ * @constructor
+ * Create a new TableRow
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.TableRow = function(config){
+ Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
+
+ cls: false,
+ align: false,
+ bgcolor: false,
+ charoff: false,
+ valign: false,
+
+ getAutoCreate : function(){
+ var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
+
+ cfg = {
+ tag: 'tr'
+ };
+
+ if(this.cls){
+ cfg.cls = this.cls;
+ }
+ if(this.align){
+ cfg.align = this.align;
+ }
+ if(this.bgcolor){
+ cfg.bgcolor = this.bgcolor;
+ }
+ if(this.charoff){
+ cfg.charoff = this.charoff;
+ }
+ if(this.valign){
+ cfg.valign = this.valign;
+ }
+
+ return cfg;
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * table body
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.TableBody
+ * @extends Roo.bootstrap.Component
+ * Bootstrap TableBody class
+ * @cfg {String} cls element class
+ * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
+ * @cfg {String} align Aligns the content inside the element
+ * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
+ * @cfg {String} valign Vertical aligns the content inside the <tbody> element
+ *
+ * @constructor
+ * Create a new TableBody
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.TableBody = function(config){
+ Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
+
+ cls: false,
+ tag: false,
+ align: false,
+ charoff: false,
+ valign: false,
+
+ getAutoCreate : function(){
+ var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
cfg = {
tag: 'tbody'
var cn = node.className.split(/\W+/);
var cna = [];
- Roo.each(cn, function(cls) {
- if (cls.match(/Mso[a-zA-Z]+/)) {
- return;
- }
- cna.push(cls);
- });
- node.className = cna.length ? cna.join(' ') : '';
- if (!cna.length) {
- node.removeAttribute("class");
- }
- }
-
- if (node.hasAttribute("lang")) {
- node.removeAttribute("lang");
- }
-
- if (node.hasAttribute("style")) {
-
- var styles = node.getAttribute("style").split(";");
- var nstyle = [];
- Roo.each(styles, function(s) {
- if (!s.match(/:/)) {
- return;
- }
- var kv = s.split(":");
- if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
- return;
- }
- // what ever is left... we allow.
- nstyle.push(s);
- });
- node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
- if (!nstyle.length) {
- node.removeAttribute('style');
- }
- }
- this.iterateChildren(node, this.cleanWord);
-
-
-
- },
- /**
- * iterateChildren of a Node, calling fn each time, using this as the scole..
- * @param {DomNode} node node to iterate children of.
- * @param {Function} fn method of this class to call on each item.
- */
- iterateChildren : function(node, fn)
- {
- if (!node.childNodes.length) {
- return;
- }
- for (var i = node.childNodes.length-1; i > -1 ; i--) {
- fn.call(this, node.childNodes[i])
- }
- },
-
-
- /**
- * cleanTableWidths.
- *
- * Quite often pasting from word etc.. results in tables with column and widths.
- * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
- *
- */
- cleanTableWidths : function(node)
- {
-
-
- if (!node) {
- this.cleanTableWidths(this.doc.body);
- return;
- }
-
- // ignore list...
- if (node.nodeName == "#text" || node.nodeName == "#comment") {
- return;
- }
- Roo.log(node.tagName);
- if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
- this.iterateChildren(node, this.cleanTableWidths);
- return;
- }
- if (node.hasAttribute('width')) {
- node.removeAttribute('width');
- }
-
-
- if (node.hasAttribute("style")) {
- // pretty basic...
-
- var styles = node.getAttribute("style").split(";");
- var nstyle = [];
- Roo.each(styles, function(s) {
- if (!s.match(/:/)) {
- return;
- }
- var kv = s.split(":");
- if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
- return;
- }
- // what ever is left... we allow.
- nstyle.push(s);
- });
- node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
- if (!nstyle.length) {
- node.removeAttribute('style');
- }
- }
-
- this.iterateChildren(node, this.cleanTableWidths);
-
-
- },
-
-
-
-
- domToHTML : function(currentElement, depth, nopadtext) {
-
- depth = depth || 0;
- nopadtext = nopadtext || false;
-
- if (!currentElement) {
- return this.domToHTML(this.doc.body);
- }
-
- //Roo.log(currentElement);
- var j;
- var allText = false;
- var nodeName = currentElement.nodeName;
- var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
-
- if (nodeName == '#text') {
-
- return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
- }
-
-
- var ret = '';
- if (nodeName != 'BODY') {
-
- var i = 0;
- // Prints the node tagName, such as <A>, <IMG>, etc
- if (tagName) {
- var attr = [];
- for(i = 0; i < currentElement.attributes.length;i++) {
- // quoting?
- var aname = currentElement.attributes.item(i).name;
- if (!currentElement.attributes.item(i).value.length) {
- continue;
- }
- attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
- }
-
- ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
- }
- else {
-
- // eack
- }
- } else {
- tagName = false;
- }
- if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
- return ret;
- }
- if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
- nopadtext = true;
- }
-
-
- // Traverse the tree
- i = 0;
- var currentElementChild = currentElement.childNodes.item(i);
- var allText = true;
- var innerHTML = '';
- lastnode = '';
- while (currentElementChild) {
- // Formatting code (indent the tree so it looks nice on the screen)
- var nopad = nopadtext;
- if (lastnode == 'SPAN') {
- nopad = true;
- }
- // text
- if (currentElementChild.nodeName == '#text') {
- var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
- toadd = nopadtext ? toadd : toadd.trim();
- if (!nopad && toadd.length > 80) {
- innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
- }
- innerHTML += toadd;
-
- i++;
- currentElementChild = currentElement.childNodes.item(i);
- lastNode = '';
- continue;
- }
- allText = false;
-
- innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
-
- // Recursively traverse the tree structure of the child node
- innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
- lastnode = currentElementChild.nodeName;
- i++;
- currentElementChild=currentElement.childNodes.item(i);
- }
-
- ret += innerHTML;
-
- if (!allText) {
- // The remaining code is mostly for formatting the tree
- ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
- }
-
-
- if (tagName) {
- ret+= "</"+tagName+">";
- }
- return ret;
-
- },
-
- applyBlacklists : function()
- {
- var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
- var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
-
- this.white = [];
- this.black = [];
- Roo.each(Roo.HtmlEditorCore.white, function(tag) {
- if (b.indexOf(tag) > -1) {
- return;
- }
- this.white.push(tag);
-
- }, this);
-
- Roo.each(w, function(tag) {
- if (b.indexOf(tag) > -1) {
- return;
- }
- if (this.white.indexOf(tag) > -1) {
- return;
- }
- this.white.push(tag);
-
- }, this);
-
-
- Roo.each(Roo.HtmlEditorCore.black, function(tag) {
- if (w.indexOf(tag) > -1) {
- return;
- }
- this.black.push(tag);
-
- }, this);
-
- Roo.each(b, function(tag) {
- if (w.indexOf(tag) > -1) {
- return;
- }
- if (this.black.indexOf(tag) > -1) {
- return;
- }
- this.black.push(tag);
-
- }, this);
-
-
- w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
- b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
-
- this.cwhite = [];
- this.cblack = [];
- Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
- if (b.indexOf(tag) > -1) {
- return;
- }
- this.cwhite.push(tag);
-
- }, this);
-
- Roo.each(w, function(tag) {
- if (b.indexOf(tag) > -1) {
- return;
- }
- if (this.cwhite.indexOf(tag) > -1) {
- return;
- }
- this.cwhite.push(tag);
-
- }, this);
-
-
- Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
- if (w.indexOf(tag) > -1) {
- return;
- }
- this.cblack.push(tag);
-
- }, this);
-
- Roo.each(b, function(tag) {
- if (w.indexOf(tag) > -1) {
- return;
- }
- if (this.cblack.indexOf(tag) > -1) {
- return;
- }
- this.cblack.push(tag);
-
- }, this);
- },
-
- setStylesheets : function(stylesheets)
- {
- if(typeof(stylesheets) == 'string'){
- Roo.get(this.iframe.contentDocument.head).createChild({
- tag : 'link',
- rel : 'stylesheet',
- type : 'text/css',
- href : stylesheets
- });
-
- return;
- }
- var _this = this;
-
- Roo.each(stylesheets, function(s) {
- if(!s.length){
- return;
- }
-
- Roo.get(_this.iframe.contentDocument.head).createChild({
- tag : 'link',
- rel : 'stylesheet',
- type : 'text/css',
- href : s
- });
- });
-
-
- },
-
- removeStylesheets : function()
- {
- var _this = this;
-
- Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
- s.remove();
- });
- },
-
- setStyle : function(style)
- {
- Roo.get(this.iframe.contentDocument.head).createChild({
- tag : 'style',
- type : 'text/css',
- html : style
- });
-
- return;
- }
-
- // hide stuff that is not compatible
- /**
- * @event blur
- * @hide
- */
- /**
- * @event change
- * @hide
- */
- /**
- * @event focus
- * @hide
- */
- /**
- * @event specialkey
- * @hide
- */
- /**
- * @cfg {String} fieldClass @hide
- */
- /**
- * @cfg {String} focusClass @hide
- */
- /**
- * @cfg {String} autoCreate @hide
- */
- /**
- * @cfg {String} inputType @hide
- */
- /**
- * @cfg {String} invalidClass @hide
- */
- /**
- * @cfg {String} invalidText @hide
- */
- /**
- * @cfg {String} msgFx @hide
- */
- /**
- * @cfg {String} validateOnBlur @hide
- */
-});
-
-Roo.HtmlEditorCore.white = [
- 'area', 'br', 'img', 'input', 'hr', 'wbr',
-
- 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
- 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
- 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
- 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
- 'table', 'ul', 'xmp',
-
- 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
- 'thead', 'tr',
-
- 'dir', 'menu', 'ol', 'ul', 'dl',
-
- 'embed', 'object'
-];
-
-
-Roo.HtmlEditorCore.black = [
- // 'embed', 'object', // enable - backend responsiblity to clean thiese
- 'applet', //
- 'base', 'basefont', 'bgsound', 'blink', 'body',
- 'frame', 'frameset', 'head', 'html', 'ilayer',
- 'iframe', 'layer', 'link', 'meta', 'object',
- 'script', 'style' ,'title', 'xml' // clean later..
-];
-Roo.HtmlEditorCore.clean = [
- 'script', 'style', 'title', 'xml'
-];
-Roo.HtmlEditorCore.remove = [
- 'font'
-];
-// attributes..
-
-Roo.HtmlEditorCore.ablack = [
- 'on'
-];
-
-Roo.HtmlEditorCore.aclean = [
- 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
-];
-
-// protocols..
-Roo.HtmlEditorCore.pwhite= [
- 'http', 'https', 'mailto'
-];
-
-// white listed style attributes.
-Roo.HtmlEditorCore.cwhite= [
- // 'text-align', /// default is to allow most things..
-
-
-// 'font-size'//??
-];
-
-// black listed style attributes.
-Roo.HtmlEditorCore.cblack= [
- // 'font-size' -- this can be set by the project
-];
-
-
-Roo.HtmlEditorCore.swapCodes =[
- [ 8211, "–" ],
- [ 8212, "—" ],
- [ 8216, "'" ],
- [ 8217, "'" ],
- [ 8220, '"' ],
- [ 8221, '"' ],
- [ 8226, "*" ],
- [ 8230, "..." ]
-];
-
- /*
- * - LGPL
- *
- * HtmlEditor
- *
- */
-
-/**
- * @class Roo.bootstrap.HtmlEditor
- * @extends Roo.bootstrap.TextArea
- * Bootstrap HtmlEditor class
-
- * @constructor
- * Create a new HtmlEditor
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.HtmlEditor = function(config){
- Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
- if (!this.toolbars) {
- this.toolbars = [];
- }
-
- this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
- this.addEvents({
- /**
- * @event initialize
- * Fires when the editor is fully initialized (including the iframe)
- * @param {HtmlEditor} this
- */
- initialize: true,
- /**
- * @event activate
- * Fires when the editor is first receives the focus. Any insertion must wait
- * until after this event.
- * @param {HtmlEditor} this
- */
- activate: true,
- /**
- * @event beforesync
- * Fires before the textarea is updated with content from the editor iframe. Return false
- * to cancel the sync.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- beforesync: true,
- /**
- * @event beforepush
- * Fires before the iframe editor is updated with content from the textarea. Return false
- * to cancel the push.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- beforepush: true,
- /**
- * @event sync
- * Fires when the textarea is updated with content from the editor iframe.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- sync: true,
- /**
- * @event push
- * Fires when the iframe editor is updated with content from the textarea.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- push: true,
- /**
- * @event editmodechange
- * Fires when the editor switches edit modes
- * @param {HtmlEditor} this
- * @param {Boolean} sourceEdit True if source edit, false if standard editing.
- */
- editmodechange: true,
- /**
- * @event editorevent
- * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
- * @param {HtmlEditor} this
- */
- editorevent: true,
- /**
- * @event firstfocus
- * Fires when on first focus - needed by toolbars..
- * @param {HtmlEditor} this
- */
- firstfocus: true,
- /**
- * @event autosave
- * Auto save the htmlEditor value as a file into Events
- * @param {HtmlEditor} this
- */
- autosave: true,
- /**
- * @event savedpreview
- * preview the saved version of htmlEditor
- * @param {HtmlEditor} this
- */
- savedpreview: true
- });
-};
-
-
-Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
-
-
- /**
- * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
+ Roo.each(cn, function(cls) {
+ if (cls.match(/Mso[a-zA-Z]+/)) {
+ return;
+ }
+ cna.push(cls);
+ });
+ node.className = cna.length ? cna.join(' ') : '';
+ if (!cna.length) {
+ node.removeAttribute("class");
+ }
+ }
+
+ if (node.hasAttribute("lang")) {
+ node.removeAttribute("lang");
+ }
+
+ if (node.hasAttribute("style")) {
+
+ var styles = node.getAttribute("style").split(";");
+ var nstyle = [];
+ Roo.each(styles, function(s) {
+ if (!s.match(/:/)) {
+ return;
+ }
+ var kv = s.split(":");
+ if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
+ return;
+ }
+ // what ever is left... we allow.
+ nstyle.push(s);
+ });
+ node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+ if (!nstyle.length) {
+ node.removeAttribute('style');
+ }
+ }
+ this.iterateChildren(node, this.cleanWord);
+
+
+
+ },
+ /**
+ * iterateChildren of a Node, calling fn each time, using this as the scole..
+ * @param {DomNode} node node to iterate children of.
+ * @param {Function} fn method of this class to call on each item.
*/
- toolbars : false,
+ iterateChildren : function(node, fn)
+ {
+ if (!node.childNodes.length) {
+ return;
+ }
+ for (var i = node.childNodes.length-1; i > -1 ; i--) {
+ fn.call(this, node.childNodes[i])
+ }
+ },
- /**
- * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
- */
- btns : [],
-
- /**
- * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
- * Roo.resizable.
- */
- resizable : false,
- /**
- * @cfg {Number} height (in pixels)
- */
- height: 300,
- /**
- * @cfg {Number} width (in pixels)
- */
- width: false,
/**
- * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
- *
+ * cleanTableWidths.
+ *
+ * Quite often pasting from word etc.. results in tables with column and widths.
+ * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
+ *
*/
- stylesheets: false,
-
- // id of frame..
- frameId: false,
+ cleanTableWidths : function(node)
+ {
+
+
+ if (!node) {
+ this.cleanTableWidths(this.doc.body);
+ return;
+ }
+
+ // ignore list...
+ if (node.nodeName == "#text" || node.nodeName == "#comment") {
+ return;
+ }
+ Roo.log(node.tagName);
+ if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
+ this.iterateChildren(node, this.cleanTableWidths);
+ return;
+ }
+ if (node.hasAttribute('width')) {
+ node.removeAttribute('width');
+ }
+
+
+ if (node.hasAttribute("style")) {
+ // pretty basic...
+
+ var styles = node.getAttribute("style").split(";");
+ var nstyle = [];
+ Roo.each(styles, function(s) {
+ if (!s.match(/:/)) {
+ return;
+ }
+ var kv = s.split(":");
+ if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
+ return;
+ }
+ // what ever is left... we allow.
+ nstyle.push(s);
+ });
+ node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+ if (!nstyle.length) {
+ node.removeAttribute('style');
+ }
+ }
+
+ this.iterateChildren(node, this.cleanTableWidths);
+
+
+ },
- // private properties
- validationEvent : false,
- deferHeight: true,
- initialized : false,
- activated : false,
- onFocus : Roo.emptyFn,
- iframePad:3,
- hideMode:'offsets',
- tbContainer : false,
- bodyCls : '',
+ domToHTML : function(currentElement, depth, nopadtext) {
+
+ depth = depth || 0;
+ nopadtext = nopadtext || false;
- toolbarContainer :function() {
- return this.wrap.select('.x-html-editor-tb',true).first();
- },
-
- /**
- * Protected method that will not generally be called directly. It
- * is called when the editor creates its toolbar. Override this method if you need to
- * add custom toolbar buttons.
- * @param {HtmlEditor} editor
- */
- createToolbar : function(){
- Roo.log('renewing');
- Roo.log("create toolbars");
+ if (!currentElement) {
+ return this.domToHTML(this.doc.body);
+ }
- this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
- this.toolbars[0].render(this.toolbarContainer());
+ //Roo.log(currentElement);
+ var j;
+ var allText = false;
+ var nodeName = currentElement.nodeName;
+ var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
- return;
+ if (nodeName == '#text') {
+
+ return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
+ }
-// if (!editor.toolbars || !editor.toolbars.length) {
-// editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
-// }
-//
-// for (var i =0 ; i < editor.toolbars.length;i++) {
-// editor.toolbars[i] = Roo.factory(
-// typeof(editor.toolbars[i]) == 'string' ?
-// { xtype: editor.toolbars[i]} : editor.toolbars[i],
-// Roo.bootstrap.HtmlEditor);
-// editor.toolbars[i].init(editor);
-// }
- },
-
-
- // private
- onRender : function(ct, position)
- {
- // Roo.log("Call onRender: " + this.xtype);
- var _t = this;
- Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
-
- this.wrap = this.inputEl().wrap({
- cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
- });
- this.editorcore.onRender(ct, position);
-
- if (this.resizable) {
- this.resizeEl = new Roo.Resizable(this.wrap, {
- pinned : true,
- wrap: true,
- dynamic : true,
- minHeight : this.height,
- height: this.height,
- handles : this.resizable,
- width: this.width,
- listeners : {
- resize : function(r, w, h) {
- _t.onResize(w,h); // -something
+ var ret = '';
+ if (nodeName != 'BODY') {
+
+ var i = 0;
+ // Prints the node tagName, such as <A>, <IMG>, etc
+ if (tagName) {
+ var attr = [];
+ for(i = 0; i < currentElement.attributes.length;i++) {
+ // quoting?
+ var aname = currentElement.attributes.item(i).name;
+ if (!currentElement.attributes.item(i).value.length) {
+ continue;
}
+ attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
+ }
+
+ ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
+ }
+ else {
+
+ // eack
+ }
+ } else {
+ tagName = false;
+ }
+ if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
+ return ret;
+ }
+ if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
+ nopadtext = true;
+ }
+
+
+ // Traverse the tree
+ i = 0;
+ var currentElementChild = currentElement.childNodes.item(i);
+ var allText = true;
+ var innerHTML = '';
+ lastnode = '';
+ while (currentElementChild) {
+ // Formatting code (indent the tree so it looks nice on the screen)
+ var nopad = nopadtext;
+ if (lastnode == 'SPAN') {
+ nopad = true;
+ }
+ // text
+ if (currentElementChild.nodeName == '#text') {
+ var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
+ toadd = nopadtext ? toadd : toadd.trim();
+ if (!nopad && toadd.length > 80) {
+ innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
}
- });
+ innerHTML += toadd;
+
+ i++;
+ currentElementChild = currentElement.childNodes.item(i);
+ lastNode = '';
+ continue;
+ }
+ allText = false;
+ innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
+
+ // Recursively traverse the tree structure of the child node
+ innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
+ lastnode = currentElementChild.nodeName;
+ i++;
+ currentElementChild=currentElement.childNodes.item(i);
}
- this.createToolbar(this);
-
- if(!this.width && this.resizable){
- this.setSize(this.wrap.getSize());
+ ret += innerHTML;
+
+ if (!allText) {
+ // The remaining code is mostly for formatting the tree
+ ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
}
- if (this.resizeEl) {
- this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
- // should trigger onReize..
+
+
+ if (tagName) {
+ ret+= "</"+tagName+">";
}
+ return ret;
},
-
- // private
- onResize : function(w, h)
+
+ applyBlacklists : function()
{
- Roo.log('resize: ' +w + ',' + h );
- Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
- var ew = false;
- var eh = false;
+ var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
+ var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
- if(this.inputEl() ){
- if(typeof w == 'number'){
- var aw = w - this.wrap.getFrameWidth('lr');
- this.inputEl().setWidth(this.adjustWidth('textarea', aw));
- ew = aw;
+ this.white = [];
+ this.black = [];
+ Roo.each(Roo.HtmlEditorCore.white, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
}
- if(typeof h == 'number'){
- var tbh = -11; // fixme it needs to tool bar size!
- for (var i =0; i < this.toolbars.length;i++) {
- // fixme - ask toolbars for heights?
- tbh += this.toolbars[i].el.getHeight();
- //if (this.toolbars[i].footer) {
- // tbh += this.toolbars[i].footer.el.getHeight();
- //}
- }
-
-
-
-
-
- var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
- ah -= 5; // knock a few pixes off for look..
- this.inputEl().setHeight(this.adjustWidth('textarea', ah));
- var eh = ah;
+ this.white.push(tag);
+
+ }, this);
+
+ Roo.each(w, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
}
- }
- Roo.log('onResize:' + [w,h,ew,eh].join(',') );
- this.editorcore.onResize(ew,eh);
+ if (this.white.indexOf(tag) > -1) {
+ return;
+ }
+ this.white.push(tag);
+
+ }, this);
- },
-
- /**
- * Toggles the editor between standard and source edit mode.
- * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
- */
- toggleSourceEdit : function(sourceEditMode)
- {
- this.editorcore.toggleSourceEdit(sourceEditMode);
- if(this.editorcore.sourceEditMode){
- Roo.log('editor - showing textarea');
+ Roo.each(Roo.HtmlEditorCore.black, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ this.black.push(tag);
-// Roo.log('in');
-// Roo.log(this.syncValue());
- this.syncValue();
- this.inputEl().removeClass(['hide', 'x-hidden']);
- this.inputEl().dom.removeAttribute('tabIndex');
- this.inputEl().focus();
- }else{
- Roo.log('editor - hiding textarea');
-// Roo.log('out')
-// Roo.log(this.pushValue());
- this.pushValue();
+ }, this);
+
+ Roo.each(b, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.black.indexOf(tag) > -1) {
+ return;
+ }
+ this.black.push(tag);
- this.inputEl().addClass(['hide', 'x-hidden']);
- this.inputEl().dom.setAttribute('tabIndex', -1);
- //this.deferFocus();
- }
-
- if(this.resizable){
- this.setSize(this.wrap.getSize());
- }
+ }, this);
- this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
- },
-
- // private (for BoxComponent)
- adjustSize : Roo.BoxComponent.prototype.adjustSize,
-
- // private (for BoxComponent)
- getResizeEl : function(){
- return this.wrap;
- },
-
- // private (for BoxComponent)
- getPositionEl : function(){
- return this.wrap;
- },
-
- // private
- initEvents : function(){
- this.originalValue = this.getValue();
- },
-
-// /**
-// * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
-// * @method
-// */
-// markInvalid : Roo.emptyFn,
-// /**
-// * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
-// * @method
-// */
-// clearInvalid : Roo.emptyFn,
-
- setValue : function(v){
- Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
- this.editorcore.pushValue();
- },
-
-
- // private
- deferFocus : function(){
- this.focus.defer(10, this);
- },
-
- // doc'ed in Field
- focus : function(){
- this.editorcore.focus();
- },
-
-
- // private
- onDestroy : function(){
+ w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
+ b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
+
+ this.cwhite = [];
+ this.cblack = [];
+ Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ this.cwhite.push(tag);
+
+ }, this);
+ Roo.each(w, function(tag) {
+ if (b.indexOf(tag) > -1) {
+ return;
+ }
+ if (this.cwhite.indexOf(tag) > -1) {
+ return;
+ }
+ this.cwhite.push(tag);
+
+ }, this);
- if(this.rendered){
+ Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
+ }
+ this.cblack.push(tag);
- for (var i =0; i < this.toolbars.length;i++) {
- // fixme - ask toolbars for heights?
- this.toolbars[i].onDestroy();
+ }, this);
+
+ Roo.each(b, function(tag) {
+ if (w.indexOf(tag) > -1) {
+ return;
}
+ if (this.cblack.indexOf(tag) > -1) {
+ return;
+ }
+ this.cblack.push(tag);
- this.wrap.dom.innerHTML = '';
- this.wrap.remove();
- }
+ }, this);
},
+
+ setStylesheets : function(stylesheets)
+ {
+ if(typeof(stylesheets) == 'string'){
+ Roo.get(this.iframe.contentDocument.head).createChild({
+ tag : 'link',
+ rel : 'stylesheet',
+ type : 'text/css',
+ href : stylesheets
+ });
+
+ return;
+ }
+ var _this = this;
+
+ Roo.each(stylesheets, function(s) {
+ if(!s.length){
+ return;
+ }
+
+ Roo.get(_this.iframe.contentDocument.head).createChild({
+ tag : 'link',
+ rel : 'stylesheet',
+ type : 'text/css',
+ href : s
+ });
+ });
- // private
- onFirstFocus : function(){
- //Roo.log("onFirstFocus");
- this.editorcore.onFirstFocus();
- for (var i =0; i < this.toolbars.length;i++) {
- this.toolbars[i].onFirstFocus();
- }
},
- // private
- syncValue : function()
- {
- this.editorcore.syncValue();
+ removeStylesheets : function()
+ {
+ var _this = this;
+
+ Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
+ s.remove();
+ });
},
- pushValue : function()
- {
- this.editorcore.pushValue();
+ setStyle : function(style)
+ {
+ Roo.get(this.iframe.contentDocument.head).createChild({
+ tag : 'style',
+ type : 'text/css',
+ html : style
+ });
+
+ return;
}
-
// hide stuff that is not compatible
/**
/**
* @cfg {String} inputType @hide
*/
-
+ /**
+ * @cfg {String} invalidClass @hide
+ */
/**
* @cfg {String} invalidText @hide
*/
* @cfg {String} validateOnBlur @hide
*/
});
-
+
+Roo.HtmlEditorCore.white = [
+ 'area', 'br', 'img', 'input', 'hr', 'wbr',
+
+ 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
+ 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
+ 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
+ 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
+ 'table', 'ul', 'xmp',
+
+ 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
+ 'thead', 'tr',
+
+ 'dir', 'menu', 'ol', 'ul', 'dl',
+
+ 'embed', 'object'
+];
+
+
+Roo.HtmlEditorCore.black = [
+ // 'embed', 'object', // enable - backend responsiblity to clean thiese
+ 'applet', //
+ 'base', 'basefont', 'bgsound', 'blink', 'body',
+ 'frame', 'frameset', 'head', 'html', 'ilayer',
+ 'iframe', 'layer', 'link', 'meta', 'object',
+ 'script', 'style' ,'title', 'xml' // clean later..
+];
+Roo.HtmlEditorCore.clean = [
+ 'script', 'style', 'title', 'xml'
+];
+Roo.HtmlEditorCore.remove = [
+ 'font'
+];
+// attributes..
+
+Roo.HtmlEditorCore.ablack = [
+ 'on'
+];
-
-
-
+Roo.HtmlEditorCore.aclean = [
+ 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
+];
+
+// protocols..
+Roo.HtmlEditorCore.pwhite= [
+ 'http', 'https', 'mailto'
+];
+
+// white listed style attributes.
+Roo.HtmlEditorCore.cwhite= [
+ // 'text-align', /// default is to allow most things..
-Roo.namespace('Roo.bootstrap.htmleditor');
-/**
- * @class Roo.bootstrap.HtmlEditorToolbar1
- * Basic Toolbar
- *
- * @example
- * Usage:
+
+// 'font-size'//??
+];
+
+// black listed style attributes.
+Roo.HtmlEditorCore.cblack= [
+ // 'font-size' -- this can be set by the project
+];
+
+
+Roo.HtmlEditorCore.swapCodes =[
+ [ 8211, "–" ],
+ [ 8212, "—" ],
+ [ 8216, "'" ],
+ [ 8217, "'" ],
+ [ 8220, '"' ],
+ [ 8221, '"' ],
+ [ 8226, "*" ],
+ [ 8230, "..." ]
+];
+
+ /*
+ * - LGPL
*
- new Roo.bootstrap.HtmlEditor({
- ....
- toolbars : [
- new Roo.bootstrap.HtmlEditorToolbar1({
- disable : { fonts: 1 , format: 1, ..., ... , ...],
- btns : [ .... ]
- })
- }
-
- *
- * @cfg {Object} disable List of elements to disable..
- * @cfg {Array} btns List of additional buttons.
- *
+ * HtmlEditor
*
- * NEEDS Extra CSS?
- * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
*/
-
-Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
-{
+
+/**
+ * @class Roo.bootstrap.HtmlEditor
+ * @extends Roo.bootstrap.TextArea
+ * Bootstrap HtmlEditor class
+
+ * @constructor
+ * Create a new HtmlEditor
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.HtmlEditor = function(config){
+ Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
+ if (!this.toolbars) {
+ this.toolbars = [];
+ }
- Roo.apply(this, config);
+ this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
+ this.addEvents({
+ /**
+ * @event initialize
+ * Fires when the editor is fully initialized (including the iframe)
+ * @param {HtmlEditor} this
+ */
+ initialize: true,
+ /**
+ * @event activate
+ * Fires when the editor is first receives the focus. Any insertion must wait
+ * until after this event.
+ * @param {HtmlEditor} this
+ */
+ activate: true,
+ /**
+ * @event beforesync
+ * Fires before the textarea is updated with content from the editor iframe. Return false
+ * to cancel the sync.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ beforesync: true,
+ /**
+ * @event beforepush
+ * Fires before the iframe editor is updated with content from the textarea. Return false
+ * to cancel the push.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ beforepush: true,
+ /**
+ * @event sync
+ * Fires when the textarea is updated with content from the editor iframe.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ sync: true,
+ /**
+ * @event push
+ * Fires when the iframe editor is updated with content from the textarea.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ push: true,
+ /**
+ * @event editmodechange
+ * Fires when the editor switches edit modes
+ * @param {HtmlEditor} this
+ * @param {Boolean} sourceEdit True if source edit, false if standard editing.
+ */
+ editmodechange: true,
+ /**
+ * @event editorevent
+ * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
+ * @param {HtmlEditor} this
+ */
+ editorevent: true,
+ /**
+ * @event firstfocus
+ * Fires when on first focus - needed by toolbars..
+ * @param {HtmlEditor} this
+ */
+ firstfocus: true,
+ /**
+ * @event autosave
+ * Auto save the htmlEditor value as a file into Events
+ * @param {HtmlEditor} this
+ */
+ autosave: true,
+ /**
+ * @event savedpreview
+ * preview the saved version of htmlEditor
+ * @param {HtmlEditor} this
+ */
+ savedpreview: true
+ });
+};
+
+
+Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
- // default disabled, based on 'good practice'..
- this.disable = this.disable || {};
- Roo.applyIf(this.disable, {
- fontSize : true,
- colors : true,
- specialElements : true
- });
- Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
- this.editor = config.editor;
- this.editorcore = config.editor.editorcore;
+ /**
+ * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
+ */
+ toolbars : false,
+
+ /**
+ * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
+ */
+ btns : [],
+
+ /**
+ * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
+ * Roo.resizable.
+ */
+ resizable : false,
+ /**
+ * @cfg {Number} height (in pixels)
+ */
+ height: 300,
+ /**
+ * @cfg {Number} width (in pixels)
+ */
+ width: false,
- this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
+ /**
+ * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+ *
+ */
+ stylesheets: false,
- //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
- // dont call parent... till later.
-}
-Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
-
- bar : true,
+ // id of frame..
+ frameId: false,
- editor : false,
- editorcore : false,
+ // private properties
+ validationEvent : false,
+ deferHeight: true,
+ initialized : false,
+ activated : false,
+ onFocus : Roo.emptyFn,
+ iframePad:3,
+ hideMode:'offsets',
- formats : [
- "p" ,
- "h1","h2","h3","h4","h5","h6",
- "pre", "code",
- "abbr", "acronym", "address", "cite", "samp", "var",
- 'div','span'
- ],
+ tbContainer : false,
- onRender : function(ct, position)
- {
- // Roo.log("Call onRender: " + this.xtype);
-
- Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
- Roo.log(this.el);
- this.el.dom.style.marginBottom = '0';
- var _this = this;
- var editorcore = this.editorcore;
- var editor= this.editor;
-
- var children = [];
- var btn = function(id,cmd , toggle, handler, html){
-
- var event = toggle ? 'toggle' : 'click';
-
- var a = {
- size : 'sm',
- xtype: 'Button',
- xns: Roo.bootstrap,
- //glyphicon : id,
- fa: id,
- cmd : id || cmd,
- enableToggle:toggle !== false,
- html : html || '',
- pressed : toggle ? false : null,
- listeners : {}
- };
- a.listeners[toggle ? 'toggle' : 'click'] = function() {
- handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
- };
- children.push(a);
- return a;
- }
-
- // var cb_box = function...
-
- var style = {
- xtype: 'Button',
- size : 'sm',
- xns: Roo.bootstrap,
- fa : 'font',
- //html : 'submit'
- menu : {
- xtype: 'Menu',
- xns: Roo.bootstrap,
- items: []
- }
- };
- Roo.each(this.formats, function(f) {
- style.menu.items.push({
- xtype :'MenuItem',
- xns: Roo.bootstrap,
- html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
- tagname : f,
- listeners : {
- click : function()
- {
- editorcore.insertTag(this.tagname);
- editor.focus();
- }
- }
-
- });
- });
- children.push(style);
-
- btn('bold',false,true);
- btn('italic',false,true);
- btn('align-left', 'justifyleft',true);
- btn('align-center', 'justifycenter',true);
- btn('align-right' , 'justifyright',true);
- btn('link', false, false, function(btn) {
- //Roo.log("create link?");
- var url = prompt(this.createLinkText, this.defaultLinkValue);
- if(url && url != 'http:/'+'/'){
- this.editorcore.relayCmd('createlink', url);
- }
- }),
- btn('list','insertunorderedlist',true);
- btn('pencil', false,true, function(btn){
- Roo.log(this);
- this.toggleSourceEdit(btn.pressed);
- });
-
- if (this.editor.btns.length > 0) {
- for (var i = 0; i<this.editor.btns.length; i++) {
- children.push(this.editor.btns[i]);
- }
- }
-
- /*
- var cog = {
- xtype: 'Button',
- size : 'sm',
- xns: Roo.bootstrap,
- glyphicon : 'cog',
- //html : 'submit'
- menu : {
- xtype: 'Menu',
- xns: Roo.bootstrap,
- items: []
- }
- };
-
- cog.menu.items.push({
- xtype :'MenuItem',
- xns: Roo.bootstrap,
- html : Clean styles,
- tagname : f,
- listeners : {
- click : function()
- {
- editorcore.insertTag(this.tagname);
- editor.focus();
- }
- }
-
- });
- */
-
-
- this.xtype = 'NavSimplebar';
-
- for(var i=0;i< children.length;i++) {
-
- this.buttons.add(this.addxtypeChild(children[i]));
-
- }
-
- editor.on('editorevent', this.updateToolbar, this);
- },
- onBtnClick : function(id)
- {
- this.editorcore.relayCmd(id);
- this.editorcore.focus();
- },
+ bodyCls : '',
+ toolbarContainer :function() {
+ return this.wrap.select('.x-html-editor-tb',true).first();
+ },
+
/**
- * Protected method that will not generally be called directly. It triggers
- * a toolbar update by reading the markup state of the current selection in the editor.
+ * Protected method that will not generally be called directly. It
+ * is called when the editor creates its toolbar. Override this method if you need to
+ * add custom toolbar buttons.
+ * @param {HtmlEditor} editor
*/
- updateToolbar: function(){
-
- if(!this.editorcore.activated){
- this.editor.onFirstFocus(); // is this neeed?
- return;
- }
-
- var btns = this.buttons;
- var doc = this.editorcore.doc;
- btns.get('bold').setActive(doc.queryCommandState('bold'));
- btns.get('italic').setActive(doc.queryCommandState('italic'));
- //btns.get('underline').setActive(doc.queryCommandState('underline'));
-
- btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
- btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
- btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
-
- //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
- btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
- /*
-
- var ans = this.editorcore.getAllAncestors();
- if (this.formatCombo) {
-
-
- var store = this.formatCombo.store;
- this.formatCombo.setValue("");
- for (var i =0; i < ans.length;i++) {
- if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
- // select it..
- this.formatCombo.setValue(ans[i].tagName.toLowerCase());
- break;
- }
- }
- }
+ createToolbar : function(){
+ Roo.log('renewing');
+ Roo.log("create toolbars");
+ this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
+ this.toolbars[0].render(this.toolbarContainer());
+ return;
- // hides menus... - so this cant be on a menu...
- Roo.bootstrap.MenuMgr.hideAll();
- */
- Roo.bootstrap.MenuMgr.hideAll();
- //this.editorsyncValue();
+// if (!editor.toolbars || !editor.toolbars.length) {
+// editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
+// }
+//
+// for (var i =0 ; i < editor.toolbars.length;i++) {
+// editor.toolbars[i] = Roo.factory(
+// typeof(editor.toolbars[i]) == 'string' ?
+// { xtype: editor.toolbars[i]} : editor.toolbars[i],
+// Roo.bootstrap.HtmlEditor);
+// editor.toolbars[i].init(editor);
+// }
},
- onFirstFocus: function() {
- this.buttons.each(function(item){
- item.enable();
+
+
+ // private
+ onRender : function(ct, position)
+ {
+ // Roo.log("Call onRender: " + this.xtype);
+ var _t = this;
+ Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
+
+ this.wrap = this.inputEl().wrap({
+ cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
});
- },
- toggleSourceEdit : function(sourceEditMode){
-
- if(sourceEditMode){
- Roo.log("disabling buttons");
- this.buttons.each( function(item){
- if(item.cmd != 'pencil'){
- item.disable();
+ this.editorcore.onRender(ct, position);
+
+ if (this.resizable) {
+ this.resizeEl = new Roo.Resizable(this.wrap, {
+ pinned : true,
+ wrap: true,
+ dynamic : true,
+ minHeight : this.height,
+ height: this.height,
+ handles : this.resizable,
+ width: this.width,
+ listeners : {
+ resize : function(r, w, h) {
+ _t.onResize(w,h); // -something
+ }
}
});
-
- }else{
- Roo.log("enabling buttons");
- if(this.editorcore.initialized){
- this.buttons.each( function(item){
- item.enable();
- });
- }
}
- Roo.log("calling toggole on editor");
- // tell the editor that it's been pressed..
- this.editor.toggleSourceEdit(sourceEditMode);
+ this.createToolbar(this);
- }
-});
-
-
-
-
-
-/*
- * - LGPL
- */
-
-/**
- * @class Roo.bootstrap.Markdown
- * @extends Roo.bootstrap.TextArea
- * Bootstrap Showdown editable area
- * @cfg {string} content
- *
- * @constructor
- * Create a new Showdown
- */
-
-Roo.bootstrap.Markdown = function(config){
- Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
-
-};
-
-Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
-
- editing :false,
-
- initEvents : function()
- {
- Roo.bootstrap.TextArea.prototype.initEvents.call(this);
- this.markdownEl = this.el.createChild({
- cls : 'roo-markdown-area'
- });
- this.inputEl().addClass('d-none');
- if (this.getValue() == '') {
- this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
-
- } else {
- this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
- }
- this.markdownEl.on('click', this.toggleTextEdit, this);
- this.on('blur', this.toggleTextEdit, this);
- this.on('specialkey', this.resizeTextArea, this);
- },
-
- toggleTextEdit : function()
- {
- var sh = this.markdownEl.getHeight();
- this.inputEl().addClass('d-none');
- this.markdownEl.addClass('d-none');
- if (!this.editing) {
- // show editor?
- this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
- this.inputEl().removeClass('d-none');
- this.inputEl().focus();
- this.editing = true;
- return;
+ if(!this.width && this.resizable){
+ this.setSize(this.wrap.getSize());
}
- // show showdown...
- this.updateMarkdown();
- this.markdownEl.removeClass('d-none');
- this.editing = false;
- return;
- },
- updateMarkdown : function()
- {
- if (this.getValue() == '') {
- this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
- return;
+ if (this.resizeEl) {
+ this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
+ // should trigger onReize..
}
-
- this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
- },
-
- resizeTextArea: function () {
- var sh = 100;
- Roo.log([sh, this.getValue().split("\n").length * 30]);
- this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
},
- setValue : function(val)
+
+ // private
+ onResize : function(w, h)
{
- Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
- if (!this.editing) {
- this.updateMarkdown();
- }
+ Roo.log('resize: ' +w + ',' + h );
+ Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
+ var ew = false;
+ var eh = false;
- },
- focus : function()
- {
- if (!this.editing) {
- this.toggleTextEdit();
+ if(this.inputEl() ){
+ if(typeof w == 'number'){
+ var aw = w - this.wrap.getFrameWidth('lr');
+ this.inputEl().setWidth(this.adjustWidth('textarea', aw));
+ ew = aw;
+ }
+ if(typeof h == 'number'){
+ var tbh = -11; // fixme it needs to tool bar size!
+ for (var i =0; i < this.toolbars.length;i++) {
+ // fixme - ask toolbars for heights?
+ tbh += this.toolbars[i].el.getHeight();
+ //if (this.toolbars[i].footer) {
+ // tbh += this.toolbars[i].footer.el.getHeight();
+ //}
+ }
+
+
+
+
+
+ var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
+ ah -= 5; // knock a few pixes off for look..
+ this.inputEl().setHeight(this.adjustWidth('textarea', ah));
+ var eh = ah;
+ }
}
+ Roo.log('onResize:' + [w,h,ew,eh].join(',') );
+ this.editorcore.onResize(ew,eh);
- }
-
-
-});
-/**
- * @class Roo.bootstrap.Table.AbstractSelectionModel
- * @extends Roo.util.Observable
- * Abstract base class for grid SelectionModels. It provides the interface that should be
- * implemented by descendant classes. This class should not be directly instantiated.
- * @constructor
- */
-Roo.bootstrap.Table.AbstractSelectionModel = function(){
- this.locked = false;
- Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
-};
-
-
-Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
- /** @ignore Called by the grid automatically. Do not call directly. */
- init : function(grid){
- this.grid = grid;
- this.initEvents();
- },
-
- /**
- * Locks the selections.
- */
- lock : function(){
- this.locked = true;
- },
-
- /**
- * Unlocks the selections.
- */
- unlock : function(){
- this.locked = false;
},
/**
- * Returns true if the selections are locked.
- * @return {Boolean}
+ * Toggles the editor between standard and source edit mode.
+ * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
*/
- isLocked : function(){
- return this.locked;
- },
-
-
- initEvents : function ()
+ toggleSourceEdit : function(sourceEditMode)
{
+ this.editorcore.toggleSourceEdit(sourceEditMode);
- }
-});
-/**
- * @extends Roo.bootstrap.Table.AbstractSelectionModel
- * @class Roo.bootstrap.Table.RowSelectionModel
- * The default SelectionModel used by {@link Roo.bootstrap.Table}.
- * It supports multiple selections and keyboard selection/navigation.
- * @constructor
- * @param {Object} config
- */
-
-Roo.bootstrap.Table.RowSelectionModel = function(config){
- Roo.apply(this, config);
- this.selections = new Roo.util.MixedCollection(false, function(o){
- return o.id;
- });
-
- this.last = false;
- this.lastActive = false;
+ if(this.editorcore.sourceEditMode){
+ Roo.log('editor - showing textarea');
+
+// Roo.log('in');
+// Roo.log(this.syncValue());
+ this.syncValue();
+ this.inputEl().removeClass(['hide', 'x-hidden']);
+ this.inputEl().dom.removeAttribute('tabIndex');
+ this.inputEl().focus();
+ }else{
+ Roo.log('editor - hiding textarea');
+// Roo.log('out')
+// Roo.log(this.pushValue());
+ this.pushValue();
+
+ this.inputEl().addClass(['hide', 'x-hidden']);
+ this.inputEl().dom.setAttribute('tabIndex', -1);
+ //this.deferFocus();
+ }
+
+ if(this.resizable){
+ this.setSize(this.wrap.getSize());
+ }
+
+ this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
+ },
+
+ // private (for BoxComponent)
+ adjustSize : Roo.BoxComponent.prototype.adjustSize,
- this.addEvents({
- /**
- * @event selectionchange
- * Fires when the selection changes
- * @param {SelectionModel} this
- */
- "selectionchange" : true,
- /**
- * @event afterselectionchange
- * Fires after the selection changes (eg. by key press or clicking)
- * @param {SelectionModel} this
- */
- "afterselectionchange" : true,
- /**
- * @event beforerowselect
- * Fires when a row is selected being selected, return false to cancel.
- * @param {SelectionModel} this
- * @param {Number} rowIndex The selected index
- * @param {Boolean} keepExisting False if other selections will be cleared
- */
- "beforerowselect" : true,
- /**
- * @event rowselect
- * Fires when a row is selected.
- * @param {SelectionModel} this
- * @param {Number} rowIndex The selected index
- * @param {Roo.data.Record} r The record
- */
- "rowselect" : true,
- /**
- * @event rowdeselect
- * Fires when a row is deselected.
- * @param {SelectionModel} this
- * @param {Number} rowIndex The selected index
- */
- "rowdeselect" : true
- });
- Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
- this.locked = false;
- };
+ // private (for BoxComponent)
+ getResizeEl : function(){
+ return this.wrap;
+ },
-Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
- /**
- * @cfg {Boolean} singleSelect
- * True to allow selection of only one row at a time (defaults to false)
- */
- singleSelect : false,
+ // private (for BoxComponent)
+ getPositionEl : function(){
+ return this.wrap;
+ },
// private
- initEvents : function()
- {
+ initEvents : function(){
+ this.originalValue = this.getValue();
+ },
- //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
- // this.growclickrid.on("mousedown", this.handleMouseDown, this);
- //}else{ // allow click to work like normal
- // this.grid.on("rowclick", this.handleDragableRowClick, this);
- //}
- //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
- this.grid.on("rowclick", this.handleMouseDown, this);
-
- this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
- "up" : function(e){
- if(!e.shiftKey){
- this.selectPrevious(e.shiftKey);
- }else if(this.last !== false && this.lastActive !== false){
- var last = this.last;
- this.selectRange(this.last, this.lastActive-1);
- this.grid.getView().focusRow(this.lastActive);
- if(last !== false){
- this.last = last;
- }
- }else{
- this.selectFirstRow();
- }
- this.fireEvent("afterselectionchange", this);
- },
- "down" : function(e){
- if(!e.shiftKey){
- this.selectNext(e.shiftKey);
- }else if(this.last !== false && this.lastActive !== false){
- var last = this.last;
- this.selectRange(this.last, this.lastActive+1);
- this.grid.getView().focusRow(this.lastActive);
- if(last !== false){
- this.last = last;
- }
- }else{
- this.selectFirstRow();
- }
- this.fireEvent("afterselectionchange", this);
- },
- scope: this
- });
- this.grid.store.on('load', function(){
- this.selections.clear();
- },this);
- /*
- var view = this.grid.view;
- view.on("refresh", this.onRefresh, this);
- view.on("rowupdated", this.onRowUpdated, this);
- view.on("rowremoved", this.onRemove, this);
- */
+// /**
+// * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+// * @method
+// */
+// markInvalid : Roo.emptyFn,
+// /**
+// * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
+// * @method
+// */
+// clearInvalid : Roo.emptyFn,
+
+ setValue : function(v){
+ Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
+ this.editorcore.pushValue();
},
+
// private
- onRefresh : function()
- {
- var ds = this.grid.store, i, v = this.grid.view;
- var s = this.selections;
- s.each(function(r){
- if((i = ds.indexOfId(r.id)) != -1){
- v.onRowSelect(i);
- }else{
- s.remove(r);
- }
- });
+ deferFocus : function(){
+ this.focus.defer(10, this);
},
- // private
- onRemove : function(v, index, r){
- this.selections.remove(r);
+ // doc'ed in Field
+ focus : function(){
+ this.editorcore.focus();
+
},
+
// private
- onRowUpdated : function(v, index, r){
- if(this.isSelected(r)){
- v.onRowSelect(index);
+ onDestroy : function(){
+
+
+
+ if(this.rendered){
+
+ for (var i =0; i < this.toolbars.length;i++) {
+ // fixme - ask toolbars for heights?
+ this.toolbars[i].onDestroy();
+ }
+
+ this.wrap.dom.innerHTML = '';
+ this.wrap.remove();
}
},
- /**
- * Select records.
- * @param {Array} records The records to select
- * @param {Boolean} keepExisting (optional) True to keep existing selections
- */
- selectRecords : function(records, keepExisting)
- {
- if(!keepExisting){
- this.clearSelections();
- }
- var ds = this.grid.store;
- for(var i = 0, len = records.length; i < len; i++){
- this.selectRow(ds.indexOf(records[i]), true);
+ // private
+ onFirstFocus : function(){
+ //Roo.log("onFirstFocus");
+ this.editorcore.onFirstFocus();
+ for (var i =0; i < this.toolbars.length;i++) {
+ this.toolbars[i].onFirstFocus();
}
+
},
-
- /**
- * Gets the number of selected rows.
- * @return {Number}
- */
- getCount : function(){
- return this.selections.length;
+
+ // private
+ syncValue : function()
+ {
+ this.editorcore.syncValue();
},
-
+
+ pushValue : function()
+ {
+ this.editorcore.pushValue();
+ }
+
+
+ // hide stuff that is not compatible
/**
- * Selects the first row in the grid.
+ * @event blur
+ * @hide
*/
- selectFirstRow : function(){
- this.selectRow(0);
- },
-
/**
- * Select the last row.
- * @param {Boolean} keepExisting (optional) True to keep existing selections
+ * @event change
+ * @hide
*/
- selectLastRow : function(keepExisting){
- //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
- this.selectRow(this.grid.store.getCount() - 1, keepExisting);
- },
-
/**
- * Selects the row immediately following the last selected row.
- * @param {Boolean} keepExisting (optional) True to keep existing selections
+ * @event focus
+ * @hide
*/
- selectNext : function(keepExisting)
- {
- if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
- this.selectRow(this.last+1, keepExisting);
- this.grid.getView().focusRow(this.last);
- }
- },
-
/**
- * Selects the row that precedes the last selected row.
- * @param {Boolean} keepExisting (optional) True to keep existing selections
+ * @event specialkey
+ * @hide
*/
- selectPrevious : function(keepExisting){
- if(this.last){
- this.selectRow(this.last-1, keepExisting);
- this.grid.getView().focusRow(this.last);
- }
- },
-
/**
- * Returns the selected records
- * @return {Array} Array of selected records
+ * @cfg {String} fieldClass @hide
*/
- getSelections : function(){
- return [].concat(this.selections.items);
- },
-
/**
- * Returns the first selected record.
- * @return {Record}
+ * @cfg {String} focusClass @hide
*/
- getSelected : function(){
- return this.selections.itemAt(0);
- },
-
-
/**
- * Clears all selections.
+ * @cfg {String} autoCreate @hide
*/
- clearSelections : function(fast)
- {
- if(this.locked) {
- return;
- }
- if(fast !== true){
- var ds = this.grid.store;
- var s = this.selections;
- s.each(function(r){
- this.deselectRow(ds.indexOfId(r.id));
- }, this);
- s.clear();
- }else{
- this.selections.clear();
- }
- this.last = false;
- },
-
-
/**
- * Selects all rows.
+ * @cfg {String} inputType @hide
*/
- selectAll : function(){
- if(this.locked) {
- return;
- }
- this.selections.clear();
- for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
- this.selectRow(i, true);
- }
- },
-
+
/**
- * Returns True if there is a selection.
- * @return {Boolean}
+ * @cfg {String} invalidText @hide
*/
- hasSelection : function(){
- return this.selections.length > 0;
- },
-
/**
- * Returns True if the specified row is selected.
- * @param {Number/Record} record The record or index of the record to check
- * @return {Boolean}
+ * @cfg {String} msgFx @hide
*/
- isSelected : function(index){
- var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
- return (r && this.selections.key(r.id) ? true : false);
- },
-
/**
- * Returns True if the specified record id is selected.
- * @param {String} id The id of record to check
- * @return {Boolean}
+ * @cfg {String} validateOnBlur @hide
*/
- isIdSelected : function(id){
- return (this.selections.key(id) ? true : false);
- },
-
-
- // private
- handleMouseDBClick : function(e, t){
-
- },
- // private
- handleMouseDown : function(e, t)
+});
+
+
+
+
+
+
+Roo.namespace('Roo.bootstrap.htmleditor');
+/**
+ * @class Roo.bootstrap.HtmlEditorToolbar1
+ * Basic Toolbar
+ *
+ * @example
+ * Usage:
+ *
+ new Roo.bootstrap.HtmlEditor({
+ ....
+ toolbars : [
+ new Roo.bootstrap.HtmlEditorToolbar1({
+ disable : { fonts: 1 , format: 1, ..., ... , ...],
+ btns : [ .... ]
+ })
+ }
+
+ *
+ * @cfg {Object} disable List of elements to disable..
+ * @cfg {Array} btns List of additional buttons.
+ *
+ *
+ * NEEDS Extra CSS?
+ * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
+ */
+
+Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
+{
+
+ Roo.apply(this, config);
+
+ // default disabled, based on 'good practice'..
+ this.disable = this.disable || {};
+ Roo.applyIf(this.disable, {
+ fontSize : true,
+ colors : true,
+ specialElements : true
+ });
+ Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
+
+ this.editor = config.editor;
+ this.editorcore = config.editor.editorcore;
+
+ this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
+
+ //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
+ // dont call parent... till later.
+}
+Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
+
+ bar : true,
+
+ editor : false,
+ editorcore : false,
+
+
+ formats : [
+ "p" ,
+ "h1","h2","h3","h4","h5","h6",
+ "pre", "code",
+ "abbr", "acronym", "address", "cite", "samp", "var",
+ 'div','span'
+ ],
+
+ onRender : function(ct, position)
{
- var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
- if(this.isLocked() || rowIndex < 0 ){
- return;
+ // Roo.log("Call onRender: " + this.xtype);
+
+ Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
+ Roo.log(this.el);
+ this.el.dom.style.marginBottom = '0';
+ var _this = this;
+ var editorcore = this.editorcore;
+ var editor= this.editor;
+
+ var children = [];
+ var btn = function(id,cmd , toggle, handler, html){
+
+ var event = toggle ? 'toggle' : 'click';
+
+ var a = {
+ size : 'sm',
+ xtype: 'Button',
+ xns: Roo.bootstrap,
+ //glyphicon : id,
+ fa: id,
+ cmd : id || cmd,
+ enableToggle:toggle !== false,
+ html : html || '',
+ pressed : toggle ? false : null,
+ listeners : {}
+ };
+ a.listeners[toggle ? 'toggle' : 'click'] = function() {
+ handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
+ };
+ children.push(a);
+ return a;
+ }
+
+ // var cb_box = function...
+
+ var style = {
+ xtype: 'Button',
+ size : 'sm',
+ xns: Roo.bootstrap,
+ fa : 'font',
+ //html : 'submit'
+ menu : {
+ xtype: 'Menu',
+ xns: Roo.bootstrap,
+ items: []
+ }
};
- if(e.shiftKey && this.last !== false){
- var last = this.last;
- this.selectRange(last, rowIndex, e.ctrlKey);
- this.last = last; // reset the last
- t.focus();
-
- }else{
- var isSelected = this.isSelected(rowIndex);
- //Roo.log("select row:" + rowIndex);
- if(isSelected){
- this.deselectRow(rowIndex);
- } else {
- this.selectRow(rowIndex, true);
+ Roo.each(this.formats, function(f) {
+ style.menu.items.push({
+ xtype :'MenuItem',
+ xns: Roo.bootstrap,
+ html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
+ tagname : f,
+ listeners : {
+ click : function()
+ {
+ editorcore.insertTag(this.tagname);
+ editor.focus();
+ }
+ }
+
+ });
+ });
+ children.push(style);
+
+ btn('bold',false,true);
+ btn('italic',false,true);
+ btn('align-left', 'justifyleft',true);
+ btn('align-center', 'justifycenter',true);
+ btn('align-right' , 'justifyright',true);
+ btn('link', false, false, function(btn) {
+ //Roo.log("create link?");
+ var url = prompt(this.createLinkText, this.defaultLinkValue);
+ if(url && url != 'http:/'+'/'){
+ this.editorcore.relayCmd('createlink', url);
}
-
- /*
- if(e.button !== 0 && isSelected){
- alert('rowIndex 2: ' + rowIndex);
- view.focusRow(rowIndex);
- }else if(e.ctrlKey && isSelected){
- this.deselectRow(rowIndex);
- }else if(!isSelected){
- this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
- view.focusRow(rowIndex);
+ }),
+ btn('list','insertunorderedlist',true);
+ btn('pencil', false,true, function(btn){
+ Roo.log(this);
+ this.toggleSourceEdit(btn.pressed);
+ });
+
+ if (this.editor.btns.length > 0) {
+ for (var i = 0; i<this.editor.btns.length; i++) {
+ children.push(this.editor.btns[i]);
+ }
+ }
+
+ /*
+ var cog = {
+ xtype: 'Button',
+ size : 'sm',
+ xns: Roo.bootstrap,
+ glyphicon : 'cog',
+ //html : 'submit'
+ menu : {
+ xtype: 'Menu',
+ xns: Roo.bootstrap,
+ items: []
}
- */
+ };
+
+ cog.menu.items.push({
+ xtype :'MenuItem',
+ xns: Roo.bootstrap,
+ html : Clean styles,
+ tagname : f,
+ listeners : {
+ click : function()
+ {
+ editorcore.insertTag(this.tagname);
+ editor.focus();
+ }
+ }
+
+ });
+ */
+
+
+ this.xtype = 'NavSimplebar';
+
+ for(var i=0;i< children.length;i++) {
+
+ this.buttons.add(this.addxtypeChild(children[i]));
+
}
- this.fireEvent("afterselectionchange", this);
+
+ editor.on('editorevent', this.updateToolbar, this);
},
- // private
- handleDragableRowClick : function(grid, rowIndex, e)
+ onBtnClick : function(id)
{
- if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
- this.selectRow(rowIndex, false);
- grid.view.focusRow(rowIndex);
- this.fireEvent("afterselectionchange", this);
- }
+ this.editorcore.relayCmd(id);
+ this.editorcore.focus();
},
/**
- * Selects multiple rows.
- * @param {Array} rows Array of the indexes of the row to select
- * @param {Boolean} keepExisting (optional) True to keep existing selections
- */
- selectRows : function(rows, keepExisting){
- if(!keepExisting){
- this.clearSelections();
- }
- for(var i = 0, len = rows.length; i < len; i++){
- this.selectRow(rows[i], true);
- }
- },
-
- /**
- * Selects a range of rows. All rows in between startRow and endRow are also selected.
- * @param {Number} startRow The index of the first row in the range
- * @param {Number} endRow The index of the last row in the range
- * @param {Boolean} keepExisting (optional) True to retain existing selections
+ * Protected method that will not generally be called directly. It triggers
+ * a toolbar update by reading the markup state of the current selection in the editor.
*/
- selectRange : function(startRow, endRow, keepExisting){
- if(this.locked) {
+ updateToolbar: function(){
+
+ if(!this.editorcore.activated){
+ this.editor.onFirstFocus(); // is this neeed?
return;
}
- if(!keepExisting){
- this.clearSelections();
- }
- if(startRow <= endRow){
- for(var i = startRow; i <= endRow; i++){
- this.selectRow(i, true);
+
+ var btns = this.buttons;
+ var doc = this.editorcore.doc;
+ btns.get('bold').setActive(doc.queryCommandState('bold'));
+ btns.get('italic').setActive(doc.queryCommandState('italic'));
+ //btns.get('underline').setActive(doc.queryCommandState('underline'));
+
+ btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
+ btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
+ btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
+
+ //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
+ btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
+ /*
+
+ var ans = this.editorcore.getAllAncestors();
+ if (this.formatCombo) {
+
+
+ var store = this.formatCombo.store;
+ this.formatCombo.setValue("");
+ for (var i =0; i < ans.length;i++) {
+ if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
+ // select it..
+ this.formatCombo.setValue(ans[i].tagName.toLowerCase());
+ break;
+ }
}
+ }
+
+
+
+ // hides menus... - so this cant be on a menu...
+ Roo.bootstrap.MenuMgr.hideAll();
+ */
+ Roo.bootstrap.MenuMgr.hideAll();
+ //this.editorsyncValue();
+ },
+ onFirstFocus: function() {
+ this.buttons.each(function(item){
+ item.enable();
+ });
+ },
+ toggleSourceEdit : function(sourceEditMode){
+
+
+ if(sourceEditMode){
+ Roo.log("disabling buttons");
+ this.buttons.each( function(item){
+ if(item.cmd != 'pencil'){
+ item.disable();
+ }
+ });
+
}else{
- for(var i = startRow; i >= endRow; i--){
- this.selectRow(i, true);
+ Roo.log("enabling buttons");
+ if(this.editorcore.initialized){
+ this.buttons.each( function(item){
+ item.enable();
+ });
}
+
}
- },
+ Roo.log("calling toggole on editor");
+ // tell the editor that it's been pressed..
+ this.editor.toggleSourceEdit(sourceEditMode);
+
+ }
+});
- /**
- * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
- * @param {Number} startRow The index of the first row in the range
- * @param {Number} endRow The index of the last row in the range
- */
- deselectRange : function(startRow, endRow, preventViewNotify){
- if(this.locked) {
- return;
- }
- for(var i = startRow; i <= endRow; i++){
- this.deselectRow(i, preventViewNotify);
- }
- },
- /**
- * Selects a row.
- * @param {Number} row The index of the row to select
- * @param {Boolean} keepExisting (optional) True to keep existing selections
- */
- selectRow : function(index, keepExisting, preventViewNotify)
+
+
+
+/*
+ * - LGPL
+ */
+
+/**
+ * @class Roo.bootstrap.Markdown
+ * @extends Roo.bootstrap.TextArea
+ * Bootstrap Showdown editable area
+ * @cfg {string} content
+ *
+ * @constructor
+ * Create a new Showdown
+ */
+
+Roo.bootstrap.Markdown = function(config){
+ Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
+
+};
+
+Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
+
+ editing :false,
+
+ initEvents : function()
{
- if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
- return;
- }
- if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
- if(!keepExisting || this.singleSelect){
- this.clearSelections();
- }
-
- var r = this.grid.store.getAt(index);
- //console.log('selectRow - record id :' + r.id);
+
+ Roo.bootstrap.TextArea.prototype.initEvents.call(this);
+ this.markdownEl = this.el.createChild({
+ cls : 'roo-markdown-area'
+ });
+ this.inputEl().addClass('d-none');
+ if (this.getValue() == '') {
+ this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
- this.selections.add(r);
- this.last = this.lastActive = index;
- if(!preventViewNotify){
- var proxy = new Roo.Element(
- this.grid.getRowDom(index)
- );
- proxy.addClass('bg-info info');
- }
- this.fireEvent("rowselect", this, index, r);
- this.fireEvent("selectionchange", this);
+ } else {
+ this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
}
+ this.markdownEl.on('click', this.toggleTextEdit, this);
+ this.on('blur', this.toggleTextEdit, this);
+ this.on('specialkey', this.resizeTextArea, this);
},
-
- /**
- * Deselects a row.
- * @param {Number} row The index of the row to deselect
- */
- deselectRow : function(index, preventViewNotify)
+
+ toggleTextEdit : function()
{
- if(this.locked) {
+ var sh = this.markdownEl.getHeight();
+ this.inputEl().addClass('d-none');
+ this.markdownEl.addClass('d-none');
+ if (!this.editing) {
+ // show editor?
+ this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
+ this.inputEl().removeClass('d-none');
+ this.inputEl().focus();
+ this.editing = true;
return;
}
- if(this.last == index){
- this.last = false;
- }
- if(this.lastActive == index){
- this.lastActive = false;
- }
-
- var r = this.grid.store.getAt(index);
- if (!r) {
+ // show showdown...
+ this.updateMarkdown();
+ this.markdownEl.removeClass('d-none');
+ this.editing = false;
+ return;
+ },
+ updateMarkdown : function()
+ {
+ if (this.getValue() == '') {
+ this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
return;
}
+
+ this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
+ },
+
+ resizeTextArea: function () {
- this.selections.remove(r);
- //.console.log('deselectRow - record id :' + r.id);
- if(!preventViewNotify){
-
- var proxy = new Roo.Element(
- this.grid.getRowDom(index)
- );
- proxy.removeClass('bg-info info');
- }
- this.fireEvent("rowdeselect", this, index);
- this.fireEvent("selectionchange", this);
+ var sh = 100;
+ Roo.log([sh, this.getValue().split("\n").length * 30]);
+ this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
},
-
- // private
- restoreLast : function(){
- if(this._last){
- this.last = this._last;
+ setValue : function(val)
+ {
+ Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
+ if (!this.editing) {
+ this.updateMarkdown();
}
+
},
-
- // private
- acceptsNav : function(row, col, cm){
- return !cm.isHidden(col) && cm.isCellEditable(col, row);
- },
-
- // private
- onEditorKey : function(field, e){
- var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
- if(k == e.TAB){
- e.stopEvent();
- ed.completeEdit();
- if(e.shiftKey){
- newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
- }else{
- newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
- }
- }else if(k == e.ENTER && !e.ctrlKey){
- e.stopEvent();
- ed.completeEdit();
- if(e.shiftKey){
- newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
- }else{
- newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
- }
- }else if(k == e.ESC){
- ed.cancelEdit();
- }
- if(newCell){
- g.startEditing(newCell[0], newCell[1]);
+ focus : function()
+ {
+ if (!this.editing) {
+ this.toggleTextEdit();
}
+
}
-});
-/*
+
+
+});/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.