}
return cfg;
+ },
+
+ titleEl : function()
+ {
+ if(!this.el || !this.panel.length || !this.header.length){
+ return;
+ }
+
+ return this.el.select('.panel-title',true).first();
+ },
+
+ setTitle : function(v)
+ {
+ var titleEl = this.titleEl();
+
+ if(!titleEl){
+ return;
+ }
+
+ titleEl.dom.innerHTML = v;
+ },
+
+ getTitle : function()
+ {
+
+ var titleEl = this.titleEl();
+
+ if(!titleEl){
+ return '';
+ }
+
+ return titleEl.dom.innerHTML;
}
});
* @cfg {String} href a tag href
* @cfg {String} target (_self|_blank|_parent|_top) target for a href.
* @cfg {String} html the content of the link.
+ * @cfg {Boolean} preventDefault (true | false) default false
*
* @constructor
href: false,
target: false,
+ preventDefault: false,
getAutoCreate : function(){
onClick : function(e)
{
+ if(this.preventDefault){
+ e.preventDefault();
+ }
//Roo.log('img onclick');
this.fireEvent('click', this, e);
}
};
if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
- if (typeof(this.parent().setActiveItem) !== 'undefined') {
- this.parent().setActiveItem(this);
- }
+ if (typeof(this.parent().setActiveItem) !== 'undefined') {
+ this.parent().setActiveItem(this);
+ }
});
/*
- * - LGPL
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
*
- * table
- *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
*/
+
/**
- * @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 {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 {Roo.bootstrap.PagingToolbar} footer a paging toolbar
+ * @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 Table
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Table = function(config){
- Roo.bootstrap.Table.superclass.constructor.call(this, config);
-
- if (this.sm) {
- 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);
- }
-};
+ * @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.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,
- bordered: false,
- hover: false,
- condensed : false,
- responsive : false,
- sm : false,
- cm : false,
- store : false,
- loadMask : false,
-
- getAutoCreate : function(){
- var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag: 'table',
- cls : 'table',
- cn : []
- }
-
- 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...
-
- if (this.align) {
- cfg.align=this.align;
- }
- if (this.bgcolor) {
- cfg.bgcolor=this.bgcolor;
- }
- if (this.border) {
- cfg.border=this.border;
- }
- if (this.cellpadding) {
- cfg.cellpadding=this.cellpadding;
- }
- if (this.cellspacing) {
- cfg.cellspacing=this.cellspacing;
- }
- if (this.frame) {
- cfg.frame=this.frame;
- }
- if (this.rules) {
- cfg.rules=this.rules;
- }
- if (this.sortable) {
- cfg.sortable=this.sortable;
- }
- if (this.summary) {
- cfg.summary=this.summary;
+ // 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++){
+ var c = config[i];
+ if(typeof c.dataIndex == "undefined"){
+ c.dataIndex = i;
}
- if (this.width) {
- cfg.width=this.width;
+ if(typeof c.renderer == "string"){
+ c.renderer = Roo.util.Format[c.renderer];
}
-
- if(this.store || this.cm){
- cfg.cn.push(this.renderHeader());
- cfg.cn.push(this.renderBody());
- cfg.cn.push(this.renderFooter());
-
- cfg.cls+= ' TableGrid';
+ if(typeof c.id == "undefined"){
+ c.id = Roo.id();
}
-
- return cfg;
- },
-//
-// initTableGrid : function()
-// {
-// var cfg = {};
-//
-// var header = {
-// tag: 'thead',
-// cn : []
-// };
-//
-// var cm = this.cm;
-//
-// for(var i = 0, len = cm.getColumnCount(); i < len; i++){
-// header.cn.push({
-// tag: 'th',
-// html: cm.getColumnHeader(i)
-// })
-// }
-//
-// cfg.push(header);
-//
-// return cfg;
-//
-//
-// },
-
- initEvents : function()
- {
- if(!this.store || !this.cm){
- return;
+ if(c.editor && c.editor.xtype){
+ c.editor = Roo.factory(c.editor, Roo.grid);
}
-
- Roo.log('initEvents with ds!!!!');
-
- var _this = this;
-
- Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
- e.on('click', _this.sort, _this);
- });
-// this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
-// this.maskEl.enableDisplayMode("block");
-// this.maskEl.show();
-
- this.parent().el.setStyle('position', 'relative');
- if (this.footer) {
- this.footer.parentId = this.id;
- this.footer.onRender(this.el.select('tfoot tr td'), null);
+ if(c.editor && c.editor.isFormField){
+ c.editor = new Roo.grid.GridEditor(c.editor);
}
-
-
- // mask should be using Roo.bootstrap.Mask...
-
- var mark = {
- tag: "div",
- cls:"x-dlg-mask",
- style: "text-align:center",
- cn: [
- {
- tag: "div",
- style: "background-color:white;width:50%;margin:100 auto",
- cn: [
- {
- tag: "img",
- src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
- },
- {
- tag: "span",
- html: "Loading"
- }
-
- ]
- }
- ]
- }
- this.maskEl = Roo.DomHelper.append(this.parent().el, mark, true);
-
- var size = this.parent().el.getSize();
-
- this.maskEl.setSize(size.width, 300); // we will fix the height at the beginning...
-
- this.maskEl.enableDisplayMode("block");
-
- this.store.on('load', this.onLoad, this);
- this.store.on('beforeload', this.onBeforeLoad, this);
-
- this.store.load();
-
-
-
+ this.lookup[c.id] = c;
+ }
+
+ /**
+ * The width of columns which have no width specified (defaults to 100)
+ * @type Number
+ */
+ this.defaultWidth = 100;
+
+ /**
+ * Default sortable of columns which have no sortable specified (defaults to false)
+ * @type Boolean
+ */
+ 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} 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 uses the raw data value.
+ */
+ /**
+ * @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.
+ */
+
+ /**
+ * 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;
+ },
+
+ /**
+ * Returns the column for a specified id.
+ * @param {String} id The column id
+ * @return {Object} the column
+ */
+ getColumnById : function(id){
+ return this.lookup[id];
},
+
- sort : function(e,el)
- {
- var col = Roo.get(el)
-
- if(!col.hasClass('sortable')){
- return;
- }
-
- var sort = col.attr('sort');
- var dir = 'ASC';
-
- if(col.hasClass('glyphicon-arrow-up')){
- dir = 'DESC';
- }
-
- this.store.sortInfo = {field : sort, direction : dir};
-
- this.store.load();
+ /**
+ * Returns the column 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;
},
- renderHeader : function()
- {
- var header = {
- tag: 'thead',
- cn : []
- };
-
- var cm = this.cm;
-
- for(var i = 0, len = cm.getColumnCount(); i < len; i++){
-
- var config = cm.config[i];
-
- if(typeof(config.hidden) != 'undefined' && config.hidden){
- continue;
- }
-
- var c = {
- tag: 'th',
- html: cm.getColumnHeader(i)
- };
-
- if(typeof(config.dataIndex) != 'undefined'){
- c.sort = config.dataIndex;
- }
-
- if(typeof(config.sortable) != 'undefined' && config.sortable){
- c.cls = 'sortable';
- }
-
- if(typeof(config.width) != 'undefined'){
- c.style = 'width:' + config.width + 'px';
+ /**
+ * 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;
}
-
- header.cn.push(c)
}
-
- return header;
+ return -1;
},
- renderBody : function()
- {
- var body = {
- tag: 'tbody',
- cn : []
- };
-
- return body;
+ /**
+ * 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;
},
- renderFooter : function()
- {
- var footer = {
- tag: 'tfoot',
- cn : [
- {
- tag: 'tr',
- cn : [
- {
- tag : 'td',
- colspan : 1
- }
- ]
- }
- ]
- };
-
- return footer;
- },
- onLoad : function()
- {
- Roo.log('ds onload');
-
- var _this = this;
- var cm = this.cm;
-
- Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
- e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
-
- if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
- e.addClass(['glyphicon', 'glyphicon-arrow-up']);
+ 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;
+ },
+
+ setLocked : function(colIndex, value, suppressEvent){
+ if(this.isLocked(colIndex) == value){
+ return;
+ }
+ this.config[colIndex].locked = value;
+ if(!suppressEvent){
+ this.fireEvent("columnlockchange", this, colIndex, value);
+ }
+ },
+
+ 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);
}
-
- if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
- e.addClass(['glyphicon', 'glyphicon-arrow-down']);
+ }
+ return totalWidth;
+ },
+
+ getLockedCount : function(){
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(!this.isLocked(i)){
+ return i;
}
- });
-
- var tbody = this.el.select('tbody', true).first();
-
- var renders = [];
-
- if(this.store.getCount() > 0){
- this.store.data.each(function(d){
- var row = {
- tag : 'tr',
- cn : []
- };
-
- for(var i = 0, len = cm.getColumnCount(); i < len; i++){
- var config = cm.config[i];
-
- if(typeof(config.hidden) != 'undefined' && config.hidden){
- continue;
- }
-
- var renderer = cm.getRenderer(i);
- var value = '';
- var id = Roo.id();
-
- if(typeof(renderer) !== 'undefined'){
- value = renderer(d.data[cm.getDataIndex(i)], false, d);
- }
-
- if(typeof(value) === 'object'){
- renders.push({
- id : id,
- cfg : value
- })
- }
-
- var td = {
- tag: 'td',
- id: id,
- html: (typeof(value) === 'object') ? '' : value
- };
-
- if(typeof(config.width) != 'undefined'){
- td.style = 'width:' + config.width + 'px';
- }
-
- row.cn.push(td);
-
+ }
+ },
+
+ /**
+ * 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++;
}
-
- tbody.createChild(row);
-
- });
+ }
+ return c;
}
-
-
- if(renders.length){
- var _this = this;
- Roo.each(renders, function(r){
- _this.renderColumn(r);
- })
+ 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;
+ }
}
+ return r;
+ },
- if(this.loadMask){
- this.maskEl.hide();
+ /**
+ * 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;
}
+ return this.config[col].sortable;
},
-
- onBeforeLoad : function()
- {
- Roo.log('ds onBeforeLoad');
-
- this.clear();
-
- if(this.loadMask){
- this.maskEl.show();
+
+ /**
+ * 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;
}
+ return this.config[col].renderer;
},
-
- clear : function()
- {
- this.el.select('tbody', true).first().dom.innerHTML = '';
+
+ /**
+ * 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;
},
-
- getSelectionModel : function(){
- if(!this.selModel){
- this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
+
+ /**
+ * Returns the width for the specified column.
+ * @param {Number} col The column index
+ * @return {Number}
+ */
+ getColumnWidth : function(col){
+ return this.config[col].width * 1 || this.defaultWidth;
+ },
+
+ /**
+ * 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);
}
- return this.selModel;
},
-
- renderColumn : function(r)
- {
- var _this = this;
- r.cfg.render(Roo.get(r.id));
-
- if(r.cfg.cn){
- Roo.each(r.cfg.cn, function(c){
- var child = {
- id: r.id,
- cfg: c
+
+ /**
+ * 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);
}
- _this.renderColumn(child);
- })
+ }
}
- }
-
-});
-
-
+ return this.totalWidth;
+ },
- /*
- * - LGPL
- *
- * table cell
- *
- */
+ /**
+ * Returns the header for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnHeader : function(col){
+ return this.config[col].header;
+ },
-/**
- * @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
- */
+ /**
+ * 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);
+ },
-Roo.bootstrap.TableCell = function(config){
- Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
-};
+ /**
+ * 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;
+ },
-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.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;
+ /**
+ * Returns true if the cell is editable.
+ * @param {Number} colIndex The column index
+ * @param {Number} rowIndex The row index
+ * @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;
}
-
});
-
+Roo.grid.ColumnModel.defaultRenderer = function(value){
+ if(typeof value == "string" && value.length < 1){
+ return " ";
+ }
+ return value;
+};
- /*
- * - LGPL
+// 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.
*
- * table row
- *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
*/
-
+
/**
- * @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
- *
+ * @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 TableRow
+ * 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.bootstrap.TableRow = function(config){
- Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
+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.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
-
- cls: false,
- align: false,
- bgcolor: false,
- charoff: false,
- valign: false,
+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;
+ },
+
+ /**
+ * Enables the mask so that it can be displayed
+ */
+ enable : function(){
+ this.disabled = false;
+ },
- getAutoCreate : function(){
- var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag: 'tr'
- }
+ 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) {
- 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;
+ */
+
+
+
+ this.el.unmask(this.removeMask);
+ },
+ // private
+ onLoad : function()
+ {
+ this.el.unmask(this.removeMask);
+ },
+
+ // private
+ onBeforeLoad : function(){
+ if(!this.disabled){
+ this.el.mask(this.msg, this.msgCls);
}
- if(this.valign){
- cfg.valign = this.valign;
+ },
+
+ // 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);
}
-
- return cfg;
}
-
-});
-
-
-
- /*
+};/*
* - LGPL
*
- * table body
+ * table
*
*/
/**
- * @class Roo.bootstrap.TableBody
+ * @class Roo.bootstrap.Table
* @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 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} tfoot (true|false) generate tfoot, default true
+ * @cfg {Boolean} thead (true|false) generate thead, default true
+ * @cfg {Boolean} RowSelection (true|false) default false
+ * @cfg {Boolean} CellSelection (true|false) default false
+ *
+ * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
+
*
* @constructor
- * Create a new TableBody
+ * Create a new Table
* @param {Object} config The config object
*/
-Roo.bootstrap.TableBody = function(config){
- Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+Roo.bootstrap.Table = function(config){
+ Roo.bootstrap.Table.superclass.constructor.call(this, config);
+
+ if (this.sm) {
+ 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 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
+
+ });
};
-Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
cls: false,
- tag: false,
align: false,
- charoff: false,
- valign: false,
+ bgcolor: false,
+ border: false,
+ cellpadding: false,
+ cellspacing: false,
+ frame: false,
+ rules: false,
+ sortable: false,
+ summary: false,
+ width: false,
+ striped : false,
+ bordered: false,
+ hover: false,
+ condensed : false,
+ responsive : false,
+ sm : false,
+ cm : false,
+ store : false,
+ loadMask : false,
+ tfoot : true,
+ thead : true,
+ RowSelection : false,
+ CellSelection : false,
+ layout : false,
+
getAutoCreate : function(){
- var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
-
+ var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
+
cfg = {
- tag: 'tbody'
+ tag: 'table',
+ cls : 'table',
+ cn : []
}
- if (this.cls) {
- cfg.cls=this.cls
+ if (this.striped) {
+ cfg.cls += ' table-striped';
}
- if(this.tag){
- cfg.tag = this.tag;
+
+ if (this.hover) {
+ cfg.cls += ' table-hover';
}
-
- if(this.align){
- cfg.align = this.align;
+ if (this.bordered) {
+ cfg.cls += ' table-bordered';
}
- if(this.charoff){
- cfg.charoff = this.charoff;
+ if (this.condensed) {
+ cfg.cls += ' table-condensed';
}
- if(this.valign){
- cfg.valign = this.valign;
+ if (this.responsive) {
+ cfg.cls += ' table-responsive';
}
- return cfg;
- }
-
-
-// initEvents : function()
-// {
-//
-// if(!this.store){
-// return;
-// }
-//
-// this.store = Roo.factory(this.store, Roo.data);
-// this.store.on('load', this.onLoad, this);
-//
-// this.store.load();
-//
-// },
-//
-// onLoad: function ()
-// {
-// this.fireEvent('load', this);
-// }
-//
-//
-});
-
-
-
- /*
- * 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">
- */
-
-// as we use this in bootstrap.
-Roo.namespace('Roo.form');
- /**
- * @class Roo.form.Action
- * Internal Class used to handle form actions
- * @constructor
- * @param {Roo.form.BasicForm} el The form element or its id
- * @param {Object} config Configuration options
- */
-
-
-
-// define the action interface
-Roo.form.Action = function(form, options){
- this.form = form;
- this.options = options || {};
-};
-/**
- * Client Validation Failed
- * @const
- */
-Roo.form.Action.CLIENT_INVALID = 'client';
-/**
- * Server Validation Failed
- * @const
- */
-Roo.form.Action.SERVER_INVALID = 'server';
- /**
- * Connect to Server Failed
- * @const
- */
-Roo.form.Action.CONNECT_FAILURE = 'connect';
-/**
- * Reading Data from Server Failed
- * @const
- */
-Roo.form.Action.LOAD_FAILURE = 'load';
-
-Roo.form.Action.prototype = {
- type : 'default',
- failureType : undefined,
- response : undefined,
- result : undefined,
-
- // interface method
- run : function(options){
-
- },
-
- // interface method
- success : function(response){
-
- },
-
- // interface method
- handleResponse : function(response){
-
- },
-
- // default connection failure
- failure : function(response){
+ if (this.cls) {
+ cfg.cls+= ' ' +this.cls;
+ }
- this.response = response;
- this.failureType = Roo.form.Action.CONNECT_FAILURE;
- this.form.afterAction(this, false);
- },
-
- processResponse : function(response){
- this.response = response;
- if(!response.responseText){
- return true;
+ // this lot should be simplifed...
+
+ if (this.align) {
+ cfg.align=this.align;
}
- this.result = this.handleResponse(response);
- return this.result;
- },
-
- // utility functions used internally
- getUrl : function(appendParams){
- var url = this.options.url || this.form.url || this.form.el.dom.action;
- if(appendParams){
- var p = this.getParams();
- if(p){
- url += (url.indexOf('?') != -1 ? '&' : '?') + p;
+ if (this.bgcolor) {
+ cfg.bgcolor=this.bgcolor;
+ }
+ if (this.border) {
+ cfg.border=this.border;
+ }
+ if (this.cellpadding) {
+ cfg.cellpadding=this.cellpadding;
+ }
+ if (this.cellspacing) {
+ cfg.cellspacing=this.cellspacing;
+ }
+ if (this.frame) {
+ cfg.frame=this.frame;
+ }
+ if (this.rules) {
+ cfg.rules=this.rules;
+ }
+ if (this.sortable) {
+ cfg.sortable=this.sortable;
+ }
+ if (this.summary) {
+ cfg.summary=this.summary;
+ }
+ if (this.width) {
+ cfg.width=this.width;
+ }
+ 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.thead){
+ cfg.cn.push(this.renderHeader());
+ }
+
+ cfg.cn.push(this.renderBody());
+
+ if(this.tfoot){
+ cfg.cn.push(this.renderFooter());
}
+
+ cfg.cls+= ' TableGrid';
}
- return url;
+
+ return { cn : [ cfg ] };
},
-
- getMethod : function(){
- return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
+
+ initEvents : function()
+ {
+ if(!this.store || !this.cm){
+ return;
+ }
+
+ Roo.log('initEvents with ds!!!!');
+
+ var _this = this;
+
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.on('click', _this.sort, _this);
+ });
+
+ this.el.on("click", this.onClick, this);
+ this.el.on("dblclick", this.onDblClick, this);
+
+ 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);
+ }
+
+ 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);
+
},
-
- getParams : function(){
- var bp = this.form.baseParams;
- var p = this.options.params;
- if(p){
- if(typeof p == "object"){
- p = Roo.urlEncode(Roo.applyIf(p, bp));
- }else if(typeof p == 'string' && bp){
- p += '&' + Roo.urlEncode(bp);
- }
- }else if(bp){
- p = Roo.urlEncode(bp);
+
+ onClick : function(e, el)
+ {
+ var cell = Roo.get(el);
+ var row = cell.findParent('tr', false, true);
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = row.dom.rowIndex;
+
+ if(this.CellSelection){
+ this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
}
- return p;
+
+ if(this.RowSelection){
+ this.fireEvent('rowclick', this, row, rowIndex, e);
+ }
+
+
},
-
- createCallback : function(){
- return {
- success: this.success,
- failure: this.failure,
- scope: this,
- timeout: (this.form.timeout*1000),
- upload: this.form.fileUpload ? this.success : undefined
- };
- }
-};
-
-Roo.form.Action.Submit = function(form, options){
- Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
-};
-
-Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
- type : 'submit',
-
- haveProgress : false,
- uploadComplete : false,
- // uploadProgress indicator.
- uploadProgress : function()
+ onDblClick : function(e,el)
{
- if (!this.form.progressUrl) {
+ var cell = Roo.get(el);;
+ var row = cell.findParent('tr', false, true);
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = row.dom.rowIndex;
+
+ if(this.CellSelection){
+ this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
+ }
+
+ if(this.RowSelection){
+ this.fireEvent('rowdblclick', this, row, rowIndex, e);
+ }
+ },
+
+ sort : function(e,el)
+ {
+ var col = Roo.get(el)
+
+ if(!col.hasClass('sortable')){
return;
}
- if (!this.haveProgress) {
- Roo.MessageBox.progress("Uploading", "Uploading");
+ var sort = col.attr('sort');
+ var dir = 'ASC';
+
+ if(col.hasClass('glyphicon-arrow-up')){
+ dir = 'DESC';
}
- if (this.uploadComplete) {
- Roo.MessageBox.hide();
- return;
+
+ 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 } });
}
+ },
+
+ renderHeader : function()
+ {
+ var header = {
+ tag: 'thead',
+ cn : []
+ };
- this.haveProgress = true;
-
- var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
+ var cm = this.cm;
- var c = new Roo.data.Connection();
- c.request({
- url : this.form.progressUrl,
- params: {
- id : uid
- },
- method: 'GET',
- success : function(req){
- //console.log(data);
- var rdata = false;
- var edata;
- try {
- rdata = Roo.decode(req.responseText)
- } catch (e) {
- Roo.log("Invalid data from server..");
- Roo.log(edata);
- return;
- }
- if (!rdata || !rdata.success) {
- Roo.log(rdata);
- Roo.MessageBox.alert(Roo.encode(rdata));
- return;
- }
- var data = rdata.data;
-
- if (this.uploadComplete) {
- Roo.MessageBox.hide();
- return;
- }
-
- if (data){
- Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
- Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
- );
- }
- this.uploadProgress.defer(2000,this);
- },
-
- failure: function(data) {
- Roo.log('progress url failed ');
- Roo.log(data);
- },
- scope : this
- });
-
- },
-
-
- run : function()
- {
- // run get Values on the form, so it syncs any secondary forms.
- this.form.getValues();
-
- var o = this.options;
- var method = this.getMethod();
- var isPost = method == 'POST';
- if(o.clientValidation === false || this.form.isValid()){
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
- if (this.form.progressUrl) {
- this.form.findField('UPLOAD_IDENTIFIER').setValue(
- (new Date() * 1) + '' + Math.random());
+ var config = cm.config[i];
+
+ if(typeof(config.hidden) != 'undefined' && config.hidden){
+ continue;
+ }
- }
+ var c = {
+ tag: 'th',
+ style : '',
+ html: cm.getColumnHeader(i)
+ };
+ if(typeof(config.dataIndex) != 'undefined'){
+ c.sort = config.dataIndex;
+ }
- Roo.Ajax.request(Roo.apply(this.createCallback(), {
- form:this.form.el.dom,
- url:this.getUrl(!isPost),
- method: method,
- params:isPost ? this.getParams() : null,
- isUpload: this.form.fileUpload
- }));
+ if(typeof(config.sortable) != 'undefined' && config.sortable){
+ c.cls = 'sortable';
+ }
- this.uploadProgress();
-
- }else if (o.clientValidation !== false){ // client validation failed
- this.failureType = Roo.form.Action.CLIENT_INVALID;
- this.form.afterAction(this, false);
+// if(typeof(config.align) != 'undefined' && config.align.length){
+// c.style += ' text-align:' + config.align + ';';
+// }
+
+ if(typeof(config.width) != 'undefined'){
+ c.style += ' width:' + config.width + 'px;';
+ }
+
+ header.cn.push(c)
}
+
+ return header;
},
-
- success : function(response)
+
+ renderBody : function()
{
- this.uploadComplete= true;
- if (this.haveProgress) {
- Roo.MessageBox.hide();
- }
-
+ var body = {
+ tag: 'tbody',
+ cn : [
+ {
+ tag: 'tr',
+ cn : [
+ {
+ tag : 'td',
+ colspan : this.cm.getColumnCount()
+ }
+ ]
+ }
+ ]
+ };
- var result = this.processResponse(response);
- if(result === true || result.success){
- this.form.afterAction(this, true);
- return;
- }
- if(result.errors){
- this.form.markInvalid(result.errors);
- this.failureType = Roo.form.Action.SERVER_INVALID;
- }
- this.form.afterAction(this, false);
+ return body;
},
- failure : function(response)
+
+ renderFooter : function()
{
- this.uploadComplete= true;
- if (this.haveProgress) {
- Roo.MessageBox.hide();
- }
+ var footer = {
+ tag: 'tfoot',
+ cn : [
+ {
+ tag: 'tr',
+ cn : [
+ {
+ tag : 'td',
+ colspan : this.cm.getColumnCount()
+ }
+ ]
+ }
+ ]
+ };
- this.response = response;
- this.failureType = Roo.form.Action.CONNECT_FAILURE;
- this.form.afterAction(this, false);
+ return footer;
},
- handleResponse : function(response){
- if(this.form.errorReader){
- var rs = this.form.errorReader.read(response);
- var errors = [];
- if(rs.records){
- for(var i = 0, len = rs.records.length; i < len; i++) {
- var r = rs.records[i];
- errors[i] = r.data;
- }
+ onLoad : function()
+ {
+ Roo.log('ds onload');
+ this.clear();
+
+ var _this = this;
+ var cm = this.cm;
+
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
+
+ if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
+ e.addClass(['glyphicon', 'glyphicon-arrow-up']);
}
- if(errors.length < 1){
- errors = null;
+
+ if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
+ e.addClass(['glyphicon', 'glyphicon-arrow-down']);
}
- return {
- success : rs.success,
- errors : errors
- };
- }
- var ret = false;
- try {
- ret = Roo.decode(response.responseText);
- } catch (e) {
- ret = {
- success: false,
- errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
- errors : []
- };
- }
- return ret;
+ });
- }
-});
-
-
-Roo.form.Action.Load = function(form, options){
- Roo.form.Action.Load.superclass.constructor.call(this, form, options);
- this.reader = this.form.reader;
-};
-
-Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
- type : 'load',
-
- run : function(){
+ var tbody = this.el.select('tbody', true).first();
- Roo.Ajax.request(Roo.apply(
- this.createCallback(), {
- method:this.getMethod(),
- url:this.getUrl(false),
- params:this.getParams()
- }));
- },
+ var renders = [];
+
+ if(this.store.getCount() > 0){
+ this.store.data.each(function(d,rowIndex){
+ var row = {
+ tag : 'tr',
+ cn : []
+ };
+
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+ var config = cm.config[i];
+
+ if(typeof(config.hidden) != 'undefined' && config.hidden){
+ continue;
+ }
+
+ var renderer = cm.getRenderer(i);
+ var value = '';
+ var id = Roo.id();
+
+ if(typeof(renderer) !== 'undefined'){
+ value = renderer(d.data[cm.getDataIndex(i)], false, d);
+ }
+
+ if(typeof(value) === 'object'){
+ renders.push({
+ id : id,
+ cfg : value
+ })
+ }
+
+ var rowcfg = {
+ record: d,
+ rowIndex : rowIndex,
+ colIndex : i,
+ rowClass : ''
+ }
- success : function(response){
-
- var result = this.processResponse(response);
- if(result === true || !result.success || !result.data){
- this.failureType = Roo.form.Action.LOAD_FAILURE;
- this.form.afterAction(this, false);
- return;
- }
- this.form.clearInvalid();
- this.form.setValues(result.data);
- this.form.afterAction(this, true);
- },
+ _this.fireEvent('rowclass', this, rowcfg);
+
+ var td = {
+ tag: 'td',
+ id: id,
+ cls : rowcfg.rowClass,
+ style: '',
+ html: (typeof(value) === 'object') ? '' : value
+ };
+
+ if(typeof(config.align) != 'undefined' && config.align.length){
+ td.style += ' text-align:' + config.align + ';';
+ }
+
+ if(typeof(config.width) != 'undefined'){
+ td.style += ' width:' + config.width + 'px;';
+ }
+
+
+ row.cn.push(td);
+
+ }
+
+ tbody.createChild(row);
+
+ });
+ }
+
+
+ if(renders.length){
+ var _this = this;
+ Roo.each(renders, function(r){
+ _this.renderColumn(r);
+ })
+ }
- handleResponse : function(response){
- if(this.form.reader){
- var rs = this.form.reader.read(response);
- var data = rs.records && rs.records[0] ? rs.records[0].data : null;
- return {
- success : rs.success,
- data : data
- };
+ //if(this.loadMask){
+ // this.maskEl.hide();
+ //}
+ },
+
+ onBeforeLoad : function()
+ {
+ //Roo.log('ds onBeforeLoad');
+
+ //this.clear();
+
+ //if(this.loadMask){
+ // this.maskEl.show();
+ //}
+ },
+
+ clear : function()
+ {
+ this.el.select('tbody', true).first().dom.innerHTML = '';
+ },
+
+ getSelectionModel : function(){
+ if(!this.selModel){
+ this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
+ }
+ return this.selModel;
+ },
+
+ renderColumn : function(r)
+ {
+ var _this = this;
+ r.cfg.render(Roo.get(r.id));
+
+ if(r.cfg.cn){
+ Roo.each(r.cfg.cn, function(c){
+ var child = {
+ id: r.id,
+ cfg: c
+ }
+ _this.renderColumn(child);
+ })
}
- return Roo.decode(response.responseText);
}
+
});
-Roo.form.Action.ACTION_TYPES = {
- 'load' : Roo.form.Action.Load,
- 'submit' : Roo.form.Action.Submit
-};/*
+
+
+ /*
* - LGPL
*
- * form
+ * table cell
*
*/
/**
- * @class Roo.bootstrap.Form
+ * @class Roo.bootstrap.TableCell
* @extends Roo.bootstrap.Component
- * Bootstrap Form class
- * @cfg {String} method GET | POST (default POST)
- * @cfg {String} labelAlign top | left (default top)
- * @cfg {String} align left | right - for navbars
-
+ * 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 Form
+ * Create a new TableCell
* @param {Object} config The config object
*/
-
-Roo.bootstrap.Form = function(config){
- Roo.bootstrap.Form.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event clientvalidation
- * If the monitorValid config option is true, this event fires repetitively to notify of valid state
- * @param {Form} this
- * @param {Boolean} valid true if the form has passed client-side validation
- */
- clientvalidation: true,
- /**
- * @event beforeaction
- * Fires before any action is performed. Return false to cancel the action.
- * @param {Form} this
- * @param {Action} action The action to be performed
- */
- beforeaction: true,
- /**
- * @event actionfailed
- * Fires when an action fails.
- * @param {Form} this
- * @param {Action} action The action that failed
- */
- actionfailed : true,
- /**
- * @event actioncomplete
- * Fires when an action is completed.
- * @param {Form} this
- * @param {Action} action The action that completed
- */
- actioncomplete : true
- });
-
+Roo.bootstrap.TableCell = function(config){
+ Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
-
- /**
- * @cfg {String} method
- * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
- */
- method : 'POST',
- /**
- * @cfg {String} url
- * The URL to use for form actions if one isn't supplied in the action options.
- */
- /**
- * @cfg {Boolean} fileUpload
- * Set to true if this form is a file upload.
- */
-
- /**
- * @cfg {Object} baseParams
- * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
- */
-
- /**
- * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
- */
- timeout: 30,
- /**
- * @cfg {Sting} align (left|right) for navbar forms
- */
- align : 'left',
-
- // private
- activeAction : null,
-
- /**
- * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
- * element by passing it or its id or mask the form itself by passing in true.
- * @type Mixed
- */
- waitMsgTarget : false,
+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,
- /**
- * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
- * element by passing it or its id or mask the form itself by passing in true.
- * @type Mixed
- */
getAutoCreate : function(){
+ var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
+
+ cfg = {
+ tag: 'td'
+ }
- var cfg = {
- tag: 'form',
- method : this.method || 'POST',
- id : this.id || Roo.id(),
- cls : ''
- }
- if (this.parent().xtype.match(/^Nav/)) {
- cfg.cls = 'navbar-form navbar-' + this.align;
-
+ if(this.tag){
+ cfg.tag = this.tag;
}
- if (this.labelAlign == 'left' ) {
- cfg.cls += ' form-horizontal';
+ if (this.html) {
+ cfg.html=this.html
}
-
-
- return cfg;
- },
- initEvents : function()
- {
- this.el.on('submit', this.onSubmit, this);
-
-
- },
- // private
- onSubmit : function(e){
- e.stopEvent();
- },
-
- /**
- * Returns true if client-side validation on the form is successful.
- * @return Boolean
- */
- isValid : function(){
- var items = this.getItems();
- var valid = true;
- items.each(function(f){
- if(!f.validate()){
- valid = false;
-
- }
- });
- return valid;
- },
- /**
- * Returns true if any fields in this form have changed since their original load.
- * @return Boolean
- */
- isDirty : function(){
- var dirty = false;
- var items = this.getItems();
- items.each(function(f){
- if(f.isDirty()){
- dirty = true;
- return false;
- }
- return true;
- });
- return dirty;
- },
- /**
- * Performs a predefined action (submit or load) or custom actions you define on this form.
- * @param {String} actionName The name of the action type
- * @param {Object} options (optional) The options to pass to the action. All of the config options listed
- * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
- * accept other config options):
- * <pre>
-Property Type Description
----------------- --------------- ----------------------------------------------------------------------------------
-url String The url for the action (defaults to the form's url)
-method String The form method to use (defaults to the form's method, or POST if not defined)
-params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
-clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
- validate the form on the client (defaults to false)
- * </pre>
- * @return {BasicForm} this
- */
- doAction : function(action, options){
- if(typeof action == 'string'){
- action = new Roo.form.Action.ACTION_TYPES[action](this, options);
+ if (this.cls) {
+ cfg.cls=this.cls
}
- if(this.fireEvent('beforeaction', this, action) !== false){
- this.beforeAction(action);
- action.run.defer(100, action);
+ if (this.abbr) {
+ cfg.abbr=this.abbr
}
- return this;
- },
-
- // private
- beforeAction : function(action){
- var o = action.options;
-
- // not really supported yet.. ??
-
- //if(this.waitMsgTarget === true){
- this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
- //}else if(this.waitMsgTarget){
- // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
- // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
- //}else {
- // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
- // }
-
- },
-
- // private
- afterAction : function(action, success){
- this.activeAction = null;
- var o = action.options;
-
- //if(this.waitMsgTarget === true){
- this.el.unmask();
- //}else if(this.waitMsgTarget){
- // this.waitMsgTarget.unmask();
- //}else{
- // Roo.MessageBox.updateProgress(1);
- // Roo.MessageBox.hide();
- // }
- //
- if(success){
- if(o.reset){
- this.reset();
- }
- Roo.callback(o.success, o.scope, [this, action]);
- this.fireEvent('actioncomplete', this, action);
-
- }else{
-
- // failure condition..
- // we have a scenario where updates need confirming.
- // eg. if a locking scenario exists..
- // we look for { errors : { needs_confirm : true }} in the response.
- if (
- (typeof(action.result) != 'undefined') &&
- (typeof(action.result.errors) != 'undefined') &&
- (typeof(action.result.errors.needs_confirm) != 'undefined')
- ){
- var _t = this;
- Roo.log("not supported yet");
- /*
-
- Roo.MessageBox.confirm(
- "Change requires confirmation",
- action.result.errorMsg,
- function(r) {
- if (r != 'yes') {
- return;
- }
- _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
- }
-
- );
- */
-
-
- return;
- }
-
- Roo.callback(o.failure, o.scope, [this, action]);
- // show an error message if no failed handler is set..
- if (!this.hasListener('actionfailed')) {
- Roo.log("need to add dialog support");
- /*
- Roo.MessageBox.alert("Error",
- (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
- action.result.errorMsg :
- "Saving Failed, please check your entries or try again"
- );
- */
- }
-
- this.fireEvent('actionfailed', this, action);
+ if (this.align) {
+ cfg.align=this.align
}
-
- },
- /**
- * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
- * @param {String} id The value to search for
- * @return Field
- */
- findField : function(id){
- var items = this.getItems();
- var field = items.get(id);
- if(!field){
- items.each(function(f){
- if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
- field = f;
- return false;
- }
- return true;
- });
+ if (this.axis) {
+ cfg.axis=this.axis
}
- return field || null;
- },
- /**
- * Mark fields in this form invalid in bulk.
- * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
- * @return {BasicForm} this
- */
- markInvalid : function(errors){
- if(errors instanceof Array){
- for(var i = 0, len = errors.length; i < len; i++){
- var fieldError = errors[i];
- var f = this.findField(fieldError.id);
- if(f){
- f.markInvalid(fieldError.msg);
- }
- }
- }else{
- var field, id;
- for(id in errors){
- if(typeof errors[id] != 'function' && (field = this.findField(id))){
- field.markInvalid(errors[id]);
- }
- }
+ 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
}
- //Roo.each(this.childForms || [], function (f) {
- // f.markInvalid(errors);
- //});
- return this;
- },
+
+ return cfg;
+ }
+
+});
- /**
- * Set values for fields in this form in bulk.
- * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
- * @return {BasicForm} this
- */
- setValues : function(values){
- if(values instanceof Array){ // array of objects
- for(var i = 0, len = values.length; i < len; i++){
- var v = values[i];
- var f = this.findField(v.id);
- if(f){
- f.setValue(v.value);
- if(this.trackResetOnLoad){
- f.originalValue = f.getValue();
- }
- }
- }
- }else{ // object hash
- var field, id;
- for(id in values){
- if(typeof values[id] != 'function' && (field = this.findField(id))){
-
- if (field.setFromData &&
- field.valueField &&
- field.displayField &&
- // combos' with local stores can
- // be queried via setValue()
- // to set their value..
- (field.store && !field.store.isLocal)
- ) {
- // it's a combo
- var sd = { };
- sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
- sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
- field.setFromData(sd);
-
- } else {
- field.setValue(values[id]);
- }
-
-
- if(this.trackResetOnLoad){
- field.originalValue = field.getValue();
- }
- }
- }
- }
-
- //Roo.each(this.childForms || [], function (f) {
- // f.setValues(values);
- //});
-
- return this;
- },
+
- /**
- * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
- * they are returned as an array.
- * @param {Boolean} asString
- * @return {Object}
- */
- getValues : function(asString){
- //if (this.childForms) {
- // copy values from the child forms
- // Roo.each(this.childForms, function (f) {
- // this.setValues(f.getValues());
- // }, this);
- //}
-
-
-
- var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
- if(asString === true){
- return fs;
- }
- return Roo.urlDecode(fs);
- },
-
- /**
- * Returns the fields in this form as an object with key/value pairs.
- * This differs from getValues as it calls getValue on each child item, rather than using dom data.
- * @return {Object}
- */
- getFieldValues : function(with_hidden)
- {
- var items = this.getItems();
- var ret = {};
- items.each(function(f){
- if (!f.getName()) {
- return;
- }
- var v = f.getValue();
- if (f.inputType =='radio') {
- if (typeof(ret[f.getName()]) == 'undefined') {
- ret[f.getName()] = ''; // empty..
- }
-
- if (!f.el.dom.checked) {
- return;
-
- }
- v = f.el.dom.value;
-
- }
-
- // not sure if this supported any more..
- if ((typeof(v) == 'object') && f.getRawValue) {
- v = f.getRawValue() ; // dates..
- }
- // combo boxes where name != hiddenName...
- if (f.name != f.getName()) {
- ret[f.name] = f.getRawValue();
- }
- ret[f.getName()] = v;
- });
-
- return ret;
- },
+ /*
+ * - LGPL
+ *
+ * table row
+ *
+ */
- /**
- * Clears all invalid messages in this form.
- * @return {BasicForm} this
- */
- clearInvalid : function(){
- var items = this.getItems();
-
- items.each(function(f){
- f.clearInvalid();
- });
-
-
-
- return this;
- },
+/**
+ * @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
+ */
- /**
- * Resets this form.
- * @return {BasicForm} this
- */
- reset : function(){
- var items = this.getItems();
- items.each(function(f){
- f.reset();
- });
-
- Roo.each(this.childForms || [], function (f) {
- f.reset();
- });
-
-
- return this;
- },
- getItems : function()
- {
- var r=new Roo.util.MixedCollection(false, function(o){
- return o.id || (o.id = Roo.id());
- });
- var iter = function(el) {
- if (el.inputEl) {
- r.add(el);
- }
- if (!el.items) {
- return;
- }
- Roo.each(el.items,function(e) {
- iter(e);
- });
-
+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'
+ }
- };
- iter(this);
- return r;
-
-
-
-
+ 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;
}
-
+
});
-/*
- * 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.
+
+ /*
+ * - LGPL
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * table body
+ *
*/
+
/**
- * @class Roo.form.VTypes
- * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
- * @singleton
+ * @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.form.VTypes = function(){
- // closure these in so they are only created once.
- var alpha = /^[a-zA-Z_]+$/;
- var alphanum = /^[a-zA-Z0-9_]+$/;
- var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
- var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
- // All these messages and functions are configurable
- return {
- /**
- * The function used to validate email addresses
- * @param {String} value The email address
- */
- 'email' : function(v){
- return email.test(v);
- },
- /**
- * The error text to display when the email validation function returns false
- * @type String
- */
- 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
- /**
- * The keystroke filter mask to be applied on email input
- * @type RegExp
- */
- 'emailMask' : /[a-z0-9_\.\-@]/i,
+Roo.bootstrap.TableBody = function(config){
+ Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+};
- /**
- * The function used to validate URLs
- * @param {String} value The URL
- */
- 'url' : function(v){
- return url.test(v);
- },
- /**
- * The error text to display when the url validation function returns false
- * @type String
- */
- 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
+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'
+ }
+
+ if (this.cls) {
+ cfg.cls=this.cls
+ }
+ if(this.tag){
+ cfg.tag = this.tag;
+ }
+
+ if(this.align){
+ cfg.align = this.align;
+ }
+ if(this.charoff){
+ cfg.charoff = this.charoff;
+ }
+ if(this.valign){
+ cfg.valign = this.valign;
+ }
- /**
- * The function used to validate alpha values
- * @param {String} value The value
- */
- 'alpha' : function(v){
- return alpha.test(v);
- },
- /**
- * The error text to display when the alpha validation function returns false
- * @type String
- */
- 'alphaText' : 'This field should only contain letters and _',
- /**
- * The keystroke filter mask to be applied on alpha input
- * @type RegExp
- */
- 'alphaMask' : /[a-z_]/i,
+ return cfg;
+ }
+
+
+// initEvents : function()
+// {
+//
+// if(!this.store){
+// return;
+// }
+//
+// this.store = Roo.factory(this.store, Roo.data);
+// this.store.on('load', this.onLoad, this);
+//
+// this.store.load();
+//
+// },
+//
+// onLoad: function ()
+// {
+// this.fireEvent('load', this);
+// }
+//
+//
+});
- /**
- * The function used to validate alphanumeric values
- * @param {String} value The value
- */
- 'alphanum' : function(v){
- return alphanum.test(v);
- },
- /**
- * The error text to display when the alphanumeric validation function returns false
- * @type String
- */
- 'alphanumText' : 'This field should only contain letters, numbers and _',
- /**
- * The keystroke filter mask to be applied on alphanumeric input
- * @type RegExp
- */
- 'alphanumMask' : /[a-z0-9_]/i
- };
-}();/*
- * - LGPL
+
+
+ /*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
*
- * Input
- *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
*/
-/**
- * @class Roo.bootstrap.Input
- * @extends Roo.bootstrap.Component
- * Bootstrap Input class
- * @cfg {Boolean} disabled is it disabled
- * @cfg {String} fieldLabel - the label associated
- * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
- * @cfg {String} name name of the input
- * @cfg {string} fieldLabel - the label associated
- * @cfg {string} inputType - input / file submit ...
- * @cfg {string} placeholder - placeholder to put in text.
- * @cfg {string} before - input group add on before
- * @cfg {string} after - input group add on after
- * @cfg {string} size - (lg|sm) or leave empty..
- * @cfg {Number} xs colspan out of 12 for mobile-sized screens
- * @cfg {Number} sm colspan out of 12 for tablet-sized screens
- * @cfg {Number} md colspan out of 12 for computer-sized screens
- * @cfg {Number} lg colspan out of 12 for large computer-sized screens
- * @cfg {string} value default value of the input
- * @cfg {Number} labelWidth set the width of label (0-12)
- * @cfg {String} labelAlign (top|left)
- * @cfg {Boolean} readOnly Specifies that the field should be read-only
- *
- *
+// as we use this in bootstrap.
+Roo.namespace('Roo.form');
+ /**
+ * @class Roo.form.Action
+ * Internal Class used to handle form actions
* @constructor
- * Create a new Input
- * @param {Object} config The config object
+ * @param {Roo.form.BasicForm} el The form element or its id
+ * @param {Object} config Configuration options
*/
-Roo.bootstrap.Input = function(config){
- Roo.bootstrap.Input.superclass.constructor.call(this, config);
-
- this.addEvents({
- /**
- * @event focus
- * Fires when this field receives input focus.
- * @param {Roo.form.Field} this
- */
- focus : true,
- /**
- * @event blur
- * Fires when this field loses input focus.
- * @param {Roo.form.Field} this
- */
- blur : true,
- /**
- * @event specialkey
- * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
- * {@link Roo.EventObject#getKey} to determine which key was pressed.
- * @param {Roo.form.Field} this
- * @param {Roo.EventObject} e The event object
- */
- specialkey : true,
- /**
- * @event change
- * Fires just before the field blurs if the field value has changed.
- * @param {Roo.form.Field} this
- * @param {Mixed} newValue The new value
- * @param {Mixed} oldValue The original value
- */
- change : true,
- /**
- * @event invalid
- * Fires after the field has been marked as invalid.
- * @param {Roo.form.Field} this
- * @param {String} msg The validation message
- */
- invalid : true,
- /**
- * @event valid
- * Fires after the field has been validated with no errors.
- * @param {Roo.form.Field} this
- */
- valid : true,
- /**
- * @event keyup
- * Fires after the key up
- * @param {Roo.form.Field} this
- * @param {Roo.EventObject} e The event Object
- */
- keyup : true
- });
+
+
+// define the action interface
+Roo.form.Action = function(form, options){
+ this.form = form;
+ this.options = options || {};
};
+/**
+ * Client Validation Failed
+ * @const
+ */
+Roo.form.Action.CLIENT_INVALID = 'client';
+/**
+ * Server Validation Failed
+ * @const
+ */
+Roo.form.Action.SERVER_INVALID = 'server';
+ /**
+ * Connect to Server Failed
+ * @const
+ */
+Roo.form.Action.CONNECT_FAILURE = 'connect';
+/**
+ * Reading Data from Server Failed
+ * @const
+ */
+Roo.form.Action.LOAD_FAILURE = 'load';
+
+Roo.form.Action.prototype = {
+ type : 'default',
+ failureType : undefined,
+ response : undefined,
+ result : undefined,
+
+ // interface method
+ run : function(options){
-Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
- /**
- * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
- automatic validation (defaults to "keyup").
- */
- validationEvent : "keyup",
- /**
- * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
- */
- validateOnBlur : true,
- /**
- * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
- */
- validationDelay : 250,
- /**
- * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
- */
- focusClass : "x-form-focus", // not needed???
-
-
- /**
- * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
- */
- invalidClass : "has-error",
-
- /**
- * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
- */
- selectOnFocus : false,
-
- /**
- * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
- */
- maskRe : null,
- /**
- * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
- */
- vtype : null,
-
- /**
- * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
- */
- disableKeyFilter : false,
-
- /**
- * @cfg {Boolean} disabled True to disable the field (defaults to false).
- */
- disabled : false,
- /**
- * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
- */
- allowBlank : true,
- /**
- * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
- */
- blankText : "This field is required",
-
- /**
- * @cfg {Number} minLength Minimum input field length required (defaults to 0)
- */
- minLength : 0,
- /**
- * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
- */
- maxLength : Number.MAX_VALUE,
- /**
- * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
- */
- minLengthText : "The minimum length for this field is {0}",
- /**
- * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
- */
- maxLengthText : "The maximum length for this field is {0}",
-
-
- /**
- * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
- * If available, this function will be called only after the basic validators all return true, and will be passed the
- * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
- */
- validator : null,
- /**
- * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
- * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
- * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
- */
- regex : null,
- /**
- * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
- */
- regexText : "",
-
-
-
- fieldLabel : '',
- inputType : 'text',
-
- name : false,
- placeholder: false,
- before : false,
- after : false,
- size : false,
- // private
- hasFocus : false,
- preventMark: false,
- isFormField : true,
- value : '',
- labelWidth : 2,
- labelAlign : false,
- readOnly : false,
-
- parentLabelAlign : function()
- {
- var parent = this;
- while (parent.parent()) {
- parent = parent.parent();
- if (typeof(parent.labelAlign) !='undefined') {
- return parent.labelAlign;
- }
- }
- return 'left';
-
},
-
- getAutoCreate : function(){
-
- var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
-
- var id = Roo.id();
-
- var cfg = {};
+
+ // interface method
+ success : function(response){
+
+ },
+
+ // interface method
+ handleResponse : function(response){
+
+ },
+
+ // default connection failure
+ failure : function(response){
- if(this.inputType != 'hidden'){
- cfg.cls = 'form-group' //input-group
+ this.response = response;
+ this.failureType = Roo.form.Action.CONNECT_FAILURE;
+ this.form.afterAction(this, false);
+ },
+
+ processResponse : function(response){
+ this.response = response;
+ if(!response.responseText){
+ return true;
}
-
- var input = {
- tag: 'input',
- id : id,
- type : this.inputType,
- value : this.value,
- cls : 'form-control',
- placeholder : this.placeholder || ''
-
- };
-
- if(this.maxLength && this.maxLength != Number.MAX_VALUE){
- input.maxLength = this.maxLength;
+ this.result = this.handleResponse(response);
+ return this.result;
+ },
+
+ // utility functions used internally
+ getUrl : function(appendParams){
+ var url = this.options.url || this.form.url || this.form.el.dom.action;
+ if(appendParams){
+ var p = this.getParams();
+ if(p){
+ url += (url.indexOf('?') != -1 ? '&' : '?') + p;
+ }
}
-
- if (this.disabled) {
- input.disabled=true;
+ return url;
+ },
+
+ getMethod : function(){
+ return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
+ },
+
+ getParams : function(){
+ var bp = this.form.baseParams;
+ var p = this.options.params;
+ if(p){
+ if(typeof p == "object"){
+ p = Roo.urlEncode(Roo.applyIf(p, bp));
+ }else if(typeof p == 'string' && bp){
+ p += '&' + Roo.urlEncode(bp);
+ }
+ }else if(bp){
+ p = Roo.urlEncode(bp);
}
-
- if (this.readOnly) {
- input.readonly=true;
+ return p;
+ },
+
+ createCallback : function(){
+ return {
+ success: this.success,
+ failure: this.failure,
+ scope: this,
+ timeout: (this.form.timeout*1000),
+ upload: this.form.fileUpload ? this.success : undefined
+ };
+ }
+};
+
+Roo.form.Action.Submit = function(form, options){
+ Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
+};
+
+Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
+ type : 'submit',
+
+ haveProgress : false,
+ uploadComplete : false,
+
+ // uploadProgress indicator.
+ uploadProgress : function()
+ {
+ if (!this.form.progressUrl) {
+ return;
}
- if (this.name) {
- input.name = this.name;
+ if (!this.haveProgress) {
+ Roo.MessageBox.progress("Uploading", "Uploading");
}
- if (this.size) {
- input.cls += ' input-' + this.size;
+ if (this.uploadComplete) {
+ Roo.MessageBox.hide();
+ return;
}
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
- }
- });
- var inputblock = input;
+ this.haveProgress = true;
+
+ var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
- if (this.before || this.after) {
-
- inputblock = {
- cls : 'input-group',
- cn : []
- };
- if (this.before && typeof(this.before) == 'string') {
+ var c = new Roo.data.Connection();
+ c.request({
+ url : this.form.progressUrl,
+ params: {
+ id : uid
+ },
+ method: 'GET',
+ success : function(req){
+ //console.log(data);
+ var rdata = false;
+ var edata;
+ try {
+ rdata = Roo.decode(req.responseText)
+ } catch (e) {
+ Roo.log("Invalid data from server..");
+ Roo.log(edata);
+ return;
+ }
+ if (!rdata || !rdata.success) {
+ Roo.log(rdata);
+ Roo.MessageBox.alert(Roo.encode(rdata));
+ return;
+ }
+ var data = rdata.data;
- inputblock.cn.push({
- tag :'span',
- cls : 'roo-input-before input-group-addon',
- html : this.before
- });
- }
- if (this.before && typeof(this.before) == 'object') {
- this.before = Roo.factory(this.before);
- Roo.log(this.before);
- inputblock.cn.push({
- tag :'span',
- cls : 'roo-input-before input-group-' +
- (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
- });
- }
-
- inputblock.cn.push(input);
-
- if (this.after && typeof(this.after) == 'string') {
- inputblock.cn.push({
- tag :'span',
- cls : 'roo-input-after input-group-addon',
- html : this.after
- });
- }
- if (this.after && typeof(this.after) == 'object') {
- this.after = Roo.factory(this.after);
- Roo.log(this.after);
- inputblock.cn.push({
- tag :'span',
- cls : 'roo-input-after input-group-' +
- (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
- });
- }
- };
-
- if (align ==='left' && this.fieldLabel.length) {
- Roo.log("left and has label");
- cfg.cn = [
-
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-sm-' + this.labelWidth,
- html : this.fieldLabel
-
- },
- {
- cls : "col-sm-" + (12 - this.labelWidth),
- cn: [
- inputblock
- ]
- }
-
- ];
- } else if ( this.fieldLabel.length) {
- Roo.log(" label");
- cfg.cn = [
+ if (this.uploadComplete) {
+ Roo.MessageBox.hide();
+ return;
+ }
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
-
- inputblock
-
- ];
-
- } else {
-
- Roo.log(" no label && no align");
- cfg.cn = [
-
- inputblock
-
- ];
-
-
- };
- Roo.log('input-parentType: ' + this.parentType);
-
- if (this.parentType === 'Navbar' && this.parent().bar) {
- cfg.cls += ' navbar-form';
- Roo.log(cfg);
- }
-
- return cfg;
-
- },
- /**
- * return the real input element.
- */
- inputEl: function ()
- {
- return this.el.select('input.form-control',true).first();
+ if (data){
+ Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
+ Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
+ );
+ }
+ this.uploadProgress.defer(2000,this);
+ },
+
+ failure: function(data) {
+ Roo.log('progress url failed ');
+ Roo.log(data);
+ },
+ scope : this
+ });
+
},
- setDisabled : function(v)
+
+
+ run : function()
{
- var i = this.inputEl().dom;
- if (!v) {
- i.removeAttribute('disabled');
- return;
+ // run get Values on the form, so it syncs any secondary forms.
+ this.form.getValues();
+
+ var o = this.options;
+ var method = this.getMethod();
+ var isPost = method == 'POST';
+ if(o.clientValidation === false || this.form.isValid()){
+ if (this.form.progressUrl) {
+ this.form.findField('UPLOAD_IDENTIFIER').setValue(
+ (new Date() * 1) + '' + Math.random());
+
+ }
+
+
+ Roo.Ajax.request(Roo.apply(this.createCallback(), {
+ form:this.form.el.dom,
+ url:this.getUrl(!isPost),
+ method: method,
+ params:isPost ? this.getParams() : null,
+ isUpload: this.form.fileUpload
+ }));
+
+ this.uploadProgress();
+
+ }else if (o.clientValidation !== false){ // client validation failed
+ this.failureType = Roo.form.Action.CLIENT_INVALID;
+ this.form.afterAction(this, false);
}
- i.setAttribute('disabled','true');
},
- initEvents : function()
- {
-
- this.inputEl().on("keydown" , this.fireKey, this);
- this.inputEl().on("focus", this.onFocus, this);
- this.inputEl().on("blur", this.onBlur, this);
-
- this.inputEl().relayEvent('keyup', this);
- // reference to original value for reset
- this.originalValue = this.getValue();
- //Roo.form.TextField.superclass.initEvents.call(this);
- if(this.validationEvent == 'keyup'){
- this.validationTask = new Roo.util.DelayedTask(this.validate, this);
- this.inputEl().on('keyup', this.filterValidation, this);
- }
- else if(this.validationEvent !== false){
- this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
+ success : function(response)
+ {
+ this.uploadComplete= true;
+ if (this.haveProgress) {
+ Roo.MessageBox.hide();
}
- if(this.selectOnFocus){
- this.on("focus", this.preFocus, this);
-
- }
- if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
- this.inputEl().on("keypress", this.filterKeys, this);
+
+ var result = this.processResponse(response);
+ if(result === true || result.success){
+ this.form.afterAction(this, true);
+ return;
}
- /* if(this.grow){
- this.el.on("keyup", this.onKeyUp, this, {buffer:50});
- this.el.on("click", this.autoSize, this);
+ if(result.errors){
+ this.form.markInvalid(result.errors);
+ this.failureType = Roo.form.Action.SERVER_INVALID;
}
- */
- if(this.inputEl().is('input[type=password]') && Roo.isSafari){
- this.inputEl().on('keydown', this.SafariOnKeyDown, this);
+ this.form.afterAction(this, false);
+ },
+ failure : function(response)
+ {
+ this.uploadComplete= true;
+ if (this.haveProgress) {
+ Roo.MessageBox.hide();
}
- if (typeof(this.before) == 'object') {
- this.before.render(this.el.select('.roo-input-before',true).first());
+ this.response = response;
+ this.failureType = Roo.form.Action.CONNECT_FAILURE;
+ this.form.afterAction(this, false);
+ },
+
+ handleResponse : function(response){
+ if(this.form.errorReader){
+ var rs = this.form.errorReader.read(response);
+ var errors = [];
+ if(rs.records){
+ for(var i = 0, len = rs.records.length; i < len; i++) {
+ var r = rs.records[i];
+ errors[i] = r.data;
+ }
+ }
+ if(errors.length < 1){
+ errors = null;
+ }
+ return {
+ success : rs.success,
+ errors : errors
+ };
}
- if (typeof(this.after) == 'object') {
- this.after.render(this.el.select('.roo-input-after',true).first());
+ var ret = false;
+ try {
+ ret = Roo.decode(response.responseText);
+ } catch (e) {
+ ret = {
+ success: false,
+ errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
+ errors : []
+ };
}
+ return ret;
+ }
+});
+
+
+Roo.form.Action.Load = function(form, options){
+ Roo.form.Action.Load.superclass.constructor.call(this, form, options);
+ this.reader = this.form.reader;
+};
+
+Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
+ type : 'load',
+
+ run : function(){
+ Roo.Ajax.request(Roo.apply(
+ this.createCallback(), {
+ method:this.getMethod(),
+ url:this.getUrl(false),
+ params:this.getParams()
+ }));
},
- filterValidation : function(e){
- if(!e.isNavKeyPress()){
- this.validationTask.delay(this.validationDelay);
+
+ success : function(response){
+
+ var result = this.processResponse(response);
+ if(result === true || !result.success || !result.data){
+ this.failureType = Roo.form.Action.LOAD_FAILURE;
+ this.form.afterAction(this, false);
+ return;
}
+ this.form.clearInvalid();
+ this.form.setValues(result.data);
+ this.form.afterAction(this, true);
},
+
+ handleResponse : function(response){
+ if(this.form.reader){
+ var rs = this.form.reader.read(response);
+ var data = rs.records && rs.records[0] ? rs.records[0].data : null;
+ return {
+ success : rs.success,
+ data : data
+ };
+ }
+ return Roo.decode(response.responseText);
+ }
+});
+
+Roo.form.Action.ACTION_TYPES = {
+ 'load' : Roo.form.Action.Load,
+ 'submit' : Roo.form.Action.Submit
+};/*
+ * - LGPL
+ *
+ * form
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.Form
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Form class
+ * @cfg {String} method GET | POST (default POST)
+ * @cfg {String} labelAlign top | left (default top)
+ * @cfg {String} align left | right - for navbars
+
+ *
+ * @constructor
+ * Create a new Form
+ * @param {Object} config The config object
+ */
+
+
+Roo.bootstrap.Form = function(config){
+ Roo.bootstrap.Form.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event clientvalidation
+ * If the monitorValid config option is true, this event fires repetitively to notify of valid state
+ * @param {Form} this
+ * @param {Boolean} valid true if the form has passed client-side validation
+ */
+ clientvalidation: true,
+ /**
+ * @event beforeaction
+ * Fires before any action is performed. Return false to cancel the action.
+ * @param {Form} this
+ * @param {Action} action The action to be performed
+ */
+ beforeaction: true,
+ /**
+ * @event actionfailed
+ * Fires when an action fails.
+ * @param {Form} this
+ * @param {Action} action The action that failed
+ */
+ actionfailed : true,
+ /**
+ * @event actioncomplete
+ * Fires when an action is completed.
+ * @param {Form} this
+ * @param {Action} action The action that completed
+ */
+ actioncomplete : true
+ });
+
+};
+
+Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
+
/**
- * Validates the field value
- * @return {Boolean} True if the value is valid, else false
+ * @cfg {String} method
+ * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
*/
- validate : function(){
- //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
- if(this.disabled || this.validateValue(this.getRawValue())){
- this.clearInvalid();
- return true;
- }
- return false;
- },
+ method : 'POST',
+ /**
+ * @cfg {String} url
+ * The URL to use for form actions if one isn't supplied in the action options.
+ */
+ /**
+ * @cfg {Boolean} fileUpload
+ * Set to true if this form is a file upload.
+ */
+
+ /**
+ * @cfg {Object} baseParams
+ * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
+ */
+
+ /**
+ * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
+ */
+ timeout: 30,
+ /**
+ * @cfg {Sting} align (left|right) for navbar forms
+ */
+ align : 'left',
+
+ // private
+ activeAction : null,
+
+ /**
+ * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
+ * element by passing it or its id or mask the form itself by passing in true.
+ * @type Mixed
+ */
+ waitMsgTarget : false,
+
/**
- * Validates a value according to the field's validation rules and marks the field as invalid
- * if the validation fails
- * @param {Mixed} value The value to validate
- * @return {Boolean} True if the value is valid, else false
+ * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
+ * element by passing it or its id or mask the form itself by passing in true.
+ * @type Mixed
*/
- validateValue : function(value){
- if(value.length < 1) { // if it's blank
- if(this.allowBlank){
- this.clearInvalid();
- return true;
- }else{
- this.markInvalid(this.blankText);
- return false;
- }
- }
- if(value.length < this.minLength){
- this.markInvalid(String.format(this.minLengthText, this.minLength));
- return false;
- }
- if(value.length > this.maxLength){
- this.markInvalid(String.format(this.maxLengthText, this.maxLength));
- return false;
- }
- if(this.vtype){
- var vt = Roo.form.VTypes;
- if(!vt[this.vtype](value, this)){
- this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
- return false;
- }
- }
- if(typeof this.validator == "function"){
- var msg = this.validator(value);
- if(msg !== true){
- this.markInvalid(msg);
- return false;
- }
+
+ getAutoCreate : function(){
+
+ var cfg = {
+ tag: 'form',
+ method : this.method || 'POST',
+ id : this.id || Roo.id(),
+ cls : ''
}
- if(this.regex && !this.regex.test(value)){
- this.markInvalid(this.regexText);
- return false;
+ if (this.parent().xtype.match(/^Nav/)) {
+ cfg.cls = 'navbar-form navbar-' + this.align;
+
}
- return true;
- },
-
-
-
- // private
- fireKey : function(e){
- //Roo.log('field ' + e.getKey());
- if(e.isNavKeyPress()){
- this.fireEvent("specialkey", this, e);
+
+ if (this.labelAlign == 'left' ) {
+ cfg.cls += ' form-horizontal';
}
+
+
+ return cfg;
},
- focus : function (selectText){
- if(this.rendered){
- this.inputEl().focus();
- if(selectText === true){
- this.inputEl().dom.select();
+ initEvents : function()
+ {
+ this.el.on('submit', this.onSubmit, this);
+ this.el.on('keypress', function(e) {
+ if (e.getCharCode() != 13) {
+ return true;
}
- }
- return this;
- } ,
-
- onFocus : function(){
- if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
- // this.el.addClass(this.focusClass);
- }
- if(!this.hasFocus){
- this.hasFocus = true;
- this.startValue = this.getValue();
- this.fireEvent("focus", this);
- }
+ e.preventDefault();
+ return false;
+ });
+
},
-
- beforeBlur : Roo.emptyFn,
-
-
// private
- onBlur : function(){
- this.beforeBlur();
- if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
- //this.el.removeClass(this.focusClass);
- }
- this.hasFocus = false;
- if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
- this.validate();
- }
- var v = this.getValue();
- if(String(v) !== String(this.startValue)){
- this.fireEvent('change', this, v, this.startValue);
- }
- this.fireEvent("blur", this);
+ onSubmit : function(e){
+ e.stopEvent();
},
- /**
- * Resets the current field value to the originally loaded value and clears any validation messages
- */
- reset : function(){
- this.setValue(this.originalValue);
- this.clearInvalid();
- },
/**
- * Returns the name of the field
- * @return {Mixed} name The name field
- */
- getName: function(){
- return this.name;
- },
- /**
- * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
- * @return {Mixed} value The field value
+ * Returns true if client-side validation on the form is successful.
+ * @return Boolean
*/
- getValue : function(){
- return this.inputEl().getValue();
+ isValid : function(){
+ var items = this.getItems();
+ var valid = true;
+ items.each(function(f){
+ if(!f.validate()){
+ valid = false;
+
+ }
+ });
+ return valid;
},
/**
- * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
- * @return {Mixed} value The field value
+ * Returns true if any fields in this form have changed since their original load.
+ * @return Boolean
*/
- getRawValue : function(){
- var v = this.inputEl().getValue();
-
- return v;
+ isDirty : function(){
+ var dirty = false;
+ var items = this.getItems();
+ items.each(function(f){
+ if(f.isDirty()){
+ dirty = true;
+ return false;
+ }
+ return true;
+ });
+ return dirty;
},
-
- /**
- * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
- * @param {Mixed} value The value to set
+ /**
+ * Performs a predefined action (submit or load) or custom actions you define on this form.
+ * @param {String} actionName The name of the action type
+ * @param {Object} options (optional) The options to pass to the action. All of the config options listed
+ * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
+ * accept other config options):
+ * <pre>
+Property Type Description
+---------------- --------------- ----------------------------------------------------------------------------------
+url String The url for the action (defaults to the form's url)
+method String The form method to use (defaults to the form's method, or POST if not defined)
+params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
+clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
+ validate the form on the client (defaults to false)
+ * </pre>
+ * @return {BasicForm} this
*/
- setRawValue : function(v){
- return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
+ doAction : function(action, options){
+ if(typeof action == 'string'){
+ action = new Roo.form.Action.ACTION_TYPES[action](this, options);
+ }
+ if(this.fireEvent('beforeaction', this, action) !== false){
+ this.beforeAction(action);
+ action.run.defer(100, action);
+ }
+ return this;
},
- selectText : function(start, end){
- var v = this.getRawValue();
- if(v.length > 0){
- start = start === undefined ? 0 : start;
- end = end === undefined ? v.length : end;
- var d = this.inputEl().dom;
- if(d.setSelectionRange){
- d.setSelectionRange(start, end);
- }else if(d.createTextRange){
- var range = d.createTextRange();
- range.moveStart("character", start);
- range.moveEnd("character", v.length-end);
- range.select();
+ // private
+ beforeAction : function(action){
+ var o = action.options;
+
+ // not really supported yet.. ??
+
+ //if(this.waitMsgTarget === true){
+ this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
+ //}else if(this.waitMsgTarget){
+ // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
+ // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
+ //}else {
+ // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
+ // }
+
+ },
+
+ // private
+ afterAction : function(action, success){
+ this.activeAction = null;
+ var o = action.options;
+
+ //if(this.waitMsgTarget === true){
+ this.el.unmask();
+ //}else if(this.waitMsgTarget){
+ // this.waitMsgTarget.unmask();
+ //}else{
+ // Roo.MessageBox.updateProgress(1);
+ // Roo.MessageBox.hide();
+ // }
+ //
+ if(success){
+ if(o.reset){
+ this.reset();
+ }
+ Roo.callback(o.success, o.scope, [this, action]);
+ this.fireEvent('actioncomplete', this, action);
+
+ }else{
+
+ // failure condition..
+ // we have a scenario where updates need confirming.
+ // eg. if a locking scenario exists..
+ // we look for { errors : { needs_confirm : true }} in the response.
+ if (
+ (typeof(action.result) != 'undefined') &&
+ (typeof(action.result.errors) != 'undefined') &&
+ (typeof(action.result.errors.needs_confirm) != 'undefined')
+ ){
+ var _t = this;
+ Roo.log("not supported yet");
+ /*
+
+ Roo.MessageBox.confirm(
+ "Change requires confirmation",
+ action.result.errorMsg,
+ function(r) {
+ if (r != 'yes') {
+ return;
+ }
+ _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
+ }
+
+ );
+ */
+
+
+ return;
+ }
+
+ Roo.callback(o.failure, o.scope, [this, action]);
+ // show an error message if no failed handler is set..
+ if (!this.hasListener('actionfailed')) {
+ Roo.log("need to add dialog support");
+ /*
+ Roo.MessageBox.alert("Error",
+ (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
+ action.result.errorMsg :
+ "Saving Failed, please check your entries or try again"
+ );
+ */
}
+
+ this.fireEvent('actionfailed', this, action);
}
+
},
-
/**
- * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
- * @param {Mixed} value The value to set
+ * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
+ * @param {String} id The value to search for
+ * @return Field
*/
- setValue : function(v){
- this.value = v;
- if(this.rendered){
- this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
- this.validate();
+ findField : function(id){
+ var items = this.getItems();
+ var field = items.get(id);
+ if(!field){
+ items.each(function(f){
+ if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
+ field = f;
+ return false;
+ }
+ return true;
+ });
}
+ return field || null;
},
-
- /*
- processValue : function(value){
- if(this.stripCharsRe){
- var newValue = value.replace(this.stripCharsRe, '');
- if(newValue !== value){
- this.setRawValue(newValue);
- return newValue;
+ /**
+ * Mark fields in this form invalid in bulk.
+ * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
+ * @return {BasicForm} this
+ */
+ markInvalid : function(errors){
+ if(errors instanceof Array){
+ for(var i = 0, len = errors.length; i < len; i++){
+ var fieldError = errors[i];
+ var f = this.findField(fieldError.id);
+ if(f){
+ f.markInvalid(fieldError.msg);
+ }
+ }
+ }else{
+ var field, id;
+ for(id in errors){
+ if(typeof errors[id] != 'function' && (field = this.findField(id))){
+ field.markInvalid(errors[id]);
+ }
}
}
- return value;
- },
- */
- preFocus : function(){
+ //Roo.each(this.childForms || [], function (f) {
+ // f.markInvalid(errors);
+ //});
- if(this.selectOnFocus){
- this.inputEl().dom.select();
- }
- },
- filterKeys : function(e){
- var k = e.getKey();
- if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
- return;
- }
- var c = e.getCharCode(), cc = String.fromCharCode(c);
- if(Roo.isIE && (e.isSpecialKey() || !cc)){
- return;
- }
- if(!this.maskRe.test(cc)){
- e.stopEvent();
- }
+ return this;
},
- /**
- * Clear any invalid styles/messages for this field
+
+ /**
+ * Set values for fields in this form in bulk.
+ * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
+ * @return {BasicForm} this
*/
- clearInvalid : function(){
-
- if(!this.el || this.preventMark){ // not rendered
- return;
- }
- this.el.removeClass(this.invalidClass);
- /*
- switch(this.msgTarget){
- case 'qtip':
- this.el.dom.qtip = '';
- break;
- case 'title':
- this.el.dom.title = '';
- break;
- case 'under':
- if(this.errorEl){
- Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
+ setValues : function(values){
+ if(values instanceof Array){ // array of objects
+ for(var i = 0, len = values.length; i < len; i++){
+ var v = values[i];
+ var f = this.findField(v.id);
+ if(f){
+ f.setValue(v.value);
+ if(this.trackResetOnLoad){
+ f.originalValue = f.getValue();
+ }
}
- break;
- case 'side':
- if(this.errorIcon){
- this.errorIcon.dom.qtip = '';
- this.errorIcon.hide();
- this.un('resize', this.alignErrorIcon, this);
+ }
+ }else{ // object hash
+ var field, id;
+ for(id in values){
+ if(typeof values[id] != 'function' && (field = this.findField(id))){
+
+ if (field.setFromData &&
+ field.valueField &&
+ field.displayField &&
+ // combos' with local stores can
+ // be queried via setValue()
+ // to set their value..
+ (field.store && !field.store.isLocal)
+ ) {
+ // it's a combo
+ var sd = { };
+ sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
+ sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
+ field.setFromData(sd);
+
+ } else {
+ field.setValue(values[id]);
+ }
+
+
+ if(this.trackResetOnLoad){
+ field.originalValue = field.getValue();
+ }
}
- break;
- default:
- var t = Roo.getDom(this.msgTarget);
- t.innerHTML = '';
- t.style.display = 'none';
- break;
+ }
}
- */
- this.fireEvent('valid', this);
+
+ //Roo.each(this.childForms || [], function (f) {
+ // f.setValues(values);
+ //});
+
+ return this;
},
- /**
- * Mark this field as invalid
- * @param {String} msg The validation message
+
+ /**
+ * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
+ * they are returned as an array.
+ * @param {Boolean} asString
+ * @return {Object}
*/
- markInvalid : function(msg){
- if(!this.el || this.preventMark){ // not rendered
- return;
- }
- this.el.addClass(this.invalidClass);
- /*
- msg = msg || this.invalidText;
- switch(this.msgTarget){
- case 'qtip':
- this.el.dom.qtip = msg;
- this.el.dom.qclass = 'x-form-invalid-tip';
- if(Roo.QuickTips){ // fix for floating editors interacting with DND
- Roo.QuickTips.enable();
- }
- break;
- case 'title':
- this.el.dom.title = msg;
- break;
- case 'under':
- if(!this.errorEl){
- var elp = this.el.findParent('.x-form-element', 5, true);
- this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
- this.errorEl.setWidth(elp.getWidth(true)-20);
- }
- this.errorEl.update(msg);
- Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
- break;
- case 'side':
- if(!this.errorIcon){
- var elp = this.el.findParent('.x-form-element', 5, true);
- this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
- }
- this.alignErrorIcon();
- this.errorIcon.dom.qtip = msg;
- this.errorIcon.dom.qclass = 'x-form-invalid-tip';
- this.errorIcon.show();
- this.on('resize', this.alignErrorIcon, this);
- break;
- default:
- var t = Roo.getDom(this.msgTarget);
- t.innerHTML = msg;
- t.style.display = this.msgDisplay;
- break;
- }
- */
- this.fireEvent('invalid', this, msg);
- },
- // private
- SafariOnKeyDown : function(event)
- {
- // this is a workaround for a password hang bug on chrome/ webkit.
+ getValues : function(asString){
+ //if (this.childForms) {
+ // copy values from the child forms
+ // Roo.each(this.childForms, function (f) {
+ // this.setValues(f.getValues());
+ // }, this);
+ //}
- var isSelectAll = false;
- if(this.inputEl().dom.selectionEnd > 0){
- isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
- }
- if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
- event.preventDefault();
- this.setValue('');
- return;
- }
- if(isSelectAll){ // backspace and delete key
-
- event.preventDefault();
- // this is very hacky as keydown always get's upper case.
- //
- var cc = String.fromCharCode(event.getCharCode());
- this.setValue( event.shiftKey ? cc : cc.toLowerCase());
-
+ var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
+ if(asString === true){
+ return fs;
}
+ return Roo.urlDecode(fs);
},
- adjustWidth : function(tag, w){
- tag = tag.toLowerCase();
- if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
- if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
- if(tag == 'input'){
- return w + 2;
- }
- if(tag == 'textarea'){
- return w-2;
- }
- }else if(Roo.isOpera){
- if(tag == 'input'){
- return w + 2;
+
+ /**
+ * Returns the fields in this form as an object with key/value pairs.
+ * This differs from getValues as it calls getValue on each child item, rather than using dom data.
+ * @return {Object}
+ */
+ getFieldValues : function(with_hidden)
+ {
+ var items = this.getItems();
+ var ret = {};
+ items.each(function(f){
+ if (!f.getName()) {
+ return;
+ }
+ var v = f.getValue();
+ if (f.inputType =='radio') {
+ if (typeof(ret[f.getName()]) == 'undefined') {
+ ret[f.getName()] = ''; // empty..
}
- if(tag == 'textarea'){
- return w-2;
+
+ if (!f.el.dom.checked) {
+ return;
+
}
+ v = f.el.dom.value;
+
}
- }
- return w;
- }
-
-});
-
-
-/*
- * - LGPL
- *
- * Input
- *
- */
+
+ // not sure if this supported any more..
+ if ((typeof(v) == 'object') && f.getRawValue) {
+ v = f.getRawValue() ; // dates..
+ }
+ // combo boxes where name != hiddenName...
+ if (f.name != f.getName()) {
+ ret[f.name] = f.getRawValue();
+ }
+ ret[f.getName()] = v;
+ });
+
+ return ret;
+ },
-/**
- * @class Roo.bootstrap.TextArea
- * @extends Roo.bootstrap.Input
- * Bootstrap TextArea class
- * @cfg {Number} cols Specifies the visible width of a text area
- * @cfg {Number} rows Specifies the visible number of lines in a text area
- * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
- * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
- * @cfg {string} html text
- *
- * @constructor
- * Create a new TextArea
- * @param {Object} config The config object
- */
+ /**
+ * Clears all invalid messages in this form.
+ * @return {BasicForm} this
+ */
+ clearInvalid : function(){
+ var items = this.getItems();
+
+ items.each(function(f){
+ f.clearInvalid();
+ });
+
+
+
+ return this;
+ },
-Roo.bootstrap.TextArea = function(config){
- Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
-
-};
-
-Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
-
- cols : false,
- rows : 5,
- readOnly : false,
- warp : 'soft',
- resize : false,
- value: false,
- html: false,
-
- getAutoCreate : function(){
-
- var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
-
- var id = Roo.id();
-
- var cfg = {};
-
- var input = {
- tag: 'textarea',
- id : id,
- warp : this.warp,
- rows : this.rows,
- value : this.value || '',
- html: this.html || '',
- cls : 'form-control',
- placeholder : this.placeholder || ''
-
- };
-
- if(this.maxLength && this.maxLength != Number.MAX_VALUE){
- input.maxLength = this.maxLength;
- }
-
- if(this.resize){
- input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
- }
-
- if(this.cols){
- input.cols = this.cols;
- }
-
- if (this.readOnly) {
- input.readonly = true;
- }
-
- if (this.name) {
- input.name = this.name;
- }
-
- if (this.size) {
- input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
- }
-
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
- }
+ /**
+ * Resets this form.
+ * @return {BasicForm} this
+ */
+ reset : function(){
+ var items = this.getItems();
+ items.each(function(f){
+ f.reset();
});
- var inputblock = input;
+ Roo.each(this.childForms || [], function (f) {
+ f.reset();
+ });
+
- if (this.before || this.after) {
-
- inputblock = {
- cls : 'input-group',
- cn : []
- };
- if (this.before) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.before
- });
+ return this;
+ },
+ getItems : function()
+ {
+ var r=new Roo.util.MixedCollection(false, function(o){
+ return o.id || (o.id = Roo.id());
+ });
+ var iter = function(el) {
+ if (el.inputEl) {
+ r.add(el);
}
- inputblock.cn.push(input);
- if (this.after) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.after
- });
+ if (!el.items) {
+ return;
}
+ Roo.each(el.items,function(e) {
+ iter(e);
+ });
- }
-
- if (align ==='left' && this.fieldLabel.length) {
- Roo.log("left and has label");
- cfg.cn = [
-
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-sm-' + this.labelWidth,
- html : this.fieldLabel
-
- },
- {
- cls : "col-sm-" + (12 - this.labelWidth),
- cn: [
- inputblock
- ]
- }
-
- ];
- } else if ( this.fieldLabel.length) {
- Roo.log(" label");
- cfg.cn = [
-
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
-
- inputblock
-
- ];
-
- } else {
- Roo.log(" no label && no align");
- cfg.cn = [
-
- inputblock
-
- ];
-
-
- }
+ };
+ iter(this);
+ return r;
+
- if (this.disabled) {
- input.disabled=true;
- }
- return cfg;
- },
- /**
- * return the real textarea element.
- */
- inputEl: function ()
- {
- return this.el.select('textarea.form-control',true).first();
}
+
});
/*
- * - LGPL
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
*
- * trigger field - base class for combo..
- *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
*/
-
/**
- * @class Roo.bootstrap.TriggerField
- * @extends Roo.bootstrap.Input
- * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
- * The trigger has no default action, so you must assign a function to implement the trigger click handler by
- * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
- * for which you can provide a custom implementation. For example:
- * <pre><code>
-var trigger = new Roo.bootstrap.TriggerField();
-trigger.onTriggerClick = myTriggerFn;
-trigger.applyTo('my-field');
-</code></pre>
- *
- * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
- * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
- * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
- * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
- * @constructor
- * Create a new TriggerField.
- * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
- * to the base TextField)
+ * @class Roo.form.VTypes
+ * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
+ * @singleton
*/
-Roo.bootstrap.TriggerField = function(config){
- this.mimicing = false;
- Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
- /**
- * @cfg {String} triggerClass A CSS class to apply to the trigger
- */
- /**
- * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
- */
- hideTrigger:false,
-
- /** @cfg {Boolean} grow @hide */
- /** @cfg {Number} growMin @hide */
- /** @cfg {Number} growMax @hide */
+Roo.form.VTypes = function(){
+ // closure these in so they are only created once.
+ var alpha = /^[a-zA-Z_]+$/;
+ var alphanum = /^[a-zA-Z0-9_]+$/;
+ var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
+ var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
- /**
- * @hide
- * @method
- */
- autoSize: Roo.emptyFn,
- // private
- monitorTab : true,
- // private
- deferHeight : true,
+ // All these messages and functions are configurable
+ return {
+ /**
+ * The function used to validate email addresses
+ * @param {String} value The email address
+ */
+ 'email' : function(v){
+ return email.test(v);
+ },
+ /**
+ * The error text to display when the email validation function returns false
+ * @type String
+ */
+ 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
+ /**
+ * The keystroke filter mask to be applied on email input
+ * @type RegExp
+ */
+ 'emailMask' : /[a-z0-9_\.\-@]/i,
-
- actionMode : 'wrap',
-
-
-
- getAutoCreate : function(){
-
- var parent = this.parent();
-
- var align = this.labelAlign || this.parentLabelAlign();
-
- var id = Roo.id();
-
- var cfg = {
- cls: 'form-group' //input-group
- };
-
-
- var input = {
- tag: 'input',
- id : id,
- type : this.inputType,
- cls : 'form-control',
- autocomplete: 'off',
- placeholder : this.placeholder || ''
-
- };
- if (this.name) {
- input.name = this.name;
- }
- if (this.size) {
- input.cls += ' input-' + this.size;
- }
-
- if (this.disabled) {
- input.disabled=true;
- }
-
- var inputblock = input;
-
- if (this.before || this.after) {
-
- inputblock = {
- cls : 'input-group',
- cn : []
- };
- if (this.before) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.before
- });
- }
- inputblock.cn.push(input);
- if (this.after) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.after
- });
- }
-
- };
-
- var box = {
- tag: 'div',
- cn: [
- {
- tag: 'input',
- type : 'hidden',
- cls: 'form-hidden-field'
- },
- inputblock
- ]
-
- };
-
- if(this.multiple){
- Roo.log('multiple');
-
- box = {
- tag: 'div',
- cn: [
- {
- tag: 'input',
- type : 'hidden',
- cls: 'form-hidden-field'
- },
- {
- tag: 'ul',
- cls: 'select2-choices',
- cn:[
- {
- tag: 'li',
- cls: 'select2-search-field',
- cn: [
-
- inputblock
- ]
- }
- ]
- }
- ]
- }
- };
-
- var combobox = {
- cls: 'select2-container input-group',
- cn: [
- box,
- {
- tag: 'ul',
- cls: 'typeahead typeahead-long dropdown-menu',
- style: 'display:none'
- }
- ]
- };
-
- if(!this.multiple){
- combobox.cn.push({
- tag :'span',
- cls : 'input-group-addon btn dropdown-toggle',
- cn : [
- {
- tag: 'span',
- cls: 'caret'
- },
- {
- tag: 'span',
- cls: 'combobox-clear',
- cn : [
- {
- tag : 'i',
- cls: 'icon-remove'
- }
- ]
- }
- ]
-
- })
- }
-
- if(this.multiple){
- combobox.cls += ' select2-container-multi';
- }
-
- if (align ==='left' && this.fieldLabel.length) {
-
- Roo.log("left and has label");
- cfg.cn = [
-
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-sm-' + this.labelWidth,
- html : this.fieldLabel
-
- },
- {
- cls : "col-sm-" + (12 - this.labelWidth),
- cn: [
- combobox
- ]
- }
-
- ];
- } else if ( this.fieldLabel.length) {
- Roo.log(" label");
- cfg.cn = [
-
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
-
- combobox
-
- ];
-
- } else {
-
- Roo.log(" no label && no align");
- cfg = combobox
-
-
- }
-
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
- }
- });
-
- return cfg;
-
- },
-
-
-
- // private
- onResize : function(w, h){
-// Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
-// if(typeof w == 'number'){
-// var x = w - this.trigger.getWidth();
-// this.inputEl().setWidth(this.adjustWidth('input', x));
-// this.trigger.setStyle('left', x+'px');
-// }
- },
-
- // private
- adjustSize : Roo.BoxComponent.prototype.adjustSize,
-
- // private
- getResizeEl : function(){
- return this.inputEl();
- },
-
- // private
- getPositionEl : function(){
- return this.inputEl();
- },
-
- // private
- alignErrorIcon : function(){
- this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
- },
-
- // private
- initEvents : function(){
-
- Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
- //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
- if(!this.multiple){
- this.trigger = this.el.select('span.dropdown-toggle',true).first();
- if(this.hideTrigger){
- this.trigger.setDisplayed(false);
- }
- this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
- }
-
- if(this.multiple){
- this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
- }
-
- //this.trigger.addClassOnOver('x-form-trigger-over');
- //this.trigger.addClassOnClick('x-form-trigger-click');
-
- //if(!this.width){
- // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
- //}
- },
-
- // private
- initTrigger : function(){
-
- },
-
- // private
- onDestroy : function(){
- if(this.trigger){
- this.trigger.removeAllListeners();
- // this.trigger.remove();
- }
- //if(this.wrap){
- // this.wrap.remove();
- //}
- Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
- },
-
- // private
- onFocus : function(){
- Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
- /*
- if(!this.mimicing){
- this.wrap.addClass('x-trigger-wrap-focus');
- this.mimicing = true;
- Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
- if(this.monitorTab){
- this.el.on("keydown", this.checkTab, this);
- }
- }
- */
- },
-
- // private
- checkTab : function(e){
- if(e.getKey() == e.TAB){
- this.triggerBlur();
- }
- },
-
- // private
- onBlur : function(){
- // do nothing
- },
-
- // private
- mimicBlur : function(e, t){
- /*
- if(!this.wrap.contains(t) && this.validateBlur()){
- this.triggerBlur();
- }
- */
- },
-
- // private
- triggerBlur : function(){
- this.mimicing = false;
- Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
- if(this.monitorTab){
- this.el.un("keydown", this.checkTab, this);
- }
- //this.wrap.removeClass('x-trigger-wrap-focus');
- Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
- },
-
- // private
- // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
- validateBlur : function(e, t){
- return true;
- },
-
- // private
- onDisable : function(){
- this.inputEl().dom.disabled = true;
- //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
- //if(this.wrap){
- // this.wrap.addClass('x-item-disabled');
- //}
- },
-
- // private
- onEnable : function(){
- this.inputEl().dom.disabled = false;
- //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
- //if(this.wrap){
- // this.el.removeClass('x-item-disabled');
- //}
- },
-
- // private
- onShow : function(){
- var ae = this.getActionEl();
-
- if(ae){
- ae.dom.style.display = '';
- ae.dom.style.visibility = 'visible';
- }
- },
-
- // private
-
- onHide : function(){
- var ae = this.getActionEl();
- ae.dom.style.display = 'none';
- },
-
- /**
- * The function that should handle the trigger's click event. This method does nothing by default until overridden
- * by an implementing function.
- * @method
- * @param {EventObject} e
- */
- onTriggerClick : Roo.emptyFn
-});
- /*
- * 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.data.SortTypes
- * @singleton
- * Defines the default sorting (casting?) comparison functions used when sorting data.
- */
-Roo.data.SortTypes = {
- /**
- * Default sort that does nothing
- * @param {Mixed} s The value being converted
- * @return {Mixed} The comparison value
- */
- none : function(s){
- return s;
- },
-
- /**
- * The regular expression used to strip tags
- * @type {RegExp}
- * @property
- */
- stripTagsRE : /<\/?[^>]+>/gi,
-
- /**
- * Strips all HTML tags to sort on text only
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asText : function(s){
- return String(s).replace(this.stripTagsRE, "");
- },
-
- /**
- * Strips all HTML tags to sort on text only - Case insensitive
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asUCText : function(s){
- return String(s).toUpperCase().replace(this.stripTagsRE, "");
- },
-
- /**
- * Case insensitive string
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asUCString : function(s) {
- return String(s).toUpperCase();
- },
-
- /**
- * Date sorting
- * @param {Mixed} s The value being converted
- * @return {Number} The comparison value
- */
- asDate : function(s) {
- if(!s){
- return 0;
- }
- if(s instanceof Date){
- return s.getTime();
- }
- return Date.parse(String(s));
- },
-
- /**
- * Float sorting
- * @param {Mixed} s The value being converted
- * @return {Float} The comparison value
- */
- asFloat : function(s) {
- var val = parseFloat(String(s).replace(/,/g, ""));
- if(isNaN(val)) val = 0;
- return val;
- },
-
- /**
- * Integer sorting
- * @param {Mixed} s The value being converted
- * @return {Number} The comparison value
- */
- asInt : function(s) {
- var val = parseInt(String(s).replace(/,/g, ""));
- if(isNaN(val)) val = 0;
- return val;
- }
-};/*
- * 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.data.Record
- * Instances of this class encapsulate both record <em>definition</em> information, and record
- * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
- * to access Records cached in an {@link Roo.data.Store} object.<br>
- * <p>
- * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
- * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
- * objects.<br>
- * <p>
- * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
- * @constructor
- * This constructor should not be used to create Record objects. Instead, use the constructor generated by
- * {@link #create}. The parameters are the same.
- * @param {Array} data An associative Array of data values keyed by the field name.
- * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
- * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
- * not specified an integer id is generated.
- */
-Roo.data.Record = function(data, id){
- this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
- this.data = data;
-};
-
-/**
- * Generate a constructor for a specific record layout.
- * @param {Array} o An Array of field definition objects which specify field names, and optionally,
- * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
- * Each field definition object may contain the following properties: <ul>
- * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
- * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
- * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
- * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
- * is being used, then this is a string containing the javascript expression to reference the data relative to
- * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
- * to the data item relative to the record element. If the mapping expression is the same as the field name,
- * this may be omitted.</p></li>
- * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
- * <ul><li>auto (Default, implies no conversion)</li>
- * <li>string</li>
- * <li>int</li>
- * <li>float</li>
- * <li>boolean</li>
- * <li>date</li></ul></p></li>
- * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
- * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
- * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
- * by the Reader into an object that will be stored in the Record. It is passed the
- * following parameters:<ul>
- * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
- * </ul></p></li>
- * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
- * </ul>
- * <br>usage:<br><pre><code>
-var TopicRecord = Roo.data.Record.create(
- {name: 'title', mapping: 'topic_title'},
- {name: 'author', mapping: 'username'},
- {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
- {name: 'lastPost', mapping: 'post_time', type: 'date'},
- {name: 'lastPoster', mapping: 'user2'},
- {name: 'excerpt', mapping: 'post_text'}
-);
-
-var myNewRecord = new TopicRecord({
- title: 'Do my job please',
- author: 'noobie',
- totalPosts: 1,
- lastPost: new Date(),
- lastPoster: 'Animal',
- excerpt: 'No way dude!'
-});
-myStore.add(myNewRecord);
-</code></pre>
- * @method create
- * @static
- */
-Roo.data.Record.create = function(o){
- var f = function(){
- f.superclass.constructor.apply(this, arguments);
- };
- Roo.extend(f, Roo.data.Record);
- var p = f.prototype;
- p.fields = new Roo.util.MixedCollection(false, function(field){
- return field.name;
- });
- for(var i = 0, len = o.length; i < len; i++){
- p.fields.add(new Roo.data.Field(o[i]));
- }
- f.getField = function(name){
- return p.fields.get(name);
- };
- return f;
-};
-
-Roo.data.Record.AUTO_ID = 1000;
-Roo.data.Record.EDIT = 'edit';
-Roo.data.Record.REJECT = 'reject';
-Roo.data.Record.COMMIT = 'commit';
-
-Roo.data.Record.prototype = {
- /**
- * Readonly flag - true if this record has been modified.
- * @type Boolean
- */
- dirty : false,
- editing : false,
- error: null,
- modified: null,
-
- // private
- join : function(store){
- this.store = store;
- },
-
- /**
- * Set the named field to the specified value.
- * @param {String} name The name of the field to set.
- * @param {Object} value The value to set the field to.
- */
- set : function(name, value){
- if(this.data[name] == value){
- return;
- }
- this.dirty = true;
- if(!this.modified){
- this.modified = {};
- }
- if(typeof this.modified[name] == 'undefined'){
- this.modified[name] = this.data[name];
- }
- this.data[name] = value;
- if(!this.editing && this.store){
- this.store.afterEdit(this);
- }
- },
-
- /**
- * Get the value of the named field.
- * @param {String} name The name of the field to get the value of.
- * @return {Object} The value of the field.
- */
- get : function(name){
- return this.data[name];
- },
-
- // private
- beginEdit : function(){
- this.editing = true;
- this.modified = {};
- },
-
- // private
- cancelEdit : function(){
- this.editing = false;
- delete this.modified;
- },
-
- // private
- endEdit : function(){
- this.editing = false;
- if(this.dirty && this.store){
- this.store.afterEdit(this);
- }
- },
-
- /**
- * Usually called by the {@link Roo.data.Store} which owns the Record.
- * Rejects all changes made to the Record since either creation, or the last commit operation.
- * Modified fields are reverted to their original values.
- * <p>
- * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
- * of reject operations.
- */
- reject : function(){
- var m = this.modified;
- for(var n in m){
- if(typeof m[n] != "function"){
- this.data[n] = m[n];
- }
- }
- this.dirty = false;
- delete this.modified;
- this.editing = false;
- if(this.store){
- this.store.afterReject(this);
- }
- },
-
- /**
- * Usually called by the {@link Roo.data.Store} which owns the Record.
- * Commits all changes made to the Record since either creation, or the last commit operation.
- * <p>
- * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
- * of commit operations.
- */
- commit : function(){
- this.dirty = false;
- delete this.modified;
- this.editing = false;
- if(this.store){
- this.store.afterCommit(this);
- }
- },
-
- // private
- hasError : function(){
- return this.error != null;
- },
-
- // private
- clearError : function(){
- this.error = null;
- },
-
- /**
- * Creates a copy of this record.
- * @param {String} id (optional) A new record id if you don't want to use this record's id
- * @return {Record}
- */
- copy : function(newId) {
- return new this.constructor(Roo.apply({}, this.data), newId || this.id);
- }
-};/*
- * 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.data.Store
- * @extends Roo.util.Observable
- * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
- * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
- * <p>
- * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
- * has no knowledge of the format of the data returned by the Proxy.<br>
- * <p>
- * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
- * instances from the data object. These records are cached and made available through accessor functions.
- * @constructor
- * Creates a new Store.
- * @param {Object} config A config object containing the objects needed for the Store to access data,
- * and read the data into Records.
- */
-Roo.data.Store = function(config){
- this.data = new Roo.util.MixedCollection(false);
- this.data.getKey = function(o){
- return o.id;
- };
- this.baseParams = {};
- // private
- this.paramNames = {
- "start" : "start",
- "limit" : "limit",
- "sort" : "sort",
- "dir" : "dir",
- "multisort" : "_multisort"
- };
-
- if(config && config.data){
- this.inlineData = config.data;
- delete config.data;
- }
-
- Roo.apply(this, config);
-
- if(this.reader){ // reader passed
- this.reader = Roo.factory(this.reader, Roo.data);
- this.reader.xmodule = this.xmodule || false;
- if(!this.recordType){
- this.recordType = this.reader.recordType;
- }
- if(this.reader.onMetaChange){
- this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
- }
- }
-
- if(this.recordType){
- this.fields = this.recordType.prototype.fields;
- }
- this.modified = [];
-
- this.addEvents({
- /**
- * @event datachanged
- * Fires when the data cache has changed, and a widget which is using this Store
- * as a Record cache should refresh its view.
- * @param {Store} this
- */
- datachanged : true,
- /**
- * @event metachange
- * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
- * @param {Store} this
- * @param {Object} meta The JSON metadata
- */
- metachange : true,
- /**
- * @event add
- * Fires when Records have been added to the Store
- * @param {Store} this
- * @param {Roo.data.Record[]} records The array of Records added
- * @param {Number} index The index at which the record(s) were added
- */
- add : true,
- /**
- * @event remove
- * Fires when a Record has been removed from the Store
- * @param {Store} this
- * @param {Roo.data.Record} record The Record that was removed
- * @param {Number} index The index at which the record was removed
- */
- remove : true,
- /**
- * @event update
- * Fires when a Record has been updated
- * @param {Store} this
- * @param {Roo.data.Record} record The Record that was updated
- * @param {String} operation The update operation being performed. Value may be one of:
- * <pre><code>
- Roo.data.Record.EDIT
- Roo.data.Record.REJECT
- Roo.data.Record.COMMIT
- * </code></pre>
- */
- update : true,
- /**
- * @event clear
- * Fires when the data cache has been cleared.
- * @param {Store} this
- */
- clear : true,
- /**
- * @event beforeload
- * Fires before a request is made for a new data object. If the beforeload handler returns false
- * the load action will be canceled.
- * @param {Store} this
- * @param {Object} options The loading options that were specified (see {@link #load} for details)
- */
- beforeload : true,
- /**
- * @event beforeloadadd
- * Fires after a new set of Records has been loaded.
- * @param {Store} this
- * @param {Roo.data.Record[]} records The Records that were loaded
- * @param {Object} options The loading options that were specified (see {@link #load} for details)
- */
- beforeloadadd : true,
- /**
- * @event load
- * Fires after a new set of Records has been loaded, before they are added to the store.
- * @param {Store} this
- * @param {Roo.data.Record[]} records The Records that were loaded
- * @param {Object} options The loading options that were specified (see {@link #load} for details)
- * @params {Object} return from reader
- */
- load : true,
- /**
- * @event loadexception
- * Fires if an exception occurs in the Proxy during loading.
- * Called with the signature of the Proxy's "loadexception" event.
- * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
- *
- * @param {Proxy}
- * @param {Object} return from JsonData.reader() - success, totalRecords, records
- * @param {Object} load options
- * @param {Object} jsonData from your request (normally this contains the Exception)
- */
- loadexception : true
- });
-
- if(this.proxy){
- this.proxy = Roo.factory(this.proxy, Roo.data);
- this.proxy.xmodule = this.xmodule || false;
- this.relayEvents(this.proxy, ["loadexception"]);
- }
- this.sortToggle = {};
- this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
-
- Roo.data.Store.superclass.constructor.call(this);
-
- if(this.inlineData){
- this.loadData(this.inlineData);
- delete this.inlineData;
- }
-};
-
-Roo.extend(Roo.data.Store, Roo.util.Observable, {
- /**
- * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
- * without a remote query - used by combo/forms at present.
- */
-
- /**
- * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
- */
- /**
- * @cfg {Array} data Inline data to be loaded when the store is initialized.
- */
- /**
- * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
- * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
- */
- /**
- * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
- * on any HTTP request
- */
- /**
- * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
- */
- /**
- * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
- */
- multiSort: false,
- /**
- * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
- * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
- */
- remoteSort : false,
-
- /**
- * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
- * loaded or when a record is removed. (defaults to false).
- */
- pruneModifiedRecords : false,
-
- // private
- lastOptions : null,
-
- /**
- * Add Records to the Store and fires the add event.
- * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
- */
- add : function(records){
- records = [].concat(records);
- for(var i = 0, len = records.length; i < len; i++){
- records[i].join(this);
- }
- var index = this.data.length;
- this.data.addAll(records);
- this.fireEvent("add", this, records, index);
- },
-
- /**
- * Remove a Record from the Store and fires the remove event.
- * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
- */
- remove : function(record){
- var index = this.data.indexOf(record);
- this.data.removeAt(index);
- if(this.pruneModifiedRecords){
- this.modified.remove(record);
- }
- this.fireEvent("remove", this, record, index);
- },
-
- /**
- * Remove all Records from the Store and fires the clear event.
- */
- removeAll : function(){
- this.data.clear();
- if(this.pruneModifiedRecords){
- this.modified = [];
- }
- this.fireEvent("clear", this);
- },
-
- /**
- * Inserts Records to the Store at the given index and fires the add event.
- * @param {Number} index The start index at which to insert the passed Records.
- * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
- */
- insert : function(index, records){
- records = [].concat(records);
- for(var i = 0, len = records.length; i < len; i++){
- this.data.insert(index, records[i]);
- records[i].join(this);
- }
- this.fireEvent("add", this, records, index);
- },
-
- /**
- * Get the index within the cache of the passed Record.
- * @param {Roo.data.Record} record The Roo.data.Record object to to find.
- * @return {Number} The index of the passed Record. Returns -1 if not found.
- */
- indexOf : function(record){
- return this.data.indexOf(record);
- },
-
- /**
- * Get the index within the cache of the Record with the passed id.
- * @param {String} id The id of the Record to find.
- * @return {Number} The index of the Record. Returns -1 if not found.
- */
- indexOfId : function(id){
- return this.data.indexOfKey(id);
- },
-
- /**
- * Get the Record with the specified id.
- * @param {String} id The id of the Record to find.
- * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
- */
- getById : function(id){
- return this.data.key(id);
- },
-
- /**
- * Get the Record at the specified index.
- * @param {Number} index The index of the Record to find.
- * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
- */
- getAt : function(index){
- return this.data.itemAt(index);
- },
-
- /**
- * Returns a range of Records between specified indices.
- * @param {Number} startIndex (optional) The starting index (defaults to 0)
- * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
- * @return {Roo.data.Record[]} An array of Records
- */
- getRange : function(start, end){
- return this.data.getRange(start, end);
- },
-
- // private
- storeOptions : function(o){
- o = Roo.apply({}, o);
- delete o.callback;
- delete o.scope;
- this.lastOptions = o;
- },
-
- /**
- * Loads the Record cache from the configured Proxy using the configured Reader.
- * <p>
- * If using remote paging, then the first load call must specify the <em>start</em>
- * and <em>limit</em> properties in the options.params property to establish the initial
- * position within the dataset, and the number of Records to cache on each read from the Proxy.
- * <p>
- * <strong>It is important to note that for remote data sources, loading is asynchronous,
- * and this call will return before the new data has been loaded. Perform any post-processing
- * in a callback function, or in a "load" event handler.</strong>
- * <p>
- * @param {Object} options An object containing properties which control loading options:<ul>
- * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
- * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
- * passed the following arguments:<ul>
- * <li>r : Roo.data.Record[]</li>
- * <li>options: Options object from the load call</li>
- * <li>success: Boolean success indicator</li></ul></li>
- * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
- * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
- * </ul>
- */
- load : function(options){
- options = options || {};
- if(this.fireEvent("beforeload", this, options) !== false){
- this.storeOptions(options);
- var p = Roo.apply(options.params || {}, this.baseParams);
- // if meta was not loaded from remote source.. try requesting it.
- if (!this.reader.metaFromRemote) {
- p._requestMeta = 1;
- }
- if(this.sortInfo && this.remoteSort){
- var pn = this.paramNames;
- p[pn["sort"]] = this.sortInfo.field;
- p[pn["dir"]] = this.sortInfo.direction;
- }
- if (this.multiSort) {
- var pn = this.paramNames;
- p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
- }
-
- this.proxy.load(p, this.reader, this.loadRecords, this, options);
- }
- },
-
- /**
- * Reloads the Record cache from the configured Proxy using the configured Reader and
- * the options from the last load operation performed.
- * @param {Object} options (optional) An object containing properties which may override the options
- * used in the last load operation. See {@link #load} for details (defaults to null, in which case
- * the most recently used options are reused).
- */
- reload : function(options){
- this.load(Roo.applyIf(options||{}, this.lastOptions));
- },
-
- // private
- // Called as a callback by the Reader during a load operation.
- loadRecords : function(o, options, success){
- if(!o || success === false){
- if(success !== false){
- this.fireEvent("load", this, [], options, o);
- }
- if(options.callback){
- options.callback.call(options.scope || this, [], options, false);
- }
- return;
- }
- // if data returned failure - throw an exception.
- if (o.success === false) {
- // show a message if no listener is registered.
- if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
- Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
- }
- // loadmask wil be hooked into this..
- this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
- return;
- }
- var r = o.records, t = o.totalRecords || r.length;
-
- this.fireEvent("beforeloadadd", this, r, options, o);
-
- if(!options || options.add !== true){
- if(this.pruneModifiedRecords){
- this.modified = [];
- }
- for(var i = 0, len = r.length; i < len; i++){
- r[i].join(this);
- }
- if(this.snapshot){
- this.data = this.snapshot;
- delete this.snapshot;
- }
- this.data.clear();
- this.data.addAll(r);
- this.totalLength = t;
- this.applySort();
- this.fireEvent("datachanged", this);
- }else{
- this.totalLength = Math.max(t, this.data.length+r.length);
- this.add(r);
- }
- this.fireEvent("load", this, r, options, o);
- if(options.callback){
- options.callback.call(options.scope || this, r, options, true);
- }
- },
-
-
- /**
- * Loads data from a passed data block. A Reader which understands the format of the data
- * must have been configured in the constructor.
- * @param {Object} data The data block from which to read the Records. The format of the data expected
- * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
- * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
- */
- loadData : function(o, append){
- var r = this.reader.readRecords(o);
- this.loadRecords(r, {add: append}, true);
- },
-
- /**
- * Gets the number of cached records.
- * <p>
- * <em>If using paging, this may not be the total size of the dataset. If the data object
- * used by the Reader contains the dataset size, then the getTotalCount() function returns
- * the data set size</em>
- */
- getCount : function(){
- return this.data.length || 0;
- },
-
- /**
- * Gets the total number of records in the dataset as returned by the server.
- * <p>
- * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
- * the dataset size</em>
- */
- getTotalCount : function(){
- return this.totalLength || 0;
- },
-
- /**
- * Returns the sort state of the Store as an object with two properties:
- * <pre><code>
- field {String} The name of the field by which the Records are sorted
- direction {String} The sort order, "ASC" or "DESC"
- * </code></pre>
- */
- getSortState : function(){
- return this.sortInfo;
- },
-
- // private
- applySort : function(){
- if(this.sortInfo && !this.remoteSort){
- var s = this.sortInfo, f = s.field;
- var st = this.fields.get(f).sortType;
- var fn = function(r1, r2){
- var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
- return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
- };
- this.data.sort(s.direction, fn);
- if(this.snapshot && this.snapshot != this.data){
- this.snapshot.sort(s.direction, fn);
- }
- }
- },
-
- /**
- * Sets the default sort column and order to be used by the next load operation.
- * @param {String} fieldName The name of the field to sort by.
- * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
- */
- setDefaultSort : function(field, dir){
- this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
- },
-
- /**
- * Sort the Records.
- * If remote sorting is used, the sort is performed on the server, and the cache is
- * reloaded. If local sorting is used, the cache is sorted internally.
- * @param {String} fieldName The name of the field to sort by.
- * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
- */
- sort : function(fieldName, dir){
- var f = this.fields.get(fieldName);
- if(!dir){
- this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
-
- if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
- dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
- }else{
- dir = f.sortDir;
- }
- }
- this.sortToggle[f.name] = dir;
- this.sortInfo = {field: f.name, direction: dir};
- if(!this.remoteSort){
- this.applySort();
- this.fireEvent("datachanged", this);
- }else{
- this.load(this.lastOptions);
- }
- },
-
- /**
- * Calls the specified function for each of the Records in the cache.
- * @param {Function} fn The function to call. The Record is passed as the first parameter.
- * Returning <em>false</em> aborts and exits the iteration.
- * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
- */
- each : function(fn, scope){
- this.data.each(fn, scope);
- },
-
- /**
- * Gets all records modified since the last commit. Modified records are persisted across load operations
- * (e.g., during paging).
- * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
- */
- getModifiedRecords : function(){
- return this.modified;
- },
-
- // private
- createFilterFn : function(property, value, anyMatch){
- if(!value.exec){ // not a regex
- value = String(value);
- if(value.length == 0){
- return false;
- }
- value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
- }
- return function(r){
- return value.test(r.data[property]);
- };
- },
-
- /**
- * Sums the value of <i>property</i> for each record between start and end and returns the result.
- * @param {String} property A field on your records
- * @param {Number} start The record index to start at (defaults to 0)
- * @param {Number} end The last record index to include (defaults to length - 1)
- * @return {Number} The sum
- */
- sum : function(property, start, end){
- var rs = this.data.items, v = 0;
- start = start || 0;
- end = (end || end === 0) ? end : rs.length-1;
-
- for(var i = start; i <= end; i++){
- v += (rs[i].data[property] || 0);
- }
- return v;
- },
-
- /**
- * Filter the records by a specified property.
- * @param {String} field A field on your records
- * @param {String/RegExp} value Either a string that the field
- * should start with or a RegExp to test against the field
- * @param {Boolean} anyMatch True to match any part not just the beginning
- */
- filter : function(property, value, anyMatch){
- var fn = this.createFilterFn(property, value, anyMatch);
- return fn ? this.filterBy(fn) : this.clearFilter();
- },
-
- /**
- * Filter by a function. The specified function will be called with each
- * record in this data source. If the function returns true the record is included,
- * otherwise it is filtered.
- * @param {Function} fn The function to be called, it will receive 2 args (record, id)
- * @param {Object} scope (optional) The scope of the function (defaults to this)
- */
- filterBy : function(fn, scope){
- this.snapshot = this.snapshot || this.data;
- this.data = this.queryBy(fn, scope||this);
- this.fireEvent("datachanged", this);
- },
-
- /**
- * Query the records by a specified property.
- * @param {String} field A field on your records
- * @param {String/RegExp} value Either a string that the field
- * should start with or a RegExp to test against the field
- * @param {Boolean} anyMatch True to match any part not just the beginning
- * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
- */
- query : function(property, value, anyMatch){
- var fn = this.createFilterFn(property, value, anyMatch);
- return fn ? this.queryBy(fn) : this.data.clone();
- },
-
- /**
- * Query by a function. The specified function will be called with each
- * record in this data source. If the function returns true the record is included
- * in the results.
- * @param {Function} fn The function to be called, it will receive 2 args (record, id)
- * @param {Object} scope (optional) The scope of the function (defaults to this)
- @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
- **/
- queryBy : function(fn, scope){
- var data = this.snapshot || this.data;
- return data.filterBy(fn, scope||this);
- },
-
- /**
- * Collects unique values for a particular dataIndex from this store.
- * @param {String} dataIndex The property to collect
- * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
- * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
- * @return {Array} An array of the unique values
- **/
- collect : function(dataIndex, allowNull, bypassFilter){
- var d = (bypassFilter === true && this.snapshot) ?
- this.snapshot.items : this.data.items;
- var v, sv, r = [], l = {};
- for(var i = 0, len = d.length; i < len; i++){
- v = d[i].data[dataIndex];
- sv = String(v);
- if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
- l[sv] = true;
- r[r.length] = v;
- }
- }
- return r;
- },
-
- /**
- * Revert to a view of the Record cache with no filtering applied.
- * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
- */
- clearFilter : function(suppressEvent){
- if(this.snapshot && this.snapshot != this.data){
- this.data = this.snapshot;
- delete this.snapshot;
- if(suppressEvent !== true){
- this.fireEvent("datachanged", this);
- }
- }
- },
-
- // private
- afterEdit : function(record){
- if(this.modified.indexOf(record) == -1){
- this.modified.push(record);
- }
- this.fireEvent("update", this, record, Roo.data.Record.EDIT);
- },
-
- // private
- afterReject : function(record){
- this.modified.remove(record);
- this.fireEvent("update", this, record, Roo.data.Record.REJECT);
- },
-
- // private
- afterCommit : function(record){
- this.modified.remove(record);
- this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
- },
-
- /**
- * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
- * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
- */
- commitChanges : function(){
- var m = this.modified.slice(0);
- this.modified = [];
- for(var i = 0, len = m.length; i < len; i++){
- m[i].commit();
- }
- },
-
- /**
- * Cancel outstanding changes on all changed records.
- */
- rejectChanges : function(){
- var m = this.modified.slice(0);
- this.modified = [];
- for(var i = 0, len = m.length; i < len; i++){
- m[i].reject();
- }
- },
-
- onMetaChange : function(meta, rtype, o){
- this.recordType = rtype;
- this.fields = rtype.prototype.fields;
- delete this.snapshot;
- this.sortInfo = meta.sortInfo || this.sortInfo;
- this.modified = [];
- this.fireEvent('metachange', this, this.reader.meta);
- },
-
- moveIndex : function(data, type)
- {
- var index = this.indexOf(data);
-
- var newIndex = index + type;
-
- this.remove(data);
-
- this.insert(newIndex, data);
-
- }
-});/*
- * 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.data.SimpleStore
- * @extends Roo.data.Store
- * Small helper class to make creating Stores from Array data easier.
- * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
- * @cfg {Array} fields An array of field definition objects, or field name strings.
- * @cfg {Array} data The multi-dimensional array of data
- * @constructor
- * @param {Object} config
- */
-Roo.data.SimpleStore = function(config){
- Roo.data.SimpleStore.superclass.constructor.call(this, {
- isLocal : true,
- reader: new Roo.data.ArrayReader({
- id: config.id
- },
- Roo.data.Record.create(config.fields)
- ),
- proxy : new Roo.data.MemoryProxy(config.data)
- });
- this.load();
-};
-Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
- * 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.data.Store
- * @class Roo.data.JsonStore
- * Small helper class to make creating Stores for JSON data easier. <br/>
-<pre><code>
-var store = new Roo.data.JsonStore({
- url: 'get-images.php',
- root: 'images',
- fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
-});
-</code></pre>
- * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
- * JsonReader and HttpProxy (unless inline data is provided).</b>
- * @cfg {Array} fields An array of field definition objects, or field name strings.
- * @constructor
- * @param {Object} config
- */
-Roo.data.JsonStore = function(c){
- Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
- proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
- reader: new Roo.data.JsonReader(c, c.fields)
- }));
-};
-Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
- * 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">
- */
-
-
-Roo.data.Field = function(config){
- if(typeof config == "string"){
- config = {name: config};
- }
- Roo.apply(this, config);
-
- if(!this.type){
- this.type = "auto";
- }
-
- var st = Roo.data.SortTypes;
- // named sortTypes are supported, here we look them up
- if(typeof this.sortType == "string"){
- this.sortType = st[this.sortType];
- }
-
- // set default sortType for strings and dates
- if(!this.sortType){
- switch(this.type){
- case "string":
- this.sortType = st.asUCString;
- break;
- case "date":
- this.sortType = st.asDate;
- break;
- default:
- this.sortType = st.none;
- }
- }
-
- // define once
- var stripRe = /[\$,%]/g;
-
- // prebuilt conversion function for this field, instead of
- // switching every time we're reading a value
- if(!this.convert){
- var cv, dateFormat = this.dateFormat;
- switch(this.type){
- case "":
- case "auto":
- case undefined:
- cv = function(v){ return v; };
- break;
- case "string":
- cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
- break;
- case "int":
- cv = function(v){
- return v !== undefined && v !== null && v !== '' ?
- parseInt(String(v).replace(stripRe, ""), 10) : '';
- };
- break;
- case "float":
- cv = function(v){
- return v !== undefined && v !== null && v !== '' ?
- parseFloat(String(v).replace(stripRe, ""), 10) : '';
- };
- break;
- case "bool":
- case "boolean":
- cv = function(v){ return v === true || v === "true" || v == 1; };
- break;
- case "date":
- cv = function(v){
- if(!v){
- return '';
- }
- if(v instanceof Date){
- return v;
- }
- if(dateFormat){
- if(dateFormat == "timestamp"){
- return new Date(v*1000);
- }
- return Date.parseDate(v, dateFormat);
- }
- var parsed = Date.parse(v);
- return parsed ? new Date(parsed) : null;
- };
- break;
-
- }
- this.convert = cv;
- }
-};
+ /**
+ * The function used to validate URLs
+ * @param {String} value The URL
+ */
+ 'url' : function(v){
+ return url.test(v);
+ },
+ /**
+ * The error text to display when the url validation function returns false
+ * @type String
+ */
+ 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
+
+ /**
+ * The function used to validate alpha values
+ * @param {String} value The value
+ */
+ 'alpha' : function(v){
+ return alpha.test(v);
+ },
+ /**
+ * The error text to display when the alpha validation function returns false
+ * @type String
+ */
+ 'alphaText' : 'This field should only contain letters and _',
+ /**
+ * The keystroke filter mask to be applied on alpha input
+ * @type RegExp
+ */
+ 'alphaMask' : /[a-z_]/i,
-Roo.data.Field.prototype = {
- dateFormat: null,
- defaultValue: "",
- mapping: null,
- sortType : null,
- sortDir : "ASC"
-};/*
- * 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.
+ /**
+ * The function used to validate alphanumeric values
+ * @param {String} value The value
+ */
+ 'alphanum' : function(v){
+ return alphanum.test(v);
+ },
+ /**
+ * The error text to display when the alphanumeric validation function returns false
+ * @type String
+ */
+ 'alphanumText' : 'This field should only contain letters, numbers and _',
+ /**
+ * The keystroke filter mask to be applied on alphanumeric input
+ * @type RegExp
+ */
+ 'alphanumMask' : /[a-z0-9_]/i
+ };
+}();/*
+ * - LGPL
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * Input
+ *
*/
-
-// Base class for reading structured data from a data source. This class is intended to be
-// extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
/**
- * @class Roo.data.DataReader
- * Base class for reading structured data from a data source. This class is intended to be
- * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
+ * @class Roo.bootstrap.Input
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Input class
+ * @cfg {Boolean} disabled is it disabled
+ * @cfg {String} fieldLabel - the label associated
+ * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
+ * @cfg {String} name name of the input
+ * @cfg {string} fieldLabel - the label associated
+ * @cfg {string} inputType - input / file submit ...
+ * @cfg {string} placeholder - placeholder to put in text.
+ * @cfg {string} before - input group add on before
+ * @cfg {string} after - input group add on after
+ * @cfg {string} size - (lg|sm) or leave empty..
+ * @cfg {Number} xs colspan out of 12 for mobile-sized screens
+ * @cfg {Number} sm colspan out of 12 for tablet-sized screens
+ * @cfg {Number} md colspan out of 12 for computer-sized screens
+ * @cfg {Number} lg colspan out of 12 for large computer-sized screens
+ * @cfg {string} value default value of the input
+ * @cfg {Number} labelWidth set the width of label (0-12)
+ * @cfg {String} labelAlign (top|left)
+ * @cfg {Boolean} readOnly Specifies that the field should be read-only
+ * @cfg {String} align (left|center|right) Default left
+ *
+ *
+ * @constructor
+ * Create a new Input
+ * @param {Object} config The config object
*/
-Roo.data.DataReader = function(meta, recordType){
-
- this.meta = meta;
-
- this.recordType = recordType instanceof Array ?
- Roo.data.Record.create(recordType) : recordType;
+Roo.bootstrap.Input = function(config){
+ Roo.bootstrap.Input.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ /**
+ * @event focus
+ * Fires when this field receives input focus.
+ * @param {Roo.form.Field} this
+ */
+ focus : true,
+ /**
+ * @event blur
+ * Fires when this field loses input focus.
+ * @param {Roo.form.Field} this
+ */
+ blur : true,
+ /**
+ * @event specialkey
+ * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
+ * {@link Roo.EventObject#getKey} to determine which key was pressed.
+ * @param {Roo.form.Field} this
+ * @param {Roo.EventObject} e The event object
+ */
+ specialkey : true,
+ /**
+ * @event change
+ * Fires just before the field blurs if the field value has changed.
+ * @param {Roo.form.Field} this
+ * @param {Mixed} newValue The new value
+ * @param {Mixed} oldValue The original value
+ */
+ change : true,
+ /**
+ * @event invalid
+ * Fires after the field has been marked as invalid.
+ * @param {Roo.form.Field} this
+ * @param {String} msg The validation message
+ */
+ invalid : true,
+ /**
+ * @event valid
+ * Fires after the field has been validated with no errors.
+ * @param {Roo.form.Field} this
+ */
+ valid : true,
+ /**
+ * @event keyup
+ * Fires after the key up
+ * @param {Roo.form.Field} this
+ * @param {Roo.EventObject} e The event Object
+ */
+ keyup : true
+ });
};
-Roo.data.DataReader.prototype = {
+Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
/**
- * Create an empty record
- * @param {Object} data (optional) - overlay some values
- * @return {Roo.data.Record} record created.
+ * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
+ automatic validation (defaults to "keyup").
+ */
+ validationEvent : "keyup",
+ /**
+ * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
+ */
+ validateOnBlur : true,
+ /**
+ * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
+ */
+ validationDelay : 250,
+ /**
+ * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
+ */
+ focusClass : "x-form-focus", // not needed???
+
+
+ /**
+ * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
+ */
+ invalidClass : "has-error",
+
+ /**
+ * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
+ */
+ selectOnFocus : false,
+
+ /**
+ * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
+ */
+ maskRe : null,
+ /**
+ * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
+ */
+ vtype : null,
+
+ /**
+ * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
+ */
+ disableKeyFilter : false,
+
+ /**
+ * @cfg {Boolean} disabled True to disable the field (defaults to false).
+ */
+ disabled : false,
+ /**
+ * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
+ */
+ allowBlank : true,
+ /**
+ * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
+ */
+ blankText : "This field is required",
+
+ /**
+ * @cfg {Number} minLength Minimum input field length required (defaults to 0)
+ */
+ minLength : 0,
+ /**
+ * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
+ */
+ maxLength : Number.MAX_VALUE,
+ /**
+ * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
+ */
+ minLengthText : "The minimum length for this field is {0}",
+ /**
+ * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
+ */
+ maxLengthText : "The maximum length for this field is {0}",
+
+
+ /**
+ * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
+ * If available, this function will be called only after the basic validators all return true, and will be passed the
+ * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
+ */
+ validator : null,
+ /**
+ * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
+ * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
+ * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
+ */
+ regex : null,
+ /**
+ * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
*/
- newRow : function(d) {
- var da = {};
- this.recordType.prototype.fields.each(function(c) {
- switch( c.type) {
- case 'int' : da[c.name] = 0; break;
- case 'date' : da[c.name] = new Date(); break;
- case 'float' : da[c.name] = 0.0; break;
- case 'boolean' : da[c.name] = false; break;
- default : da[c.name] = ""; break;
+ regexText : "",
+
+
+
+ fieldLabel : '',
+ inputType : 'text',
+
+ name : false,
+ placeholder: false,
+ before : false,
+ after : false,
+ size : false,
+ // private
+ hasFocus : false,
+ preventMark: false,
+ isFormField : true,
+ value : '',
+ labelWidth : 2,
+ labelAlign : false,
+ readOnly : false,
+ align : false,
+
+ parentLabelAlign : function()
+ {
+ var parent = this;
+ while (parent.parent()) {
+ parent = parent.parent();
+ if (typeof(parent.labelAlign) !='undefined') {
+ return parent.labelAlign;
}
+ }
+ return 'left';
+
+ },
+
+ getAutoCreate : function(){
+
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+
+ var id = Roo.id();
+
+ var cfg = {};
+
+ if(this.inputType != 'hidden'){
+ cfg.cls = 'form-group' //input-group
+ }
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ value : this.value,
+ cls : 'form-control',
+ placeholder : this.placeholder || ''
+ };
+
+ if(this.align){
+ input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
+ }
+
+ if(this.maxLength && this.maxLength != Number.MAX_VALUE){
+ input.maxLength = this.maxLength;
+ }
+
+ if (this.disabled) {
+ input.disabled=true;
+ }
+
+ if (this.readOnly) {
+ input.readonly=true;
+ }
+
+ if (this.name) {
+ input.name = this.name;
+ }
+ if (this.size) {
+ input.cls += ' input-' + this.size;
+ }
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
});
- return new this.recordType(Roo.apply(da, d));
- }
-
-};/*
- * 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.data.DataProxy
- * @extends Roo.data.Observable
- * This class is an abstract base class for implementations which provide retrieval of
- * unformatted data objects.<br>
- * <p>
- * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
- * (of the appropriate type which knows how to parse the data object) to provide a block of
- * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
- * <p>
- * Custom implementations must implement the load method as described in
- * {@link Roo.data.HttpProxy#load}.
- */
-Roo.data.DataProxy = function(){
- this.addEvents({
- /**
- * @event beforeload
- * Fires before a network request is made to retrieve a data object.
- * @param {Object} This DataProxy object.
- * @param {Object} params The params parameter to the load function.
- */
- beforeload : true,
- /**
- * @event load
- * Fires before the load method's callback is called.
- * @param {Object} This DataProxy object.
- * @param {Object} o The data object.
- * @param {Object} arg The callback argument object passed to the load function.
- */
- load : true,
- /**
- * @event loadexception
- * Fires if an Exception occurs during data retrieval.
- * @param {Object} This DataProxy object.
- * @param {Object} o The data object.
- * @param {Object} arg The callback argument object passed to the load function.
- * @param {Object} e The Exception.
- */
- loadexception : true
- });
- Roo.data.DataProxy.superclass.constructor.call(this);
-};
-
-Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
-
- /**
- * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
- */
-/*
- * 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.data.MemoryProxy
- * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
- * to the Reader when its load method is called.
- * @constructor
- * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
- */
-Roo.data.MemoryProxy = function(data){
- if (data.data) {
- data = data.data;
- }
- Roo.data.MemoryProxy.superclass.constructor.call(this);
- this.data = data;
-};
+
+ var inputblock = input;
+
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
+ };
+ if (this.before && typeof(this.before) == 'string') {
+
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'roo-input-before input-group-addon',
+ html : this.before
+ });
+ }
+ if (this.before && typeof(this.before) == 'object') {
+ this.before = Roo.factory(this.before);
+ Roo.log(this.before);
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'roo-input-before input-group-' +
+ (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
+ });
+ }
+
+ inputblock.cn.push(input);
+
+ if (this.after && typeof(this.after) == 'string') {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'roo-input-after input-group-addon',
+ html : this.after
+ });
+ }
+ if (this.after && typeof(this.after) == 'object') {
+ this.after = Roo.factory(this.after);
+ Roo.log(this.after);
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'roo-input-after input-group-' +
+ (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
+ });
+ }
+ };
+
+ if (align ==='left' && this.fieldLabel.length) {
+ Roo.log("left and has label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-sm-' + this.labelWidth,
+ html : this.fieldLabel
+
+ },
+ {
+ cls : "col-sm-" + (12 - this.labelWidth),
+ cn: [
+ inputblock
+ ]
+ }
+
+ ];
+ } else if ( this.fieldLabel.length) {
+ Roo.log(" label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+
+ },
+
+ inputblock
+
+ ];
-Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
+ } else {
+
+ Roo.log(" no label && no align");
+ cfg.cn = [
+
+ inputblock
+
+ ];
+
+
+ };
+ Roo.log('input-parentType: ' + this.parentType);
+
+ if (this.parentType === 'Navbar' && this.parent().bar) {
+ cfg.cls += ' navbar-form';
+ Roo.log(cfg);
+ }
+
+ return cfg;
+
+ },
/**
- * Load data from the requested source (in this case an in-memory
- * data object passed to the constructor), read the data object into
- * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
- * process that block using the passed callback.
- * @param {Object} params This parameter is not used by the MemoryProxy class.
- * @param {Roo.data.DataReader} reader The Reader object which converts the data
- * object into a block of Roo.data.Records.
- * @param {Function} callback The function into which to pass the block of Roo.data.records.
- * The function must be passed <ul>
- * <li>The Record block object</li>
- * <li>The "arg" argument from the load function</li>
- * <li>A boolean success indicator</li>
- * </ul>
- * @param {Object} scope The scope in which to call the callback
- * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ * return the real input element.
*/
- load : function(params, reader, callback, scope, arg){
- params = params || {};
- var result;
- try {
- result = reader.readRecords(this.data);
- }catch(e){
- this.fireEvent("loadexception", this, arg, null, e);
- callback.call(scope, null, arg, false);
+ inputEl: function ()
+ {
+ return this.el.select('input.form-control',true).first();
+ },
+ setDisabled : function(v)
+ {
+ var i = this.inputEl().dom;
+ if (!v) {
+ i.removeAttribute('disabled');
return;
+
}
- callback.call(scope, result, arg, true);
+ i.setAttribute('disabled','true');
},
-
- // private
- update : function(params, records){
+ initEvents : function()
+ {
- }
-});/*
- * 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.data.HttpProxy
- * @extends Roo.data.DataProxy
- * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
- * configured to reference a certain URL.<br><br>
- * <p>
- * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
- * from which the running page was served.<br><br>
- * <p>
- * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
- * <p>
- * Be aware that to enable the browser to parse an XML document, the server must set
- * the Content-Type header in the HTTP response to "text/xml".
- * @constructor
- * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
- * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
- * will be used to make the request.
- */
-Roo.data.HttpProxy = function(conn){
- Roo.data.HttpProxy.superclass.constructor.call(this);
- // is conn a conn config or a real conn?
- this.conn = conn;
- this.useAjax = !conn || !conn.events;
-
-};
+ this.inputEl().on("keydown" , this.fireKey, this);
+ this.inputEl().on("focus", this.onFocus, this);
+ this.inputEl().on("blur", this.onBlur, this);
+
+ this.inputEl().relayEvent('keyup', this);
-Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
- // thse are take from connection...
-
- /**
- * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
- */
- /**
- * @cfg {Object} extraParams (Optional) An object containing properties which are used as
- * extra parameters to each request made by this object. (defaults to undefined)
+ // reference to original value for reset
+ this.originalValue = this.getValue();
+ //Roo.form.TextField.superclass.initEvents.call(this);
+ if(this.validationEvent == 'keyup'){
+ this.validationTask = new Roo.util.DelayedTask(this.validate, this);
+ this.inputEl().on('keyup', this.filterValidation, this);
+ }
+ else if(this.validationEvent !== false){
+ this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
+ }
+
+ if(this.selectOnFocus){
+ this.on("focus", this.preFocus, this);
+
+ }
+ if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
+ this.inputEl().on("keypress", this.filterKeys, this);
+ }
+ /* if(this.grow){
+ this.el.on("keyup", this.onKeyUp, this, {buffer:50});
+ this.el.on("click", this.autoSize, this);
+ }
+ */
+ if(this.inputEl().is('input[type=password]') && Roo.isSafari){
+ this.inputEl().on('keydown', this.SafariOnKeyDown, this);
+ }
+
+ if (typeof(this.before) == 'object') {
+ this.before.render(this.el.select('.roo-input-before',true).first());
+ }
+ if (typeof(this.after) == 'object') {
+ this.after.render(this.el.select('.roo-input-after',true).first());
+ }
+
+
+ },
+ filterValidation : function(e){
+ if(!e.isNavKeyPress()){
+ this.validationTask.delay(this.validationDelay);
+ }
+ },
+ /**
+ * Validates the field value
+ * @return {Boolean} True if the value is valid, else false
*/
+ validate : function(){
+ //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
+ if(this.disabled || this.validateValue(this.getRawValue())){
+ this.clearInvalid();
+ return true;
+ }
+ return false;
+ },
+
+
/**
- * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
- * to each request made by this object. (defaults to undefined)
+ * Validates a value according to the field's validation rules and marks the field as invalid
+ * if the validation fails
+ * @param {Mixed} value The value to validate
+ * @return {Boolean} True if the value is valid, else false
*/
+ validateValue : function(value){
+ if(value.length < 1) { // if it's blank
+ if(this.allowBlank){
+ this.clearInvalid();
+ return true;
+ }else{
+ this.markInvalid(this.blankText);
+ return false;
+ }
+ }
+ if(value.length < this.minLength){
+ this.markInvalid(String.format(this.minLengthText, this.minLength));
+ return false;
+ }
+ if(value.length > this.maxLength){
+ this.markInvalid(String.format(this.maxLengthText, this.maxLength));
+ return false;
+ }
+ if(this.vtype){
+ var vt = Roo.form.VTypes;
+ if(!vt[this.vtype](value, this)){
+ this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
+ return false;
+ }
+ }
+ if(typeof this.validator == "function"){
+ var msg = this.validator(value);
+ if(msg !== true){
+ this.markInvalid(msg);
+ return false;
+ }
+ }
+ if(this.regex && !this.regex.test(value)){
+ this.markInvalid(this.regexText);
+ return false;
+ }
+ return true;
+ },
+
+
+
+ // private
+ fireKey : function(e){
+ //Roo.log('field ' + e.getKey());
+ if(e.isNavKeyPress()){
+ this.fireEvent("specialkey", this, e);
+ }
+ },
+ focus : function (selectText){
+ if(this.rendered){
+ this.inputEl().focus();
+ if(selectText === true){
+ this.inputEl().dom.select();
+ }
+ }
+ return this;
+ } ,
+
+ onFocus : function(){
+ if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
+ // this.el.addClass(this.focusClass);
+ }
+ if(!this.hasFocus){
+ this.hasFocus = true;
+ this.startValue = this.getValue();
+ this.fireEvent("focus", this);
+ }
+ },
+
+ beforeBlur : Roo.emptyFn,
+
+
+ // private
+ onBlur : function(){
+ this.beforeBlur();
+ if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
+ //this.el.removeClass(this.focusClass);
+ }
+ this.hasFocus = false;
+ if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
+ this.validate();
+ }
+ var v = this.getValue();
+ if(String(v) !== String(this.startValue)){
+ this.fireEvent('change', this, v, this.startValue);
+ }
+ this.fireEvent("blur", this);
+ },
+
/**
- * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
+ * Resets the current field value to the originally loaded value and clears any validation messages
*/
- /**
- * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+ reset : function(){
+ this.setValue(this.originalValue);
+ this.clearInvalid();
+ },
+ /**
+ * Returns the name of the field
+ * @return {Mixed} name The name field
*/
+ getName: function(){
+ return this.name;
+ },
/**
- * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
- * @type Boolean
+ * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
+ * @return {Mixed} value The field value
*/
-
-
+ getValue : function(){
+ return this.inputEl().getValue();
+ },
/**
- * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
- * @type Boolean
+ * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
+ * @return {Mixed} value The field value
*/
+ getRawValue : function(){
+ var v = this.inputEl().getValue();
+
+ return v;
+ },
+
/**
- * Return the {@link Roo.data.Connection} object being used by this Proxy.
- * @return {Connection} The Connection object. This object may be used to subscribe to events on
- * a finer-grained basis than the DataProxy events.
+ * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
+ * @param {Mixed} value The value to set
*/
- getConnection : function(){
- return this.useAjax ? Roo.Ajax : this.conn;
+ setRawValue : function(v){
+ return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
},
-
+
+ selectText : function(start, end){
+ var v = this.getRawValue();
+ if(v.length > 0){
+ start = start === undefined ? 0 : start;
+ end = end === undefined ? v.length : end;
+ var d = this.inputEl().dom;
+ if(d.setSelectionRange){
+ d.setSelectionRange(start, end);
+ }else if(d.createTextRange){
+ var range = d.createTextRange();
+ range.moveStart("character", start);
+ range.moveEnd("character", v.length-end);
+ range.select();
+ }
+ }
+ },
+
/**
- * Load data from the configured {@link Roo.data.Connection}, read the data object into
- * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
- * process that block using the passed callback.
- * @param {Object} params An object containing properties which are to be used as HTTP parameters
- * for the request to the remote server.
- * @param {Roo.data.DataReader} reader The Reader object which converts the data
- * object into a block of Roo.data.Records.
- * @param {Function} callback The function into which to pass the block of Roo.data.Records.
- * The function must be passed <ul>
- * <li>The Record block object</li>
- * <li>The "arg" argument from the load function</li>
- * <li>A boolean success indicator</li>
- * </ul>
- * @param {Object} scope The scope in which to call the callback
- * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
+ * @param {Mixed} value The value to set
*/
- load : function(params, reader, callback, scope, arg){
- if(this.fireEvent("beforeload", this, params) !== false){
- var o = {
- params : params || {},
- request: {
- callback : callback,
- scope : scope,
- arg : arg
- },
- reader: reader,
- callback : this.loadResponse,
- scope: this
- };
- if(this.useAjax){
- Roo.applyIf(o, this.conn);
- if(this.activeRequest){
- Roo.Ajax.abort(this.activeRequest);
- }
- this.activeRequest = Roo.Ajax.request(o);
- }else{
- this.conn.request(o);
+ setValue : function(v){
+ this.value = v;
+ if(this.rendered){
+ this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
+ this.validate();
+ }
+ },
+
+ /*
+ processValue : function(value){
+ if(this.stripCharsRe){
+ var newValue = value.replace(this.stripCharsRe, '');
+ if(newValue !== value){
+ this.setRawValue(newValue);
+ return newValue;
}
- }else{
- callback.call(scope||this, null, arg, false);
}
+ return value;
},
-
- // private
- loadResponse : function(o, success, response){
- delete this.activeRequest;
- if(!success){
- this.fireEvent("loadexception", this, o, response);
- o.request.callback.call(o.request.scope, null, o.request.arg, false);
+ */
+ preFocus : function(){
+
+ if(this.selectOnFocus){
+ this.inputEl().dom.select();
+ }
+ },
+ filterKeys : function(e){
+ var k = e.getKey();
+ if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
return;
}
- var result;
- try {
- result = o.reader.read(response);
- }catch(e){
- this.fireEvent("loadexception", this, o, response, e);
- o.request.callback.call(o.request.scope, null, o.request.arg, false);
+ var c = e.getCharCode(), cc = String.fromCharCode(c);
+ if(Roo.isIE && (e.isSpecialKey() || !cc)){
+ return;
+ }
+ if(!this.maskRe.test(cc)){
+ e.stopEvent();
+ }
+ },
+ /**
+ * Clear any invalid styles/messages for this field
+ */
+ clearInvalid : function(){
+
+ if(!this.el || this.preventMark){ // not rendered
+ return;
+ }
+ this.el.removeClass(this.invalidClass);
+ /*
+ switch(this.msgTarget){
+ case 'qtip':
+ this.el.dom.qtip = '';
+ break;
+ case 'title':
+ this.el.dom.title = '';
+ break;
+ case 'under':
+ if(this.errorEl){
+ Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
+ }
+ break;
+ case 'side':
+ if(this.errorIcon){
+ this.errorIcon.dom.qtip = '';
+ this.errorIcon.hide();
+ this.un('resize', this.alignErrorIcon, this);
+ }
+ break;
+ default:
+ var t = Roo.getDom(this.msgTarget);
+ t.innerHTML = '';
+ t.style.display = 'none';
+ break;
+ }
+ */
+ this.fireEvent('valid', this);
+ },
+ /**
+ * Mark this field as invalid
+ * @param {String} msg The validation message
+ */
+ markInvalid : function(msg){
+ if(!this.el || this.preventMark){ // not rendered
+ return;
+ }
+ this.el.addClass(this.invalidClass);
+ /*
+ msg = msg || this.invalidText;
+ switch(this.msgTarget){
+ case 'qtip':
+ this.el.dom.qtip = msg;
+ this.el.dom.qclass = 'x-form-invalid-tip';
+ if(Roo.QuickTips){ // fix for floating editors interacting with DND
+ Roo.QuickTips.enable();
+ }
+ break;
+ case 'title':
+ this.el.dom.title = msg;
+ break;
+ case 'under':
+ if(!this.errorEl){
+ var elp = this.el.findParent('.x-form-element', 5, true);
+ this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
+ this.errorEl.setWidth(elp.getWidth(true)-20);
+ }
+ this.errorEl.update(msg);
+ Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
+ break;
+ case 'side':
+ if(!this.errorIcon){
+ var elp = this.el.findParent('.x-form-element', 5, true);
+ this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
+ }
+ this.alignErrorIcon();
+ this.errorIcon.dom.qtip = msg;
+ this.errorIcon.dom.qclass = 'x-form-invalid-tip';
+ this.errorIcon.show();
+ this.on('resize', this.alignErrorIcon, this);
+ break;
+ default:
+ var t = Roo.getDom(this.msgTarget);
+ t.innerHTML = msg;
+ t.style.display = this.msgDisplay;
+ break;
+ }
+ */
+ this.fireEvent('invalid', this, msg);
+ },
+ // private
+ SafariOnKeyDown : function(event)
+ {
+ // this is a workaround for a password hang bug on chrome/ webkit.
+
+ var isSelectAll = false;
+
+ if(this.inputEl().dom.selectionEnd > 0){
+ isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
+ }
+ if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
+ event.preventDefault();
+ this.setValue('');
return;
}
- this.fireEvent("load", this, o, o.request.arg);
- o.request.callback.call(o.request.scope, result, o.request.arg, true);
- },
-
- // private
- update : function(dataSet){
-
+ if(isSelectAll){ // backspace and delete key
+
+ event.preventDefault();
+ // this is very hacky as keydown always get's upper case.
+ //
+ var cc = String.fromCharCode(event.getCharCode());
+ this.setValue( event.shiftKey ? cc : cc.toLowerCase());
+
+ }
},
-
- // private
- updateResponse : function(dataSet){
-
+ adjustWidth : function(tag, w){
+ tag = tag.toLowerCase();
+ if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
+ if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
+ if(tag == 'input'){
+ return w + 2;
+ }
+ if(tag == 'textarea'){
+ return w-2;
+ }
+ }else if(Roo.isOpera){
+ if(tag == 'input'){
+ return w + 2;
+ }
+ if(tag == 'textarea'){
+ return w-2;
+ }
+ }
+ }
+ return w;
}
-});/*
- * 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.
+
+});
+
+
+/*
+ * - LGPL
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * Input
+ *
*/
/**
- * @class Roo.data.ScriptTagProxy
- * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
- * other than the originating domain of the running page.<br><br>
- * <p>
- * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
- * of the running page, you must use this class, rather than DataProxy.</em><br><br>
- * <p>
- * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
- * source code that is used as the source inside a <script> tag.<br><br>
- * <p>
- * In order for the browser to process the returned data, the server must wrap the data object
- * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
- * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
- * depending on whether the callback name was passed:
- * <p>
- * <pre><code>
-boolean scriptTag = false;
-String cb = request.getParameter("callback");
-if (cb != null) {
- scriptTag = true;
- response.setContentType("text/javascript");
-} else {
- response.setContentType("application/x-json");
-}
-Writer out = response.getWriter();
-if (scriptTag) {
- out.write(cb + "(");
-}
-out.print(dataBlock.toJsonString());
-if (scriptTag) {
- out.write(");");
-}
-</pre></code>
- *
+ * @class Roo.bootstrap.TextArea
+ * @extends Roo.bootstrap.Input
+ * Bootstrap TextArea class
+ * @cfg {Number} cols Specifies the visible width of a text area
+ * @cfg {Number} rows Specifies the visible number of lines in a text area
+ * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
+ * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
+ * @cfg {string} html text
+ *
* @constructor
- * @param {Object} config A configuration object.
+ * Create a new TextArea
+ * @param {Object} config The config object
*/
-Roo.data.ScriptTagProxy = function(config){
- Roo.data.ScriptTagProxy.superclass.constructor.call(this);
- Roo.apply(this, config);
- this.head = document.getElementsByTagName("head")[0];
-};
-
-Roo.data.ScriptTagProxy.TRANS_ID = 1000;
-
-Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
- /**
- * @cfg {String} url The URL from which to request the data object.
- */
- /**
- * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
- */
- timeout : 30000,
- /**
- * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
- * the server the name of the callback function set up by the load call to process the returned data object.
- * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
- * javascript output which calls this named function passing the data object as its only parameter.
- */
- callbackParam : "callback",
- /**
- * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
- * name to the request.
- */
- nocache : true,
-
- /**
- * Load data from the configured URL, read the data object into
- * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
- * process that block using the passed callback.
- * @param {Object} params An object containing properties which are to be used as HTTP parameters
- * for the request to the remote server.
- * @param {Roo.data.DataReader} reader The Reader object which converts the data
- * object into a block of Roo.data.Records.
- * @param {Function} callback The function into which to pass the block of Roo.data.Records.
- * The function must be passed <ul>
- * <li>The Record block object</li>
- * <li>The "arg" argument from the load function</li>
- * <li>A boolean success indicator</li>
- * </ul>
- * @param {Object} scope The scope in which to call the callback
- * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
- */
- load : function(params, reader, callback, scope, arg){
- if(this.fireEvent("beforeload", this, params) !== false){
-
- var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
-
- var url = this.url;
- url += (url.indexOf("?") != -1 ? "&" : "?") + p;
- if(this.nocache){
- url += "&_dc=" + (new Date().getTime());
- }
- var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
- var trans = {
- id : transId,
- cb : "stcCallback"+transId,
- scriptId : "stcScript"+transId,
- params : params,
- arg : arg,
- url : url,
- callback : callback,
- scope : scope,
- reader : reader
- };
- var conn = this;
-
- window[trans.cb] = function(o){
- conn.handleResponse(o, trans);
- };
-
- url += String.format("&{0}={1}", this.callbackParam, trans.cb);
-
- if(this.autoAbort !== false){
- this.abort();
- }
-
- trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
- var script = document.createElement("script");
- script.setAttribute("src", url);
- script.setAttribute("type", "text/javascript");
- script.setAttribute("id", trans.scriptId);
- this.head.appendChild(script);
+Roo.bootstrap.TextArea = function(config){
+ Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
+
+};
- this.trans = trans;
- }else{
- callback.call(scope||this, null, arg, false);
+Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
+
+ cols : false,
+ rows : 5,
+ readOnly : false,
+ warp : 'soft',
+ resize : false,
+ value: false,
+ html: false,
+
+ getAutoCreate : function(){
+
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+
+ var id = Roo.id();
+
+ var cfg = {};
+
+ var input = {
+ tag: 'textarea',
+ id : id,
+ warp : this.warp,
+ rows : this.rows,
+ value : this.value || '',
+ html: this.html || '',
+ cls : 'form-control',
+ placeholder : this.placeholder || ''
+
+ };
+
+ if(this.maxLength && this.maxLength != Number.MAX_VALUE){
+ input.maxLength = this.maxLength;
}
- },
-
- // private
- isLoading : function(){
- return this.trans ? true : false;
- },
-
- /**
- * Abort the current server request.
- */
- abort : function(){
- if(this.isLoading()){
- this.destroyTrans(this.trans);
+
+ if(this.resize){
+ input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
+ }
+
+ if(this.cols){
+ input.cols = this.cols;
+ }
+
+ if (this.readOnly) {
+ input.readonly = true;
+ }
+
+ if (this.name) {
+ input.name = this.name;
}
- },
-
- // private
- destroyTrans : function(trans, isLoaded){
- this.head.removeChild(document.getElementById(trans.scriptId));
- clearTimeout(trans.timeoutId);
- if(isLoaded){
- window[trans.cb] = undefined;
- try{
- delete window[trans.cb];
- }catch(e){}
- }else{
- // if hasn't been loaded, wait for load to remove it to prevent script error
- window[trans.cb] = function(){
- window[trans.cb] = undefined;
- try{
- delete window[trans.cb];
- }catch(e){}
+
+ if (this.size) {
+ input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
+ }
+
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ var inputblock = input;
+
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
};
+ if (this.before) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.before
+ });
+ }
+ inputblock.cn.push(input);
+ if (this.after) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.after
+ });
+ }
+
}
- },
+
+ if (align ==='left' && this.fieldLabel.length) {
+ Roo.log("left and has label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-sm-' + this.labelWidth,
+ html : this.fieldLabel
+
+ },
+ {
+ cls : "col-sm-" + (12 - this.labelWidth),
+ cn: [
+ inputblock
+ ]
+ }
+
+ ];
+ } else if ( this.fieldLabel.length) {
+ Roo.log(" label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+
+ },
+
+ inputblock
+
+ ];
- // private
- handleResponse : function(o, trans){
- this.trans = false;
- this.destroyTrans(trans, true);
- var result;
- try {
- result = trans.reader.readRecords(o);
- }catch(e){
- this.fireEvent("loadexception", this, o, trans.arg, e);
- trans.callback.call(trans.scope||window, null, trans.arg, false);
- return;
+ } else {
+
+ Roo.log(" no label && no align");
+ cfg.cn = [
+
+ inputblock
+
+ ];
+
+
}
- this.fireEvent("load", this, o, trans.arg);
- trans.callback.call(trans.scope||window, result, trans.arg, true);
+
+ if (this.disabled) {
+ input.disabled=true;
+ }
+
+ return cfg;
+
},
-
- // private
- handleFailure : function(trans){
- this.trans = false;
- this.destroyTrans(trans, false);
- this.fireEvent("loadexception", this, null, trans.arg);
- trans.callback.call(trans.scope||window, null, trans.arg, false);
+ /**
+ * return the real textarea element.
+ */
+ inputEl: function ()
+ {
+ return this.el.select('textarea.form-control',true).first();
}
-});/*
- * 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.
+});
+
+
+/*
+ * - LGPL
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * trigger field - base class for combo..
+ *
*/
-
+
/**
- * @class Roo.data.JsonReader
- * @extends Roo.data.DataReader
- * Data reader class to create an Array of Roo.data.Record objects from a JSON response
- * based on mappings in a provided Roo.data.Record constructor.
- *
- * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
- * in the reply previously.
- *
- * <p>
- * Example code:
- * <pre><code>
-var RecordDef = Roo.data.Record.create([
- {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
- {name: 'occupation'} // This field will use "occupation" as the mapping.
-]);
-var myReader = new Roo.data.JsonReader({
- totalProperty: "results", // The property which contains the total dataset size (optional)
- root: "rows", // The property which contains an Array of row objects
- id: "id" // The property within each row object that provides an ID for the record (optional)
-}, RecordDef);
-</code></pre>
- * <p>
- * This would consume a JSON file like this:
+ * @class Roo.bootstrap.TriggerField
+ * @extends Roo.bootstrap.Input
+ * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
+ * The trigger has no default action, so you must assign a function to implement the trigger click handler by
+ * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
+ * for which you can provide a custom implementation. For example:
* <pre><code>
-{ 'results': 2, 'rows': [
- { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
- { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
-}
+var trigger = new Roo.bootstrap.TriggerField();
+trigger.onTriggerClick = myTriggerFn;
+trigger.applyTo('my-field');
</code></pre>
- * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
- * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
- * paged from the remote server.
- * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
- * @cfg {String} root name of the property which contains the Array of row objects.
- * @cfg {String} id Name of the property within a row object that contains a record identifier value.
+ *
+ * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
+ * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
+ * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
+ * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
* @constructor
- * Create a new JsonReader
- * @param {Object} meta Metadata configuration options
- * @param {Object} recordType Either an Array of field definition objects,
- * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
+ * Create a new TriggerField.
+ * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
+ * to the base TextField)
*/
-Roo.data.JsonReader = function(meta, recordType){
-
- meta = meta || {};
- // set some defaults:
- Roo.applyIf(meta, {
- totalProperty: 'total',
- successProperty : 'success',
- root : 'data',
- id : 'id'
- });
-
- Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+Roo.bootstrap.TriggerField = function(config){
+ this.mimicing = false;
+ Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
-
- /**
- * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
- * Used by Store query builder to append _requestMeta to params.
- *
- */
- metaFromRemote : false,
- /**
- * This method is only used by a DataProxy which has retrieved data from a remote server.
- * @param {Object} response The XHR object which contains the JSON data in its responseText.
- * @return {Object} data A data block which is used by an Roo.data.Store object as
- * a cache of Roo.data.Records.
- */
- read : function(response){
- var json = response.responseText;
-
- var o = /* eval:var:o */ eval("("+json+")");
- if(!o) {
- throw {message: "JsonReader.read: Json object not found"};
- }
-
- if(o.metaData){
-
- delete this.ef;
- this.metaFromRemote = true;
- this.meta = o.metaData;
- this.recordType = Roo.data.Record.create(o.metaData.fields);
- this.onMetaChange(this.meta, this.recordType, o);
- }
- return this.readRecords(o);
- },
-
- // private function a store will implement
- onMetaChange : function(meta, recordType, o){
-
- },
+Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
/**
- * @ignore
- */
- simpleAccess: function(obj, subsc) {
- return obj[subsc];
- },
+ * @cfg {String} triggerClass A CSS class to apply to the trigger
+ */
+ /**
+ * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
+ */
+ hideTrigger:false,
- /**
- * @ignore
- */
- getJsonAccessor: function(){
- var re = /[\[\.]/;
- return function(expr) {
- try {
- return(re.test(expr))
- ? new Function("obj", "return obj." + expr)
- : function(obj){
- return obj[expr];
- };
- } catch(e){}
- return Roo.emptyFn;
- };
- }(),
+ /** @cfg {Boolean} grow @hide */
+ /** @cfg {Number} growMin @hide */
+ /** @cfg {Number} growMax @hide */
/**
- * Create a data block containing Roo.data.Records from an XML document.
- * @param {Object} o An object which contains an Array of row objects in the property specified
- * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
- * which contains the total size of the dataset.
- * @return {Object} data A data block which is used by an Roo.data.Store object as
- * a cache of Roo.data.Records.
+ * @hide
+ * @method
*/
- readRecords : function(o){
- /**
- * After any data loads, the raw JSON data is available for further custom processing.
- * @type Object
- */
- this.o = o;
- var s = this.meta, Record = this.recordType,
- f = Record.prototype.fields, fi = f.items, fl = f.length;
+ autoSize: Roo.emptyFn,
+ // private
+ monitorTab : true,
+ // private
+ deferHeight : true,
-// Generate extraction functions for the totalProperty, the root, the id, and for each field
- if (!this.ef) {
- if(s.totalProperty) {
- this.getTotal = this.getJsonAccessor(s.totalProperty);
- }
- if(s.successProperty) {
- this.getSuccess = this.getJsonAccessor(s.successProperty);
- }
- this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
- if (s.id) {
- var g = this.getJsonAccessor(s.id);
- this.getId = function(rec) {
- var r = g(rec);
- return (r === undefined || r === "") ? null : r;
- };
- } else {
- this.getId = function(){return null;};
- }
- this.ef = [];
- for(var jj = 0; jj < fl; jj++){
- f = fi[jj];
- var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
- this.ef[jj] = this.getJsonAccessor(map);
- }
+
+ actionMode : 'wrap',
+
+
+
+ getAutoCreate : function(){
+
+ var parent = this.parent();
+
+ var align = this.labelAlign || this.parentLabelAlign();
+
+ var id = Roo.id();
+
+ var cfg = {
+ cls: 'form-group' //input-group
+ };
+
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ cls : 'form-control',
+ autocomplete: 'off',
+ placeholder : this.placeholder || ''
+
+ };
+ if (this.name) {
+ input.name = this.name;
}
-
- var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
- if(s.totalProperty){
- var vt = parseInt(this.getTotal(o), 10);
- if(!isNaN(vt)){
- totalRecords = vt;
- }
+ if (this.size) {
+ input.cls += ' input-' + this.size;
}
- if(s.successProperty){
- var vs = this.getSuccess(o);
- if(vs === false || vs === 'false'){
- success = false;
- }
+
+ if (this.disabled) {
+ input.disabled=true;
}
- var records = [];
- for(var i = 0; i < c; i++){
- var n = root[i];
- var values = {};
- var id = this.getId(n);
- for(var j = 0; j < fl; j++){
- f = fi[j];
- var v = this.ef[j](n);
- if (!f.convert) {
- Roo.log('missing convert for ' + f.name);
- Roo.log(f);
- continue;
+
+ var inputblock = input;
+
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
+ };
+ if (this.before) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.before
+ });
+ }
+ inputblock.cn.push(input);
+ if (this.after) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.after
+ });
+ }
+
+ };
+
+ var box = {
+ tag: 'div',
+ cn: [
+ {
+ tag: 'input',
+ type : 'hidden',
+ cls: 'form-hidden-field'
+ },
+ inputblock
+ ]
+
+ };
+
+ if(this.multiple){
+ Roo.log('multiple');
+
+ box = {
+ tag: 'div',
+ cn: [
+ {
+ tag: 'input',
+ type : 'hidden',
+ cls: 'form-hidden-field'
+ },
+ {
+ tag: 'ul',
+ cls: 'select2-choices',
+ cn:[
+ {
+ tag: 'li',
+ cls: 'select2-search-field',
+ cn: [
+
+ inputblock
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ };
+
+ var combobox = {
+ cls: 'select2-container input-group',
+ cn: [
+ box,
+ {
+ tag: 'ul',
+ cls: 'typeahead typeahead-long dropdown-menu',
+ style: 'display:none'
}
- values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
- }
- var record = new Record(values, id);
- record.json = n;
- records[i] = record;
- }
- return {
- raw : o,
- success : success,
- records : records,
- totalRecords : totalRecords
- };
- }
-});/*
- * 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">
- */
+ ]
+ };
+
+ if(!this.multiple){
+ combobox.cn.push({
+ tag :'span',
+ cls : 'input-group-addon btn dropdown-toggle',
+ cn : [
+ {
+ tag: 'span',
+ cls: 'caret'
+ },
+ {
+ tag: 'span',
+ cls: 'combobox-clear',
+ cn : [
+ {
+ tag : 'i',
+ cls: 'icon-remove'
+ }
+ ]
+ }
+ ]
-/**
- * @class Roo.data.ArrayReader
- * @extends Roo.data.DataReader
- * Data reader class to create an Array of Roo.data.Record objects from an Array.
- * Each element of that Array represents a row of data fields. The
- * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
- * of the field definition if it exists, or the field's ordinal position in the definition.<br>
- * <p>
- * Example code:.
- * <pre><code>
-var RecordDef = Roo.data.Record.create([
- {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
- {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
-]);
-var myReader = new Roo.data.ArrayReader({
- id: 0 // The subscript within row Array that provides an ID for the Record (optional)
-}, RecordDef);
-</code></pre>
- * <p>
- * This would consume an Array like this:
- * <pre><code>
-[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
- </code></pre>
- * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
- * @constructor
- * Create a new JsonReader
- * @param {Object} meta Metadata configuration options.
- * @param {Object} recordType Either an Array of field definition objects
- * as specified to {@link Roo.data.Record#create},
- * or an {@link Roo.data.Record} object
- * created using {@link Roo.data.Record#create}.
- */
-Roo.data.ArrayReader = function(meta, recordType){
- Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
-};
+ })
+ }
+
+ if(this.multiple){
+ combobox.cls += ' select2-container-multi';
+ }
+
+ if (align ==='left' && this.fieldLabel.length) {
+
+ Roo.log("left and has label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-sm-' + this.labelWidth,
+ html : this.fieldLabel
+
+ },
+ {
+ cls : "col-sm-" + (12 - this.labelWidth),
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+ } else if ( this.fieldLabel.length) {
+ Roo.log(" label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+
+ },
+
+ combobox
+
+ ];
-Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
- /**
- * Create a data block containing Roo.data.Records from an XML document.
- * @param {Object} o An Array of row objects which represents the dataset.
- * @return {Object} data A data block which is used by an Roo.data.Store object as
- * a cache of Roo.data.Records.
- */
- readRecords : function(o){
- var sid = this.meta ? this.meta.id : null;
- var recordType = this.recordType, fields = recordType.prototype.fields;
- var records = [];
- var root = o;
- for(var i = 0; i < root.length; i++){
- var n = root[i];
- var values = {};
- var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
- for(var j = 0, jlen = fields.length; j < jlen; j++){
- var f = fields.items[j];
- var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
- var v = n[k] !== undefined ? n[k] : f.defaultValue;
- v = f.convert(v);
- values[f.name] = v;
+ } else {
+
+ Roo.log(" no label && no align");
+ cfg = combobox
+
+
+ }
+
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
}
- var record = new recordType(values, id);
- record.json = n;
- records[records.length] = record;
- }
- return {
- records : records,
- totalRecords : records.length
- };
- }
-});/*
- * - LGPL
- * *
- */
-
-/**
- * @class Roo.bootstrap.ComboBox
- * @extends Roo.bootstrap.TriggerField
- * A combobox control with support for autocomplete, remote-loading, paging and many other features.
- * @cfg {Boolean} append (true|false) default false
- * @constructor
- * Create a new ComboBox.
- * @param {Object} config Configuration options
- */
-Roo.bootstrap.ComboBox = function(config){
- Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event expand
- * Fires when the dropdown list is expanded
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'expand' : true,
- /**
- * @event collapse
- * Fires when the dropdown list is collapsed
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'collapse' : true,
- /**
- * @event beforeselect
- * Fires before a list item is selected. Return false to cancel the selection.
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {Roo.data.Record} record The data record returned from the underlying store
- * @param {Number} index The index of the selected item in the dropdown list
- */
- 'beforeselect' : true,
- /**
- * @event select
- * Fires when a list item is selected
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
- * @param {Number} index The index of the selected item in the dropdown list
- */
- 'select' : true,
- /**
- * @event beforequery
- * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
- * The event object passed has these properties:
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {String} query The query
- * @param {Boolean} forceAll true to force "all" query
- * @param {Boolean} cancel true to cancel the query
- * @param {Object} e The query event object
- */
- 'beforequery': true,
- /**
- * @event add
- * Fires when the 'add' icon is pressed (add a listener to enable add button)
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'add' : true,
- /**
- * @event edit
- * Fires when the 'edit' icon is pressed (add a listener to enable add button)
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
- */
- 'edit' : true,
- /**
- * @event remove
- * Fires when the remove value from the combobox array
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'remove' : true
+ });
- });
+ return cfg;
+
+ },
- this.selectedIndex = -1;
- if(this.mode == 'local'){
- if(config.queryDelay === undefined){
- this.queryDelay = 10;
+
+ // private
+ onResize : function(w, h){
+// Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
+// if(typeof w == 'number'){
+// var x = w - this.trigger.getWidth();
+// this.inputEl().setWidth(this.adjustWidth('input', x));
+// this.trigger.setStyle('left', x+'px');
+// }
+ },
+
+ // private
+ adjustSize : Roo.BoxComponent.prototype.adjustSize,
+
+ // private
+ getResizeEl : function(){
+ return this.inputEl();
+ },
+
+ // private
+ getPositionEl : function(){
+ return this.inputEl();
+ },
+
+ // private
+ alignErrorIcon : function(){
+ this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
+ },
+
+ // private
+ initEvents : function(){
+
+ Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
+ //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
+ if(!this.multiple){
+ this.trigger = this.el.select('span.dropdown-toggle',true).first();
+ if(this.hideTrigger){
+ this.trigger.setDisplayed(false);
+ }
+ this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
+ }
+
+ if(this.multiple){
+ this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
+ }
+
+ //this.trigger.addClassOnOver('x-form-trigger-over');
+ //this.trigger.addClassOnClick('x-form-trigger-click');
+
+ //if(!this.width){
+ // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
+ //}
+ },
+
+ // private
+ initTrigger : function(){
+
+ },
+
+ // private
+ onDestroy : function(){
+ if(this.trigger){
+ this.trigger.removeAllListeners();
+ // this.trigger.remove();
}
- if(config.minChars === undefined){
- this.minChars = 0;
+ //if(this.wrap){
+ // this.wrap.remove();
+ //}
+ Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
+ },
+
+ // private
+ onFocus : function(){
+ Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
+ /*
+ if(!this.mimicing){
+ this.wrap.addClass('x-trigger-wrap-focus');
+ this.mimicing = true;
+ Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
+ if(this.monitorTab){
+ this.el.on("keydown", this.checkTab, this);
+ }
}
- }
-};
+ */
+ },
-Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
-
- /**
- * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
- * rendering into an Roo.Editor, defaults to false)
- */
- /**
- * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
- * {tag: "input", type: "text", size: "24", autocomplete: "off"})
- */
- /**
- * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
- */
- /**
- * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
- * the dropdown list (defaults to undefined, with no header element)
- */
+ // private
+ checkTab : function(e){
+ if(e.getKey() == e.TAB){
+ this.triggerBlur();
+ }
+ },
- /**
- * @cfg {String/Roo.Template} tpl The template to use to render the output
- */
-
- /**
- * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
- */
- listWidth: undefined,
- /**
- * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
- * mode = 'remote' or 'text' if mode = 'local')
- */
- displayField: undefined,
- /**
- * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
- * mode = 'remote' or 'value' if mode = 'local').
- * Note: use of a valueField requires the user make a selection
- * in order for a value to be mapped.
- */
- valueField: undefined,
-
-
- /**
- * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
- * field's data value (defaults to the underlying DOM element's name)
- */
- hiddenName: undefined,
- /**
- * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
- */
- listClass: '',
- /**
- * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
- */
- selectedClass: 'active',
+ // private
+ onBlur : function(){
+ // do nothing
+ },
+
+ // private
+ mimicBlur : function(e, t){
+ /*
+ if(!this.wrap.contains(t) && this.validateBlur()){
+ this.triggerBlur();
+ }
+ */
+ },
+
+ // private
+ triggerBlur : function(){
+ this.mimicing = false;
+ Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
+ if(this.monitorTab){
+ this.el.un("keydown", this.checkTab, this);
+ }
+ //this.wrap.removeClass('x-trigger-wrap-focus');
+ Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
+ },
+
+ // private
+ // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
+ validateBlur : function(e, t){
+ return true;
+ },
+
+ // private
+ onDisable : function(){
+ this.inputEl().dom.disabled = true;
+ //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
+ //if(this.wrap){
+ // this.wrap.addClass('x-item-disabled');
+ //}
+ },
+
+ // private
+ onEnable : function(){
+ this.inputEl().dom.disabled = false;
+ //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
+ //if(this.wrap){
+ // this.el.removeClass('x-item-disabled');
+ //}
+ },
+
+ // private
+ onShow : function(){
+ var ae = this.getActionEl();
+
+ if(ae){
+ ae.dom.style.display = '';
+ ae.dom.style.visibility = 'visible';
+ }
+ },
+
+ // private
+ onHide : function(){
+ var ae = this.getActionEl();
+ ae.dom.style.display = 'none';
+ },
+
/**
- * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
- */
- shadow:'sides',
- /**
- * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
- * anchor positions (defaults to 'tl-bl')
- */
- listAlign: 'tl-bl?',
- /**
- * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
- */
- maxHeight: 300,
- /**
- * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
- * query specified by the allQuery config option (defaults to 'query')
- */
- triggerAction: 'query',
- /**
- * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
- * (defaults to 4, does not apply if editable = false)
- */
- minChars : 4,
- /**
- * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
- * delay (typeAheadDelay) if it matches a known value (defaults to false)
- */
- typeAhead: false,
- /**
- * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
- * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
- */
- queryDelay: 500,
- /**
- * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
- * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
- */
- pageSize: 0,
- /**
- * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
- * when editable = true (defaults to false)
- */
- selectOnFocus:false,
- /**
- * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
- */
- queryParam: 'query',
- /**
- * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
- * when mode = 'remote' (defaults to 'Loading...')
- */
- loadingText: 'Loading...',
- /**
- * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
- */
- resizable: false,
- /**
- * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
- */
- handleHeight : 8,
- /**
- * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
- * traditional select (defaults to true)
- */
- editable: true,
- /**
- * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
- */
- allQuery: '',
- /**
- * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
- */
- mode: 'remote',
- /**
- * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
- * listWidth has a higher value)
+ * The function that should handle the trigger's click event. This method does nothing by default until overridden
+ * by an implementing function.
+ * @method
+ * @param {EventObject} e
*/
- minListWidth : 70,
+ onTriggerClick : Roo.emptyFn
+});
+ /*
+ * 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.data.SortTypes
+ * @singleton
+ * Defines the default sorting (casting?) comparison functions used when sorting data.
+ */
+Roo.data.SortTypes = {
/**
- * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
- * allow the user to set arbitrary text into the field (defaults to false)
+ * Default sort that does nothing
+ * @param {Mixed} s The value being converted
+ * @return {Mixed} The comparison value
*/
- forceSelection:false,
+ none : function(s){
+ return s;
+ },
+
/**
- * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
- * if typeAhead = true (defaults to 250)
+ * The regular expression used to strip tags
+ * @type {RegExp}
+ * @property
*/
- typeAheadDelay : 250,
+ stripTagsRE : /<\/?[^>]+>/gi,
+
/**
- * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
- * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
+ * Strips all HTML tags to sort on text only
+ * @param {Mixed} s The value being converted
+ * @return {String} The comparison value
*/
- valueNotFoundText : undefined,
+ asText : function(s){
+ return String(s).replace(this.stripTagsRE, "");
+ },
+
/**
- * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
+ * Strips all HTML tags to sort on text only - Case insensitive
+ * @param {Mixed} s The value being converted
+ * @return {String} The comparison value
*/
- blockFocus : false,
+ asUCText : function(s){
+ return String(s).toUpperCase().replace(this.stripTagsRE, "");
+ },
/**
- * @cfg {Boolean} disableClear Disable showing of clear button.
+ * Case insensitive string
+ * @param {Mixed} s The value being converted
+ * @return {String} The comparison value
*/
- disableClear : false,
+ asUCString : function(s) {
+ return String(s).toUpperCase();
+ },
+
/**
- * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
+ * Date sorting
+ * @param {Mixed} s The value being converted
+ * @return {Number} The comparison value
*/
- alwaysQuery : false,
+ asDate : function(s) {
+ if(!s){
+ return 0;
+ }
+ if(s instanceof Date){
+ return s.getTime();
+ }
+ return Date.parse(String(s));
+ },
/**
- * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
+ * Float sorting
+ * @param {Mixed} s The value being converted
+ * @return {Float} The comparison value
*/
- multiple : false,
-
- //private
- addicon : false,
- editicon: false,
-
- page: 0,
- hasQuery: false,
- append: false,
- loadNext: false,
- item: [],
+ asFloat : function(s) {
+ var val = parseFloat(String(s).replace(/,/g, ""));
+ if(isNaN(val)) val = 0;
+ return val;
+ },
- // element that contains real text value.. (when hidden is used..)
-
- // private
- initEvents: function(){
-
- if (!this.store) {
- throw "can not find store for combo";
- }
- this.store = Roo.factory(this.store, Roo.data);
-
-
-
- Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
-
-
- if(this.hiddenName){
-
- this.hiddenField = this.el.select('input.form-hidden-field',true).first();
-
- this.hiddenField.dom.value =
- this.hiddenValue !== undefined ? this.hiddenValue :
- this.value !== undefined ? this.value : '';
+ /**
+ * Integer sorting
+ * @param {Mixed} s The value being converted
+ * @return {Number} The comparison value
+ */
+ asInt : function(s) {
+ var val = parseInt(String(s).replace(/,/g, ""));
+ if(isNaN(val)) val = 0;
+ return val;
+ }
+};/*
+ * 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">
+ */
- // prevent input submission
- this.el.dom.removeAttribute('name');
- this.hiddenField.dom.setAttribute('name', this.hiddenName);
-
-
- }
- //if(Roo.isGecko){
- // this.el.dom.setAttribute('autocomplete', 'off');
- //}
+/**
+* @class Roo.data.Record
+ * Instances of this class encapsulate both record <em>definition</em> information, and record
+ * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
+ * to access Records cached in an {@link Roo.data.Store} object.<br>
+ * <p>
+ * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
+ * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
+ * objects.<br>
+ * <p>
+ * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
+ * @constructor
+ * This constructor should not be used to create Record objects. Instead, use the constructor generated by
+ * {@link #create}. The parameters are the same.
+ * @param {Array} data An associative Array of data values keyed by the field name.
+ * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
+ * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
+ * not specified an integer id is generated.
+ */
+Roo.data.Record = function(data, id){
+ this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
+ this.data = data;
+};
- var cls = 'x-combo-list';
- this.list = this.el.select('ul.dropdown-menu',true).first();
+/**
+ * Generate a constructor for a specific record layout.
+ * @param {Array} o An Array of field definition objects which specify field names, and optionally,
+ * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
+ * Each field definition object may contain the following properties: <ul>
+ * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
+ * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
+ * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
+ * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
+ * is being used, then this is a string containing the javascript expression to reference the data relative to
+ * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
+ * to the data item relative to the record element. If the mapping expression is the same as the field name,
+ * this may be omitted.</p></li>
+ * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
+ * <ul><li>auto (Default, implies no conversion)</li>
+ * <li>string</li>
+ * <li>int</li>
+ * <li>float</li>
+ * <li>boolean</li>
+ * <li>date</li></ul></p></li>
+ * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
+ * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
+ * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
+ * by the Reader into an object that will be stored in the Record. It is passed the
+ * following parameters:<ul>
+ * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
+ * </ul></p></li>
+ * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
+ * </ul>
+ * <br>usage:<br><pre><code>
+var TopicRecord = Roo.data.Record.create(
+ {name: 'title', mapping: 'topic_title'},
+ {name: 'author', mapping: 'username'},
+ {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
+ {name: 'lastPost', mapping: 'post_time', type: 'date'},
+ {name: 'lastPoster', mapping: 'user2'},
+ {name: 'excerpt', mapping: 'post_text'}
+);
- //this.list = new Roo.Layer({
- // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
- //});
-
- var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
- this.list.setWidth(lw);
-
- this.list.on('mouseover', this.onViewOver, this);
- this.list.on('mousemove', this.onViewMove, this);
-
- this.list.on('scroll', this.onViewScroll, this);
-
- /*
- this.list.swallowEvent('mousewheel');
- this.assetHeight = 0;
+var myNewRecord = new TopicRecord({
+ title: 'Do my job please',
+ author: 'noobie',
+ totalPosts: 1,
+ lastPost: new Date(),
+ lastPoster: 'Animal',
+ excerpt: 'No way dude!'
+});
+myStore.add(myNewRecord);
+</code></pre>
+ * @method create
+ * @static
+ */
+Roo.data.Record.create = function(o){
+ var f = function(){
+ f.superclass.constructor.apply(this, arguments);
+ };
+ Roo.extend(f, Roo.data.Record);
+ var p = f.prototype;
+ p.fields = new Roo.util.MixedCollection(false, function(field){
+ return field.name;
+ });
+ for(var i = 0, len = o.length; i < len; i++){
+ p.fields.add(new Roo.data.Field(o[i]));
+ }
+ f.getField = function(name){
+ return p.fields.get(name);
+ };
+ return f;
+};
- if(this.title){
- this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
- this.assetHeight += this.header.getHeight();
- }
+Roo.data.Record.AUTO_ID = 1000;
+Roo.data.Record.EDIT = 'edit';
+Roo.data.Record.REJECT = 'reject';
+Roo.data.Record.COMMIT = 'commit';
+
+Roo.data.Record.prototype = {
+ /**
+ * Readonly flag - true if this record has been modified.
+ * @type Boolean
+ */
+ dirty : false,
+ editing : false,
+ error: null,
+ modified: null,
- this.innerList = this.list.createChild({cls:cls+'-inner'});
- this.innerList.on('mouseover', this.onViewOver, this);
- this.innerList.on('mousemove', this.onViewMove, this);
- this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
-
- if(this.allowBlank && !this.pageSize && !this.disableClear){
- this.footer = this.list.createChild({cls:cls+'-ft'});
- this.pageTb = new Roo.Toolbar(this.footer);
-
- }
- if(this.pageSize){
- this.footer = this.list.createChild({cls:cls+'-ft'});
- this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
- {pageSize: this.pageSize});
-
- }
-
- if (this.pageTb && this.allowBlank && !this.disableClear) {
- var _this = this;
- this.pageTb.add(new Roo.Toolbar.Fill(), {
- cls: 'x-btn-icon x-btn-clear',
- text: ' ',
- handler: function()
- {
- _this.collapse();
- _this.clearValue();
- _this.onSelect(false, -1);
- }
- });
+ // private
+ join : function(store){
+ this.store = store;
+ },
+
+ /**
+ * Set the named field to the specified value.
+ * @param {String} name The name of the field to set.
+ * @param {Object} value The value to set the field to.
+ */
+ set : function(name, value){
+ if(this.data[name] == value){
+ return;
}
- if (this.footer) {
- this.assetHeight += this.footer.getHeight();
+ this.dirty = true;
+ if(!this.modified){
+ this.modified = {};
}
- */
-
- if(!this.tpl){
- this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
+ if(typeof this.modified[name] == 'undefined'){
+ this.modified[name] = this.data[name];
}
+ this.data[name] = value;
+ if(!this.editing && this.store){
+ this.store.afterEdit(this);
+ }
+ },
- this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
- singleSelect:true, store: this.store, selectedClass: this.selectedClass
- });
- //this.view.wrapEl.setDisplayed(false);
- this.view.on('click', this.onViewClick, this);
-
-
-
- this.store.on('beforeload', this.onBeforeLoad, this);
- this.store.on('load', this.onLoad, this);
- this.store.on('loadexception', this.onLoadException, this);
- /*
- if(this.resizable){
- this.resizer = new Roo.Resizable(this.list, {
- pinned:true, handles:'se'
- });
- this.resizer.on('resize', function(r, w, h){
- this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
- this.listWidth = w;
- this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
- this.restrictHeight();
- }, this);
- this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
+ /**
+ * Get the value of the named field.
+ * @param {String} name The name of the field to get the value of.
+ * @return {Object} The value of the field.
+ */
+ get : function(name){
+ return this.data[name];
+ },
+
+ // private
+ beginEdit : function(){
+ this.editing = true;
+ this.modified = {};
+ },
+
+ // private
+ cancelEdit : function(){
+ this.editing = false;
+ delete this.modified;
+ },
+
+ // private
+ endEdit : function(){
+ this.editing = false;
+ if(this.dirty && this.store){
+ this.store.afterEdit(this);
}
- */
- if(!this.editable){
- this.editable = true;
- this.setEditable(false);
+ },
+
+ /**
+ * Usually called by the {@link Roo.data.Store} which owns the Record.
+ * Rejects all changes made to the Record since either creation, or the last commit operation.
+ * Modified fields are reverted to their original values.
+ * <p>
+ * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
+ * of reject operations.
+ */
+ reject : function(){
+ var m = this.modified;
+ for(var n in m){
+ if(typeof m[n] != "function"){
+ this.data[n] = m[n];
+ }
}
-
- /*
-
- if (typeof(this.events.add.listeners) != 'undefined') {
-
- this.addicon = this.wrap.createChild(
- {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
-
- this.addicon.on('click', function(e) {
- this.fireEvent('add', this);
- }, this);
+ this.dirty = false;
+ delete this.modified;
+ this.editing = false;
+ if(this.store){
+ this.store.afterReject(this);
}
- if (typeof(this.events.edit.listeners) != 'undefined') {
-
- this.editicon = this.wrap.createChild(
- {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
- if (this.addicon) {
- this.editicon.setStyle('margin-left', '40px');
- }
- this.editicon.on('click', function(e) {
-
- // we fire even if inothing is selected..
- this.fireEvent('edit', this, this.lastData );
-
- }, this);
+ },
+
+ /**
+ * Usually called by the {@link Roo.data.Store} which owns the Record.
+ * Commits all changes made to the Record since either creation, or the last commit operation.
+ * <p>
+ * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
+ * of commit operations.
+ */
+ commit : function(){
+ this.dirty = false;
+ delete this.modified;
+ this.editing = false;
+ if(this.store){
+ this.store.afterCommit(this);
}
- */
-
- this.keyNav = new Roo.KeyNav(this.inputEl(), {
- "up" : function(e){
- this.inKeyMode = true;
- this.selectPrev();
- },
+ },
- "down" : function(e){
- if(!this.isExpanded()){
- this.onTriggerClick();
- }else{
- this.inKeyMode = true;
- this.selectNext();
- }
- },
+ // private
+ hasError : function(){
+ return this.error != null;
+ },
- "enter" : function(e){
- this.onViewClick();
- //return true;
- },
+ // private
+ clearError : function(){
+ this.error = null;
+ },
- "esc" : function(e){
- this.collapse();
- },
+ /**
+ * Creates a copy of this record.
+ * @param {String} id (optional) A new record id if you don't want to use this record's id
+ * @return {Record}
+ */
+ copy : function(newId) {
+ return new this.constructor(Roo.apply({}, this.data), newId || this.id);
+ }
+};/*
+ * 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">
+ */
- "tab" : function(e){
- this.collapse();
-
- if(this.fireEvent("specialkey", this, e)){
- this.onViewClick(false);
- }
-
- return true;
- },
- scope : this,
- doRelay : function(foo, bar, hname){
- if(hname == 'down' || this.scope.isExpanded()){
- return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
- }
- return true;
- },
+/**
+ * @class Roo.data.Store
+ * @extends Roo.util.Observable
+ * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
+ * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
+ * <p>
+ * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
+ * has no knowledge of the format of the data returned by the Proxy.<br>
+ * <p>
+ * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
+ * instances from the data object. These records are cached and made available through accessor functions.
+ * @constructor
+ * Creates a new Store.
+ * @param {Object} config A config object containing the objects needed for the Store to access data,
+ * and read the data into Records.
+ */
+Roo.data.Store = function(config){
+ this.data = new Roo.util.MixedCollection(false);
+ this.data.getKey = function(o){
+ return o.id;
+ };
+ this.baseParams = {};
+ // private
+ this.paramNames = {
+ "start" : "start",
+ "limit" : "limit",
+ "sort" : "sort",
+ "dir" : "dir",
+ "multisort" : "_multisort"
+ };
+
+ if(config && config.data){
+ this.inlineData = config.data;
+ delete config.data;
+ }
- forceKeyDown: true
- });
-
-
- this.queryDelay = Math.max(this.queryDelay || 10,
- this.mode == 'local' ? 10 : 250);
-
-
- this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
-
- if(this.typeAhead){
- this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
- }
- if(this.editable !== false){
- this.inputEl().on("keyup", this.onKeyUp, this);
- }
- if(this.forceSelection){
- this.inputEl().on('blur', this.doForce, this);
+ Roo.apply(this, config);
+
+ if(this.reader){ // reader passed
+ this.reader = Roo.factory(this.reader, Roo.data);
+ this.reader.xmodule = this.xmodule || false;
+ if(!this.recordType){
+ this.recordType = this.reader.recordType;
}
-
- if(this.multiple){
- this.choices = this.el.select('ul.select2-choices', true).first();
- this.searchField = this.el.select('ul li.select2-search-field', true).first();
+ if(this.reader.onMetaChange){
+ this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
}
- },
+ }
- onDestroy : function(){
- if(this.view){
- this.view.setStore(null);
- this.view.el.removeAllListeners();
- this.view.el.remove();
- this.view.purgeListeners();
- }
- if(this.list){
- this.list.dom.innerHTML = '';
- }
- if(this.store){
- this.store.un('beforeload', this.onBeforeLoad, this);
- this.store.un('load', this.onLoad, this);
- this.store.un('loadexception', this.onLoadException, this);
- }
- Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
- },
+ if(this.recordType){
+ this.fields = this.recordType.prototype.fields;
+ }
+ this.modified = [];
- // private
- fireKey : function(e){
- if(e.isNavKeyPress() && !this.list.isVisible()){
- this.fireEvent("specialkey", this, e);
- }
- },
+ this.addEvents({
+ /**
+ * @event datachanged
+ * Fires when the data cache has changed, and a widget which is using this Store
+ * as a Record cache should refresh its view.
+ * @param {Store} this
+ */
+ datachanged : true,
+ /**
+ * @event metachange
+ * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
+ * @param {Store} this
+ * @param {Object} meta The JSON metadata
+ */
+ metachange : true,
+ /**
+ * @event add
+ * Fires when Records have been added to the Store
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The array of Records added
+ * @param {Number} index The index at which the record(s) were added
+ */
+ add : true,
+ /**
+ * @event remove
+ * Fires when a Record has been removed from the Store
+ * @param {Store} this
+ * @param {Roo.data.Record} record The Record that was removed
+ * @param {Number} index The index at which the record was removed
+ */
+ remove : true,
+ /**
+ * @event update
+ * Fires when a Record has been updated
+ * @param {Store} this
+ * @param {Roo.data.Record} record The Record that was updated
+ * @param {String} operation The update operation being performed. Value may be one of:
+ * <pre><code>
+ Roo.data.Record.EDIT
+ Roo.data.Record.REJECT
+ Roo.data.Record.COMMIT
+ * </code></pre>
+ */
+ update : true,
+ /**
+ * @event clear
+ * Fires when the data cache has been cleared.
+ * @param {Store} this
+ */
+ clear : true,
+ /**
+ * @event beforeload
+ * Fires before a request is made for a new data object. If the beforeload handler returns false
+ * the load action will be canceled.
+ * @param {Store} this
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ */
+ beforeload : true,
+ /**
+ * @event beforeloadadd
+ * Fires after a new set of Records has been loaded.
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The Records that were loaded
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ */
+ beforeloadadd : true,
+ /**
+ * @event load
+ * Fires after a new set of Records has been loaded, before they are added to the store.
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The Records that were loaded
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ * @params {Object} return from reader
+ */
+ load : true,
+ /**
+ * @event loadexception
+ * Fires if an exception occurs in the Proxy during loading.
+ * Called with the signature of the Proxy's "loadexception" event.
+ * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
+ *
+ * @param {Proxy}
+ * @param {Object} return from JsonData.reader() - success, totalRecords, records
+ * @param {Object} load options
+ * @param {Object} jsonData from your request (normally this contains the Exception)
+ */
+ loadexception : true
+ });
+
+ if(this.proxy){
+ this.proxy = Roo.factory(this.proxy, Roo.data);
+ this.proxy.xmodule = this.xmodule || false;
+ this.relayEvents(this.proxy, ["loadexception"]);
+ }
+ this.sortToggle = {};
+ this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
- // private
- onResize: function(w, h){
-// Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
-//
-// if(typeof w != 'number'){
-// // we do not handle it!?!?
-// return;
-// }
-// var tw = this.trigger.getWidth();
-// // tw += this.addicon ? this.addicon.getWidth() : 0;
-// // tw += this.editicon ? this.editicon.getWidth() : 0;
-// var x = w - tw;
-// this.inputEl().setWidth( this.adjustWidth('input', x));
-//
-// //this.trigger.setStyle('left', x+'px');
-//
-// if(this.list && this.listWidth === undefined){
-// var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
-// this.list.setWidth(lw);
-// this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
-// }
-
+ Roo.data.Store.superclass.constructor.call(this);
+
+ if(this.inlineData){
+ this.loadData(this.inlineData);
+ delete this.inlineData;
+ }
+};
+
+Roo.extend(Roo.data.Store, Roo.util.Observable, {
+ /**
+ * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
+ * without a remote query - used by combo/forms at present.
+ */
-
- },
+ /**
+ * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
+ */
+ /**
+ * @cfg {Array} data Inline data to be loaded when the store is initialized.
+ */
+ /**
+ * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
+ * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
+ */
+ /**
+ * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
+ * on any HTTP request
+ */
+ /**
+ * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
+ */
+ /**
+ * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
+ */
+ multiSort: false,
+ /**
+ * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
+ * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
+ */
+ remoteSort : false,
/**
- * Allow or prevent the user from directly editing the field text. If false is passed,
- * the user will only be able to select from the items defined in the dropdown list. This method
- * is the runtime equivalent of setting the 'editable' config option at config time.
- * @param {Boolean} value True to allow the user to directly edit the field text
- */
- setEditable : function(value){
- if(value == this.editable){
- return;
- }
- this.editable = value;
- if(!value){
- this.inputEl().dom.setAttribute('readOnly', true);
- this.inputEl().on('mousedown', this.onTriggerClick, this);
- this.inputEl().addClass('x-combo-noedit');
- }else{
- this.inputEl().dom.setAttribute('readOnly', false);
- this.inputEl().un('mousedown', this.onTriggerClick, this);
- this.inputEl().removeClass('x-combo-noedit');
- }
- },
+ * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
+ * loaded or when a record is removed. (defaults to false).
+ */
+ pruneModifiedRecords : false,
// private
-
- onBeforeLoad : function(combo,opts){
- if(!this.hasFocus){
- return;
- }
- if (!opts.add) {
- this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
- }
- this.restrictHeight();
- this.selectedIndex = -1;
- },
+ lastOptions : null,
- // private
- onLoad : function(){
-
- this.hasQuery = false;
-
- if(!this.hasFocus){
- return;
- }
-
- if(typeof(this.loading) !== 'undefined' && this.loading !== null){
- this.loading.hide();
- }
-
- if(this.store.getCount() > 0){
- this.expand();
- this.restrictHeight();
- if(this.lastQuery == this.allQuery){
- if(this.editable){
- this.inputEl().dom.select();
- }
- if(!this.selectByValue(this.value, true)){
- this.select(0, true);
- }
- }else{
- this.selectNext();
- if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
- this.taTask.delay(this.typeAheadDelay);
- }
- }
- }else{
- this.onEmptyResults();
+ /**
+ * Add Records to the Store and fires the add event.
+ * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+ */
+ add : function(records){
+ records = [].concat(records);
+ for(var i = 0, len = records.length; i < len; i++){
+ records[i].join(this);
}
-
- //this.el.focus();
+ var index = this.data.length;
+ this.data.addAll(records);
+ this.fireEvent("add", this, records, index);
},
- // private
- onLoadException : function()
- {
- this.hasQuery = false;
-
- if(typeof(this.loading) !== 'undefined' && this.loading !== null){
- this.loading.hide();
- }
-
- this.collapse();
- Roo.log(this.store.reader.jsonData);
- if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
- // fixme
- //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+
+ /**
+ * Remove a Record from the Store and fires the remove event.
+ * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
+ */
+ remove : function(record){
+ var index = this.data.indexOf(record);
+ this.data.removeAt(index);
+ if(this.pruneModifiedRecords){
+ this.modified.remove(record);
}
-
-
+ this.fireEvent("remove", this, record, index);
},
- // private
- onTypeAhead : function(){
- if(this.store.getCount() > 0){
- var r = this.store.getAt(0);
- var newValue = r.data[this.displayField];
- var len = newValue.length;
- var selStart = this.getRawValue().length;
-
- if(selStart != len){
- this.setRawValue(newValue);
- this.selectText(selStart, newValue.length);
- }
+
+ /**
+ * Remove all Records from the Store and fires the clear event.
+ */
+ removeAll : function(){
+ this.data.clear();
+ if(this.pruneModifiedRecords){
+ this.modified = [];
}
+ this.fireEvent("clear", this);
},
- // private
- onSelect : function(record, index){
-
- if(this.fireEvent('beforeselect', this, record, index) !== false){
-
- this.setFromData(index > -1 ? record.data : false);
-
- this.collapse();
- this.fireEvent('select', this, record, index);
+ /**
+ * Inserts Records to the Store at the given index and fires the add event.
+ * @param {Number} index The start index at which to insert the passed Records.
+ * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+ */
+ insert : function(index, records){
+ records = [].concat(records);
+ for(var i = 0, len = records.length; i < len; i++){
+ this.data.insert(index, records[i]);
+ records[i].join(this);
}
+ this.fireEvent("add", this, records, index);
},
/**
- * Returns the currently selected field value or empty string if no value is set.
- * @return {String} value The selected value
+ * Get the index within the cache of the passed Record.
+ * @param {Roo.data.Record} record The Roo.data.Record object to to find.
+ * @return {Number} The index of the passed Record. Returns -1 if not found.
*/
- getValue : function(){
-
- if(this.multiple){
- return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
- }
-
- if(this.valueField){
- return typeof this.value != 'undefined' ? this.value : '';
- }else{
- return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
- }
+ indexOf : function(record){
+ return this.data.indexOf(record);
},
/**
- * Clears any text/value currently set in the field
+ * Get the index within the cache of the Record with the passed id.
+ * @param {String} id The id of the Record to find.
+ * @return {Number} The index of the Record. Returns -1 if not found.
*/
- clearValue : function(){
- if(this.hiddenField){
- this.hiddenField.dom.value = '';
- }
- this.value = '';
- this.setRawValue('');
- this.lastSelectionText = '';
-
+ indexOfId : function(id){
+ return this.data.indexOfKey(id);
},
/**
- * Sets the specified value into the field. If the value finds a match, the corresponding record text
- * will be displayed in the field. If the value does not match the data value of an existing item,
- * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
- * Otherwise the field will be blank (although the value will still be set).
- * @param {String} value The value to match
+ * Get the Record with the specified id.
+ * @param {String} id The id of the Record to find.
+ * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
*/
- setValue : function(v){
- if(this.multiple){
- this.syncValue();
- return;
- }
-
- var text = v;
- if(this.valueField){
- var r = this.findRecord(this.valueField, v);
- if(r){
- text = r.data[this.displayField];
- }else if(this.valueNotFoundText !== undefined){
- text = this.valueNotFoundText;
- }
- }
- this.lastSelectionText = text;
- if(this.hiddenField){
- this.hiddenField.dom.value = v;
- }
- Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
- this.value = v;
+ getById : function(id){
+ return this.data.key(id);
},
+
/**
- * @property {Object} the last set data for the element
+ * Get the Record at the specified index.
+ * @param {Number} index The index of the Record to find.
+ * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
*/
-
- lastData : false,
+ getAt : function(index){
+ return this.data.itemAt(index);
+ },
+
/**
- * Sets the value of the field based on a object which is related to the record format for the store.
- * @param {Object} value the value to set as. or false on reset?
+ * Returns a range of Records between specified indices.
+ * @param {Number} startIndex (optional) The starting index (defaults to 0)
+ * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
+ * @return {Roo.data.Record[]} An array of Records
*/
- setFromData : function(o){
-
- if(this.multiple){
- this.addItem(o);
- return;
- }
-
- var dv = ''; // display value
- var vv = ''; // value value..
- this.lastData = o;
- if (this.displayField) {
- dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
- } else {
- // this is an error condition!!!
- Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
- }
-
- if(this.valueField){
- vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
- }
-
- if(this.hiddenField){
- this.hiddenField.dom.value = vv;
-
- this.lastSelectionText = dv;
- Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
- this.value = vv;
- return;
- }
- // no hidden field.. - we store the value in 'value', but still display
- // display field!!!!
- this.lastSelectionText = dv;
- Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
- this.value = vv;
-
-
+ getRange : function(start, end){
+ return this.data.getRange(start, end);
},
+
// private
- reset : function(){
- // overridden so that last data is reset..
- this.setValue(this.originalValue);
- this.clearInvalid();
- this.lastData = false;
- if (this.view) {
- this.view.clearSelections();
- }
+ storeOptions : function(o){
+ o = Roo.apply({}, o);
+ delete o.callback;
+ delete o.scope;
+ this.lastOptions = o;
},
- // private
- findRecord : function(prop, value){
- var record;
- if(this.store.getCount() > 0){
- this.store.each(function(r){
- if(r.data[prop] == value){
- record = r;
- return false;
- }
- return true;
- });
+
+ /**
+ * Loads the Record cache from the configured Proxy using the configured Reader.
+ * <p>
+ * If using remote paging, then the first load call must specify the <em>start</em>
+ * and <em>limit</em> properties in the options.params property to establish the initial
+ * position within the dataset, and the number of Records to cache on each read from the Proxy.
+ * <p>
+ * <strong>It is important to note that for remote data sources, loading is asynchronous,
+ * and this call will return before the new data has been loaded. Perform any post-processing
+ * in a callback function, or in a "load" event handler.</strong>
+ * <p>
+ * @param {Object} options An object containing properties which control loading options:<ul>
+ * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
+ * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
+ * passed the following arguments:<ul>
+ * <li>r : Roo.data.Record[]</li>
+ * <li>options: Options object from the load call</li>
+ * <li>success: Boolean success indicator</li></ul></li>
+ * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
+ * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
+ * </ul>
+ */
+ load : function(options){
+ options = options || {};
+ if(this.fireEvent("beforeload", this, options) !== false){
+ this.storeOptions(options);
+ var p = Roo.apply(options.params || {}, this.baseParams);
+ // if meta was not loaded from remote source.. try requesting it.
+ if (!this.reader.metaFromRemote) {
+ p._requestMeta = 1;
+ }
+ if(this.sortInfo && this.remoteSort){
+ var pn = this.paramNames;
+ p[pn["sort"]] = this.sortInfo.field;
+ p[pn["dir"]] = this.sortInfo.direction;
+ }
+ if (this.multiSort) {
+ var pn = this.paramNames;
+ p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
+ }
+
+ this.proxy.load(p, this.reader, this.loadRecords, this, options);
}
- return record;
- },
-
- getName: function()
- {
- // returns hidden if it's set..
- if (!this.rendered) {return ''};
- return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
-
},
- // private
- onViewMove : function(e, t){
- this.inKeyMode = false;
+
+ /**
+ * Reloads the Record cache from the configured Proxy using the configured Reader and
+ * the options from the last load operation performed.
+ * @param {Object} options (optional) An object containing properties which may override the options
+ * used in the last load operation. See {@link #load} for details (defaults to null, in which case
+ * the most recently used options are reused).
+ */
+ reload : function(options){
+ this.load(Roo.applyIf(options||{}, this.lastOptions));
},
// private
- onViewOver : function(e, t){
- if(this.inKeyMode){ // prevent key nav and mouse over conflicts
+ // Called as a callback by the Reader during a load operation.
+ loadRecords : function(o, options, success){
+ if(!o || success === false){
+ if(success !== false){
+ this.fireEvent("load", this, [], options, o);
+ }
+ if(options.callback){
+ options.callback.call(options.scope || this, [], options, false);
+ }
return;
}
- var item = this.view.findItemFromChild(t);
- if(item){
- var index = this.view.indexOf(item);
- this.select(index, false);
+ // if data returned failure - throw an exception.
+ if (o.success === false) {
+ // show a message if no listener is registered.
+ if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
+ }
+ // loadmask wil be hooked into this..
+ this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
+ return;
}
- },
-
- // private
- onViewClick : function(doFocus)
- {
- var index = this.view.getSelectedIndexes()[0];
- var r = this.store.getAt(index);
- if(r){
- this.onSelect(r, index);
+ var r = o.records, t = o.totalRecords || r.length;
+
+ this.fireEvent("beforeloadadd", this, r, options, o);
+
+ if(!options || options.add !== true){
+ if(this.pruneModifiedRecords){
+ this.modified = [];
+ }
+ for(var i = 0, len = r.length; i < len; i++){
+ r[i].join(this);
+ }
+ if(this.snapshot){
+ this.data = this.snapshot;
+ delete this.snapshot;
+ }
+ this.data.clear();
+ this.data.addAll(r);
+ this.totalLength = t;
+ this.applySort();
+ this.fireEvent("datachanged", this);
+ }else{
+ this.totalLength = Math.max(t, this.data.length+r.length);
+ this.add(r);
}
- if(doFocus !== false && !this.blockFocus){
- this.inputEl().focus();
+ this.fireEvent("load", this, r, options, o);
+ if(options.callback){
+ options.callback.call(options.scope || this, r, options, true);
}
},
- // private
- restrictHeight : function(){
- //this.innerList.dom.style.height = '';
- //var inner = this.innerList.dom;
- //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
- //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
- //this.list.beginUpdate();
- //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
- this.list.alignTo(this.inputEl(), this.listAlign);
- //this.list.endUpdate();
- },
- // private
- onEmptyResults : function(){
- this.collapse();
+ /**
+ * Loads data from a passed data block. A Reader which understands the format of the data
+ * must have been configured in the constructor.
+ * @param {Object} data The data block from which to read the Records. The format of the data expected
+ * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
+ * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
+ */
+ loadData : function(o, append){
+ var r = this.reader.readRecords(o);
+ this.loadRecords(r, {add: append}, true);
},
/**
- * Returns true if the dropdown list is expanded, else false.
+ * Gets the number of cached records.
+ * <p>
+ * <em>If using paging, this may not be the total size of the dataset. If the data object
+ * used by the Reader contains the dataset size, then the getTotalCount() function returns
+ * the data set size</em>
*/
- isExpanded : function(){
- return this.list.isVisible();
+ getCount : function(){
+ return this.data.length || 0;
},
/**
- * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
- * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
- * @param {String} value The data value of the item to select
- * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
- * selected item if it is not currently in view (defaults to true)
- * @return {Boolean} True if the value matched an item in the list, else false
+ * Gets the total number of records in the dataset as returned by the server.
+ * <p>
+ * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
+ * the dataset size</em>
*/
- selectByValue : function(v, scrollIntoView){
- if(v !== undefined && v !== null){
- var r = this.findRecord(this.valueField || this.displayField, v);
- if(r){
- this.select(this.store.indexOf(r), scrollIntoView);
- return true;
- }
- }
- return false;
+ getTotalCount : function(){
+ return this.totalLength || 0;
},
/**
- * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
- * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
- * @param {Number} index The zero-based index of the list item to select
- * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
- * selected item if it is not currently in view (defaults to true)
+ * Returns the sort state of the Store as an object with two properties:
+ * <pre><code>
+ field {String} The name of the field by which the Records are sorted
+ direction {String} The sort order, "ASC" or "DESC"
+ * </code></pre>
*/
- select : function(index, scrollIntoView){
- this.selectedIndex = index;
- this.view.select(index);
- if(scrollIntoView !== false){
- var el = this.view.getNode(index);
- if(el){
- //this.innerList.scrollChildIntoView(el, false);
-
- }
- }
+ getSortState : function(){
+ return this.sortInfo;
},
// private
- selectNext : function(){
- var ct = this.store.getCount();
- if(ct > 0){
- if(this.selectedIndex == -1){
- this.select(0);
- }else if(this.selectedIndex < ct-1){
- this.select(this.selectedIndex+1);
+ applySort : function(){
+ if(this.sortInfo && !this.remoteSort){
+ var s = this.sortInfo, f = s.field;
+ var st = this.fields.get(f).sortType;
+ var fn = function(r1, r2){
+ var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
+ return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
+ };
+ this.data.sort(s.direction, fn);
+ if(this.snapshot && this.snapshot != this.data){
+ this.snapshot.sort(s.direction, fn);
}
}
},
- // private
- selectPrev : function(){
- var ct = this.store.getCount();
- if(ct > 0){
- if(this.selectedIndex == -1){
- this.select(0);
- }else if(this.selectedIndex != 0){
- this.select(this.selectedIndex-1);
+ /**
+ * Sets the default sort column and order to be used by the next load operation.
+ * @param {String} fieldName The name of the field to sort by.
+ * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
+ */
+ setDefaultSort : function(field, dir){
+ this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
+ },
+
+ /**
+ * Sort the Records.
+ * If remote sorting is used, the sort is performed on the server, and the cache is
+ * reloaded. If local sorting is used, the cache is sorted internally.
+ * @param {String} fieldName The name of the field to sort by.
+ * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
+ */
+ sort : function(fieldName, dir){
+ var f = this.fields.get(fieldName);
+ if(!dir){
+ this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
+
+ if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
+ dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
+ }else{
+ dir = f.sortDir;
}
}
- },
-
- // private
- onKeyUp : function(e){
- if(this.editable !== false && !e.isSpecialKey()){
- this.lastKey = e.getKey();
- this.dqTask.delay(this.queryDelay);
+ this.sortToggle[f.name] = dir;
+ this.sortInfo = {field: f.name, direction: dir};
+ if(!this.remoteSort){
+ this.applySort();
+ this.fireEvent("datachanged", this);
+ }else{
+ this.load(this.lastOptions);
}
},
- // private
- validateBlur : function(){
- return !this.list || !this.list.isVisible();
+ /**
+ * Calls the specified function for each of the Records in the cache.
+ * @param {Function} fn The function to call. The Record is passed as the first parameter.
+ * Returning <em>false</em> aborts and exits the iteration.
+ * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
+ */
+ each : function(fn, scope){
+ this.data.each(fn, scope);
},
- // private
- initQuery : function(){
- this.doQuery(this.getRawValue());
+ /**
+ * Gets all records modified since the last commit. Modified records are persisted across load operations
+ * (e.g., during paging).
+ * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
+ */
+ getModifiedRecords : function(){
+ return this.modified;
},
// private
- doForce : function(){
- if(this.inputEl().dom.value.length > 0){
- this.inputEl().dom.value =
- this.lastSelectionText === undefined ? '' : this.lastSelectionText;
-
+ createFilterFn : function(property, value, anyMatch){
+ if(!value.exec){ // not a regex
+ value = String(value);
+ if(value.length == 0){
+ return false;
+ }
+ value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
}
+ return function(r){
+ return value.test(r.data[property]);
+ };
},
/**
- * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
- * query allowing the query action to be canceled if needed.
- * @param {String} query The SQL query to execute
- * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
- * in the field than the minimum specified by the minChars config option. It also clears any filter previously
- * saved in the current store (defaults to false)
+ * Sums the value of <i>property</i> for each record between start and end and returns the result.
+ * @param {String} property A field on your records
+ * @param {Number} start The record index to start at (defaults to 0)
+ * @param {Number} end The last record index to include (defaults to length - 1)
+ * @return {Number} The sum
*/
- doQuery : function(q, forceAll){
-
- if(q === undefined || q === null){
- q = '';
- }
- var qe = {
- query: q,
- forceAll: forceAll,
- combo: this,
- cancel:false
- };
- if(this.fireEvent('beforequery', qe)===false || qe.cancel){
- return false;
- }
- q = qe.query;
-
- forceAll = qe.forceAll;
- if(forceAll === true || (q.length >= this.minChars)){
-
- this.hasQuery = true;
-
- if(this.lastQuery != q || this.alwaysQuery){
- this.lastQuery = q;
- if(this.mode == 'local'){
- this.selectedIndex = -1;
- if(forceAll){
- this.store.clearFilter();
- }else{
- this.store.filter(this.displayField, q);
- }
- this.onLoad();
- }else{
- this.store.baseParams[this.queryParam] = q;
-
- var options = {params : this.getParams(q)};
-
- if(this.loadNext){
- options.add = true;
- options.params.start = this.page * this.pageSize;
- }
-
- this.store.load(options);
- this.expand();
- }
- }else{
- this.selectedIndex = -1;
- this.onLoad();
- }
- }
-
- this.loadNext = false;
- },
+ sum : function(property, start, end){
+ var rs = this.data.items, v = 0;
+ start = start || 0;
+ end = (end || end === 0) ? end : rs.length-1;
- // private
- getParams : function(q){
- var p = {};
- //p[this.queryParam] = q;
-
- if(this.pageSize){
- p.start = 0;
- p.limit = this.pageSize;
+ for(var i = start; i <= end; i++){
+ v += (rs[i].data[property] || 0);
}
- return p;
+ return v;
},
/**
- * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
+ * Filter the records by a specified property.
+ * @param {String} field A field on your records
+ * @param {String/RegExp} value Either a string that the field
+ * should start with or a RegExp to test against the field
+ * @param {Boolean} anyMatch True to match any part not just the beginning
*/
- collapse : function(){
- if(!this.isExpanded()){
- return;
- }
-
- this.list.hide();
- Roo.get(document).un('mousedown', this.collapseIf, this);
- Roo.get(document).un('mousewheel', this.collapseIf, this);
- if (!this.editable) {
- Roo.get(document).un('keydown', this.listKeyPress, this);
- }
- this.fireEvent('collapse', this);
+ filter : function(property, value, anyMatch){
+ var fn = this.createFilterFn(property, value, anyMatch);
+ return fn ? this.filterBy(fn) : this.clearFilter();
},
- // private
- collapseIf : function(e){
- var in_combo = e.within(this.el);
- var in_list = e.within(this.list);
-
- if (in_combo || in_list) {
- //e.stopPropagation();
- return;
- }
-
- this.collapse();
-
+ /**
+ * Filter by a function. The specified function will be called with each
+ * record in this data source. If the function returns true the record is included,
+ * otherwise it is filtered.
+ * @param {Function} fn The function to be called, it will receive 2 args (record, id)
+ * @param {Object} scope (optional) The scope of the function (defaults to this)
+ */
+ filterBy : function(fn, scope){
+ this.snapshot = this.snapshot || this.data;
+ this.data = this.queryBy(fn, scope||this);
+ this.fireEvent("datachanged", this);
},
/**
- * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
+ * Query the records by a specified property.
+ * @param {String} field A field on your records
+ * @param {String/RegExp} value Either a string that the field
+ * should start with or a RegExp to test against the field
+ * @param {Boolean} anyMatch True to match any part not just the beginning
+ * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
*/
- expand : function(){
-
- if(this.isExpanded() || !this.hasFocus){
- return;
- }
- Roo.log('expand');
- this.list.alignTo(this.inputEl(), this.listAlign);
- this.list.show();
- Roo.get(document).on('mousedown', this.collapseIf, this);
- Roo.get(document).on('mousewheel', this.collapseIf, this);
- if (!this.editable) {
- Roo.get(document).on('keydown', this.listKeyPress, this);
- }
-
- this.fireEvent('expand', this);
+ query : function(property, value, anyMatch){
+ var fn = this.createFilterFn(property, value, anyMatch);
+ return fn ? this.queryBy(fn) : this.data.clone();
},
- // private
- // Implements the default empty TriggerField.onTriggerClick function
- onTriggerClick : function()
- {
- Roo.log('trigger click');
-
- if(this.disabled){
- return;
- }
-
- this.page = 0;
- this.loadNext = false;
-
- if(this.isExpanded()){
- this.collapse();
- if (!this.blockFocus) {
- this.inputEl().focus();
- }
-
- }else {
- this.hasFocus = true;
- if(this.triggerAction == 'all') {
- this.doQuery(this.allQuery, true);
- } else {
- this.doQuery(this.getRawValue());
- }
- if (!this.blockFocus) {
- this.inputEl().focus();
+ /**
+ * Query by a function. The specified function will be called with each
+ * record in this data source. If the function returns true the record is included
+ * in the results.
+ * @param {Function} fn The function to be called, it will receive 2 args (record, id)
+ * @param {Object} scope (optional) The scope of the function (defaults to this)
+ @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
+ **/
+ queryBy : function(fn, scope){
+ var data = this.snapshot || this.data;
+ return data.filterBy(fn, scope||this);
+ },
+
+ /**
+ * Collects unique values for a particular dataIndex from this store.
+ * @param {String} dataIndex The property to collect
+ * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
+ * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
+ * @return {Array} An array of the unique values
+ **/
+ collect : function(dataIndex, allowNull, bypassFilter){
+ var d = (bypassFilter === true && this.snapshot) ?
+ this.snapshot.items : this.data.items;
+ var v, sv, r = [], l = {};
+ for(var i = 0, len = d.length; i < len; i++){
+ v = d[i].data[dataIndex];
+ sv = String(v);
+ if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
+ l[sv] = true;
+ r[r.length] = v;
}
}
+ return r;
},
- listKeyPress : function(e)
- {
- //Roo.log('listkeypress');
- // scroll to first matching element based on key pres..
- if (e.isSpecialKey()) {
- return false;
- }
- var k = String.fromCharCode(e.getKey()).toUpperCase();
- //Roo.log(k);
- var match = false;
- var csel = this.view.getSelectedNodes();
- var cselitem = false;
- if (csel.length) {
- var ix = this.view.indexOf(csel[0]);
- cselitem = this.store.getAt(ix);
- if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
- cselitem = false;
+
+ /**
+ * Revert to a view of the Record cache with no filtering applied.
+ * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
+ */
+ clearFilter : function(suppressEvent){
+ if(this.snapshot && this.snapshot != this.data){
+ this.data = this.snapshot;
+ delete this.snapshot;
+ if(suppressEvent !== true){
+ this.fireEvent("datachanged", this);
}
-
}
-
- this.store.each(function(v) {
- if (cselitem) {
- // start at existing selection.
- if (cselitem.id == v.id) {
- cselitem = false;
- }
- return true;
- }
-
- if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
- match = this.store.indexOf(v);
- return false;
- }
- return true;
- }, this);
-
- if (match === false) {
- return true; // no more action?
+ },
+
+ // private
+ afterEdit : function(record){
+ if(this.modified.indexOf(record) == -1){
+ this.modified.push(record);
}
- // scroll to?
- this.view.select(match);
- var sn = Roo.get(this.view.getSelectedNodes()[0])
- //sn.scrollIntoView(sn.dom.parentNode, false);
+ this.fireEvent("update", this, record, Roo.data.Record.EDIT);
},
- onViewScroll : function(e, t){
-
- if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
- return;
+ // private
+ afterReject : function(record){
+ this.modified.remove(record);
+ this.fireEvent("update", this, record, Roo.data.Record.REJECT);
+ },
+
+ // private
+ afterCommit : function(record){
+ this.modified.remove(record);
+ this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
+ },
+
+ /**
+ * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
+ * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
+ */
+ commitChanges : function(){
+ var m = this.modified.slice(0);
+ this.modified = [];
+ for(var i = 0, len = m.length; i < len; i++){
+ m[i].commit();
}
-
- this.hasQuery = true;
-
- this.loading = this.list.select('.loading', true).first();
-
- if(this.loading === null){
- this.list.createChild({
- tag: 'div',
- cls: 'loading select2-more-results select2-active',
- html: 'Loading more results...'
- })
-
- this.loading = this.list.select('.loading', true).first();
-
- this.loading.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.loading.hide();
+ },
+
+ /**
+ * Cancel outstanding changes on all changed records.
+ */
+ rejectChanges : function(){
+ var m = this.modified.slice(0);
+ this.modified = [];
+ for(var i = 0, len = m.length; i < len; i++){
+ m[i].reject();
}
-
- this.loading.show();
-
- var _combo = this;
-
- this.page++;
- this.loadNext = true;
-
- (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
-
- return;
+ },
+
+ onMetaChange : function(meta, rtype, o){
+ this.recordType = rtype;
+ this.fields = rtype.prototype.fields;
+ delete this.snapshot;
+ this.sortInfo = meta.sortInfo || this.sortInfo;
+ this.modified = [];
+ this.fireEvent('metachange', this, this.reader.meta);
},
- addItem : function(o)
- {
- var dv = ''; // display value
-
- if (this.displayField) {
- dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
- } else {
- // this is an error condition!!!
- Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
- }
-
- if(!dv.length){
- return;
- }
-
- var choice = this.choices.createChild({
- tag: 'li',
- cls: 'select2-search-choice',
- cn: [
- {
- tag: 'div',
- html: dv
- },
- {
- tag: 'a',
- href: '#',
- cls: 'select2-search-choice-close',
- tabindex: '-1'
- }
- ]
-
- }, this.searchField);
-
- var close = choice.select('a.select2-search-choice-close', true).first()
-
- close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
+ moveIndex : function(data, type)
+ {
+ var index = this.indexOf(data);
- this.item.push(o);
- this.lastData = o;
+ var newIndex = index + type;
- this.syncValue();
+ this.remove(data);
- this.inputEl().dom.value = '';
+ this.insert(newIndex, data);
- },
+ }
+});/*
+ * 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.data.SimpleStore
+ * @extends Roo.data.Store
+ * Small helper class to make creating Stores from Array data easier.
+ * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
+ * @cfg {Array} fields An array of field definition objects, or field name strings.
+ * @cfg {Array} data The multi-dimensional array of data
+ * @constructor
+ * @param {Object} config
+ */
+Roo.data.SimpleStore = function(config){
+ Roo.data.SimpleStore.superclass.constructor.call(this, {
+ isLocal : true,
+ reader: new Roo.data.ArrayReader({
+ id: config.id
+ },
+ Roo.data.Record.create(config.fields)
+ ),
+ proxy : new Roo.data.MemoryProxy(config.data)
+ });
+ this.load();
+};
+Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
+ * 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.data.Store
+ * @class Roo.data.JsonStore
+ * Small helper class to make creating Stores for JSON data easier. <br/>
+<pre><code>
+var store = new Roo.data.JsonStore({
+ url: 'get-images.php',
+ root: 'images',
+ fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
+});
+</code></pre>
+ * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
+ * JsonReader and HttpProxy (unless inline data is provided).</b>
+ * @cfg {Array} fields An array of field definition objects, or field name strings.
+ * @constructor
+ * @param {Object} config
+ */
+Roo.data.JsonStore = function(c){
+ Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
+ proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
+ reader: new Roo.data.JsonReader(c, c.fields)
+ }));
+};
+Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
+ * 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">
+ */
+
+
+Roo.data.Field = function(config){
+ if(typeof config == "string"){
+ config = {name: config};
+ }
+ Roo.apply(this, config);
- onRemoveItem : function(e, _self, o)
- {
- e.preventDefault();
- var index = this.item.indexOf(o.data) * 1;
-
- if( index < 0){
- Roo.log('not this item?!');
- return;
- }
-
- this.item.splice(index, 1);
- o.item.remove();
-
- this.syncValue();
-
- this.fireEvent('remove', this, e);
-
- },
+ if(!this.type){
+ this.type = "auto";
+ }
- syncValue : function()
- {
- if(!this.item.length){
- this.clearValue();
- return;
+ var st = Roo.data.SortTypes;
+ // named sortTypes are supported, here we look them up
+ if(typeof this.sortType == "string"){
+ this.sortType = st[this.sortType];
+ }
+
+ // set default sortType for strings and dates
+ if(!this.sortType){
+ switch(this.type){
+ case "string":
+ this.sortType = st.asUCString;
+ break;
+ case "date":
+ this.sortType = st.asDate;
+ break;
+ default:
+ this.sortType = st.none;
}
+ }
+
+ // define once
+ var stripRe = /[\$,%]/g;
+
+ // prebuilt conversion function for this field, instead of
+ // switching every time we're reading a value
+ if(!this.convert){
+ var cv, dateFormat = this.dateFormat;
+ switch(this.type){
+ case "":
+ case "auto":
+ case undefined:
+ cv = function(v){ return v; };
+ break;
+ case "string":
+ cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
+ break;
+ case "int":
+ cv = function(v){
+ return v !== undefined && v !== null && v !== '' ?
+ parseInt(String(v).replace(stripRe, ""), 10) : '';
+ };
+ break;
+ case "float":
+ cv = function(v){
+ return v !== undefined && v !== null && v !== '' ?
+ parseFloat(String(v).replace(stripRe, ""), 10) : '';
+ };
+ break;
+ case "bool":
+ case "boolean":
+ cv = function(v){ return v === true || v === "true" || v == 1; };
+ break;
+ case "date":
+ cv = function(v){
+ if(!v){
+ return '';
+ }
+ if(v instanceof Date){
+ return v;
+ }
+ if(dateFormat){
+ if(dateFormat == "timestamp"){
+ return new Date(v*1000);
+ }
+ return Date.parseDate(v, dateFormat);
+ }
+ var parsed = Date.parse(v);
+ return parsed ? new Date(parsed) : null;
+ };
+ break;
- var value = [];
- var _this = this;
- Roo.each(this.item, function(i){
- if(_this.valueField){
- value.push(i[_this.valueField]);
- return;
- }
+ }
+ this.convert = cv;
+ }
+};
- value.push(i);
- });
+Roo.data.Field.prototype = {
+ dateFormat: null,
+ defaultValue: "",
+ mapping: null,
+ sortType : null,
+ sortDir : "ASC"
+};/*
+ * 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">
+ */
+
+// Base class for reading structured data from a data source. This class is intended to be
+// extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
- this.value = value.join(',');
+/**
+ * @class Roo.data.DataReader
+ * Base class for reading structured data from a data source. This class is intended to be
+ * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
+ */
- if(this.hiddenField){
- this.hiddenField.dom.value = this.value;
- }
- },
+Roo.data.DataReader = function(meta, recordType){
- clearItem : function()
- {
- if(!this.multiple){
- return;
- }
-
- this.item = [];
-
- Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
- c.remove();
+ this.meta = meta;
+
+ this.recordType = recordType instanceof Array ?
+ Roo.data.Record.create(recordType) : recordType;
+};
+
+Roo.data.DataReader.prototype = {
+ /**
+ * Create an empty record
+ * @param {Object} data (optional) - overlay some values
+ * @return {Roo.data.Record} record created.
+ */
+ newRow : function(d) {
+ var da = {};
+ this.recordType.prototype.fields.each(function(c) {
+ switch( c.type) {
+ case 'int' : da[c.name] = 0; break;
+ case 'date' : da[c.name] = new Date(); break;
+ case 'float' : da[c.name] = 0.0; break;
+ case 'boolean' : da[c.name] = false; break;
+ default : da[c.name] = ""; break;
+ }
+
});
-
- this.syncValue();
+ return new this.recordType(Roo.apply(da, d));
}
-
+};/*
+ * 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.data.DataProxy
+ * @extends Roo.data.Observable
+ * This class is an abstract base class for implementations which provide retrieval of
+ * unformatted data objects.<br>
+ * <p>
+ * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
+ * (of the appropriate type which knows how to parse the data object) to provide a block of
+ * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
+ * <p>
+ * Custom implementations must implement the load method as described in
+ * {@link Roo.data.HttpProxy#load}.
+ */
+Roo.data.DataProxy = function(){
+ this.addEvents({
+ /**
+ * @event beforeload
+ * Fires before a network request is made to retrieve a data object.
+ * @param {Object} This DataProxy object.
+ * @param {Object} params The params parameter to the load function.
+ */
+ beforeload : true,
+ /**
+ * @event load
+ * Fires before the load method's callback is called.
+ * @param {Object} This DataProxy object.
+ * @param {Object} o The data object.
+ * @param {Object} arg The callback argument object passed to the load function.
+ */
+ load : true,
+ /**
+ * @event loadexception
+ * Fires if an Exception occurs during data retrieval.
+ * @param {Object} This DataProxy object.
+ * @param {Object} o The data object.
+ * @param {Object} arg The callback argument object passed to the load function.
+ * @param {Object} e The Exception.
+ */
+ loadexception : true
+ });
+ Roo.data.DataProxy.superclass.constructor.call(this);
+};
+
+Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
- /**
- * @cfg {Boolean} grow
- * @hide
- */
- /**
- * @cfg {Number} growMin
- * @hide
- */
- /**
- * @cfg {Number} growMax
- * @hide
- */
/**
- * @hide
- * @method autoSize
+ * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
*/
-});
/*
* Based on:
* Ext JS Library 1.1.1
* Fork - LGPL
* <script type="text/javascript">
*/
-
/**
- * @class Roo.View
- * @extends Roo.util.Observable
- * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
- * This class also supports single and multi selection modes. <br>
- * Create a data model bound view:
- <pre><code>
- var store = new Roo.data.Store(...);
-
- var view = new Roo.View({
- el : "my-element",
- tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
-
- singleSelect: true,
- selectedClass: "ydataview-selected",
- store: store
- });
-
- // listen for node click?
- view.on("click", function(vw, index, node, e){
- alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
- });
-
- // load XML data
- dataModel.load("foobar.xml");
- </code></pre>
- For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
- * <br><br>
- * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
- * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
- *
- * Note: old style constructor is still suported (container, template, config)
- *
+ * @class Roo.data.MemoryProxy
+ * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
+ * to the Reader when its load method is called.
* @constructor
- * Create a new View
- * @param {Object} config The config object
- *
+ * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
*/
-Roo.View = function(config, depreciated_tpl, depreciated_config){
-
- if (typeof(depreciated_tpl) == 'undefined') {
- // new way.. - universal constructor.
- Roo.apply(this, config);
- this.el = Roo.get(this.el);
- } else {
- // old format..
- this.el = Roo.get(config);
- this.tpl = depreciated_tpl;
- Roo.apply(this, depreciated_config);
- }
- this.wrapEl = this.el.wrap().wrap();
- ///this.el = this.wrapEla.appendChild(document.createElement("div"));
-
-
- if(typeof(this.tpl) == "string"){
- this.tpl = new Roo.Template(this.tpl);
- } else {
- // support xtype ctors..
- this.tpl = new Roo.factory(this.tpl, Roo);
+Roo.data.MemoryProxy = function(data){
+ if (data.data) {
+ data = data.data;
}
-
-
- this.tpl.compile();
-
-
-
-
- /** @private */
- this.addEvents({
- /**
- * @event beforeclick
- * Fires before a click is processed. Returns false to cancel the default action.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "beforeclick" : true,
- /**
- * @event click
- * Fires when a template node is clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "click" : true,
- /**
- * @event dblclick
- * Fires when a template node is double clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "dblclick" : true,
- /**
- * @event contextmenu
- * Fires when a template node is right clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "contextmenu" : true,
- /**
- * @event selectionchange
- * Fires when the selected nodes change.
- * @param {Roo.View} this
- * @param {Array} selections Array of the selected nodes
- */
- "selectionchange" : true,
-
- /**
- * @event beforeselect
- * Fires before a selection is made. If any handlers return false, the selection is cancelled.
- * @param {Roo.View} this
- * @param {HTMLElement} node The node to be selected
- * @param {Array} selections Array of currently selected nodes
- */
- "beforeselect" : true,
- /**
- * @event preparedata
- * Fires on every row to render, to allow you to change the data.
- * @param {Roo.View} this
- * @param {Object} data to be rendered (change this)
- */
- "preparedata" : true
-
-
- });
-
-
-
- this.el.on({
- "click": this.onClick,
- "dblclick": this.onDblClick,
- "contextmenu": this.onContextMenu,
- scope:this
- });
+ Roo.data.MemoryProxy.superclass.constructor.call(this);
+ this.data = data;
+};
- this.selections = [];
- this.nodes = [];
- this.cmp = new Roo.CompositeElementLite([]);
- if(this.store){
- this.store = Roo.factory(this.store, Roo.data);
- this.setStore(this.store, true);
- }
+Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
+ /**
+ * Load data from the requested source (in this case an in-memory
+ * data object passed to the constructor), read the data object into
+ * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params This parameter is not used by the MemoryProxy class.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ */
+ load : function(params, reader, callback, scope, arg){
+ params = params || {};
+ var result;
+ try {
+ result = reader.readRecords(this.data);
+ }catch(e){
+ this.fireEvent("loadexception", this, arg, null, e);
+ callback.call(scope, null, arg, false);
+ return;
+ }
+ callback.call(scope, result, arg, true);
+ },
- if ( this.footer && this.footer.xtype) {
-
- var fctr = this.wrapEl.appendChild(document.createElement("div"));
-
- this.footer.dataSource = this.store
- this.footer.container = fctr;
- this.footer = Roo.factory(this.footer, Roo);
- fctr.insertFirst(this.el);
+ // private
+ update : function(params, records){
- // this is a bit insane - as the paging toolbar seems to detach the el..
-// dom.parentNode.parentNode.parentNode
- // they get detached?
}
-
-
- Roo.View.superclass.constructor.call(this);
-
-
+});/*
+ * 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.data.HttpProxy
+ * @extends Roo.data.DataProxy
+ * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
+ * configured to reference a certain URL.<br><br>
+ * <p>
+ * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
+ * from which the running page was served.<br><br>
+ * <p>
+ * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
+ * <p>
+ * Be aware that to enable the browser to parse an XML document, the server must set
+ * the Content-Type header in the HTTP response to "text/xml".
+ * @constructor
+ * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
+ * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
+ * will be used to make the request.
+ */
+Roo.data.HttpProxy = function(conn){
+ Roo.data.HttpProxy.superclass.constructor.call(this);
+ // is conn a conn config or a real conn?
+ this.conn = conn;
+ this.useAjax = !conn || !conn.events;
+
};
-Roo.extend(Roo.View, Roo.util.Observable, {
-
- /**
- * @cfg {Roo.data.Store} store Data store to load data from.
- */
- store : false,
-
- /**
- * @cfg {String|Roo.Element} el The container element.
- */
- el : '',
+Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
+ // thse are take from connection...
/**
- * @cfg {String|Roo.Template} tpl The template used by this View
+ * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
*/
- tpl : false,
/**
- * @cfg {String} dataName the named area of the template to use as the data area
- * Works with domtemplates roo-name="name"
+ * @cfg {Object} extraParams (Optional) An object containing properties which are used as
+ * extra parameters to each request made by this object. (defaults to undefined)
*/
- dataName: false,
/**
- * @cfg {String} selectedClass The css class to add to selected nodes
- */
- selectedClass : "x-view-selected",
- /**
- * @cfg {String} emptyText The empty text to show when nothing is loaded.
+ * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
+ * to each request made by this object. (defaults to undefined)
*/
- emptyText : "",
-
/**
- * @cfg {String} text to display on mask (default Loading)
+ * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
*/
- mask : false,
/**
- * @cfg {Boolean} multiSelect Allow multiple selection
+ * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
*/
- multiSelect : false,
- /**
- * @cfg {Boolean} singleSelect Allow single selection
+ /**
+ * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
+ * @type Boolean
*/
- singleSelect: false,
-
+
+
/**
- * @cfg {Boolean} toggleSelect - selecting
+ * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
+ * @type Boolean
*/
- toggleSelect : false,
-
/**
- * Returns the element this view is bound to.
- * @return {Roo.Element}
+ * Return the {@link Roo.data.Connection} object being used by this Proxy.
+ * @return {Connection} The Connection object. This object may be used to subscribe to events on
+ * a finer-grained basis than the DataProxy events.
*/
- getEl : function(){
- return this.wrapEl;
+ getConnection : function(){
+ return this.useAjax ? Roo.Ajax : this.conn;
},
-
-
/**
- * Refreshes the view. - called by datachanged on the store. - do not call directly.
+ * Load data from the configured {@link Roo.data.Connection}, read the data object into
+ * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params An object containing properties which are to be used as HTTP parameters
+ * for the request to the remote server.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.Records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
*/
- refresh : function(){
- Roo.log('refresh');
- var t = this.tpl;
-
- // if we are using something like 'domtemplate', then
- // the what gets used is:
- // t.applySubtemplate(NAME, data, wrapping data..)
- // the outer template then get' applied with
- // the store 'extra data'
- // and the body get's added to the
- // roo-name="data" node?
- // <span class='roo-tpl-{name}'></span> ?????
-
-
-
- this.clearSelections();
- this.el.update("");
- var html = [];
- var records = this.store.getRange();
- if(records.length < 1) {
-
- // is this valid?? = should it render a template??
-
- this.el.update(this.emptyText);
- return;
+ load : function(params, reader, callback, scope, arg){
+ if(this.fireEvent("beforeload", this, params) !== false){
+ var o = {
+ params : params || {},
+ request: {
+ callback : callback,
+ scope : scope,
+ arg : arg
+ },
+ reader: reader,
+ callback : this.loadResponse,
+ scope: this
+ };
+ if(this.useAjax){
+ Roo.applyIf(o, this.conn);
+ if(this.activeRequest){
+ Roo.Ajax.abort(this.activeRequest);
+ }
+ this.activeRequest = Roo.Ajax.request(o);
+ }else{
+ this.conn.request(o);
+ }
+ }else{
+ callback.call(scope||this, null, arg, false);
}
- var el = this.el;
- if (this.dataName) {
- this.el.update(t.apply(this.store.meta)); //????
- el = this.el.child('.roo-tpl-' + this.dataName);
+ },
+
+ // private
+ loadResponse : function(o, success, response){
+ delete this.activeRequest;
+ if(!success){
+ this.fireEvent("loadexception", this, o, response);
+ o.request.callback.call(o.request.scope, null, o.request.arg, false);
+ return;
}
-
- for(var i = 0, len = records.length; i < len; i++){
- var data = this.prepareData(records[i].data, i, records[i]);
- this.fireEvent("preparedata", this, data, i, records[i]);
- html[html.length] = Roo.util.Format.trim(
- this.dataName ?
- t.applySubtemplate(this.dataName, data, this.store.meta) :
- t.apply(data)
- );
+ var result;
+ try {
+ result = o.reader.read(response);
+ }catch(e){
+ this.fireEvent("loadexception", this, o, response, e);
+ o.request.callback.call(o.request.scope, null, o.request.arg, false);
+ return;
}
-
-
- el.update(html.join(""));
- this.nodes = el.dom.childNodes;
- this.updateIndexes(0);
+ this.fireEvent("load", this, o, o.request.arg);
+ o.request.callback.call(o.request.scope, result, o.request.arg, true);
},
-
- /**
- * Function to override to reformat the data that is sent to
- * the template for each node.
- * DEPRICATED - use the preparedata event handler.
- * @param {Array/Object} data The raw data (array of colData for a data model bound view or
- * a JSON object for an UpdateManager bound view).
- */
- prepareData : function(data, index, record)
- {
- this.fireEvent("preparedata", this, data, index, record);
- return data;
- },
+ // private
+ update : function(dataSet){
- onUpdate : function(ds, record){
- Roo.log('on update');
- this.clearSelections();
- var index = this.store.indexOf(record);
- var n = this.nodes[index];
- this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
- n.parentNode.removeChild(n);
- this.updateIndexes(index, index);
},
-
-
-// --------- FIXME
- onAdd : function(ds, records, index)
- {
- Roo.log(['on Add', ds, records, index] );
- this.clearSelections();
- if(this.nodes.length == 0){
- this.refresh();
- return;
- }
- var n = this.nodes[index];
- for(var i = 0, len = records.length; i < len; i++){
- var d = this.prepareData(records[i].data, i, records[i]);
- if(n){
- this.tpl.insertBefore(n, d);
- }else{
-
- this.tpl.append(this.el, d);
- }
- }
- this.updateIndexes(index);
- },
+ // private
+ updateResponse : function(dataSet){
- onRemove : function(ds, record, index){
- Roo.log('onRemove');
- this.clearSelections();
- var el = this.dataName ?
- this.el.child('.roo-tpl-' + this.dataName) :
- this.el;
-
- el.dom.removeChild(this.nodes[index]);
- this.updateIndexes(index);
- },
+ }
+});/*
+ * 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.data.ScriptTagProxy
+ * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
+ * other than the originating domain of the running page.<br><br>
+ * <p>
+ * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
+ * of the running page, you must use this class, rather than DataProxy.</em><br><br>
+ * <p>
+ * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
+ * source code that is used as the source inside a <script> tag.<br><br>
+ * <p>
+ * In order for the browser to process the returned data, the server must wrap the data object
+ * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
+ * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
+ * depending on whether the callback name was passed:
+ * <p>
+ * <pre><code>
+boolean scriptTag = false;
+String cb = request.getParameter("callback");
+if (cb != null) {
+ scriptTag = true;
+ response.setContentType("text/javascript");
+} else {
+ response.setContentType("application/x-json");
+}
+Writer out = response.getWriter();
+if (scriptTag) {
+ out.write(cb + "(");
+}
+out.print(dataBlock.toJsonString());
+if (scriptTag) {
+ out.write(");");
+}
+</pre></code>
+ *
+ * @constructor
+ * @param {Object} config A configuration object.
+ */
+Roo.data.ScriptTagProxy = function(config){
+ Roo.data.ScriptTagProxy.superclass.constructor.call(this);
+ Roo.apply(this, config);
+ this.head = document.getElementsByTagName("head")[0];
+};
+
+Roo.data.ScriptTagProxy.TRANS_ID = 1000;
+Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
/**
- * Refresh an individual node.
- * @param {Number} index
+ * @cfg {String} url The URL from which to request the data object.
*/
- refreshNode : function(index){
- this.onUpdate(this.store, this.store.getAt(index));
- },
-
- updateIndexes : function(startIndex, endIndex){
- var ns = this.nodes;
- startIndex = startIndex || 0;
- endIndex = endIndex || ns.length - 1;
- for(var i = startIndex; i <= endIndex; i++){
- ns[i].nodeIndex = i;
- }
- },
-
/**
- * Changes the data store this view uses and refresh the view.
- * @param {Store} store
+ * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
*/
- setStore : function(store, initial){
- if(!initial && this.store){
- this.store.un("datachanged", this.refresh);
- this.store.un("add", this.onAdd);
- this.store.un("remove", this.onRemove);
- this.store.un("update", this.onUpdate);
- this.store.un("clear", this.refresh);
- this.store.un("beforeload", this.onBeforeLoad);
- this.store.un("load", this.onLoad);
- this.store.un("loadexception", this.onLoad);
- }
- if(store){
-
- store.on("datachanged", this.refresh, this);
- store.on("add", this.onAdd, this);
- store.on("remove", this.onRemove, this);
- store.on("update", this.onUpdate, this);
- store.on("clear", this.refresh, this);
- store.on("beforeload", this.onBeforeLoad, this);
- store.on("load", this.onLoad, this);
- store.on("loadexception", this.onLoad, this);
- }
-
- if(store){
- this.refresh();
- }
- },
+ timeout : 30000,
/**
- * onbeforeLoad - masks the loading area.
- *
+ * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
+ * the server the name of the callback function set up by the load call to process the returned data object.
+ * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
+ * javascript output which calls this named function passing the data object as its only parameter.
*/
- onBeforeLoad : function(store,opts)
- {
- Roo.log('onBeforeLoad');
- if (!opts.add) {
- this.el.update("");
- }
- this.el.mask(this.mask ? this.mask : "Loading" );
- },
- onLoad : function ()
- {
- this.el.unmask();
- },
-
+ callbackParam : "callback",
+ /**
+ * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
+ * name to the request.
+ */
+ nocache : true,
/**
- * Returns the template node the passed child belongs to or null if it doesn't belong to one.
- * @param {HTMLElement} node
- * @return {HTMLElement} The template node
+ * Load data from the configured URL, read the data object into
+ * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params An object containing properties which are to be used as HTTP parameters
+ * for the request to the remote server.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.Records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
*/
- findItemFromChild : function(node){
- var el = this.dataName ?
- this.el.child('.roo-tpl-' + this.dataName,true) :
- this.el.dom;
-
- if(!node || node.parentNode == el){
- return node;
- }
- var p = node.parentNode;
- while(p && p != el){
- if(p.parentNode == el){
- return p;
- }
- p = p.parentNode;
- }
- return null;
- },
+ load : function(params, reader, callback, scope, arg){
+ if(this.fireEvent("beforeload", this, params) !== false){
- /** @ignore */
- onClick : function(e){
- var item = this.findItemFromChild(e.getTarget());
- if(item){
- var index = this.indexOf(item);
- if(this.onItemClick(item, index, e) !== false){
- this.fireEvent("click", this, index, item, e);
+ var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
+
+ var url = this.url;
+ url += (url.indexOf("?") != -1 ? "&" : "?") + p;
+ if(this.nocache){
+ url += "&_dc=" + (new Date().getTime());
}
- }else{
- this.clearSelections();
- }
- },
+ var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
+ var trans = {
+ id : transId,
+ cb : "stcCallback"+transId,
+ scriptId : "stcScript"+transId,
+ params : params,
+ arg : arg,
+ url : url,
+ callback : callback,
+ scope : scope,
+ reader : reader
+ };
+ var conn = this;
- /** @ignore */
- onContextMenu : function(e){
- var item = this.findItemFromChild(e.getTarget());
- if(item){
- this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
- }
- },
+ window[trans.cb] = function(o){
+ conn.handleResponse(o, trans);
+ };
- /** @ignore */
- onDblClick : function(e){
- var item = this.findItemFromChild(e.getTarget());
- if(item){
- this.fireEvent("dblclick", this, this.indexOf(item), item, e);
- }
- },
+ url += String.format("&{0}={1}", this.callbackParam, trans.cb);
- onItemClick : function(item, index, e)
- {
- if(this.fireEvent("beforeclick", this, index, item, e) === false){
- return false;
- }
- if (this.toggleSelect) {
- var m = this.isSelected(item) ? 'unselect' : 'select';
- Roo.log(m);
- var _t = this;
- _t[m](item, true, false);
- return true;
- }
- if(this.multiSelect || this.singleSelect){
- if(this.multiSelect && e.shiftKey && this.lastSelection){
- this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
- }else{
- this.select(item, this.multiSelect && e.ctrlKey);
- this.lastSelection = item;
+ if(this.autoAbort !== false){
+ this.abort();
}
- e.preventDefault();
- }
- return true;
- },
- /**
- * Get the number of selected nodes.
- * @return {Number}
- */
- getSelectionCount : function(){
- return this.selections.length;
+ trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
+
+ var script = document.createElement("script");
+ script.setAttribute("src", url);
+ script.setAttribute("type", "text/javascript");
+ script.setAttribute("id", trans.scriptId);
+ this.head.appendChild(script);
+
+ this.trans = trans;
+ }else{
+ callback.call(scope||this, null, arg, false);
+ }
},
- /**
- * Get the currently selected nodes.
- * @return {Array} An array of HTMLElements
- */
- getSelectedNodes : function(){
- return this.selections;
+ // private
+ isLoading : function(){
+ return this.trans ? true : false;
},
/**
- * Get the indexes of the selected nodes.
- * @return {Array}
+ * Abort the current server request.
*/
- getSelectedIndexes : function(){
- var indexes = [], s = this.selections;
- for(var i = 0, len = s.length; i < len; i++){
- indexes.push(s[i].nodeIndex);
+ abort : function(){
+ if(this.isLoading()){
+ this.destroyTrans(this.trans);
}
- return indexes;
},
- /**
- * Clear all selections
- * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
- */
- clearSelections : function(suppressEvent){
- if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
- this.cmp.elements = this.selections;
- this.cmp.removeClass(this.selectedClass);
- this.selections = [];
- if(!suppressEvent){
- this.fireEvent("selectionchange", this, this.selections);
- }
+ // private
+ destroyTrans : function(trans, isLoaded){
+ this.head.removeChild(document.getElementById(trans.scriptId));
+ clearTimeout(trans.timeoutId);
+ if(isLoaded){
+ window[trans.cb] = undefined;
+ try{
+ delete window[trans.cb];
+ }catch(e){}
+ }else{
+ // if hasn't been loaded, wait for load to remove it to prevent script error
+ window[trans.cb] = function(){
+ window[trans.cb] = undefined;
+ try{
+ delete window[trans.cb];
+ }catch(e){}
+ };
}
},
- /**
- * Returns true if the passed node is selected
- * @param {HTMLElement/Number} node The node or node index
- * @return {Boolean}
- */
- isSelected : function(node){
- var s = this.selections;
- if(s.length < 1){
- return false;
+ // private
+ handleResponse : function(o, trans){
+ this.trans = false;
+ this.destroyTrans(trans, true);
+ var result;
+ try {
+ result = trans.reader.readRecords(o);
+ }catch(e){
+ this.fireEvent("loadexception", this, o, trans.arg, e);
+ trans.callback.call(trans.scope||window, null, trans.arg, false);
+ return;
}
- node = this.getNode(node);
- return s.indexOf(node) !== -1;
+ this.fireEvent("load", this, o, trans.arg);
+ trans.callback.call(trans.scope||window, result, trans.arg, true);
},
+ // private
+ handleFailure : function(trans){
+ this.trans = false;
+ this.destroyTrans(trans, false);
+ this.fireEvent("loadexception", this, null, trans.arg);
+ trans.callback.call(trans.scope||window, null, trans.arg, false);
+ }
+});/*
+ * 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.data.JsonReader
+ * @extends Roo.data.DataReader
+ * Data reader class to create an Array of Roo.data.Record objects from a JSON response
+ * based on mappings in a provided Roo.data.Record constructor.
+ *
+ * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
+ * in the reply previously.
+ *
+ * <p>
+ * Example code:
+ * <pre><code>
+var RecordDef = Roo.data.Record.create([
+ {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
+ {name: 'occupation'} // This field will use "occupation" as the mapping.
+]);
+var myReader = new Roo.data.JsonReader({
+ totalProperty: "results", // The property which contains the total dataset size (optional)
+ root: "rows", // The property which contains an Array of row objects
+ id: "id" // The property within each row object that provides an ID for the record (optional)
+}, RecordDef);
+</code></pre>
+ * <p>
+ * This would consume a JSON file like this:
+ * <pre><code>
+{ 'results': 2, 'rows': [
+ { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
+ { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
+}
+</code></pre>
+ * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
+ * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
+ * paged from the remote server.
+ * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
+ * @cfg {String} root name of the property which contains the Array of row objects.
+ * @cfg {String} id Name of the property within a row object that contains a record identifier value.
+ * @constructor
+ * Create a new JsonReader
+ * @param {Object} meta Metadata configuration options
+ * @param {Object} recordType Either an Array of field definition objects,
+ * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
+ */
+Roo.data.JsonReader = function(meta, recordType){
+
+ meta = meta || {};
+ // set some defaults:
+ Roo.applyIf(meta, {
+ totalProperty: 'total',
+ successProperty : 'success',
+ root : 'data',
+ id : 'id'
+ });
+
+ Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+};
+Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
+
/**
- * Selects nodes.
- * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
- * @param {Boolean} keepExisting (optional) true to keep existing selections
- * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+ * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
+ * Used by Store query builder to append _requestMeta to params.
+ *
*/
- select : function(nodeInfo, keepExisting, suppressEvent){
- if(nodeInfo instanceof Array){
- if(!keepExisting){
- this.clearSelections(true);
- }
- for(var i = 0, len = nodeInfo.length; i < len; i++){
- this.select(nodeInfo[i], true, true);
- }
- return;
- }
- var node = this.getNode(nodeInfo);
- if(!node || this.isSelected(node)){
- return; // already selected.
- }
- if(!keepExisting){
- this.clearSelections(true);
- }
- if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
- Roo.fly(node).addClass(this.selectedClass);
- this.selections.push(node);
- if(!suppressEvent){
- this.fireEvent("selectionchange", this, this.selections);
- }
- }
-
-
- },
- /**
- * Unselects nodes.
- * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
- * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
- * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+ metaFromRemote : false,
+ /**
+ * This method is only used by a DataProxy which has retrieved data from a remote server.
+ * @param {Object} response The XHR object which contains the JSON data in its responseText.
+ * @return {Object} data A data block which is used by an Roo.data.Store object as
+ * a cache of Roo.data.Records.
*/
- unselect : function(nodeInfo, keepExisting, suppressEvent)
- {
- if(nodeInfo instanceof Array){
- Roo.each(this.selections, function(s) {
- this.unselect(s, nodeInfo);
- }, this);
- return;
+ read : function(response){
+ var json = response.responseText;
+
+ var o = /* eval:var:o */ eval("("+json+")");
+ if(!o) {
+ throw {message: "JsonReader.read: Json object not found"};
}
- var node = this.getNode(nodeInfo);
- if(!node || !this.isSelected(node)){
- Roo.log("not selected");
- return; // not selected.
+
+ if(o.metaData){
+
+ delete this.ef;
+ this.metaFromRemote = true;
+ this.meta = o.metaData;
+ this.recordType = Roo.data.Record.create(o.metaData.fields);
+ this.onMetaChange(this.meta, this.recordType, o);
}
- // fireevent???
- var ns = [];
- Roo.each(this.selections, function(s) {
- if (s == node ) {
- Roo.fly(node).removeClass(this.selectedClass);
+ return this.readRecords(o);
+ },
+
+ // private function a store will implement
+ onMetaChange : function(meta, recordType, o){
- return;
- }
- ns.push(s);
- },this);
-
- this.selections= ns;
- this.fireEvent("selectionchange", this, this.selections);
},
/**
- * Gets a template node.
- * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
- * @return {HTMLElement} The node or null if it wasn't found
- */
- getNode : function(nodeInfo){
- if(typeof nodeInfo == "string"){
- return document.getElementById(nodeInfo);
- }else if(typeof nodeInfo == "number"){
- return this.nodes[nodeInfo];
- }
- return nodeInfo;
+ * @ignore
+ */
+ simpleAccess: function(obj, subsc) {
+ return obj[subsc];
},
+ /**
+ * @ignore
+ */
+ getJsonAccessor: function(){
+ var re = /[\[\.]/;
+ return function(expr) {
+ try {
+ return(re.test(expr))
+ ? new Function("obj", "return obj." + expr)
+ : function(obj){
+ return obj[expr];
+ };
+ } catch(e){}
+ return Roo.emptyFn;
+ };
+ }(),
+
/**
- * Gets a range template nodes.
- * @param {Number} startIndex
- * @param {Number} endIndex
- * @return {Array} An array of nodes
+ * Create a data block containing Roo.data.Records from an XML document.
+ * @param {Object} o An object which contains an Array of row objects in the property specified
+ * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
+ * which contains the total size of the dataset.
+ * @return {Object} data A data block which is used by an Roo.data.Store object as
+ * a cache of Roo.data.Records.
*/
- getNodes : function(start, end){
- var ns = this.nodes;
- start = start || 0;
- end = typeof end == "undefined" ? ns.length - 1 : end;
- var nodes = [];
- if(start <= end){
- for(var i = start; i <= end; i++){
- nodes.push(ns[i]);
- }
- } else{
- for(var i = start; i >= end; i--){
- nodes.push(ns[i]);
+ readRecords : function(o){
+ /**
+ * After any data loads, the raw JSON data is available for further custom processing.
+ * @type Object
+ */
+ this.o = o;
+ var s = this.meta, Record = this.recordType,
+ f = Record.prototype.fields, fi = f.items, fl = f.length;
+
+// Generate extraction functions for the totalProperty, the root, the id, and for each field
+ if (!this.ef) {
+ if(s.totalProperty) {
+ this.getTotal = this.getJsonAccessor(s.totalProperty);
+ }
+ if(s.successProperty) {
+ this.getSuccess = this.getJsonAccessor(s.successProperty);
+ }
+ this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
+ if (s.id) {
+ var g = this.getJsonAccessor(s.id);
+ this.getId = function(rec) {
+ var r = g(rec);
+ return (r === undefined || r === "") ? null : r;
+ };
+ } else {
+ this.getId = function(){return null;};
+ }
+ this.ef = [];
+ for(var jj = 0; jj < fl; jj++){
+ f = fi[jj];
+ var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
+ this.ef[jj] = this.getJsonAccessor(map);
}
}
- return nodes;
- },
- /**
- * Finds the index of the passed node
- * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
- * @return {Number} The index of the node or -1
- */
- indexOf : function(node){
- node = this.getNode(node);
- if(typeof node.nodeIndex == "number"){
- return node.nodeIndex;
+ var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
+ if(s.totalProperty){
+ var vt = parseInt(this.getTotal(o), 10);
+ if(!isNaN(vt)){
+ totalRecords = vt;
+ }
}
- var ns = this.nodes;
- for(var i = 0, len = ns.length; i < len; i++){
- if(ns[i] == node){
- return i;
+ if(s.successProperty){
+ var vs = this.getSuccess(o);
+ if(vs === false || vs === 'false'){
+ success = false;
}
}
- return -1;
+ var records = [];
+ for(var i = 0; i < c; i++){
+ var n = root[i];
+ var values = {};
+ var id = this.getId(n);
+ for(var j = 0; j < fl; j++){
+ f = fi[j];
+ var v = this.ef[j](n);
+ if (!f.convert) {
+ Roo.log('missing convert for ' + f.name);
+ Roo.log(f);
+ continue;
+ }
+ values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
+ }
+ var record = new Record(values, id);
+ record.json = n;
+ records[i] = record;
+ }
+ return {
+ raw : o,
+ success : success,
+ records : records,
+ totalRecords : totalRecords
+ };
}
-});
-/*
- * - LGPL
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
*
- * based on jquery fullcalendar
- *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
*/
-Roo.bootstrap = Roo.bootstrap || {};
/**
- * @class Roo.bootstrap.Calendar
- * @extends Roo.bootstrap.Component
- * Bootstrap Calendar class
- * @cfg {Boolean} loadMask (true|false) default false
- * @cfg {Object} header generate the user specific header of the calendar, default false
-
+ * @class Roo.data.ArrayReader
+ * @extends Roo.data.DataReader
+ * Data reader class to create an Array of Roo.data.Record objects from an Array.
+ * Each element of that Array represents a row of data fields. The
+ * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
+ * of the field definition if it exists, or the field's ordinal position in the definition.<br>
+ * <p>
+ * Example code:.
+ * <pre><code>
+var RecordDef = Roo.data.Record.create([
+ {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
+ {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
+]);
+var myReader = new Roo.data.ArrayReader({
+ id: 0 // The subscript within row Array that provides an ID for the Record (optional)
+}, RecordDef);
+</code></pre>
+ * <p>
+ * This would consume an Array like this:
+ * <pre><code>
+[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
+ </code></pre>
+ * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
* @constructor
- * Create a new Container
- * @param {Object} config The config object
+ * Create a new JsonReader
+ * @param {Object} meta Metadata configuration options.
+ * @param {Object} recordType Either an Array of field definition objects
+ * as specified to {@link Roo.data.Record#create},
+ * or an {@link Roo.data.Record} object
+ * created using {@link Roo.data.Record#create}.
*/
+Roo.data.ArrayReader = function(meta, recordType){
+ Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
+};
+Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
+ /**
+ * Create a data block containing Roo.data.Records from an XML document.
+ * @param {Object} o An Array of row objects which represents the dataset.
+ * @return {Object} data A data block which is used by an Roo.data.Store object as
+ * a cache of Roo.data.Records.
+ */
+ readRecords : function(o){
+ var sid = this.meta ? this.meta.id : null;
+ var recordType = this.recordType, fields = recordType.prototype.fields;
+ var records = [];
+ var root = o;
+ for(var i = 0; i < root.length; i++){
+ var n = root[i];
+ var values = {};
+ var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
+ for(var j = 0, jlen = fields.length; j < jlen; j++){
+ var f = fields.items[j];
+ var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
+ var v = n[k] !== undefined ? n[k] : f.defaultValue;
+ v = f.convert(v);
+ values[f.name] = v;
+ }
+ var record = new recordType(values, id);
+ record.json = n;
+ records[records.length] = record;
+ }
+ return {
+ records : records,
+ totalRecords : records.length
+ };
+ }
+});/*
+ * - LGPL
+ * *
+ */
-
-Roo.bootstrap.Calendar = function(config){
- Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
- this.addEvents({
+/**
+ * @class Roo.bootstrap.ComboBox
+ * @extends Roo.bootstrap.TriggerField
+ * A combobox control with support for autocomplete, remote-loading, paging and many other features.
+ * @cfg {Boolean} append (true|false) default false
+ * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
+ * @constructor
+ * Create a new ComboBox.
+ * @param {Object} config Configuration options
+ */
+Roo.bootstrap.ComboBox = function(config){
+ Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
+ this.addEvents({
/**
- * @event select
- * Fires when a date is selected
- * @param {DatePicker} this
- * @param {Date} date The selected date
+ * @event expand
+ * Fires when the dropdown list is expanded
+ * @param {Roo.bootstrap.ComboBox} combo This combo box
*/
- 'select': true,
+ 'expand' : true,
/**
- * @event monthchange
- * Fires when the displayed month changes
- * @param {DatePicker} this
- * @param {Date} date The selected month
+ * @event collapse
+ * Fires when the dropdown list is collapsed
+ * @param {Roo.bootstrap.ComboBox} combo This combo box
*/
- 'monthchange': true,
+ 'collapse' : true,
/**
- * @event evententer
- * Fires when mouse over an event
- * @param {Calendar} this
- * @param {event} Event
+ * @event beforeselect
+ * Fires before a list item is selected. Return false to cancel the selection.
+ * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @param {Roo.data.Record} record The data record returned from the underlying store
+ * @param {Number} index The index of the selected item in the dropdown list
*/
- 'evententer': true,
+ 'beforeselect' : true,
/**
- * @event eventleave
- * Fires when the mouse leaves an
- * @param {Calendar} this
- * @param {event}
+ * @event select
+ * Fires when a list item is selected
+ * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
+ * @param {Number} index The index of the selected item in the dropdown list
*/
- 'eventleave': true,
+ 'select' : true,
/**
- * @event eventclick
- * Fires when the mouse click an
- * @param {Calendar} this
- * @param {event}
+ * @event beforequery
+ * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
+ * The event object passed has these properties:
+ * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @param {String} query The query
+ * @param {Boolean} forceAll true to force "all" query
+ * @param {Boolean} cancel true to cancel the query
+ * @param {Object} e The query event object
*/
- 'eventclick': true
+ 'beforequery': true,
+ /**
+ * @event add
+ * Fires when the 'add' icon is pressed (add a listener to enable add button)
+ * @param {Roo.bootstrap.ComboBox} combo This combo box
+ */
+ 'add' : true,
+ /**
+ * @event edit
+ * Fires when the 'edit' icon is pressed (add a listener to enable add button)
+ * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
+ */
+ 'edit' : true,
+ /**
+ * @event remove
+ * Fires when the remove value from the combobox array
+ * @param {Roo.bootstrap.ComboBox} combo This combo box
+ */
+ 'remove' : true
});
-
+
+
+ this.selectedIndex = -1;
+ if(this.mode == 'local'){
+ if(config.queryDelay === undefined){
+ this.queryDelay = 10;
+ }
+ if(config.minChars === undefined){
+ this.minChars = 0;
+ }
+ }
};
-Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
-
+Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
+
+ /**
+ * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
+ * rendering into an Roo.Editor, defaults to false)
+ */
+ /**
+ * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
+ * {tag: "input", type: "text", size: "24", autocomplete: "off"})
+ */
+ /**
+ * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
+ */
+ /**
+ * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
+ * the dropdown list (defaults to undefined, with no header element)
+ */
+
/**
- * @cfg {Number} startDay
- * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
+ * @cfg {String/Roo.Template} tpl The template to use to render the output
*/
- startDay : 0,
+
+ /**
+ * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
+ */
+ listWidth: undefined,
+ /**
+ * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
+ * mode = 'remote' or 'text' if mode = 'local')
+ */
+ displayField: undefined,
+ /**
+ * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
+ * mode = 'remote' or 'value' if mode = 'local').
+ * Note: use of a valueField requires the user make a selection
+ * in order for a value to be mapped.
+ */
+ valueField: undefined,
- loadMask : false,
- header : false,
-
- getAutoCreate : function(){
-
-
- var fc_button = function(name, corner, style, content ) {
- return Roo.apply({},{
- tag : 'span',
- cls : 'fc-button fc-button-'+name+' fc-state-default ' +
- (corner.length ?
- 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
- ''
- ),
- html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
- unselectable: 'on'
- });
- };
-
- var header = {};
-
- if(!this.header){
- header = {
- tag : 'table',
- cls : 'fc-header',
- style : 'width:100%',
- cn : [
- {
- tag: 'tr',
- cn : [
- {
- tag : 'td',
- cls : 'fc-header-left',
- cn : [
- fc_button('prev', 'left', 'arrow', '‹' ),
- fc_button('next', 'right', 'arrow', '›' ),
- { tag: 'span', cls: 'fc-header-space' },
- fc_button('today', 'left right', '', 'today' ) // neds state disabled..
-
-
- ]
- },
-
- {
- tag : 'td',
- cls : 'fc-header-center',
- cn : [
- {
- tag: 'span',
- cls: 'fc-header-title',
- cn : {
- tag: 'H2',
- html : 'month / year'
- }
- }
-
- ]
- },
- {
- tag : 'td',
- cls : 'fc-header-right',
- cn : [
- /* fc_button('month', 'left', '', 'month' ),
- fc_button('week', '', '', 'week' ),
- fc_button('day', 'right', '', 'day' )
- */
-
- ]
- }
-
- ]
- }
- ]
- };
- }
-
- header = this.header;
-
-
- var cal_heads = function() {
- var ret = [];
- // fixme - handle this.
-
- for (var i =0; i < Date.dayNames.length; i++) {
- var d = Date.dayNames[i];
- ret.push({
- tag: 'th',
- cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
- html : d.substring(0,3)
- });
-
- }
- ret[0].cls += ' fc-first';
- ret[6].cls += ' fc-last';
- return ret;
- };
- var cal_cell = function(n) {
- return {
- tag: 'td',
- cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
- cn : [
- {
- cn : [
- {
- cls: 'fc-day-number',
- html: 'D'
- },
- {
- cls: 'fc-day-content',
-
- cn : [
- {
- style: 'position: relative;' // height: 17px;
- }
- ]
- }
-
-
- ]
- }
- ]
-
- }
- };
- var cal_rows = function() {
-
- var ret = []
- for (var r = 0; r < 6; r++) {
- var row= {
- tag : 'tr',
- cls : 'fc-week',
- cn : []
- };
-
- for (var i =0; i < Date.dayNames.length; i++) {
- var d = Date.dayNames[i];
- row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
-
- }
- row.cn[0].cls+=' fc-first';
- row.cn[0].cn[0].style = 'min-height:90px';
- row.cn[6].cls+=' fc-last';
- ret.push(row);
-
- }
- ret[0].cls += ' fc-first';
- ret[4].cls += ' fc-prev-last';
- ret[5].cls += ' fc-last';
- return ret;
-
- };
-
- var cal_table = {
- tag: 'table',
- cls: 'fc-border-separate',
- style : 'width:100%',
- cellspacing : 0,
- cn : [
- {
- tag: 'thead',
- cn : [
- {
- tag: 'tr',
- cls : 'fc-first fc-last',
- cn : cal_heads()
- }
- ]
- },
- {
- tag: 'tbody',
- cn : cal_rows()
- }
-
- ]
- };
-
- var cfg = {
- cls : 'fc fc-ltr',
- cn : [
- header,
- {
- cls : 'fc-content',
- style : "position: relative;",
- cn : [
- {
- cls : 'fc-view fc-view-month fc-grid',
- style : 'position: relative',
- unselectable : 'on',
- cn : [
- {
- cls : 'fc-event-container',
- style : 'position:absolute;z-index:8;top:0;left:0;'
- },
- cal_table
- ]
- }
- ]
+ /**
+ * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
+ * field's data value (defaults to the underlying DOM element's name)
+ */
+ hiddenName: undefined,
+ /**
+ * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
+ */
+ listClass: '',
+ /**
+ * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
+ */
+ selectedClass: 'active',
+
+ /**
+ * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
+ */
+ shadow:'sides',
+ /**
+ * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
+ * anchor positions (defaults to 'tl-bl')
+ */
+ listAlign: 'tl-bl?',
+ /**
+ * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
+ */
+ maxHeight: 300,
+ /**
+ * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
+ * query specified by the allQuery config option (defaults to 'query')
+ */
+ triggerAction: 'query',
+ /**
+ * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
+ * (defaults to 4, does not apply if editable = false)
+ */
+ minChars : 4,
+ /**
+ * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
+ * delay (typeAheadDelay) if it matches a known value (defaults to false)
+ */
+ typeAhead: false,
+ /**
+ * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
+ * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
+ */
+ queryDelay: 500,
+ /**
+ * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
+ * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
+ */
+ pageSize: 0,
+ /**
+ * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
+ * when editable = true (defaults to false)
+ */
+ selectOnFocus:false,
+ /**
+ * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
+ */
+ queryParam: 'query',
+ /**
+ * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
+ * when mode = 'remote' (defaults to 'Loading...')
+ */
+ loadingText: 'Loading...',
+ /**
+ * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
+ */
+ resizable: false,
+ /**
+ * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
+ */
+ handleHeight : 8,
+ /**
+ * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
+ * traditional select (defaults to true)
+ */
+ editable: true,
+ /**
+ * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
+ */
+ allQuery: '',
+ /**
+ * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
+ */
+ mode: 'remote',
+ /**
+ * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
+ * listWidth has a higher value)
+ */
+ minListWidth : 70,
+ /**
+ * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
+ * allow the user to set arbitrary text into the field (defaults to false)
+ */
+ forceSelection:false,
+ /**
+ * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
+ * if typeAhead = true (defaults to 250)
+ */
+ typeAheadDelay : 250,
+ /**
+ * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
+ * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
+ */
+ valueNotFoundText : undefined,
+ /**
+ * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
+ */
+ blockFocus : false,
- }
- ]
-
- };
-
-
-
- return cfg;
- },
+ /**
+ * @cfg {Boolean} disableClear Disable showing of clear button.
+ */
+ disableClear : false,
+ /**
+ * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
+ */
+ alwaysQuery : false,
+ /**
+ * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
+ */
+ multiple : false,
- initEvents : function()
- {
- if(!this.store){
- throw "can not find store for calendar";
- }
-
- var mark = {
- tag: "div",
- cls:"x-dlg-mask",
- style: "text-align:center",
- cn: [
- {
- tag: "div",
- style: "background-color:white;width:50%;margin:250 auto",
- cn: [
- {
- tag: "img",
- src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
- },
- {
- tag: "span",
- html: "Loading"
- }
-
- ]
- }
- ]
- }
- this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
+ //private
+ addicon : false,
+ editicon: false,
+
+ page: 0,
+ hasQuery: false,
+ append: false,
+ loadNext: false,
+ autoFocus : true,
+ item: [],
+
+ // element that contains real text value.. (when hidden is used..)
+
+ // private
+ initEvents: function(){
- var size = this.el.select('.fc-content', true).first().getSize();
- this.maskEl.setSize(size.width, size.height);
- this.maskEl.enableDisplayMode("block");
- if(!this.loadMask){
- this.maskEl.hide();
+ if (!this.store) {
+ throw "can not find store for combo";
}
-
this.store = Roo.factory(this.store, Roo.data);
- this.store.on('load', this.onLoad, this);
- this.store.on('beforeload', this.onBeforeLoad, this);
-
- this.resize();
-
- this.cells = this.el.select('.fc-day',true);
- //Roo.log(this.cells);
- this.textNodes = this.el.query('.fc-day-number');
- this.cells.addClassOnOver('fc-state-hover');
-
- this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
- this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
- this.el.select('.fc-button-today',true).on('click', this.showToday, this);
- this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
-
- this.on('monthchange', this.onMonthChange, this);
- this.update(new Date().clearTime());
- },
-
- resize : function() {
- var sz = this.el.getSize();
- this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
- this.el.select('.fc-day-content div',true).setHeight(34);
- },
-
-
- // private
- showPrevMonth : function(e){
- this.update(this.activeDate.add("mo", -1));
- },
- showToday : function(e){
- this.update(new Date().clearTime());
- },
- // private
- showNextMonth : function(e){
- this.update(this.activeDate.add("mo", 1));
- },
-
- // private
- showPrevYear : function(){
- this.update(this.activeDate.add("y", -1));
- },
-
- // private
- showNextYear : function(){
- this.update(this.activeDate.add("y", 1));
- },
-
-
- // private
- update : function(date)
- {
- var vd = this.activeDate;
- this.activeDate = date;
-// if(vd && this.el){
-// var t = date.getTime();
-// if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
-// Roo.log('using add remove');
-//
-// this.fireEvent('monthchange', this, date);
-//
-// this.cells.removeClass("fc-state-highlight");
-// this.cells.each(function(c){
-// if(c.dateValue == t){
-// c.addClass("fc-state-highlight");
-// setTimeout(function(){
-// try{c.dom.firstChild.focus();}catch(e){}
-// }, 50);
-// return false;
-// }
-// return true;
-// });
-// return;
-// }
-// }
- var days = date.getDaysInMonth();
+ Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
- var firstOfMonth = date.getFirstDateOfMonth();
- var startingPos = firstOfMonth.getDay()-this.startDay;
- if(startingPos < this.startDay){
- startingPos += 7;
+ if(this.hiddenName){
+
+ this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+
+ this.hiddenField.dom.value =
+ this.hiddenValue !== undefined ? this.hiddenValue :
+ this.value !== undefined ? this.value : '';
+
+ // prevent input submission
+ this.el.dom.removeAttribute('name');
+ this.hiddenField.dom.setAttribute('name', this.hiddenName);
+
+
}
+ //if(Roo.isGecko){
+ // this.el.dom.setAttribute('autocomplete', 'off');
+ //}
+
+ var cls = 'x-combo-list';
+ this.list = this.el.select('ul.dropdown-menu',true).first();
+
+ //this.list = new Roo.Layer({
+ // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
+ //});
- var pm = date.add(Date.MONTH, -1);
- var prevStart = pm.getDaysInMonth()-startingPos;
-//
- this.cells = this.el.select('.fc-day',true);
- this.textNodes = this.el.query('.fc-day-number');
- this.cells.addClassOnOver('fc-state-hover');
+ var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
+ this.list.setWidth(lw);
- var cells = this.cells.elements;
- var textEls = this.textNodes;
+ this.list.on('mouseover', this.onViewOver, this);
+ this.list.on('mousemove', this.onViewMove, this);
- Roo.each(cells, function(cell){
- cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
- });
+ this.list.on('scroll', this.onViewScroll, this);
- days += startingPos;
+ /*
+ this.list.swallowEvent('mousewheel');
+ this.assetHeight = 0;
- // convert everything to numbers so it's fast
- var day = 86400000;
- var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
- //Roo.log(d);
- //Roo.log(pm);
- //Roo.log(prevStart);
-
- var today = new Date().clearTime().getTime();
- var sel = date.clearTime().getTime();
- var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
- var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
- var ddMatch = this.disabledDatesRE;
- var ddText = this.disabledDatesText;
- var ddays = this.disabledDays ? this.disabledDays.join("") : false;
- var ddaysText = this.disabledDaysText;
- var format = this.format;
+ if(this.title){
+ this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
+ this.assetHeight += this.header.getHeight();
+ }
+
+ this.innerList = this.list.createChild({cls:cls+'-inner'});
+ this.innerList.on('mouseover', this.onViewOver, this);
+ this.innerList.on('mousemove', this.onViewMove, this);
+ this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
- var setCellClass = function(cal, cell){
- cell.row = 0;
- cell.events = [];
- cell.more = [];
- //Roo.log('set Cell Class');
- cell.title = "";
- var t = d.getTime();
-
- //Roo.log(d);
+ if(this.allowBlank && !this.pageSize && !this.disableClear){
+ this.footer = this.list.createChild({cls:cls+'-ft'});
+ this.pageTb = new Roo.Toolbar(this.footer);
+
+ }
+ if(this.pageSize){
+ this.footer = this.list.createChild({cls:cls+'-ft'});
+ this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
+ {pageSize: this.pageSize});
- cell.dateValue = t;
- if(t == today){
- cell.className += " fc-today";
- cell.className += " fc-state-highlight";
- cell.title = cal.todayText;
- }
- if(t == sel){
- // disable highlight in other month..
- //cell.className += " fc-state-highlight";
-
- }
- // disabling
- if(t < min) {
- cell.className = " fc-state-disabled";
- cell.title = cal.minText;
- return;
- }
- if(t > max) {
- cell.className = " fc-state-disabled";
- cell.title = cal.maxText;
- return;
- }
- if(ddays){
- if(ddays.indexOf(d.getDay()) != -1){
- cell.title = ddaysText;
- cell.className = " fc-state-disabled";
- }
- }
- if(ddMatch && format){
- var fvalue = d.dateFormat(format);
- if(ddMatch.test(fvalue)){
- cell.title = ddText.replace("%0", fvalue);
- cell.className = " fc-state-disabled";
+ }
+
+ if (this.pageTb && this.allowBlank && !this.disableClear) {
+ var _this = this;
+ this.pageTb.add(new Roo.Toolbar.Fill(), {
+ cls: 'x-btn-icon x-btn-clear',
+ text: ' ',
+ handler: function()
+ {
+ _this.collapse();
+ _this.clearValue();
+ _this.onSelect(false, -1);
}
- }
-
- if (!cell.initialClassName) {
- cell.initialClassName = cell.dom.className;
- }
+ });
+ }
+ if (this.footer) {
+ this.assetHeight += this.footer.getHeight();
+ }
+ */
- cell.dom.className = cell.initialClassName + ' ' + cell.className;
- };
+ if(!this.tpl){
+ this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
+ }
- var i = 0;
+ this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
+ singleSelect:true, store: this.store, selectedClass: this.selectedClass
+ });
+ //this.view.wrapEl.setDisplayed(false);
+ this.view.on('click', this.onViewClick, this);
- for(; i < startingPos; i++) {
- textEls[i].innerHTML = (++prevStart);
- d.setDate(d.getDate()+1);
-
- cells[i].className = "fc-past fc-other-month";
- setCellClass(this, cells[i]);
+
+
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('loadexception', this.onLoadException, this);
+ /*
+ if(this.resizable){
+ this.resizer = new Roo.Resizable(this.list, {
+ pinned:true, handles:'se'
+ });
+ this.resizer.on('resize', function(r, w, h){
+ this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
+ this.listWidth = w;
+ this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
+ this.restrictHeight();
+ }, this);
+ this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
+ }
+ */
+ if(!this.editable){
+ this.editable = true;
+ this.setEditable(false);
}
- var intDay = 0;
+ /*
- for(; i < days; i++){
- intDay = i - startingPos + 1;
- textEls[i].innerHTML = (intDay);
- d.setDate(d.getDate()+1);
+ if (typeof(this.events.add.listeners) != 'undefined') {
- cells[i].className = ''; // "x-date-active";
- setCellClass(this, cells[i]);
+ this.addicon = this.wrap.createChild(
+ {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
+
+ this.addicon.on('click', function(e) {
+ this.fireEvent('add', this);
+ }, this);
}
- var extraDays = 0;
-
- for(; i < 42; i++) {
- textEls[i].innerHTML = (++extraDays);
- d.setDate(d.getDate()+1);
+ if (typeof(this.events.edit.listeners) != 'undefined') {
- cells[i].className = "fc-future fc-other-month";
- setCellClass(this, cells[i]);
+ this.editicon = this.wrap.createChild(
+ {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
+ if (this.addicon) {
+ this.editicon.setStyle('margin-left', '40px');
+ }
+ this.editicon.on('click', function(e) {
+
+ // we fire even if inothing is selected..
+ this.fireEvent('edit', this, this.lastData );
+
+ }, this);
}
+ */
- this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
-
- var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
+ this.keyNav = new Roo.KeyNav(this.inputEl(), {
+ "up" : function(e){
+ this.inKeyMode = true;
+ this.selectPrev();
+ },
+
+ "down" : function(e){
+ if(!this.isExpanded()){
+ this.onTriggerClick();
+ }else{
+ this.inKeyMode = true;
+ this.selectNext();
+ }
+ },
+
+ "enter" : function(e){
+// this.onViewClick();
+ //return true;
+ this.collapse();
+
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
+ },
+
+ "esc" : function(e){
+ this.collapse();
+ },
+
+ "tab" : function(e){
+ this.collapse();
+
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
+ },
+
+ scope : this,
+
+ doRelay : function(foo, bar, hname){
+ if(hname == 'down' || this.scope.isExpanded()){
+ return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+ }
+ return true;
+ },
+
+ forceKeyDown: true
+ });
- this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
- this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
- if(totalRows != 6){
- this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
- this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
- }
+ this.queryDelay = Math.max(this.queryDelay || 10,
+ this.mode == 'local' ? 10 : 250);
- this.fireEvent('monthchange', this, date);
+ this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
- /*
- if(!this.internalRender){
- var main = this.el.dom.firstChild;
- var w = main.offsetWidth;
- this.el.setWidth(w + this.el.getBorderWidth("lr"));
- Roo.fly(main).setWidth(w);
- this.internalRender = true;
- // opera does not respect the auto grow header center column
- // then, after it gets a width opera refuses to recalculate
- // without a second pass
- if(Roo.isOpera && !this.secondPass){
- main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
- this.secondPass = true;
- this.update.defer(10, this, [date]);
- }
+ if(this.typeAhead){
+ this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
+ }
+ if(this.editable !== false){
+ this.inputEl().on("keyup", this.onKeyUp, this);
+ }
+ if(this.forceSelection){
+ this.inputEl().on('blur', this.doForce, this);
}
- */
+ if(this.multiple){
+ this.choices = this.el.select('ul.select2-choices', true).first();
+ this.searchField = this.el.select('ul li.select2-search-field', true).first();
+ }
},
-
- findCell : function(dt) {
- dt = dt.clearTime().getTime();
- var ret = false;
- this.cells.each(function(c){
- //Roo.log("check " +c.dateValue + '?=' + dt);
- if(c.dateValue == dt){
- ret = c;
- return false;
- }
- return true;
- });
-
- return ret;
+
+ onDestroy : function(){
+ if(this.view){
+ this.view.setStore(null);
+ this.view.el.removeAllListeners();
+ this.view.el.remove();
+ this.view.purgeListeners();
+ }
+ if(this.list){
+ this.list.dom.innerHTML = '';
+ }
+ if(this.store){
+ this.store.un('beforeload', this.onBeforeLoad, this);
+ this.store.un('load', this.onLoad, this);
+ this.store.un('loadexception', this.onLoadException, this);
+ }
+ Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
},
-
- findCells : function(ev) {
- var s = ev.start.clone().clearTime().getTime();
- // Roo.log(s);
- var e= ev.end.clone().clearTime().getTime();
- // Roo.log(e);
- var ret = [];
- this.cells.each(function(c){
- ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
-
- if(c.dateValue > e){
- return ;
- }
- if(c.dateValue < s){
- return ;
- }
- ret.push(c);
- });
-
- return ret;
+
+ // private
+ fireKey : function(e){
+ if(e.isNavKeyPress() && !this.list.isVisible()){
+ this.fireEvent("specialkey", this, e);
+ }
},
-
-// findBestRow: function(cells)
-// {
-// var ret = 0;
+
+ // private
+ onResize: function(w, h){
+// Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
//
-// for (var i =0 ; i < cells.length;i++) {
-// ret = Math.max(cells[i].rows || 0,ret);
+// if(typeof w != 'number'){
+// // we do not handle it!?!?
+// return;
// }
-// return ret;
+// var tw = this.trigger.getWidth();
+// // tw += this.addicon ? this.addicon.getWidth() : 0;
+// // tw += this.editicon ? this.editicon.getWidth() : 0;
+// var x = w - tw;
+// this.inputEl().setWidth( this.adjustWidth('input', x));
+//
+// //this.trigger.setStyle('left', x+'px');
//
-// },
+// if(this.list && this.listWidth === undefined){
+// var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
+// this.list.setWidth(lw);
+// this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+// }
+
+
+ },
+
+ /**
+ * Allow or prevent the user from directly editing the field text. If false is passed,
+ * the user will only be able to select from the items defined in the dropdown list. This method
+ * is the runtime equivalent of setting the 'editable' config option at config time.
+ * @param {Boolean} value True to allow the user to directly edit the field text
+ */
+ setEditable : function(value){
+ if(value == this.editable){
+ return;
+ }
+ this.editable = value;
+ if(!value){
+ this.inputEl().dom.setAttribute('readOnly', true);
+ this.inputEl().on('mousedown', this.onTriggerClick, this);
+ this.inputEl().addClass('x-combo-noedit');
+ }else{
+ this.inputEl().dom.setAttribute('readOnly', false);
+ this.inputEl().un('mousedown', this.onTriggerClick, this);
+ this.inputEl().removeClass('x-combo-noedit');
+ }
+ },
+
+ // private
- addItem : function(ev)
- {
- // look for vertical location slot in
- var cells = this.findCells(ev);
+ onBeforeLoad : function(combo,opts){
+ if(!this.hasFocus){
+ return;
+ }
+ if (!opts.add) {
+ this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
+ }
+ this.restrictHeight();
+ this.selectedIndex = -1;
+ },
+
+ // private
+ onLoad : function(){
-// ev.row = this.findBestRow(cells);
+ this.hasQuery = false;
- // work out the location.
+ if(!this.hasFocus){
+ return;
+ }
- var crow = false;
- var rows = [];
- for(var i =0; i < cells.length; i++) {
-
- cells[i].row = cells[0].row;
-
- if(i == 0){
- cells[i].row = cells[i].row + 1;
- }
-
- if (!crow) {
- crow = {
- start : cells[i],
- end : cells[i]
- };
- continue;
- }
- if (crow.start.getY() == cells[i].getY()) {
- // on same row.
- crow.end = cells[i];
- continue;
+ if(typeof(this.loading) !== 'undefined' && this.loading !== null){
+ this.loading.hide();
+ }
+
+ if(this.store.getCount() > 0){
+ this.expand();
+ this.restrictHeight();
+ if(this.lastQuery == this.allQuery){
+ if(this.editable){
+ this.inputEl().dom.select();
+ }
+ if(!this.selectByValue(this.value, true) && this.autoFocus){
+ this.select(0, true);
+ }
+ }else{
+ if(this.autoFocus){
+ this.selectNext();
+ }
+ if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
+ this.taTask.delay(this.typeAheadDelay);
+ }
}
- // different row.
- rows.push(crow);
- crow = {
- start: cells[i],
- end : cells[i]
- };
+ }else{
+ this.onEmptyResults();
+ }
+
+ //this.el.focus();
+ },
+ // private
+ onLoadException : function()
+ {
+ this.hasQuery = false;
+
+ if(typeof(this.loading) !== 'undefined' && this.loading !== null){
+ this.loading.hide();
+ }
+
+ this.collapse();
+ Roo.log(this.store.reader.jsonData);
+ if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+ // fixme
+ //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+ }
+
+
+ },
+ // private
+ onTypeAhead : function(){
+ if(this.store.getCount() > 0){
+ var r = this.store.getAt(0);
+ var newValue = r.data[this.displayField];
+ var len = newValue.length;
+ var selStart = this.getRawValue().length;
+ if(selStart != len){
+ this.setRawValue(newValue);
+ this.selectText(selStart, newValue.length);
+ }
}
+ },
+
+ // private
+ onSelect : function(record, index){
- rows.push(crow);
- ev.els = [];
- ev.rows = rows;
- ev.cells = cells;
+ if(this.fireEvent('beforeselect', this, record, index) !== false){
- cells[0].events.push(ev);
+ this.setFromData(index > -1 ? record.data : false);
+
+ this.collapse();
+ this.fireEvent('select', this, record, index);
+ }
+ },
+
+ /**
+ * Returns the currently selected field value or empty string if no value is set.
+ * @return {String} value The selected value
+ */
+ getValue : function(){
-// if((typeof(cells[0].events) == 'undefined')){
-// cells[0].events = [];
-// }
-//
-// cells[0].events.push(ev);
-// ev.rendered = false;
-// for (var i = 0; i < cells.length;i++) {
-// cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
-//
-// }
+ if(this.multiple){
+ return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
+ }
- this.calevents.push(ev);
+ if(this.valueField){
+ return typeof this.value != 'undefined' ? this.value : '';
+ }else{
+ return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
+ }
+ },
+
+ /**
+ * Clears any text/value currently set in the field
+ */
+ clearValue : function(){
+ if(this.hiddenField){
+ this.hiddenField.dom.value = '';
+ }
+ this.value = '';
+ this.setRawValue('');
+ this.lastSelectionText = '';
+
+ },
+
+ /**
+ * Sets the specified value into the field. If the value finds a match, the corresponding record text
+ * will be displayed in the field. If the value does not match the data value of an existing item,
+ * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
+ * Otherwise the field will be blank (although the value will still be set).
+ * @param {String} value The value to match
+ */
+ setValue : function(v){
+ if(this.multiple){
+ this.syncValue();
+ return;
+ }
+
+ var text = v;
+ if(this.valueField){
+ var r = this.findRecord(this.valueField, v);
+ if(r){
+ text = r.data[this.displayField];
+ }else if(this.valueNotFoundText !== undefined){
+ text = this.valueNotFoundText;
+ }
+ }
+ this.lastSelectionText = text;
+ if(this.hiddenField){
+ this.hiddenField.dom.value = v;
+ }
+ Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
+ this.value = v;
},
+ /**
+ * @property {Object} the last set data for the element
+ */
- clearEvents: function() {
+ lastData : false,
+ /**
+ * Sets the value of the field based on a object which is related to the record format for the store.
+ * @param {Object} value the value to set as. or false on reset?
+ */
+ setFromData : function(o){
- if(!this.calevents){
+ if(this.multiple){
+ this.addItem(o);
return;
}
+
+ var dv = ''; // display value
+ var vv = ''; // value value..
+ this.lastData = o;
+ if (this.displayField) {
+ dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+ } else {
+ // this is an error condition!!!
+ Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
+ }
- Roo.each(this.cells.elements, function(c){
- c.row = 0;
- c.events = [];
- c.more = [];
- });
+ if(this.valueField){
+ vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
+ }
- Roo.each(this.calevents, function(e) {
- Roo.each(e.els, function(el) {
- el.un('mouseenter' ,this.onEventEnter, this);
- el.un('mouseleave' ,this.onEventLeave, this);
- el.remove();
- },this);
- },this);
+ if(this.hiddenField){
+ this.hiddenField.dom.value = vv;
+
+ this.lastSelectionText = dv;
+ Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
+ this.value = vv;
+ return;
+ }
+ // no hidden field.. - we store the value in 'value', but still display
+ // display field!!!!
+ this.lastSelectionText = dv;
+ Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
+ this.value = vv;
- Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
- e.remove();
- });
},
+ // private
+ reset : function(){
+ // overridden so that last data is reset..
+ this.setValue(this.originalValue);
+ this.clearInvalid();
+ this.lastData = false;
+ if (this.view) {
+ this.view.clearSelections();
+ }
+ },
+ // private
+ findRecord : function(prop, value){
+ var record;
+ if(this.store.getCount() > 0){
+ this.store.each(function(r){
+ if(r.data[prop] == value){
+ record = r;
+ return false;
+ }
+ return true;
+ });
+ }
+ return record;
+ },
- renderEvents: function()
- {
-// for (var e = 0; e < this.calevents.length; e++) {
-//
-// var ev = this.calevents[e];
-// var cells = ev.cells;
-// var rows = ev.rows;
-//
-// for (var j = 0; j < cells.length; j++){
-//
-// if(!cells[j].more.length){
-// cells[j].row++;
-// }
-// if(cells[j].row > 3){
-// cells[j].more.push(ev);
-// continue;
-// }
-//
-// cells[j].events.push(ev);
-// }
-// }
-
-// for (var i = 0; i < rows.length; i++){
-// // how many rows should it span..
-//
-// var cfg = {
-// cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
-// style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
-//
-// unselectable : "on",
-// cn : [
-// {
-// cls: 'fc-event-inner',
-// cn : [
-//// {
-//// tag:'span',
-//// cls: 'fc-event-time',
-//// html : cells.length > 1 ? '' : ev.time
-//// },
-// {
-// tag:'span',
-// cls: 'fc-event-title',
-// html : String.format('{0}', ev.title)
-// }
-//
-//
-// ]
-// },
-// {
-// cls: 'ui-resizable-handle ui-resizable-e',
-// html : '  '
-// }
-//
-// ]
-// };
-//
-// if (i == 0) {
-// cfg.cls += ' fc-event-start';
-// }
-// if ((i+1) == rows.length) {
-// cfg.cls += ' fc-event-end';
-// }
-//
-// var ctr = this.el.select('.fc-event-container',true).first();
-// var cg = ctr.createChild(cfg);
-//
-// var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
-// var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
-//
-// cg.setXY([sbox.x +2, sbox.y +(e * 20)]);
-// cg.setWidth(ebox.right - sbox.x -2);
-//
-// cg.on('mouseenter' ,this.onEventEnter, this, ev);
-// cg.on('mouseleave' ,this.onEventLeave, this, ev);
-// cg.on('click', this.onEventClick, this, ev);
-//
-// ev.els.push(cg);
-//
-//
-// }
-// }
-
- var _this = this;
+ getName: function()
+ {
+ // returns hidden if it's set..
+ if (!this.rendered) {return ''};
+ return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
- this.cells.each(function(c) {
-
- if(c.row < 5){
- return;
- }
-
- var ev = c.events;
-
- var r = 4;
- if(c.row != c.events.length){
- r = 4 - (4 - (c.row - c.events.length));
- }
-
- c.events = ev.slice(0, r);
- c.more = ev.slice(r);
-
- if(c.more.length && c.more.length == 1){
- c.events.push(c.more.pop());
- }
-
- c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
-
- });
-
-// for (var e = 0; e < this.calevents.length; e++) {
-//
-// var ev = this.calevents[e];
-// var cells = ev.cells;
-// var rows = ev.rows;
-//
-// for(var i = 0; i < cells.length; i++){
-//
-// var cbox = this.cells.item(this.cells.indexOf(cells[i]));
-//
-// if(cells.length < 2 && cbox.rows.length > 3){
-// cbox.more.push(ev);
-// continue;
-// }
-//
-// cbox.rows.push(ev);
-// }
-// }
-//
- this.cells.each(function(c) {
-
- c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
-
-
- for (var e = 0; e < c.events.length; e++){
- var ev = c.events[e];
- var rows = ev.rows;
-
- for(var i = 0; i < rows.length; i++) {
-
- // how many rows should it span..
+ },
+ // private
+ onViewMove : function(e, t){
+ this.inKeyMode = false;
+ },
+
+ // private
+ onViewOver : function(e, t){
+ if(this.inKeyMode){ // prevent key nav and mouse over conflicts
+ return;
+ }
+ var item = this.view.findItemFromChild(t);
+ if(item){
+ var index = this.view.indexOf(item);
+ this.select(index, false);
+ }
+ },
+
+ // private
+ onViewClick : function(doFocus)
+ {
+ var index = this.view.getSelectedIndexes()[0];
+ var r = this.store.getAt(index);
+ if(r){
+ this.onSelect(r, index);
+ }
+ if(doFocus !== false && !this.blockFocus){
+ this.inputEl().focus();
+ }
+ },
+
+ // private
+ restrictHeight : function(){
+ //this.innerList.dom.style.height = '';
+ //var inner = this.innerList.dom;
+ //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
+ //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
+ //this.list.beginUpdate();
+ //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
+ this.list.alignTo(this.inputEl(), this.listAlign);
+ //this.list.endUpdate();
+ },
- var cfg = {
- cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
- style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
+ // private
+ onEmptyResults : function(){
+ this.collapse();
+ },
- unselectable : "on",
- cn : [
- {
- cls: 'fc-event-inner',
- cn : [
- // {
- // tag:'span',
- // cls: 'fc-event-time',
- // html : cells.length > 1 ? '' : ev.time
- // },
- {
- tag:'span',
- cls: 'fc-event-title',
- html : String.format('{0}', ev.title)
- }
+ /**
+ * Returns true if the dropdown list is expanded, else false.
+ */
+ isExpanded : function(){
+ return this.list.isVisible();
+ },
+ /**
+ * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
+ * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+ * @param {String} value The data value of the item to select
+ * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+ * selected item if it is not currently in view (defaults to true)
+ * @return {Boolean} True if the value matched an item in the list, else false
+ */
+ selectByValue : function(v, scrollIntoView){
+ if(v !== undefined && v !== null){
+ var r = this.findRecord(this.valueField || this.displayField, v);
+ if(r){
+ this.select(this.store.indexOf(r), scrollIntoView);
+ return true;
+ }
+ }
+ return false;
+ },
- ]
- },
- {
- cls: 'ui-resizable-handle ui-resizable-e',
- html : '  '
- }
+ /**
+ * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
+ * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+ * @param {Number} index The zero-based index of the list item to select
+ * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+ * selected item if it is not currently in view (defaults to true)
+ */
+ select : function(index, scrollIntoView){
+ this.selectedIndex = index;
+ this.view.select(index);
+ if(scrollIntoView !== false){
+ var el = this.view.getNode(index);
+ if(el){
+ //this.innerList.scrollChildIntoView(el, false);
+
+ }
+ }
+ },
- ]
- };
+ // private
+ selectNext : function(){
+ var ct = this.store.getCount();
+ if(ct > 0){
+ if(this.selectedIndex == -1){
+ this.select(0);
+ }else if(this.selectedIndex < ct-1){
+ this.select(this.selectedIndex+1);
+ }
+ }
+ },
- if (i == 0) {
- cfg.cls += ' fc-event-start';
- }
- if ((i+1) == rows.length) {
- cfg.cls += ' fc-event-end';
- }
+ // private
+ selectPrev : function(){
+ var ct = this.store.getCount();
+ if(ct > 0){
+ if(this.selectedIndex == -1){
+ this.select(0);
+ }else if(this.selectedIndex != 0){
+ this.select(this.selectedIndex-1);
+ }
+ }
+ },
- var ctr = _this.el.select('.fc-event-container',true).first();
- var cg = ctr.createChild(cfg);
+ // private
+ onKeyUp : function(e){
+ if(this.editable !== false && !e.isSpecialKey()){
+ this.lastKey = e.getKey();
+ this.dqTask.delay(this.queryDelay);
+ }
+ },
- var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
- var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
+ // private
+ validateBlur : function(){
+ return !this.list || !this.list.isVisible();
+ },
- var r = (c.more.length) ? 1 : 0;
- cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
- cg.setWidth(ebox.right - sbox.x -2);
+ // private
+ initQuery : function(){
+ this.doQuery(this.getRawValue());
+ },
- cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
- cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
- cg.on('click', _this.onEventClick, _this, ev);
+ // private
+ doForce : function(){
+ if(this.inputEl().dom.value.length > 0){
+ this.inputEl().dom.value =
+ this.lastSelectionText === undefined ? '' : this.lastSelectionText;
+
+ }
+ },
- ev.els.push(cg);
+ /**
+ * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
+ * query allowing the query action to be canceled if needed.
+ * @param {String} query The SQL query to execute
+ * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
+ * in the field than the minimum specified by the minChars config option. It also clears any filter previously
+ * saved in the current store (defaults to false)
+ */
+ doQuery : function(q, forceAll){
+
+ if(q === undefined || q === null){
+ q = '';
+ }
+ var qe = {
+ query: q,
+ forceAll: forceAll,
+ combo: this,
+ cancel:false
+ };
+ if(this.fireEvent('beforequery', qe)===false || qe.cancel){
+ return false;
+ }
+ q = qe.query;
+
+ forceAll = qe.forceAll;
+ if(forceAll === true || (q.length >= this.minChars)){
+
+ this.hasQuery = true;
+
+ if(this.lastQuery != q || this.alwaysQuery){
+ this.lastQuery = q;
+ if(this.mode == 'local'){
+ this.selectedIndex = -1;
+ if(forceAll){
+ this.store.clearFilter();
+ }else{
+ this.store.filter(this.displayField, q);
+ }
+ this.onLoad();
+ }else{
+ this.store.baseParams[this.queryParam] = q;
+
+ var options = {params : this.getParams(q)};
+
+ if(this.loadNext){
+ options.add = true;
+ options.params.start = this.page * this.pageSize;
+ }
+ this.store.load(options);
+ /*
+ * this code will make the page width larger, at the beginning, the list not align correctly,
+ * we should expand the list on onLoad
+ * so command out it
+ */
+// this.expand();
}
-
+ }else{
+ this.selectedIndex = -1;
+ this.onLoad();
}
-
-
- if(c.more.length){
- var cfg = {
- cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
- style : 'position: absolute',
- unselectable : "on",
- cn : [
- {
- cls: 'fc-event-inner',
- cn : [
- {
- tag:'span',
- cls: 'fc-event-title',
- html : 'More'
- }
+ }
+
+ this.loadNext = false;
+ },
+ // private
+ getParams : function(q){
+ var p = {};
+ //p[this.queryParam] = q;
+
+ if(this.pageSize){
+ p.start = 0;
+ p.limit = this.pageSize;
+ }
+ return p;
+ },
- ]
- },
- {
- cls: 'ui-resizable-handle ui-resizable-e',
- html : '  '
- }
+ /**
+ * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
+ */
+ collapse : function(){
+ if(!this.isExpanded()){
+ return;
+ }
+
+ this.list.hide();
+ Roo.get(document).un('mousedown', this.collapseIf, this);
+ Roo.get(document).un('mousewheel', this.collapseIf, this);
+ if (!this.editable) {
+ Roo.get(document).un('keydown', this.listKeyPress, this);
+ }
+ this.fireEvent('collapse', this);
+ },
- ]
- };
+ // private
+ collapseIf : function(e){
+ var in_combo = e.within(this.el);
+ var in_list = e.within(this.list);
+
+ if (in_combo || in_list) {
+ //e.stopPropagation();
+ return;
+ }
- var ctr = _this.el.select('.fc-event-container',true).first();
- var cg = ctr.createChild(cfg);
+ this.collapse();
+
+ },
- var sbox = c.select('.fc-day-content',true).first().getBox();
- var ebox = c.select('.fc-day-content',true).first().getBox();
- //Roo.log(cg);
- cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
- cg.setWidth(ebox.right - sbox.x -2);
+ /**
+ * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
+ */
+ expand : function(){
+
+ if(this.isExpanded() || !this.hasFocus){
+ return;
+ }
+ Roo.log('expand');
+ this.list.alignTo(this.inputEl(), this.listAlign);
+ this.list.show();
+ Roo.get(document).on('mousedown', this.collapseIf, this);
+ Roo.get(document).on('mousewheel', this.collapseIf, this);
+ if (!this.editable) {
+ Roo.get(document).on('keydown', this.listKeyPress, this);
+ }
+
+ this.fireEvent('expand', this);
+ },
- cg.on('click', _this.onMoreEventClick, _this, c.more);
-
- }
-
- });
+ // private
+ // Implements the default empty TriggerField.onTriggerClick function
+ onTriggerClick : function()
+ {
+ Roo.log('trigger click');
+ if(this.disabled){
+ return;
+ }
+ this.page = 0;
+ this.loadNext = false;
+ if(this.isExpanded()){
+ this.collapse();
+ if (!this.blockFocus) {
+ this.inputEl().focus();
+ }
+
+ }else {
+ this.hasFocus = true;
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
+ }
+ if (!this.blockFocus) {
+ this.inputEl().focus();
+ }
+ }
},
-
- onEventEnter: function (e, el,event,d) {
- this.fireEvent('evententer', this, el, event);
- },
-
- onEventLeave: function (e, el,event,d) {
- this.fireEvent('eventleave', this, el, event);
- },
-
- onEventClick: function (e, el,event,d) {
- this.fireEvent('eventclick', this, el, event);
- },
-
- onMonthChange: function () {
- this.store.load();
+ listKeyPress : function(e)
+ {
+ //Roo.log('listkeypress');
+ // scroll to first matching element based on key pres..
+ if (e.isSpecialKey()) {
+ return false;
+ }
+ var k = String.fromCharCode(e.getKey()).toUpperCase();
+ //Roo.log(k);
+ var match = false;
+ var csel = this.view.getSelectedNodes();
+ var cselitem = false;
+ if (csel.length) {
+ var ix = this.view.indexOf(csel[0]);
+ cselitem = this.store.getAt(ix);
+ if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
+ cselitem = false;
+ }
+
+ }
+
+ this.store.each(function(v) {
+ if (cselitem) {
+ // start at existing selection.
+ if (cselitem.id == v.id) {
+ cselitem = false;
+ }
+ return true;
+ }
+
+ if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
+ match = this.store.indexOf(v);
+ return false;
+ }
+ return true;
+ }, this);
+
+ if (match === false) {
+ return true; // no more action?
+ }
+ // scroll to?
+ this.view.select(match);
+ var sn = Roo.get(this.view.getSelectedNodes()[0])
+ //sn.scrollIntoView(sn.dom.parentNode, false);
},
- onMoreEventClick: function(e, el, more)
- {
- var _this = this;
+ onViewScroll : function(e, t){
- this.calpopover.placement = 'right';
- this.calpopover.setTitle('More');
+ if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
+ return;
+ }
- this.calpopover.setContent('');
+ this.hasQuery = true;
- var ctr = this.calpopover.el.select('.popover-content', true).first();
+ this.loading = this.list.select('.loading', true).first();
- Roo.each(more, function(m){
- var cfg = {
- cls : 'fc-event-hori fc-event-draggable',
- html : m.title
- }
- var cg = ctr.createChild(cfg);
+ if(this.loading === null){
+ this.list.createChild({
+ tag: 'div',
+ cls: 'loading select2-more-results select2-active',
+ html: 'Loading more results...'
+ })
- cg.on('click', _this.onEventClick, _this, m);
- });
+ this.loading = this.list.select('.loading', true).first();
+
+ this.loading.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.loading.hide();
+ }
- this.calpopover.show(el);
+ this.loading.show();
+
+ var _combo = this;
+ this.page++;
+ this.loadNext = true;
+
+ (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
+ return;
},
- onLoad: function ()
+ addItem : function(o)
{
- this.calevents = [];
- var cal = this;
+ var dv = ''; // display value
- if(this.store.getCount() > 0){
- this.store.data.each(function(d){
- cal.addItem({
- id : d.data.id,
- start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
- end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
- time : d.data.start_time,
- title : d.data.title,
- description : d.data.description,
- venue : d.data.venue
- });
- });
+ if (this.displayField) {
+ dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+ } else {
+ // this is an error condition!!!
+ Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
}
- this.renderEvents();
+ if(!dv.length){
+ return;
+ }
- if(this.calevents.length && this.loadMask){
- this.maskEl.hide();
+ var choice = this.choices.createChild({
+ tag: 'li',
+ cls: 'select2-search-choice',
+ cn: [
+ {
+ tag: 'div',
+ html: dv
+ },
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'select2-search-choice-close',
+ tabindex: '-1'
+ }
+ ]
+
+ }, this.searchField);
+
+ var close = choice.select('a.select2-search-choice-close', true).first()
+
+ close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
+
+ this.item.push(o);
+ this.lastData = o;
+
+ this.syncValue();
+
+ this.inputEl().dom.value = '';
+
+ },
+
+ onRemoveItem : function(e, _self, o)
+ {
+ e.preventDefault();
+ var index = this.item.indexOf(o.data) * 1;
+
+ if( index < 0){
+ Roo.log('not this item?!');
+ return;
+ }
+
+ this.item.splice(index, 1);
+ o.item.remove();
+
+ this.syncValue();
+
+ this.fireEvent('remove', this, e);
+
+ },
+
+ syncValue : function()
+ {
+ if(!this.item.length){
+ this.clearValue();
+ return;
+ }
+
+ var value = [];
+ var _this = this;
+ Roo.each(this.item, function(i){
+ if(_this.valueField){
+ value.push(i[_this.valueField]);
+ return;
+ }
+
+ value.push(i);
+ });
+
+ this.value = value.join(',');
+
+ if(this.hiddenField){
+ this.hiddenField.dom.value = this.value;
}
},
- onBeforeLoad: function()
+ clearItem : function()
{
- this.clearEvents();
- if(this.loadMask){
- this.maskEl.show();
+ if(!this.multiple){
+ return;
}
+
+ this.item = [];
+
+ Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
+ c.remove();
+ });
+
+ this.syncValue();
}
-});
+
+
-
- /*
- * - LGPL
+ /**
+ * @cfg {Boolean} grow
+ * @hide
+ */
+ /**
+ * @cfg {Number} growMin
+ * @hide
+ */
+ /**
+ * @cfg {Number} growMax
+ * @hide
+ */
+ /**
+ * @hide
+ * @method autoSize
+ */
+});
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
*
- * element
- *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
*/
/**
- * @class Roo.bootstrap.Popover
- * @extends Roo.bootstrap.Component
- * Bootstrap Popover class
- * @cfg {String} html contents of the popover (or false to use children..)
- * @cfg {String} title of popover (or false to hide)
- * @cfg {String} placement how it is placed
- * @cfg {String} trigger click || hover (or false to trigger manually)
- * @cfg {String} over what (parent or false to trigger manually.)
+ * @class Roo.View
+ * @extends Roo.util.Observable
+ * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
+ * This class also supports single and multi selection modes. <br>
+ * Create a data model bound view:
+ <pre><code>
+ var store = new Roo.data.Store(...);
+
+ var view = new Roo.View({
+ el : "my-element",
+ tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
+
+ singleSelect: true,
+ selectedClass: "ydataview-selected",
+ store: store
+ });
+
+ // listen for node click?
+ view.on("click", function(vw, index, node, e){
+ alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
+ });
+
+ // load XML data
+ dataModel.load("foobar.xml");
+ </code></pre>
+ For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
+ * <br><br>
+ * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
+ * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
+ *
+ * Note: old style constructor is still suported (container, template, config)
*
* @constructor
- * Create a new Popover
+ * Create a new View
* @param {Object} config The config object
+ *
*/
-
-Roo.bootstrap.Popover = function(config){
- Roo.bootstrap.Popover.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
+Roo.View = function(config, depreciated_tpl, depreciated_config){
- title: 'Fill in a title',
- html: false,
+ if (typeof(depreciated_tpl) == 'undefined') {
+ // new way.. - universal constructor.
+ Roo.apply(this, config);
+ this.el = Roo.get(this.el);
+ } else {
+ // old format..
+ this.el = Roo.get(config);
+ this.tpl = depreciated_tpl;
+ Roo.apply(this, depreciated_config);
+ }
+ this.wrapEl = this.el.wrap().wrap();
+ ///this.el = this.wrapEla.appendChild(document.createElement("div"));
- placement : 'right',
- trigger : 'hover', // hover
- over: 'parent',
+ if(typeof(this.tpl) == "string"){
+ this.tpl = new Roo.Template(this.tpl);
+ } else {
+ // support xtype ctors..
+ this.tpl = new Roo.factory(this.tpl, Roo);
+ }
- can_build_overlaid : false,
- getChildContainer : function()
- {
- return this.el.select('.popover-content',true).first();
- },
+ this.tpl.compile();
+
+
- getAutoCreate : function(){
- Roo.log('make popover?');
- var cfg = {
- cls : 'popover roo-dynamic',
- style: 'display:block',
- cn : [
- {
- cls : 'arrow'
- },
- {
- cls : 'popover-inner',
- cn : [
- {
- tag: 'h3',
- cls: 'popover-title',
- html : this.title
- },
- {
- cls : 'popover-content',
- html : this.html
- }
- ]
-
- }
- ]
- };
-
- return cfg;
- },
- setTitle: function(str)
- {
- this.el.select('.popover-title',true).first().dom.innerHTML = str;
- },
- setContent: function(str)
- {
- this.el.select('.popover-content',true).first().dom.innerHTML = str;
- },
- // as it get's added to the bottom of the page.
- onRender : function(ct, position)
- {
- Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
- if(!this.el){
- var cfg = Roo.apply({}, this.getAutoCreate());
- cfg.id = Roo.id();
-
- if (this.cls) {
- cfg.cls += ' ' + this.cls;
- }
- if (this.style) {
- cfg.style = this.style;
- }
- Roo.log("adding to ")
- this.el = Roo.get(document.body).createChild(cfg, position);
- Roo.log(this.el);
- }
- this.initEvents();
- },
+
+ /** @private */
+ this.addEvents({
+ /**
+ * @event beforeclick
+ * Fires before a click is processed. Returns false to cancel the default action.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "beforeclick" : true,
+ /**
+ * @event click
+ * Fires when a template node is clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "click" : true,
+ /**
+ * @event dblclick
+ * Fires when a template node is double clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "dblclick" : true,
+ /**
+ * @event contextmenu
+ * Fires when a template node is right clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "contextmenu" : true,
+ /**
+ * @event selectionchange
+ * Fires when the selected nodes change.
+ * @param {Roo.View} this
+ * @param {Array} selections Array of the selected nodes
+ */
+ "selectionchange" : true,
+
+ /**
+ * @event beforeselect
+ * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+ * @param {Roo.View} this
+ * @param {HTMLElement} node The node to be selected
+ * @param {Array} selections Array of currently selected nodes
+ */
+ "beforeselect" : true,
+ /**
+ * @event preparedata
+ * Fires on every row to render, to allow you to change the data.
+ * @param {Roo.View} this
+ * @param {Object} data to be rendered (change this)
+ */
+ "preparedata" : true
+
+
+ });
+
+
+
+ this.el.on({
+ "click": this.onClick,
+ "dblclick": this.onDblClick,
+ "contextmenu": this.onContextMenu,
+ scope:this
+ });
+
+ this.selections = [];
+ this.nodes = [];
+ this.cmp = new Roo.CompositeElementLite([]);
+ if(this.store){
+ this.store = Roo.factory(this.store, Roo.data);
+ this.setStore(this.store, true);
+ }
- initEvents : function()
- {
- this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
- this.el.enableDisplayMode('block');
- this.el.hide();
- if (this.over === false) {
- return;
- }
- if (this.triggers === false) {
- return;
- }
- var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
- var triggers = this.trigger ? this.trigger.split(' ') : [];
- Roo.each(triggers, function(trigger) {
+ if ( this.footer && this.footer.xtype) {
+
+ var fctr = this.wrapEl.appendChild(document.createElement("div"));
- if (trigger == 'click') {
- on_el.on('click', this.toggle, this);
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
-
- on_el.on(eventIn ,this.enter, this);
- on_el.on(eventOut, this.leave, this);
- }
- }, this);
+ this.footer.dataSource = this.store
+ this.footer.container = fctr;
+ this.footer = Roo.factory(this.footer, Roo);
+ fctr.insertFirst(this.el);
- },
+ // this is a bit insane - as the paging toolbar seems to detach the el..
+// dom.parentNode.parentNode.parentNode
+ // they get detached?
+ }
- // private
- timeout : null,
- hoverState : null,
+ Roo.View.superclass.constructor.call(this);
- toggle : function () {
- this.hoverState == 'in' ? this.leave() : this.enter();
- },
- enter : function () {
-
+};
+
+Roo.extend(Roo.View, Roo.util.Observable, {
- clearTimeout(this.timeout);
+ /**
+ * @cfg {Roo.data.Store} store Data store to load data from.
+ */
+ store : false,
- this.hoverState = 'in'
+ /**
+ * @cfg {String|Roo.Element} el The container element.
+ */
+ el : '',
- if (!this.delay || !this.delay.show) {
- this.show();
- return
- }
- var _t = this;
- this.timeout = setTimeout(function () {
- if (_t.hoverState == 'in') {
- _t.show();
- }
- }, this.delay.show)
- },
- leave : function() {
- clearTimeout(this.timeout);
+ /**
+ * @cfg {String|Roo.Template} tpl The template used by this View
+ */
+ tpl : false,
+ /**
+ * @cfg {String} dataName the named area of the template to use as the data area
+ * Works with domtemplates roo-name="name"
+ */
+ dataName: false,
+ /**
+ * @cfg {String} selectedClass The css class to add to selected nodes
+ */
+ selectedClass : "x-view-selected",
+ /**
+ * @cfg {String} emptyText The empty text to show when nothing is loaded.
+ */
+ emptyText : "",
- this.hoverState = 'out'
+ /**
+ * @cfg {String} text to display on mask (default Loading)
+ */
+ mask : false,
+ /**
+ * @cfg {Boolean} multiSelect Allow multiple selection
+ */
+ multiSelect : false,
+ /**
+ * @cfg {Boolean} singleSelect Allow single selection
+ */
+ singleSelect: false,
- if (!this.delay || !this.delay.hide) {
- this.hide();
- return
- }
- var _t = this;
- this.timeout = setTimeout(function () {
- if (_t.hoverState == 'out') {
- _t.hide();
- }
- }, this.delay.hide)
- },
+ /**
+ * @cfg {Boolean} toggleSelect - selecting
+ */
+ toggleSelect : false,
- show : function (on_el)
- {
- if (!on_el) {
- on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
- }
- // set content.
- this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
- if (this.html !== false) {
- this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
- }
- this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
- if (!this.title.length) {
- this.el.select('.popover-title',true).hide();
- }
-
- var placement = typeof this.placement == 'function' ?
- this.placement.call(this, this.el, on_el) :
- this.placement;
-
- var autoToken = /\s?auto?\s?/i;
- var autoPlace = autoToken.test(placement);
- if (autoPlace) {
- placement = placement.replace(autoToken, '') || 'top';
- }
-
- //this.el.detach()
- //this.el.setXY([0,0]);
- this.el.show();
- this.el.dom.style.display='block';
- this.el.addClass(placement);
-
- //this.el.appendTo(on_el);
-
- var p = this.getPosition();
- var box = this.el.getBox();
-
- if (autoPlace) {
- // fixme..
- }
- var align = Roo.bootstrap.Popover.alignment[placement]
- this.el.alignTo(on_el, align[0],align[1]);
- //var arrow = this.el.select('.arrow',true).first();
- //arrow.set(align[2],
-
- this.el.addClass('in');
- this.hoverState = null;
-
- if (this.el.hasClass('fade')) {
- // fade it?
- }
-
+ /**
+ * Returns the element this view is bound to.
+ * @return {Roo.Element}
+ */
+ getEl : function(){
+ return this.wrapEl;
},
- hide : function()
- {
- this.el.setXY([0,0]);
- this.el.removeClass('in');
- this.el.hide();
-
- }
-
-});
-
-Roo.bootstrap.Popover.alignment = {
- 'left' : ['r-l', [-10,0], 'right'],
- 'right' : ['l-r', [10,0], 'left'],
- 'bottom' : ['t-b', [0,10], 'top'],
- 'top' : [ 'b-t', [0,-10], 'bottom']
-};
-
- /*
- * - LGPL
- *
- * Progress
- *
- */
-
-/**
- * @class Roo.bootstrap.Progress
- * @extends Roo.bootstrap.Component
- * Bootstrap Progress class
- * @cfg {Boolean} striped striped of the progress bar
- * @cfg {Boolean} active animated of the progress bar
- *
- *
- * @constructor
- * Create a new Progress
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Progress = function(config){
- Roo.bootstrap.Progress.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
- striped : false,
- active: false,
- getAutoCreate : function(){
- var cfg = {
- tag: 'div',
- cls: 'progress'
- };
+
+ /**
+ * Refreshes the view. - called by datachanged on the store. - do not call directly.
+ */
+ refresh : function(){
+ Roo.log('refresh');
+ var t = this.tpl;
+
+ // if we are using something like 'domtemplate', then
+ // the what gets used is:
+ // t.applySubtemplate(NAME, data, wrapping data..)
+ // the outer template then get' applied with
+ // the store 'extra data'
+ // and the body get's added to the
+ // roo-name="data" node?
+ // <span class='roo-tpl-{name}'></span> ?????
- if(this.striped){
- cfg.cls += ' progress-striped';
+
+ this.clearSelections();
+ this.el.update("");
+ var html = [];
+ var records = this.store.getRange();
+ if(records.length < 1) {
+
+ // is this valid?? = should it render a template??
+
+ this.el.update(this.emptyText);
+ return;
}
-
- if(this.active){
- cfg.cls += ' active';
+ var el = this.el;
+ if (this.dataName) {
+ this.el.update(t.apply(this.store.meta)); //????
+ el = this.el.child('.roo-tpl-' + this.dataName);
}
+ for(var i = 0, len = records.length; i < len; i++){
+ var data = this.prepareData(records[i].data, i, records[i]);
+ this.fireEvent("preparedata", this, data, i, records[i]);
+ html[html.length] = Roo.util.Format.trim(
+ this.dataName ?
+ t.applySubtemplate(this.dataName, data, this.store.meta) :
+ t.apply(data)
+ );
+ }
- return cfg;
- }
-
-});
-
-
-
- /*
- * - LGPL
- *
- * ProgressBar
- *
- */
+
+
+ el.update(html.join(""));
+ this.nodes = el.dom.childNodes;
+ this.updateIndexes(0);
+ },
+
-/**
- * @class Roo.bootstrap.ProgressBar
- * @extends Roo.bootstrap.Component
- * Bootstrap ProgressBar class
- * @cfg {Number} aria_valuenow aria-value now
- * @cfg {Number} aria_valuemin aria-value min
- * @cfg {Number} aria_valuemax aria-value max
- * @cfg {String} label label for the progress bar
- * @cfg {String} panel (success | info | warning | danger )
- * @cfg {String} role role of the progress bar
- * @cfg {String} sr_only text
- *
- *
- * @constructor
- * Create a new ProgressBar
- * @param {Object} config The config object
- */
+ /**
+ * Function to override to reformat the data that is sent to
+ * the template for each node.
+ * DEPRICATED - use the preparedata event handler.
+ * @param {Array/Object} data The raw data (array of colData for a data model bound view or
+ * a JSON object for an UpdateManager bound view).
+ */
+ prepareData : function(data, index, record)
+ {
+ this.fireEvent("preparedata", this, data, index, record);
+ return data;
+ },
-Roo.bootstrap.ProgressBar = function(config){
- Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
-};
+ onUpdate : function(ds, record){
+ Roo.log('on update');
+ this.clearSelections();
+ var index = this.store.indexOf(record);
+ var n = this.nodes[index];
+ this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
+ n.parentNode.removeChild(n);
+ this.updateIndexes(index, index);
+ },
-Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
- aria_valuenow : 0,
- aria_valuemin : 0,
- aria_valuemax : 100,
- label : false,
- panel : false,
- role : false,
- sr_only: false,
- getAutoCreate : function()
+// --------- FIXME
+ onAdd : function(ds, records, index)
{
-
- var cfg = {
- tag: 'div',
- cls: 'progress-bar',
- style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
- };
-
- if(this.sr_only){
- cfg.cn = {
- tag: 'span',
- cls: 'sr-only',
- html: this.sr_only
+ Roo.log(['on Add', ds, records, index] );
+ this.clearSelections();
+ if(this.nodes.length == 0){
+ this.refresh();
+ return;
+ }
+ var n = this.nodes[index];
+ for(var i = 0, len = records.length; i < len; i++){
+ var d = this.prepareData(records[i].data, i, records[i]);
+ if(n){
+ this.tpl.insertBefore(n, d);
+ }else{
+
+ this.tpl.append(this.el, d);
}
}
+ this.updateIndexes(index);
+ },
+
+ onRemove : function(ds, record, index){
+ Roo.log('onRemove');
+ this.clearSelections();
+ var el = this.dataName ?
+ this.el.child('.roo-tpl-' + this.dataName) :
+ this.el;
- if(this.role){
- cfg.role = this.role;
+ el.dom.removeChild(this.nodes[index]);
+ this.updateIndexes(index);
+ },
+
+ /**
+ * Refresh an individual node.
+ * @param {Number} index
+ */
+ refreshNode : function(index){
+ this.onUpdate(this.store, this.store.getAt(index));
+ },
+
+ updateIndexes : function(startIndex, endIndex){
+ var ns = this.nodes;
+ startIndex = startIndex || 0;
+ endIndex = endIndex || ns.length - 1;
+ for(var i = startIndex; i <= endIndex; i++){
+ ns[i].nodeIndex = i;
+ }
+ },
+
+ /**
+ * Changes the data store this view uses and refresh the view.
+ * @param {Store} store
+ */
+ setStore : function(store, initial){
+ if(!initial && this.store){
+ this.store.un("datachanged", this.refresh);
+ this.store.un("add", this.onAdd);
+ this.store.un("remove", this.onRemove);
+ this.store.un("update", this.onUpdate);
+ this.store.un("clear", this.refresh);
+ this.store.un("beforeload", this.onBeforeLoad);
+ this.store.un("load", this.onLoad);
+ this.store.un("loadexception", this.onLoad);
+ }
+ if(store){
+
+ store.on("datachanged", this.refresh, this);
+ store.on("add", this.onAdd, this);
+ store.on("remove", this.onRemove, this);
+ store.on("update", this.onUpdate, this);
+ store.on("clear", this.refresh, this);
+ store.on("beforeload", this.onBeforeLoad, this);
+ store.on("load", this.onLoad, this);
+ store.on("loadexception", this.onLoad, this);
}
- if(this.aria_valuenow){
- cfg['aria-valuenow'] = this.aria_valuenow;
+ if(store){
+ this.refresh();
+ }
+ },
+ /**
+ * onbeforeLoad - masks the loading area.
+ *
+ */
+ onBeforeLoad : function(store,opts)
+ {
+ Roo.log('onBeforeLoad');
+ if (!opts.add) {
+ this.el.update("");
}
+ this.el.mask(this.mask ? this.mask : "Loading" );
+ },
+ onLoad : function ()
+ {
+ this.el.unmask();
+ },
+
+
+ /**
+ * Returns the template node the passed child belongs to or null if it doesn't belong to one.
+ * @param {HTMLElement} node
+ * @return {HTMLElement} The template node
+ */
+ findItemFromChild : function(node){
+ var el = this.dataName ?
+ this.el.child('.roo-tpl-' + this.dataName,true) :
+ this.el.dom;
- if(this.aria_valuemin){
- cfg['aria-valuemin'] = this.aria_valuemin;
+ if(!node || node.parentNode == el){
+ return node;
+ }
+ var p = node.parentNode;
+ while(p && p != el){
+ if(p.parentNode == el){
+ return p;
+ }
+ p = p.parentNode;
+ }
+ return null;
+ },
+
+ /** @ignore */
+ onClick : function(e){
+ var item = this.findItemFromChild(e.getTarget());
+ if(item){
+ var index = this.indexOf(item);
+ if(this.onItemClick(item, index, e) !== false){
+ this.fireEvent("click", this, index, item, e);
+ }
+ }else{
+ this.clearSelections();
+ }
+ },
+
+ /** @ignore */
+ onContextMenu : function(e){
+ var item = this.findItemFromChild(e.getTarget());
+ if(item){
+ this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
+ }
+ },
+
+ /** @ignore */
+ onDblClick : function(e){
+ var item = this.findItemFromChild(e.getTarget());
+ if(item){
+ this.fireEvent("dblclick", this, this.indexOf(item), item, e);
}
-
- if(this.aria_valuemax){
- cfg['aria-valuemax'] = this.aria_valuemax;
+ },
+
+ onItemClick : function(item, index, e)
+ {
+ if(this.fireEvent("beforeclick", this, index, item, e) === false){
+ return false;
}
-
- if(this.label && !this.sr_only){
- cfg.html = this.label;
+ if (this.toggleSelect) {
+ var m = this.isSelected(item) ? 'unselect' : 'select';
+ Roo.log(m);
+ var _t = this;
+ _t[m](item, true, false);
+ return true;
}
-
- if(this.panel){
- cfg.cls += ' progress-bar-' + this.panel;
+ if(this.multiSelect || this.singleSelect){
+ if(this.multiSelect && e.shiftKey && this.lastSelection){
+ this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
+ }else{
+ this.select(item, this.multiSelect && e.ctrlKey);
+ this.lastSelection = item;
+ }
+ e.preventDefault();
}
-
- return cfg;
+ return true;
},
-
- update : function(aria_valuenow)
- {
- this.aria_valuenow = aria_valuenow;
-
- this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
- }
-
-});
-
+ /**
+ * Get the number of selected nodes.
+ * @return {Number}
+ */
+ getSelectionCount : function(){
+ return this.selections.length;
+ },
- /*
- * - LGPL
- *
- * TabPanel
- *
- */
+ /**
+ * Get the currently selected nodes.
+ * @return {Array} An array of HTMLElements
+ */
+ getSelectedNodes : function(){
+ return this.selections;
+ },
-/**
- * @class Roo.bootstrap.TabPanel
- * @extends Roo.bootstrap.Component
- * Bootstrap TabPanel class
- * @cfg {Boolean} active panel active
- * @cfg {String} html panel content
- * @cfg {String} tabId tab relate id
- * @cfg {String} navId The navbar which triggers show hide
- *
- *
- * @constructor
- * Create a new TabPanel
- * @param {Object} config The config object
- */
+ /**
+ * Get the indexes of the selected nodes.
+ * @return {Array}
+ */
+ getSelectedIndexes : function(){
+ var indexes = [], s = this.selections;
+ for(var i = 0, len = s.length; i < len; i++){
+ indexes.push(s[i].nodeIndex);
+ }
+ return indexes;
+ },
-Roo.bootstrap.TabPanel = function(config){
- Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event changed
- * Fires when the active status changes
- * @param {Roo.bootstrap.TabPanel} this
- * @param {Boolean} state the new state
-
- */
- 'changed': true
- });
-};
+ /**
+ * Clear all selections
+ * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
+ */
+ clearSelections : function(suppressEvent){
+ if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
+ this.cmp.elements = this.selections;
+ this.cmp.removeClass(this.selectedClass);
+ this.selections = [];
+ if(!suppressEvent){
+ this.fireEvent("selectionchange", this, this.selections);
+ }
+ }
+ },
-Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
-
- active: false,
- html: false,
- tabId: false,
- navId : false,
-
- getAutoCreate : function(){
- var cfg = {
- tag: 'div',
- cls: 'tab-pane',
- html: this.html || ''
- };
-
- if(this.active){
- cfg.cls += ' active';
+ /**
+ * Returns true if the passed node is selected
+ * @param {HTMLElement/Number} node The node or node index
+ * @return {Boolean}
+ */
+ isSelected : function(node){
+ var s = this.selections;
+ if(s.length < 1){
+ return false;
}
-
- if(this.tabId){
- cfg.tabId = this.tabId;
+ node = this.getNode(node);
+ return s.indexOf(node) !== -1;
+ },
+
+ /**
+ * Selects nodes.
+ * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
+ * @param {Boolean} keepExisting (optional) true to keep existing selections
+ * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+ */
+ select : function(nodeInfo, keepExisting, suppressEvent){
+ if(nodeInfo instanceof Array){
+ if(!keepExisting){
+ this.clearSelections(true);
+ }
+ for(var i = 0, len = nodeInfo.length; i < len; i++){
+ this.select(nodeInfo[i], true, true);
+ }
+ return;
+ }
+ var node = this.getNode(nodeInfo);
+ if(!node || this.isSelected(node)){
+ return; // already selected.
+ }
+ if(!keepExisting){
+ this.clearSelections(true);
+ }
+ if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
+ Roo.fly(node).addClass(this.selectedClass);
+ this.selections.push(node);
+ if(!suppressEvent){
+ this.fireEvent("selectionchange", this, this.selections);
+ }
}
- return cfg;
+
},
- onRender : function(ct, position)
+ /**
+ * Unselects nodes.
+ * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
+ * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
+ * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+ */
+ unselect : function(nodeInfo, keepExisting, suppressEvent)
{
- // Roo.log("Call onRender: " + this.xtype);
-
- Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
+ if(nodeInfo instanceof Array){
+ Roo.each(this.selections, function(s) {
+ this.unselect(s, nodeInfo);
+ }, this);
+ return;
+ }
+ var node = this.getNode(nodeInfo);
+ if(!node || !this.isSelected(node)){
+ Roo.log("not selected");
+ return; // not selected.
+ }
+ // fireevent???
+ var ns = [];
+ Roo.each(this.selections, function(s) {
+ if (s == node ) {
+ Roo.fly(node).removeClass(this.selectedClass);
+
+ return;
+ }
+ ns.push(s);
+ },this);
- if (this.navId && this.tabId) {
- var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
- if (!item) {
- Roo.log("could not find navID:" + this.navId + ", tabId: " + this.tabId);
- } else {
- item.on('changed', function(item, state) {
- this.setActive(state);
- }, this);
+ this.selections= ns;
+ this.fireEvent("selectionchange", this, this.selections);
+ },
+
+ /**
+ * Gets a template node.
+ * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
+ * @return {HTMLElement} The node or null if it wasn't found
+ */
+ getNode : function(nodeInfo){
+ if(typeof nodeInfo == "string"){
+ return document.getElementById(nodeInfo);
+ }else if(typeof nodeInfo == "number"){
+ return this.nodes[nodeInfo];
+ }
+ return nodeInfo;
+ },
+
+ /**
+ * Gets a range template nodes.
+ * @param {Number} startIndex
+ * @param {Number} endIndex
+ * @return {Array} An array of nodes
+ */
+ getNodes : function(start, end){
+ var ns = this.nodes;
+ start = start || 0;
+ end = typeof end == "undefined" ? ns.length - 1 : end;
+ var nodes = [];
+ if(start <= end){
+ for(var i = start; i <= end; i++){
+ nodes.push(ns[i]);
+ }
+ } else{
+ for(var i = start; i >= end; i--){
+ nodes.push(ns[i]);
}
}
-
+ return nodes;
},
- setActive: function(state)
- {
- Roo.log("panel - set active " + this.tabId + "=" + state);
-
- this.active = state;
- if (!state) {
- this.el.removeClass('active');
-
- } else if (!this.el.hasClass('active')) {
- this.el.addClass('active');
+
+ /**
+ * Finds the index of the passed node
+ * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
+ * @return {Number} The index of the node or -1
+ */
+ indexOf : function(node){
+ node = this.getNode(node);
+ if(typeof node.nodeIndex == "number"){
+ return node.nodeIndex;
}
- this.fireEvent('changed', this, state);
+ var ns = this.nodes;
+ for(var i = 0, len = ns.length; i < len; i++){
+ if(ns[i] == node){
+ return i;
+ }
+ }
+ return -1;
}
-
-
});
-
-
-
-
- /*
+/*
* - LGPL
*
- * DateField
+ * based on jquery fullcalendar
*
*/
+Roo.bootstrap = Roo.bootstrap || {};
/**
- * @class Roo.bootstrap.DateField
- * @extends Roo.bootstrap.Input
- * Bootstrap DateField class
- * @cfg {Number} weekStart default 0
- * @cfg {Number} weekStart default 0
- * @cfg {Number} viewMode default empty, (months|years)
- * @cfg {Number} minViewMode default empty, (months|years)
- * @cfg {Number} startDate default -Infinity
- * @cfg {Number} endDate default Infinity
- * @cfg {Boolean} todayHighlight default false
- * @cfg {Boolean} todayBtn default false
- * @cfg {Boolean} calendarWeeks default false
- * @cfg {Object} daysOfWeekDisabled default empty
- *
- * @cfg {Boolean} keyboardNavigation default true
- * @cfg {String} language default en
- *
+ * @class Roo.bootstrap.Calendar
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Calendar class
+ * @cfg {Boolean} loadMask (true|false) default false
+ * @cfg {Object} header generate the user specific header of the calendar, default false
+
* @constructor
- * Create a new DateField
+ * Create a new Container
* @param {Object} config The config object
*/
-Roo.bootstrap.DateField = function(config){
- Roo.bootstrap.DateField.superclass.constructor.call(this, config);
+
+
+Roo.bootstrap.Calendar = function(config){
+ Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
this.addEvents({
- /**
- * @event show
- * Fires when this field show.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- show : true,
- /**
- * @event show
- * Fires when this field hide.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- hide : true,
- /**
- * @event select
- * Fires when select a date.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- select : true
- });
+ /**
+ * @event select
+ * Fires when a date is selected
+ * @param {DatePicker} this
+ * @param {Date} date The selected date
+ */
+ 'select': true,
+ /**
+ * @event monthchange
+ * Fires when the displayed month changes
+ * @param {DatePicker} this
+ * @param {Date} date The selected month
+ */
+ 'monthchange': true,
+ /**
+ * @event evententer
+ * Fires when mouse over an event
+ * @param {Calendar} this
+ * @param {event} Event
+ */
+ 'evententer': true,
+ /**
+ * @event eventleave
+ * Fires when the mouse leaves an
+ * @param {Calendar} this
+ * @param {event}
+ */
+ 'eventleave': true,
+ /**
+ * @event eventclick
+ * Fires when the mouse click an
+ * @param {Calendar} this
+ * @param {event}
+ */
+ 'eventclick': true
+
+ });
+
};
-Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
+Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
- /**
- * @cfg {String} format
- * The default date format string which can be overriden for localization support. The format must be
- * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
- */
- format : "m/d/y",
- /**
- * @cfg {String} altFormats
- * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
- * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
+ /**
+ * @cfg {Number} startDay
+ * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
*/
- altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
-
- weekStart : 0,
-
- viewMode : '',
-
- minViewMode : '',
-
- todayHighlight : false,
-
- todayBtn: false,
-
- language: 'en',
-
- keyboardNavigation: true,
-
- calendarWeeks: false,
-
- startDate: -Infinity,
-
- endDate: Infinity,
-
- daysOfWeekDisabled: [],
-
- _events: [],
-
- UTCDate: function()
- {
- return new Date(Date.UTC.apply(Date, arguments));
- },
-
- UTCToday: function()
- {
- var today = new Date();
- return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
- },
-
- getDate: function() {
- var d = this.getUTCDate();
- return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
- },
-
- getUTCDate: function() {
- return this.date;
- },
+ startDay : 0,
- setDate: function(d) {
- this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
- },
+ loadMask : false,
- setUTCDate: function(d) {
- this.date = d;
- this.setValue(this.formatDate(this.date));
- },
-
- onRender: function(ct, position)
- {
-
- Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
-
- this.language = this.language || 'en';
- this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
- this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
-
- this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
- this.format = this.format || 'm/d/y';
- this.isInline = false;
- this.isInput = true;
- this.component = this.el.select('.add-on', true).first() || false;
- this.component = (this.component && this.component.length === 0) ? false : this.component;
- this.hasInput = this.component && this.inputEL().length;
-
- if (typeof(this.minViewMode === 'string')) {
- switch (this.minViewMode) {
- case 'months':
- this.minViewMode = 1;
- break;
- case 'years':
- this.minViewMode = 2;
- break;
- default:
- this.minViewMode = 0;
- break;
- }
- }
-
- if (typeof(this.viewMode === 'string')) {
- switch (this.viewMode) {
- case 'months':
- this.viewMode = 1;
- break;
- case 'years':
- this.viewMode = 2;
- break;
- default:
- this.viewMode = 0;
- break;
- }
- }
-
- this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
-
- this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-
- this.picker().on('mousedown', this.onMousedown, this);
- this.picker().on('click', this.onClick, this);
+ header : false,
+
+ getAutoCreate : function(){
- this.picker().addClass('datepicker-dropdown');
- this.startViewMode = this.viewMode;
+ var fc_button = function(name, corner, style, content ) {
+ return Roo.apply({},{
+ tag : 'span',
+ cls : 'fc-button fc-button-'+name+' fc-state-default ' +
+ (corner.length ?
+ 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
+ ''
+ ),
+ html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
+ unselectable: 'on'
+ });
+ };
+ var header = {};
- Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
- if(!this.calendarWeeks){
- v.remove();
- return;
+ if(!this.header){
+ header = {
+ tag : 'table',
+ cls : 'fc-header',
+ style : 'width:100%',
+ cn : [
+ {
+ tag: 'tr',
+ cn : [
+ {
+ tag : 'td',
+ cls : 'fc-header-left',
+ cn : [
+ fc_button('prev', 'left', 'arrow', '‹' ),
+ fc_button('next', 'right', 'arrow', '›' ),
+ { tag: 'span', cls: 'fc-header-space' },
+ fc_button('today', 'left right', '', 'today' ) // neds state disabled..
+
+
+ ]
+ },
+
+ {
+ tag : 'td',
+ cls : 'fc-header-center',
+ cn : [
+ {
+ tag: 'span',
+ cls: 'fc-header-title',
+ cn : {
+ tag: 'H2',
+ html : 'month / year'
+ }
+ }
+
+ ]
+ },
+ {
+ tag : 'td',
+ cls : 'fc-header-right',
+ cn : [
+ /* fc_button('month', 'left', '', 'month' ),
+ fc_button('week', '', '', 'week' ),
+ fc_button('day', 'right', '', 'day' )
+ */
+
+ ]
+ }
+
+ ]
+ }
+ ]
};
-
- v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
- v.attr('colspan', function(i, val){
- return parseInt(val) + 1;
- });
- })
-
+ }
- this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
+ header = this.header;
- this.setStartDate(this.startDate);
- this.setEndDate(this.endDate);
+
+ var cal_heads = function() {
+ var ret = [];
+ // fixme - handle this.
+
+ for (var i =0; i < Date.dayNames.length; i++) {
+ var d = Date.dayNames[i];
+ ret.push({
+ tag: 'th',
+ cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
+ html : d.substring(0,3)
+ });
+
+ }
+ ret[0].cls += ' fc-first';
+ ret[6].cls += ' fc-last';
+ return ret;
+ };
+ var cal_cell = function(n) {
+ return {
+ tag: 'td',
+ cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
+ cn : [
+ {
+ cn : [
+ {
+ cls: 'fc-day-number',
+ html: 'D'
+ },
+ {
+ cls: 'fc-day-content',
+
+ cn : [
+ {
+ style: 'position: relative;' // height: 17px;
+ }
+ ]
+ }
+
+
+ ]
+ }
+ ]
+
+ }
+ };
+ var cal_rows = function() {
+
+ var ret = []
+ for (var r = 0; r < 6; r++) {
+ var row= {
+ tag : 'tr',
+ cls : 'fc-week',
+ cn : []
+ };
+
+ for (var i =0; i < Date.dayNames.length; i++) {
+ var d = Date.dayNames[i];
+ row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
+
+ }
+ row.cn[0].cls+=' fc-first';
+ row.cn[0].cn[0].style = 'min-height:90px';
+ row.cn[6].cls+=' fc-last';
+ ret.push(row);
+
+ }
+ ret[0].cls += ' fc-first';
+ ret[4].cls += ' fc-prev-last';
+ ret[5].cls += ' fc-last';
+ return ret;
+
+ };
- this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
+ var cal_table = {
+ tag: 'table',
+ cls: 'fc-border-separate',
+ style : 'width:100%',
+ cellspacing : 0,
+ cn : [
+ {
+ tag: 'thead',
+ cn : [
+ {
+ tag: 'tr',
+ cls : 'fc-first fc-last',
+ cn : cal_heads()
+ }
+ ]
+ },
+ {
+ tag: 'tbody',
+ cn : cal_rows()
+ }
+
+ ]
+ };
+
+ var cfg = {
+ cls : 'fc fc-ltr',
+ cn : [
+ header,
+ {
+ cls : 'fc-content',
+ style : "position: relative;",
+ cn : [
+ {
+ cls : 'fc-view fc-view-month fc-grid',
+ style : 'position: relative',
+ unselectable : 'on',
+ cn : [
+ {
+ cls : 'fc-event-container',
+ style : 'position:absolute;z-index:8;top:0;left:0;'
+ },
+ cal_table
+ ]
+ }
+ ]
+
+ }
+ ]
+
+ };
- this.fillDow();
- this.fillMonths();
- this.update();
- this.showMode();
+
- if(this.isInline) {
- this.show();
- }
+ return cfg;
},
- picker : function()
- {
- return this.el.select('.datepicker', true).first();
- },
- fillDow: function()
+ initEvents : function()
{
- var dowCnt = this.weekStart;
+ if(!this.store){
+ throw "can not find store for calendar";
+ }
- var dow = {
- tag: 'tr',
+ var mark = {
+ tag: "div",
+ cls:"x-dlg-mask",
+ style: "text-align:center",
cn: [
-
+ {
+ tag: "div",
+ style: "background-color:white;width:50%;margin:250 auto",
+ cn: [
+ {
+ tag: "img",
+ src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
+ },
+ {
+ tag: "span",
+ html: "Loading"
+ }
+
+ ]
+ }
]
- };
-
- if(this.calendarWeeks){
- dow.cn.push({
- tag: 'th',
- cls: 'cw',
- html: ' '
- })
}
+ this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
- while (dowCnt < this.weekStart + 7) {
- dow.cn.push({
- tag: 'th',
- cls: 'dow',
- html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
- });
+ var size = this.el.select('.fc-content', true).first().getSize();
+ this.maskEl.setSize(size.width, size.height);
+ this.maskEl.enableDisplayMode("block");
+ if(!this.loadMask){
+ this.maskEl.hide();
}
- this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
- },
-
- fillMonths: function()
- {
- var i = 0
- var months = this.picker().select('>.datepicker-months td', true).first();
-
- months.dom.innerHTML = '';
+ this.store = Roo.factory(this.store, Roo.data);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('beforeload', this.onBeforeLoad, this);
- while (i < 12) {
- var month = {
- tag: 'span',
- cls: 'month',
- html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
- }
-
- months.createChild(month);
- }
+ this.resize();
- },
-
- update: function(){
+ this.cells = this.el.select('.fc-day',true);
+ //Roo.log(this.cells);
+ this.textNodes = this.el.query('.fc-day-number');
+ this.cells.addClassOnOver('fc-state-hover');
- this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
+ this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
+ this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
+ this.el.select('.fc-button-today',true).on('click', this.showToday, this);
+ this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
- if (this.date < this.startDate) {
- this.viewDate = new Date(this.startDate);
- } else if (this.date > this.endDate) {
- this.viewDate = new Date(this.endDate);
- } else {
- this.viewDate = new Date(this.date);
- }
+ this.on('monthchange', this.onMonthChange, this);
- this.fill();
+ this.update(new Date().clearTime());
},
- fill: function() {
- var d = new Date(this.viewDate),
- year = d.getUTCFullYear(),
- month = d.getUTCMonth(),
- startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
- startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
- endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
- endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
- currentDate = this.date && this.date.valueOf(),
- today = this.UTCToday();
-
- this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
-
-// this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
+ resize : function() {
+ var sz = this.el.getSize();
-// this.picker.select('>tfoot th.today').
-// .text(dates[this.language].today)
-// .toggle(this.todayBtn !== false);
+ this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
+ this.el.select('.fc-day-content div',true).setHeight(34);
+ },
- this.updateNavArrows();
- this.fillMonths();
-
- var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
+
+ // private
+ showPrevMonth : function(e){
+ this.update(this.activeDate.add("mo", -1));
+ },
+ showToday : function(e){
+ this.update(new Date().clearTime());
+ },
+ // private
+ showNextMonth : function(e){
+ this.update(this.activeDate.add("mo", 1));
+ },
+
+ // private
+ showPrevYear : function(){
+ this.update(this.activeDate.add("y", -1));
+ },
+
+ // private
+ showNextYear : function(){
+ this.update(this.activeDate.add("y", 1));
+ },
+
+
+ // private
+ update : function(date)
+ {
+ var vd = this.activeDate;
+ this.activeDate = date;
+// if(vd && this.el){
+// var t = date.getTime();
+// if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
+// Roo.log('using add remove');
+//
+// this.fireEvent('monthchange', this, date);
+//
+// this.cells.removeClass("fc-state-highlight");
+// this.cells.each(function(c){
+// if(c.dateValue == t){
+// c.addClass("fc-state-highlight");
+// setTimeout(function(){
+// try{c.dom.firstChild.focus();}catch(e){}
+// }, 50);
+// return false;
+// }
+// return true;
+// });
+// return;
+// }
+// }
- day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
-
- prevMonth.setUTCDate(day);
+ var days = date.getDaysInMonth();
- prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
+ var firstOfMonth = date.getFirstDateOfMonth();
+ var startingPos = firstOfMonth.getDay()-this.startDay;
- var nextMonth = new Date(prevMonth);
+ if(startingPos < this.startDay){
+ startingPos += 7;
+ }
- nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
+ var pm = date.add(Date.MONTH, -1);
+ var prevStart = pm.getDaysInMonth()-startingPos;
+//
+ this.cells = this.el.select('.fc-day',true);
+ this.textNodes = this.el.query('.fc-day-number');
+ this.cells.addClassOnOver('fc-state-hover');
- nextMonth = nextMonth.valueOf();
+ var cells = this.cells.elements;
+ var textEls = this.textNodes;
- var fillMonths = false;
+ Roo.each(cells, function(cell){
+ cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
+ });
- this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
+ days += startingPos;
+
+ // convert everything to numbers so it's fast
+ var day = 86400000;
+ var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
+ //Roo.log(d);
+ //Roo.log(pm);
+ //Roo.log(prevStart);
- while(prevMonth.valueOf() < nextMonth) {
- var clsName = '';
+ var today = new Date().clearTime().getTime();
+ var sel = date.clearTime().getTime();
+ var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
+ var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
+ var ddMatch = this.disabledDatesRE;
+ var ddText = this.disabledDatesText;
+ var ddays = this.disabledDays ? this.disabledDays.join("") : false;
+ var ddaysText = this.disabledDaysText;
+ var format = this.format;
+
+ var setCellClass = function(cal, cell){
+ cell.row = 0;
+ cell.events = [];
+ cell.more = [];
+ //Roo.log('set Cell Class');
+ cell.title = "";
+ var t = d.getTime();
- if (prevMonth.getUTCDay() === this.weekStart) {
- if(fillMonths){
- this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
- }
-
- fillMonths = {
- tag: 'tr',
- cn: []
- };
-
- if(this.calendarWeeks){
- // ISO 8601: First week contains first thursday.
- // ISO also states week starts on Monday, but we can be more abstract here.
- var
- // Start of current week: based on weekstart/current date
- ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
- // Thursday of this week
- th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
- // First Thursday of year, year from thursday
- yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
- // Calendar week: ms between thursdays, div ms per day, div 7 days
- calWeek = (th - yth) / 864e5 / 7 + 1;
-
- fillMonths.cn.push({
- tag: 'td',
- cls: 'cw',
- html: calWeek
- });
- }
- }
+ //Roo.log(d);
- if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
- clsName += ' old';
- } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
- clsName += ' new';
+ cell.dateValue = t;
+ if(t == today){
+ cell.className += " fc-today";
+ cell.className += " fc-state-highlight";
+ cell.title = cal.todayText;
}
- if (this.todayHighlight &&
- prevMonth.getUTCFullYear() == today.getFullYear() &&
- prevMonth.getUTCMonth() == today.getMonth() &&
- prevMonth.getUTCDate() == today.getDate()) {
- clsName += ' today';
+ if(t == sel){
+ // disable highlight in other month..
+ //cell.className += " fc-state-highlight";
+
}
-
- if (currentDate && prevMonth.valueOf() === currentDate) {
- clsName += ' active';
+ // disabling
+ if(t < min) {
+ cell.className = " fc-state-disabled";
+ cell.title = cal.minText;
+ return;
}
-
- if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
- this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
- clsName += ' disabled';
+ if(t > max) {
+ cell.className = " fc-state-disabled";
+ cell.title = cal.maxText;
+ return;
+ }
+ if(ddays){
+ if(ddays.indexOf(d.getDay()) != -1){
+ cell.title = ddaysText;
+ cell.className = " fc-state-disabled";
+ }
}
-
- fillMonths.cn.push({
- tag: 'td',
- cls: 'day ' + clsName,
- html: prevMonth.getDate()
- })
-
- prevMonth.setDate(prevMonth.getDate()+1);
- }
-
- var currentYear = this.date && this.date.getUTCFullYear();
- var currentMonth = this.date && this.date.getUTCMonth();
-
- this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
-
- Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
- v.removeClass('active');
-
- if(currentYear === year && k === currentMonth){
- v.addClass('active');
+ if(ddMatch && format){
+ var fvalue = d.dateFormat(format);
+ if(ddMatch.test(fvalue)){
+ cell.title = ddText.replace("%0", fvalue);
+ cell.className = " fc-state-disabled";
+ }
}
- if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
- v.addClass('disabled');
+ if (!cell.initialClassName) {
+ cell.initialClassName = cell.dom.className;
}
- });
-
-
- year = parseInt(year/10, 10) * 10;
-
- this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
-
- this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
+ cell.dom.className = cell.initialClassName + ' ' + cell.className;
+ };
+
+ var i = 0;
- year -= 1;
- for (var i = -1; i < 11; i++) {
- this.picker().select('>.datepicker-years tbody td',true).first().createChild({
- tag: 'span',
- cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
- html: year
- })
+ for(; i < startingPos; i++) {
+ textEls[i].innerHTML = (++prevStart);
+ d.setDate(d.getDate()+1);
- year += 1;
- }
- },
-
- showMode: function(dir) {
- if (dir) {
- this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
+ cells[i].className = "fc-past fc-other-month";
+ setCellClass(this, cells[i]);
}
- Roo.each(this.picker().select('>div',true).elements, function(v){
- v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- v.hide();
- });
- this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
- },
-
- place: function()
- {
- if(this.isInline) return;
- this.picker().removeClass(['bottom', 'top']);
+ var intDay = 0;
- if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
- /*
- * place to the top of element!
- *
- */
-
- this.picker().addClass('top');
- this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
+ for(; i < days; i++){
+ intDay = i - startingPos + 1;
+ textEls[i].innerHTML = (intDay);
+ d.setDate(d.getDate()+1);
- return;
+ cells[i].className = ''; // "x-date-active";
+ setCellClass(this, cells[i]);
}
+ var extraDays = 0;
- this.picker().addClass('bottom');
-
- this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
- },
-
- parseDate : function(value){
- if(!value || value instanceof Date){
- return value;
- }
- var v = Date.parseDate(value, this.format);
- if (!v && this.useIso) {
- v = Date.parseDate(value, 'Y-m-d');
- }
- if(!v && this.altFormats){
- if(!this.altFormatsArray){
- this.altFormatsArray = this.altFormats.split("|");
- }
- for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
- v = Date.parseDate(value, this.altFormatsArray[i]);
- }
+ for(; i < 42; i++) {
+ textEls[i].innerHTML = (++extraDays);
+ d.setDate(d.getDate()+1);
+
+ cells[i].className = "fc-future fc-other-month";
+ setCellClass(this, cells[i]);
}
- return v;
- },
-
- formatDate : function(date, fmt){
- return (!date || !(date instanceof Date)) ?
- date : date.dateFormat(fmt || this.format);
- },
-
- onFocus : function()
- {
- Roo.bootstrap.DateField.superclass.onFocus.call(this);
- this.show();
- },
-
- onBlur : function()
- {
- Roo.bootstrap.DateField.superclass.onBlur.call(this);
- this.hide();
- },
-
- show : function()
- {
- this.picker().show();
- this.update();
- this.place();
-
- this.fireEvent('show', this, this.date);
- },
-
- hide : function()
- {
- if(this.isInline) return;
- this.picker().hide();
- this.viewMode = this.startViewMode;
- this.showMode();
-
- this.fireEvent('hide', this, this.date);
- },
-
- onMousedown: function(e){
- e.stopPropagation();
- e.preventDefault();
- },
-
- keyup: function(e){
- Roo.bootstrap.DateField.superclass.keyup.call(this);
- this.update();
+ this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
- },
-
- setValue: function(v){
- Roo.bootstrap.DateField.superclass.setValue.call(this, v);
+ var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
- this.fireEvent('select', this, this.date);
+ this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
+ this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
- },
-
- fireKey: function(e){
- if (!this.picker().isVisible()){
- if (e.keyCode == 27) // allow escape to hide and re-show picker
- this.show();
- return;
- }
- var dateChanged = false,
- dir, day, month,
- newDate, newViewDate;
- switch(e.keyCode){
- case 27: // escape
- this.hide();
- e.preventDefault();
- break;
- case 37: // left
- case 39: // right
- if (!this.keyboardNavigation) break;
- dir = e.keyCode == 37 ? -1 : 1;
-
- if (e.ctrlKey){
- newDate = this.moveYear(this.date, dir);
- newViewDate = this.moveYear(this.viewDate, dir);
- } else if (e.shiftKey){
- newDate = this.moveMonth(this.date, dir);
- newViewDate = this.moveMonth(this.viewDate, dir);
- } else {
- newDate = new Date(this.date);
- newDate.setUTCDate(this.date.getUTCDate() + dir);
- newViewDate = new Date(this.viewDate);
- newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
- }
- if (this.dateWithinRange(newDate)){
- this.date = newDate;
- this.viewDate = newViewDate;
- this.setValue(this.formatDate(this.date));
- this.update();
- e.preventDefault();
- dateChanged = true;
- }
- break;
- case 38: // up
- case 40: // down
- if (!this.keyboardNavigation) break;
- dir = e.keyCode == 38 ? -1 : 1;
- if (e.ctrlKey){
- newDate = this.moveYear(this.date, dir);
- newViewDate = this.moveYear(this.viewDate, dir);
- } else if (e.shiftKey){
- newDate = this.moveMonth(this.date, dir);
- newViewDate = this.moveMonth(this.viewDate, dir);
- } else {
- newDate = new Date(this.date);
- newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
- newViewDate = new Date(this.viewDate);
- newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
- }
- if (this.dateWithinRange(newDate)){
- this.date = newDate;
- this.viewDate = newViewDate;
- this.setValue(this.formatDate(this.date));
- this.update();
- e.preventDefault();
- dateChanged = true;
- }
- break;
- case 13: // enter
- this.setValue(this.formatDate(this.date));
- this.hide();
- e.preventDefault();
- break;
- case 9: // tab
- this.setValue(this.formatDate(this.date));
- this.hide();
- break;
+ if(totalRows != 6){
+ this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
+ this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
}
- },
-
-
- onClick: function(e) {
- e.stopPropagation();
- e.preventDefault();
- var target = e.getTarget();
+ this.fireEvent('monthchange', this, date);
- if(target.nodeName.toLowerCase() === 'i'){
- target = Roo.get(target).dom.parentNode;
+
+ /*
+ if(!this.internalRender){
+ var main = this.el.dom.firstChild;
+ var w = main.offsetWidth;
+ this.el.setWidth(w + this.el.getBorderWidth("lr"));
+ Roo.fly(main).setWidth(w);
+ this.internalRender = true;
+ // opera does not respect the auto grow header center column
+ // then, after it gets a width opera refuses to recalculate
+ // without a second pass
+ if(Roo.isOpera && !this.secondPass){
+ main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
+ this.secondPass = true;
+ this.update.defer(10, this, [date]);
+ }
}
+ */
- var nodeName = target.nodeName;
- var className = target.className;
- var html = target.innerHTML;
+ },
+
+ findCell : function(dt) {
+ dt = dt.clearTime().getTime();
+ var ret = false;
+ this.cells.each(function(c){
+ //Roo.log("check " +c.dateValue + '?=' + dt);
+ if(c.dateValue == dt){
+ ret = c;
+ return false;
+ }
+ return true;
+ });
- switch(nodeName.toLowerCase()) {
- case 'th':
- switch(className) {
- case 'switch':
- this.showMode(1);
- break;
- case 'prev':
- case 'next':
- var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
- switch(this.viewMode){
- case 0:
- this.viewDate = this.moveMonth(this.viewDate, dir);
- break;
- case 1:
- case 2:
- this.viewDate = this.moveYear(this.viewDate, dir);
- break;
- }
- this.fill();
- break;
- case 'today':
- var date = new Date();
- this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
- this.fill()
- this.setValue(this.formatDate(this.date));
- this.hide();
- break;
- }
- break;
- case 'span':
- if (className.indexOf('disabled') === -1) {
- this.viewDate.setUTCDate(1);
- if (className.indexOf('month') !== -1) {
- this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
- } else {
- var year = parseInt(html, 10) || 0;
- this.viewDate.setUTCFullYear(year);
-
- }
- this.showMode(-1);
- this.fill();
- }
- break;
-
- case 'td':
- if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
- var day = parseInt(html, 10) || 1;
- var year = this.viewDate.getUTCFullYear(),
- month = this.viewDate.getUTCMonth();
-
- if (className.indexOf('old') !== -1) {
- if(month === 0 ){
- month = 11;
- year -= 1;
- }else{
- month -= 1;
- }
- } else if (className.indexOf('new') !== -1) {
- if (month == 11) {
- month = 0;
- year += 1;
- } else {
- month += 1;
- }
- }
- this.date = this.UTCDate(year, month, day,0,0,0,0);
- this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
- this.fill();
- this.setValue(this.formatDate(this.date));
- this.hide();
- }
- break;
- }
+ return ret;
},
- setStartDate: function(startDate){
- this.startDate = startDate || -Infinity;
- if (this.startDate !== -Infinity) {
- this.startDate = this.parseDate(this.startDate);
- }
- this.update();
- this.updateNavArrows();
+ findCells : function(ev) {
+ var s = ev.start.clone().clearTime().getTime();
+ // Roo.log(s);
+ var e= ev.end.clone().clearTime().getTime();
+ // Roo.log(e);
+ var ret = [];
+ this.cells.each(function(c){
+ ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
+
+ if(c.dateValue > e){
+ return ;
+ }
+ if(c.dateValue < s){
+ return ;
+ }
+ ret.push(c);
+ });
+
+ return ret;
},
-
- setEndDate: function(endDate){
- this.endDate = endDate || Infinity;
- if (this.endDate !== Infinity) {
- this.endDate = this.parseDate(this.endDate);
+
+// findBestRow: function(cells)
+// {
+// var ret = 0;
+//
+// for (var i =0 ; i < cells.length;i++) {
+// ret = Math.max(cells[i].rows || 0,ret);
+// }
+// return ret;
+//
+// },
+
+
+ addItem : function(ev)
+ {
+ // look for vertical location slot in
+ var cells = this.findCells(ev);
+
+// ev.row = this.findBestRow(cells);
+
+ // work out the location.
+
+ var crow = false;
+ var rows = [];
+ for(var i =0; i < cells.length; i++) {
+
+ cells[i].row = cells[0].row;
+
+ if(i == 0){
+ cells[i].row = cells[i].row + 1;
+ }
+
+ if (!crow) {
+ crow = {
+ start : cells[i],
+ end : cells[i]
+ };
+ continue;
+ }
+ if (crow.start.getY() == cells[i].getY()) {
+ // on same row.
+ crow.end = cells[i];
+ continue;
+ }
+ // different row.
+ rows.push(crow);
+ crow = {
+ start: cells[i],
+ end : cells[i]
+ };
+
}
- this.update();
- this.updateNavArrows();
+
+ rows.push(crow);
+ ev.els = [];
+ ev.rows = rows;
+ ev.cells = cells;
+
+ cells[0].events.push(ev);
+
+ this.calevents.push(ev);
},
- setDaysOfWeekDisabled: function(daysOfWeekDisabled){
- this.daysOfWeekDisabled = daysOfWeekDisabled || [];
- if (typeof(this.daysOfWeekDisabled) !== 'object') {
- this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
+ clearEvents: function() {
+
+ if(!this.calevents){
+ return;
}
- this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
- return parseInt(d, 10);
+
+ Roo.each(this.cells.elements, function(c){
+ c.row = 0;
+ c.events = [];
+ c.more = [];
});
- this.update();
- this.updateNavArrows();
+
+ Roo.each(this.calevents, function(e) {
+ Roo.each(e.els, function(el) {
+ el.un('mouseenter' ,this.onEventEnter, this);
+ el.un('mouseleave' ,this.onEventLeave, this);
+ el.remove();
+ },this);
+ },this);
+
+ Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
+ e.remove();
+ });
+
},
- updateNavArrows: function() {
- var d = new Date(this.viewDate),
- year = d.getUTCFullYear(),
- month = d.getUTCMonth();
+ renderEvents: function()
+ {
+ var _this = this;
- Roo.each(this.picker().select('.prev', true).elements, function(v){
- v.show();
- switch (this.viewMode) {
- case 0:
-
- if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
- v.hide();
- }
- break;
- case 1:
- case 2:
- if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
- v.hide();
- }
- break;
+ this.cells.each(function(c) {
+
+ if(c.row < 5){
+ return;
+ }
+
+ var ev = c.events;
+
+ var r = 4;
+ if(c.row != c.events.length){
+ r = 4 - (4 - (c.row - c.events.length));
+ }
+
+ c.events = ev.slice(0, r);
+ c.more = ev.slice(r);
+
+ if(c.more.length && c.more.length == 1){
+ c.events.push(c.more.pop());
}
+
+ c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
+
});
-
- Roo.each(this.picker().select('.next', true).elements, function(v){
- v.show();
- switch (this.viewMode) {
- case 0:
+
+ this.cells.each(function(c) {
+
+ c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
+
+
+ for (var e = 0; e < c.events.length; e++){
+ var ev = c.events[e];
+ var rows = ev.rows;
+
+ for(var i = 0; i < rows.length; i++) {
+
+ // how many rows should it span..
- if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
- v.hide();
+ var cfg = {
+ cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
+ style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
+
+ unselectable : "on",
+ cn : [
+ {
+ cls: 'fc-event-inner',
+ cn : [
+ // {
+ // tag:'span',
+ // cls: 'fc-event-time',
+ // html : cells.length > 1 ? '' : ev.time
+ // },
+ {
+ tag:'span',
+ cls: 'fc-event-title',
+ html : String.format('{0}', ev.title)
+ }
+
+
+ ]
+ },
+ {
+ cls: 'ui-resizable-handle ui-resizable-e',
+ html : '  '
+ }
+
+ ]
+ };
+
+ if (i == 0) {
+ cfg.cls += ' fc-event-start';
}
- break;
- case 1:
- case 2:
- if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
- v.hide();
+ if ((i+1) == rows.length) {
+ cfg.cls += ' fc-event-end';
}
- break;
- }
- })
- },
-
- moveMonth: function(date, dir){
- if (!dir) return date;
- var new_date = new Date(date.valueOf()),
- day = new_date.getUTCDate(),
- month = new_date.getUTCMonth(),
- mag = Math.abs(dir),
- new_month, test;
- dir = dir > 0 ? 1 : -1;
- if (mag == 1){
- test = dir == -1
- // If going back one month, make sure month is not current month
- // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
- ? function(){
- return new_date.getUTCMonth() == month;
- }
- // If going forward one month, make sure month is as expected
- // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
- : function(){
- return new_date.getUTCMonth() != new_month;
- };
- new_month = month + dir;
- new_date.setUTCMonth(new_month);
- // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
- if (new_month < 0 || new_month > 11)
- new_month = (new_month + 12) % 12;
- } else {
- // For magnitudes >1, move one month at a time...
- for (var i=0; i<mag; i++)
- // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
- new_date = this.moveMonth(new_date, dir);
- // ...then reset the day, keeping it in the new month
- new_month = new_date.getUTCMonth();
- new_date.setUTCDate(day);
- test = function(){
- return new_month != new_date.getUTCMonth();
- };
- }
- // Common date-resetting loop -- if date is beyond end of month, make it
- // end of month
- while (test()){
- new_date.setUTCDate(--day);
- new_date.setUTCMonth(new_month);
- }
- return new_date;
- },
- moveYear: function(date, dir){
- return this.moveMonth(date, dir*12);
- },
+ var ctr = _this.el.select('.fc-event-container',true).first();
+ var cg = ctr.createChild(cfg);
- dateWithinRange: function(date){
- return date >= this.startDate && date <= this.endDate;
- },
+ var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
+ var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
-
- remove: function() {
- this.picker().remove();
- }
-
-});
+ var r = (c.more.length) ? 1 : 0;
+ cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
+ cg.setWidth(ebox.right - sbox.x -2);
-Roo.apply(Roo.bootstrap.DateField, {
-
- head : {
- tag: 'thead',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'th',
- cls: 'prev',
- html: '<i class="icon-arrow-left"/>'
- },
- {
- tag: 'th',
- cls: 'switch',
- colspan: '5'
- },
- {
- tag: 'th',
- cls: 'next',
- html: '<i class="icon-arrow-right"/>'
+ cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
+ cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
+ cg.on('click', _this.onEventClick, _this, ev);
+
+ ev.els.push(cg);
+
+ }
+
}
+
+
+ if(c.more.length){
+ var cfg = {
+ cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
+ style : 'position: absolute',
+ unselectable : "on",
+ cn : [
+ {
+ cls: 'fc-event-inner',
+ cn : [
+ {
+ tag:'span',
+ cls: 'fc-event-title',
+ html : 'More'
+ }
- ]
- }
- ]
- },
-
- content : {
- tag: 'tbody',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- colspan: '7'
+
+ ]
+ },
+ {
+ cls: 'ui-resizable-handle ui-resizable-e',
+ html : '  '
+ }
+
+ ]
+ };
+
+ var ctr = _this.el.select('.fc-event-container',true).first();
+ var cg = ctr.createChild(cfg);
+
+ var sbox = c.select('.fc-day-content',true).first().getBox();
+ var ebox = c.select('.fc-day-content',true).first().getBox();
+ //Roo.log(cg);
+ cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
+ cg.setWidth(ebox.right - sbox.x -2);
+
+ cg.on('click', _this.onMoreEventClick, _this, c.more);
+
}
- ]
- }
- ]
+
+ });
+
+
+
},
- footer : {
- tag: 'tfoot',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'th',
- colspan: '7',
- cls: 'today'
- }
-
- ]
- }
- ]
+ onEventEnter: function (e, el,event,d) {
+ this.fireEvent('evententer', this, el, event);
+ },
+
+ onEventLeave: function (e, el,event,d) {
+ this.fireEvent('eventleave', this, el, event);
},
- dates:{
- en: {
- days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
- daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
- daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
- months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
- monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
- today: "Today"
- }
+ onEventClick: function (e, el,event,d) {
+ this.fireEvent('eventclick', this, el, event);
},
- modes: [
- {
- clsName: 'days',
- navFnc: 'Month',
- navStep: 1
+ onMonthChange: function () {
+ this.store.load();
},
+
+ onMoreEventClick: function(e, el, more)
{
- clsName: 'months',
- navFnc: 'FullYear',
- navStep: 1
+ var _this = this;
+
+ this.calpopover.placement = 'right';
+ this.calpopover.setTitle('More');
+
+ this.calpopover.setContent('');
+
+ var ctr = this.calpopover.el.select('.popover-content', true).first();
+
+ Roo.each(more, function(m){
+ var cfg = {
+ cls : 'fc-event-hori fc-event-draggable',
+ html : m.title
+ }
+ var cg = ctr.createChild(cfg);
+
+ cg.on('click', _this.onEventClick, _this, m);
+ });
+
+ this.calpopover.show(el);
+
+
+ },
+
+ onLoad: function ()
+ {
+ this.calevents = [];
+ var cal = this;
+
+ if(this.store.getCount() > 0){
+ this.store.data.each(function(d){
+ cal.addItem({
+ id : d.data.id,
+ start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
+ end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
+ time : d.data.start_time,
+ title : d.data.title,
+ description : d.data.description,
+ venue : d.data.venue
+ });
+ });
+ }
+
+ this.renderEvents();
+
+ if(this.calevents.length && this.loadMask){
+ this.maskEl.hide();
+ }
},
+
+ onBeforeLoad: function()
{
- clsName: 'years',
- navFnc: 'FullYear',
- navStep: 10
- }]
-});
-
-Roo.apply(Roo.bootstrap.DateField, {
-
- template : {
- tag: 'div',
- cls: 'datepicker dropdown-menu',
- cn: [
- {
- tag: 'div',
- cls: 'datepicker-days',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.DateField.head,
- {
- tag: 'tbody'
- },
- Roo.bootstrap.DateField.footer
- ]
- }
- ]
- },
- {
- tag: 'div',
- cls: 'datepicker-months',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.DateField.head,
- Roo.bootstrap.DateField.content,
- Roo.bootstrap.DateField.footer
- ]
- }
- ]
- },
- {
- tag: 'div',
- cls: 'datepicker-years',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.DateField.head,
- Roo.bootstrap.DateField.content,
- Roo.bootstrap.DateField.footer
- ]
- }
- ]
+ this.clearEvents();
+ if(this.loadMask){
+ this.maskEl.show();
}
- ]
}
});
-
/*
* - LGPL
*
- * TimeField
+ * element
*
*/
/**
- * @class Roo.bootstrap.TimeField
- * @extends Roo.bootstrap.Input
- * Bootstrap DateField class
- *
+ * @class Roo.bootstrap.Popover
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Popover class
+ * @cfg {String} html contents of the popover (or false to use children..)
+ * @cfg {String} title of popover (or false to hide)
+ * @cfg {String} placement how it is placed
+ * @cfg {String} trigger click || hover (or false to trigger manually)
+ * @cfg {String} over what (parent or false to trigger manually.)
*
* @constructor
- * Create a new TimeField
+ * Create a new Popover
* @param {Object} config The config object
*/
-Roo.bootstrap.TimeField = function(config){
- Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event show
- * Fires when this field show.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- show : true,
- /**
- * @event show
- * Fires when this field hide.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- hide : true,
- /**
- * @event select
- * Fires when select a date.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- select : true
- });
+Roo.bootstrap.Popover = function(config){
+ Roo.bootstrap.Popover.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
-
- /**
- * @cfg {String} format
- * The default time format string which can be overriden for localization support. The format must be
- * valid according to {@link Date#parseDate} (defaults to 'H:i').
- */
- format : "H:i",
-
- onRender: function(ct, position)
- {
-
- Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
-
- this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
-
- this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-
- this.pop = this.picker().select('>.datepicker-time',true).first();
- this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
-
- this.picker().on('mousedown', this.onMousedown, this);
- this.picker().on('click', this.onClick, this);
-
- this.picker().addClass('datepicker-dropdown');
+Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
- this.fillTime();
- this.update();
-
- this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
- this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
- this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
- this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
- this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
- this.pop.select('button.ok', true).first().on('click', this.setTime, this);
-
- },
+ title: 'Fill in a title',
+ html: false,
- fireKey: function(e){
- if (!this.picker().isVisible()){
- if (e.keyCode == 27) // allow escape to hide and re-show picker
- this.show();
- return;
- }
-
- e.preventDefault();
-
- switch(e.keyCode){
- case 27: // escape
- this.hide();
- break;
- case 37: // left
- case 39: // right
- this.onTogglePeriod();
- break;
- case 38: // up
- this.onIncrementMinutes();
- break;
- case 40: // down
- this.onDecrementMinutes();
- break;
- case 13: // enter
- case 9: // tab
- this.setTime();
- break;
- }
- },
+ placement : 'right',
+ trigger : 'hover', // hover
- onClick: function(e) {
- e.stopPropagation();
- e.preventDefault();
- },
+ over: 'parent',
- picker : function()
+ can_build_overlaid : false,
+
+ getChildContainer : function()
{
- return this.el.select('.datepicker', true).first();
+ return this.el.select('.popover-content',true).first();
},
- fillTime: function()
- {
- var time = this.pop.select('tbody', true).first();
-
- time.dom.innerHTML = '';
-
- time.createChild({
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'hours-up glyphicon glyphicon-chevron-up'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- },
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'minutes-up glyphicon glyphicon-chevron-up'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- }
- ]
- });
-
- time.createChild({
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- cn: [
- {
- tag: 'span',
- cls: 'timepicker-hour',
- html: '00'
- }
- ]
- },
+ getAutoCreate : function(){
+ Roo.log('make popover?');
+ var cfg = {
+ cls : 'popover roo-dynamic',
+ style: 'display:block',
+ cn : [
{
- tag: 'td',
- cls: 'separator',
- html: ':'
+ cls : 'arrow'
},
{
- tag: 'td',
- cn: [
+ cls : 'popover-inner',
+ cn : [
{
- tag: 'span',
- cls: 'timepicker-minute',
- html: '00'
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- },
- {
- tag: 'td',
- cn: [
+ tag: 'h3',
+ cls: 'popover-title',
+ html : this.title
+ },
{
- tag: 'button',
- type: 'button',
- cls: 'btn btn-primary period',
- html: 'AM'
-
+ cls : 'popover-content',
+ html : this.html
}
]
+
}
- ]
- });
+ ]
+ };
- time.createChild({
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'hours-down glyphicon glyphicon-chevron-down'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- },
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'minutes-down glyphicon glyphicon-chevron-down'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- }
- ]
- });
+ return cfg;
+ },
+ setTitle: function(str)
+ {
+ this.el.select('.popover-title',true).first().dom.innerHTML = str;
+ },
+ setContent: function(str)
+ {
+ this.el.select('.popover-content',true).first().dom.innerHTML = str;
+ },
+ // as it get's added to the bottom of the page.
+ onRender : function(ct, position)
+ {
+ Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
+ if(!this.el){
+ var cfg = Roo.apply({}, this.getAutoCreate());
+ cfg.id = Roo.id();
+
+ if (this.cls) {
+ cfg.cls += ' ' + this.cls;
+ }
+ if (this.style) {
+ cfg.style = this.style;
+ }
+ Roo.log("adding to ")
+ this.el = Roo.get(document.body).createChild(cfg, position);
+ Roo.log(this.el);
+ }
+ this.initEvents();
+ },
+
+ initEvents : function()
+ {
+ this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
+ this.el.enableDisplayMode('block');
+ this.el.hide();
+ if (this.over === false) {
+ return;
+ }
+ if (this.triggers === false) {
+ return;
+ }
+ var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
+ var triggers = this.trigger ? this.trigger.split(' ') : [];
+ Roo.each(triggers, function(trigger) {
+
+ if (trigger == 'click') {
+ on_el.on('click', this.toggle, this);
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
+
+ on_el.on(eventIn ,this.enter, this);
+ on_el.on(eventOut, this.leave, this);
+ }
+ }, this);
},
- update: function()
+
+ // private
+ timeout : null,
+ hoverState : null,
+
+ toggle : function () {
+ this.hoverState == 'in' ? this.leave() : this.enter();
+ },
+
+ enter : function () {
+
+
+ clearTimeout(this.timeout);
+
+ this.hoverState = 'in'
+
+ if (!this.delay || !this.delay.show) {
+ this.show();
+ return
+ }
+ var _t = this;
+ this.timeout = setTimeout(function () {
+ if (_t.hoverState == 'in') {
+ _t.show();
+ }
+ }, this.delay.show)
+ },
+ leave : function() {
+ clearTimeout(this.timeout);
+
+ this.hoverState = 'out'
+
+ if (!this.delay || !this.delay.hide) {
+ this.hide();
+ return
+ }
+ var _t = this;
+ this.timeout = setTimeout(function () {
+ if (_t.hoverState == 'out') {
+ _t.hide();
+ }
+ }, this.delay.hide)
+ },
+
+ show : function (on_el)
{
+ if (!on_el) {
+ on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
+ }
+ // set content.
+ this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
+ if (this.html !== false) {
+ this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
+ }
+ this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
+ if (!this.title.length) {
+ this.el.select('.popover-title',true).hide();
+ }
- this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
+ var placement = typeof this.placement == 'function' ?
+ this.placement.call(this, this.el, on_el) :
+ this.placement;
+
+ var autoToken = /\s?auto?\s?/i;
+ var autoPlace = autoToken.test(placement);
+ if (autoPlace) {
+ placement = placement.replace(autoToken, '') || 'top';
+ }
+
+ //this.el.detach()
+ //this.el.setXY([0,0]);
+ this.el.show();
+ this.el.dom.style.display='block';
+ this.el.addClass(placement);
+
+ //this.el.appendTo(on_el);
+
+ var p = this.getPosition();
+ var box = this.el.getBox();
+
+ if (autoPlace) {
+ // fixme..
+ }
+ var align = Roo.bootstrap.Popover.alignment[placement]
+ this.el.alignTo(on_el, align[0],align[1]);
+ //var arrow = this.el.select('.arrow',true).first();
+ //arrow.set(align[2],
+
+ this.el.addClass('in');
+ this.hoverState = null;
+
+ if (this.el.hasClass('fade')) {
+ // fade it?
+ }
- this.fill();
},
+ hide : function()
+ {
+ this.el.setXY([0,0]);
+ this.el.removeClass('in');
+ this.el.hide();
+
+ }
- fill: function()
+});
+
+Roo.bootstrap.Popover.alignment = {
+ 'left' : ['r-l', [-10,0], 'right'],
+ 'right' : ['l-r', [10,0], 'left'],
+ 'bottom' : ['t-b', [0,10], 'top'],
+ 'top' : [ 'b-t', [0,-10], 'bottom']
+};
+
+ /*
+ * - LGPL
+ *
+ * Progress
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.Progress
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Progress class
+ * @cfg {Boolean} striped striped of the progress bar
+ * @cfg {Boolean} active animated of the progress bar
+ *
+ *
+ * @constructor
+ * Create a new Progress
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Progress = function(config){
+ Roo.bootstrap.Progress.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
+
+ striped : false,
+ active: false,
+
+ getAutoCreate : function(){
+ var cfg = {
+ tag: 'div',
+ cls: 'progress'
+ };
+
+
+ if(this.striped){
+ cfg.cls += ' progress-striped';
+ }
+
+ if(this.active){
+ cfg.cls += ' active';
+ }
+
+
+ return cfg;
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * ProgressBar
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.ProgressBar
+ * @extends Roo.bootstrap.Component
+ * Bootstrap ProgressBar class
+ * @cfg {Number} aria_valuenow aria-value now
+ * @cfg {Number} aria_valuemin aria-value min
+ * @cfg {Number} aria_valuemax aria-value max
+ * @cfg {String} label label for the progress bar
+ * @cfg {String} panel (success | info | warning | danger )
+ * @cfg {String} role role of the progress bar
+ * @cfg {String} sr_only text
+ *
+ *
+ * @constructor
+ * Create a new ProgressBar
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.ProgressBar = function(config){
+ Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
+
+ aria_valuenow : 0,
+ aria_valuemin : 0,
+ aria_valuemax : 100,
+ label : false,
+ panel : false,
+ role : false,
+ sr_only: false,
+
+ getAutoCreate : function()
{
- var hours = this.time.getHours();
- var minutes = this.time.getMinutes();
- var period = 'AM';
- if(hours > 11){
- period = 'PM';
+ var cfg = {
+ tag: 'div',
+ cls: 'progress-bar',
+ style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
+ };
+
+ if(this.sr_only){
+ cfg.cn = {
+ tag: 'span',
+ cls: 'sr-only',
+ html: this.sr_only
+ }
}
- if(hours == 0){
- hours = 12;
+ if(this.role){
+ cfg.role = this.role;
}
+ if(this.aria_valuenow){
+ cfg['aria-valuenow'] = this.aria_valuenow;
+ }
- if(hours > 12){
- hours = hours - 12;
+ if(this.aria_valuemin){
+ cfg['aria-valuemin'] = this.aria_valuemin;
}
- if(hours < 10){
- hours = '0' + hours;
+ if(this.aria_valuemax){
+ cfg['aria-valuemax'] = this.aria_valuemax;
}
- if(minutes < 10){
- minutes = '0' + minutes;
+ if(this.label && !this.sr_only){
+ cfg.html = this.label;
}
- this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
- this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
- this.pop.select('button', true).first().dom.innerHTML = period;
+ if(this.panel){
+ cfg.cls += ' progress-bar-' + this.panel;
+ }
+ return cfg;
},
- place: function()
- {
- this.picker().removeClass(['bottom', 'top']);
+ update : function(aria_valuenow)
+ {
+ this.aria_valuenow = aria_valuenow;
+
+ this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * TabPanel
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.TabPanel
+ * @extends Roo.bootstrap.Component
+ * Bootstrap TabPanel class
+ * @cfg {Boolean} active panel active
+ * @cfg {String} html panel content
+ * @cfg {String} tabId tab relate id
+ * @cfg {String} navId The navbar which triggers show hide
+ *
+ *
+ * @constructor
+ * Create a new TabPanel
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.TabPanel = function(config){
+ Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event changed
+ * Fires when the active status changes
+ * @param {Roo.bootstrap.TabPanel} this
+ * @param {Boolean} state the new state
+
+ */
+ 'changed': true
+ });
+};
+
+Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
+
+ active: false,
+ html: false,
+ tabId: false,
+ navId : false,
+
+ getAutoCreate : function(){
+ var cfg = {
+ tag: 'div',
+ cls: 'tab-pane',
+ html: this.html || ''
+ };
- if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
- /*
- * place to the top of element!
- *
- */
-
- this.picker().addClass('top');
- this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
-
- return;
+ if(this.active){
+ cfg.cls += ' active';
}
- this.picker().addClass('bottom');
-
- this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
- },
-
- onFocus : function()
- {
- Roo.bootstrap.TimeField.superclass.onFocus.call(this);
- this.show();
- },
-
- onBlur : function()
- {
- Roo.bootstrap.TimeField.superclass.onBlur.call(this);
- this.hide();
- },
-
- show : function()
- {
- this.picker().show();
- this.pop.show();
- this.update();
- this.place();
-
- this.fireEvent('show', this, this.date);
- },
-
- hide : function()
- {
- this.picker().hide();
- this.pop.hide();
+ if(this.tabId){
+ cfg.tabId = this.tabId;
+ }
- this.fireEvent('hide', this, this.date);
+ return cfg;
},
-
- setTime : function()
+ onRender : function(ct, position)
{
- this.hide();
- this.setValue(this.time.format(this.format));
+ // Roo.log("Call onRender: " + this.xtype);
- this.fireEvent('select', this, this.date);
+ Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
+ if (this.navId && this.tabId) {
+ var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
+ if (!item) {
+ Roo.log("could not find navID:" + this.navId + ", tabId: " + this.tabId);
+ } else {
+ item.on('changed', function(item, state) {
+ this.setActive(state);
+ }, this);
+ }
+ }
},
-
- onMousedown: function(e){
- e.stopPropagation();
- e.preventDefault();
- },
-
- onIncrementHours: function()
+ setActive: function(state)
{
- Roo.log('onIncrementHours');
- this.time = this.time.add(Date.HOUR, 1);
- this.update();
+ Roo.log("panel - set active " + this.tabId + "=" + state);
- },
-
- onDecrementHours: function()
- {
- Roo.log('onDecrementHours');
- this.time = this.time.add(Date.HOUR, -1);
- this.update();
- },
-
- onIncrementMinutes: function()
- {
- Roo.log('onIncrementMinutes');
- this.time = this.time.add(Date.MINUTE, 1);
- this.update();
- },
-
- onDecrementMinutes: function()
- {
- Roo.log('onDecrementMinutes');
- this.time = this.time.add(Date.MINUTE, -1);
- this.update();
- },
-
- onTogglePeriod: function()
- {
- Roo.log('onTogglePeriod');
- this.time = this.time.add(Date.HOUR, 12);
- this.update();
+ this.active = state;
+ if (!state) {
+ this.el.removeClass('active');
+
+ } else if (!this.el.hasClass('active')) {
+ this.el.addClass('active');
+ }
+ this.fireEvent('changed', this, state);
}
-
-});
-
-Roo.apply(Roo.bootstrap.TimeField, {
-
- content : {
- tag: 'tbody',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- colspan: '7'
- }
- ]
- }
- ]
- },
- footer : {
- tag: 'tfoot',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'th',
- colspan: '7',
- cls: '',
- cn: [
- {
- tag: 'button',
- cls: 'btn btn-info ok',
- html: 'OK'
- }
- ]
- }
-
- ]
- }
- ]
- }
-});
-
-Roo.apply(Roo.bootstrap.TimeField, {
-
- template : {
- tag: 'div',
- cls: 'datepicker dropdown-menu',
- cn: [
- {
- tag: 'div',
- cls: 'datepicker-time',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.TimeField.content,
- Roo.bootstrap.TimeField.footer
- ]
- }
- ]
- }
- ]
- }
});
+
/*
* - LGPL
*
- * CheckBox
+ * DateField
*
*/
/**
- * @class Roo.bootstrap.CheckBox
+ * @class Roo.bootstrap.DateField
* @extends Roo.bootstrap.Input
- * Bootstrap CheckBox class
- *
- * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
- * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
- * @cfg {String} boxLabel The text that appears beside the checkbox
- * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
- * @cfg {Boolean} checked initnal the element
+ * Bootstrap DateField class
+ * @cfg {Number} weekStart default 0
+ * @cfg {Number} weekStart default 0
+ * @cfg {Number} viewMode default empty, (months|years)
+ * @cfg {Number} minViewMode default empty, (months|years)
+ * @cfg {Number} startDate default -Infinity
+ * @cfg {Number} endDate default Infinity
+ * @cfg {Boolean} todayHighlight default false
+ * @cfg {Boolean} todayBtn default false
+ * @cfg {Boolean} calendarWeeks default false
+ * @cfg {Object} daysOfWeekDisabled default empty
*
+ * @cfg {Boolean} keyboardNavigation default true
+ * @cfg {String} language default en
*
* @constructor
- * Create a new CheckBox
+ * Create a new DateField
* @param {Object} config The config object
*/
-Roo.bootstrap.CheckBox = function(config){
- Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
-
- this.addEvents({
+Roo.bootstrap.DateField = function(config){
+ Roo.bootstrap.DateField.superclass.constructor.call(this, config);
+ this.addEvents({
/**
- * @event check
- * Fires when the element is checked or unchecked.
- * @param {Roo.bootstrap.CheckBox} this This input
- * @param {Boolean} checked The new checked value
- */
- check : true
+ * @event show
+ * Fires when this field show.
+ * @param {Roo.bootstrap.DateField} this
+ * @param {Mixed} date The date value
+ */
+ show : true,
+ /**
+ * @event show
+ * Fires when this field hide.
+ * @param {Roo.bootstrap.DateField} this
+ * @param {Mixed} date The date value
+ */
+ hide : true,
+ /**
+ * @event select
+ * Fires when select a date.
+ * @param {Roo.bootstrap.DateField} this
+ * @param {Mixed} date The date value
+ */
+ select : true
});
};
-Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
+Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
- inputType: 'checkbox',
- inputValue: 1,
- valueOff: 0,
- boxLabel: false,
- checked: false,
- weight : false,
+ /**
+ * @cfg {String} format
+ * The default date format string which can be overriden for localization support. The format must be
+ * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
+ */
+ format : "m/d/y",
+ /**
+ * @cfg {String} altFormats
+ * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
+ * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
+ */
+ altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
- getAutoCreate : function()
+ weekStart : 0,
+
+ viewMode : '',
+
+ minViewMode : '',
+
+ todayHighlight : false,
+
+ todayBtn: false,
+
+ language: 'en',
+
+ keyboardNavigation: true,
+
+ calendarWeeks: false,
+
+ startDate: -Infinity,
+
+ endDate: Infinity,
+
+ daysOfWeekDisabled: [],
+
+ _events: [],
+
+ UTCDate: function()
{
- var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
-
- var id = Roo.id();
-
- var cfg = {};
-
- cfg.cls = 'form-group checkbox' //input-group
-
-
-
-
- var input = {
- tag: 'input',
- id : id,
- type : this.inputType,
- value : (!this.checked) ? this.valueOff : this.inputValue,
- cls : 'roo-checkbox', //'form-box',
- placeholder : this.placeholder || ''
-
- };
+ return new Date(Date.UTC.apply(Date, arguments));
+ },
+
+ UTCToday: function()
+ {
+ var today = new Date();
+ return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
+ },
+
+ getDate: function() {
+ var d = this.getUTCDate();
+ return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
+ },
+
+ getUTCDate: function() {
+ return this.date;
+ },
+
+ setDate: function(d) {
+ this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
+ },
+
+ setUTCDate: function(d) {
+ this.date = d;
+ this.setValue(this.formatDate(this.date));
+ },
- if (this.weight) { // Validity check?
- cfg.cls += " checkbox-" + this.weight;
- }
+ onRender: function(ct, position)
+ {
- if (this.disabled) {
- input.disabled=true;
- }
+ Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
- if(this.checked){
- input.checked = this.checked;
- }
+ this.language = this.language || 'en';
+ this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
+ this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
- if (this.name) {
- input.name = this.name;
- }
+ this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
+ this.format = this.format || 'm/d/y';
+ this.isInline = false;
+ this.isInput = true;
+ this.component = this.el.select('.add-on', true).first() || false;
+ this.component = (this.component && this.component.length === 0) ? false : this.component;
+ this.hasInput = this.component && this.inputEL().length;
- if (this.size) {
- input.cls += ' input-' + this.size;
+ if (typeof(this.minViewMode === 'string')) {
+ switch (this.minViewMode) {
+ case 'months':
+ this.minViewMode = 1;
+ break;
+ case 'years':
+ this.minViewMode = 2;
+ break;
+ default:
+ this.minViewMode = 0;
+ break;
+ }
}
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
+ if (typeof(this.viewMode === 'string')) {
+ switch (this.viewMode) {
+ case 'months':
+ this.viewMode = 1;
+ break;
+ case 'years':
+ this.viewMode = 2;
+ break;
+ default:
+ this.viewMode = 0;
+ break;
}
- });
+ }
+
+ this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
-
+ this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- var inputblock = input;
+ this.picker().on('mousedown', this.onMousedown, this);
+ this.picker().on('click', this.onClick, this);
+ this.picker().addClass('datepicker-dropdown');
+ this.startViewMode = this.viewMode;
- if (this.before || this.after) {
-
- inputblock = {
- cls : 'input-group',
- cn : []
+ Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
+ if(!this.calendarWeeks){
+ v.remove();
+ return;
};
- if (this.before) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.before
- });
- }
- inputblock.cn.push(input);
- if (this.after) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.after
- });
- }
-
- };
-
- if (align ==='left' && this.fieldLabel.length) {
- Roo.log("left and has label");
- cfg.cn = [
-
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-md-' + this.labelWidth,
- html : this.fieldLabel
-
- },
- {
- cls : "col-md-" + (12 - this.labelWidth),
- cn: [
- inputblock
- ]
- }
-
- ];
- } else if ( this.fieldLabel.length) {
- Roo.log(" label");
- cfg.cn = [
-
- {
- tag: this.boxLabel ? 'span' : 'label',
- 'for': id,
- cls: 'control-label box-input-label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
-
- inputblock
-
- ];
-
- } else {
- Roo.log(" no label && no align");
- cfg.cn = [ inputblock ] ;
-
-
- };
- if(this.boxLabel){
- cfg.cn.push( {
- tag: 'label',
- 'for': id,
- cls: 'box-label',
- html: this.boxLabel
-
+ v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
+ v.attr('colspan', function(i, val){
+ return parseInt(val) + 1;
});
- }
+ })
+
+ this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
-
- return cfg;
+ this.setStartDate(this.startDate);
+ this.setEndDate(this.endDate);
+ this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
+
+ this.fillDow();
+ this.fillMonths();
+ this.update();
+ this.showMode();
+
+ if(this.isInline) {
+ this.show();
+ }
},
- /**
- * return the real input element.
- */
- inputEl: function ()
- {
- return this.el.select('input.roo-checkbox',true).first();
- },
-
- label: function()
+ picker : function()
{
- return this.el.select('label.control-label',true).first();
+ return this.el.select('.datepicker', true).first();
},
- initEvents : function()
+ fillDow: function()
{
-// Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
+ var dowCnt = this.weekStart;
- this.inputEl().on('click', this.onClick, this);
+ var dow = {
+ tag: 'tr',
+ cn: [
+
+ ]
+ };
+
+ if(this.calendarWeeks){
+ dow.cn.push({
+ tag: 'th',
+ cls: 'cw',
+ html: ' '
+ })
+ }
+
+ while (dowCnt < this.weekStart + 7) {
+ dow.cn.push({
+ tag: 'th',
+ cls: 'dow',
+ html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
+ });
+ }
+ this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
},
- onClick : function()
- {
- this.setChecked(!this.checked);
+ fillMonths: function()
+ {
+ var i = 0
+ var months = this.picker().select('>.datepicker-months td', true).first();
+
+ months.dom.innerHTML = '';
+
+ while (i < 12) {
+ var month = {
+ tag: 'span',
+ cls: 'month',
+ html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
+ }
+
+ months.createChild(month);
+ }
+
},
- setChecked : function(state,suppressEvent)
- {
- this.checked = state;
+ update: function(){
- this.inputEl().dom.checked = state;
+ this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
- if(suppressEvent !== true){
- this.fireEvent('check', this, state);
+ if (this.date < this.startDate) {
+ this.viewDate = new Date(this.startDate);
+ } else if (this.date > this.endDate) {
+ this.viewDate = new Date(this.endDate);
+ } else {
+ this.viewDate = new Date(this.date);
}
- this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
-
+ this.fill();
},
- setValue : function(v,suppressEvent)
- {
- this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
- }
-
-});
-
-
-/*
- * - LGPL
- *
- * Radio
- *
- */
-
-/**
- * @class Roo.bootstrap.Radio
- * @extends Roo.bootstrap.CheckBox
- * Bootstrap Radio class
-
- * @constructor
- * Create a new Radio
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Radio = function(config){
- Roo.bootstrap.Radio.superclass.constructor.call(this, config);
-
-};
-
-Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
-
- inputType: 'radio',
- inputValue: '',
- valueOff: '',
-
- getAutoCreate : function()
- {
- var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+ fill: function() {
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth(),
+ startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
+ startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
+ endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
+ endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
+ currentDate = this.date && this.date.valueOf(),
+ today = this.UTCToday();
- var id = Roo.id();
+ this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
- var cfg = {};
+// this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
- cfg.cls = 'form-group radio' //input-group
+// this.picker.select('>tfoot th.today').
+// .text(dates[this.language].today)
+// .toggle(this.todayBtn !== false);
+
+ this.updateNavArrows();
+ this.fillMonths();
+
+ var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
- var input = {
- tag: 'input',
- id : id,
- type : this.inputType,
- value : (!this.checked) ? this.valueOff : this.inputValue,
- cls : 'roo-radio',
- placeholder : this.placeholder || ''
-
- };
- if (this.weight) { // Validity check?
- cfg.cls += " radio-" + this.weight;
- }
- if (this.disabled) {
- input.disabled=true;
- }
+ day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
+
+ prevMonth.setUTCDate(day);
- if(this.checked){
- input.checked = this.checked;
- }
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
- if (this.name) {
- input.name = this.name;
- }
+ var nextMonth = new Date(prevMonth);
- if (this.size) {
- input.cls += ' input-' + this.size;
- }
+ nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
- }
- });
+ nextMonth = nextMonth.valueOf();
- var inputblock = input;
+ var fillMonths = false;
- if (this.before || this.after) {
-
- inputblock = {
- cls : 'input-group',
- cn : []
- };
- if (this.before) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.before
- });
- }
- inputblock.cn.push(input);
- if (this.after) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.after
- });
- }
-
- };
+ this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
- if (align ==='left' && this.fieldLabel.length) {
- Roo.log("left and has label");
- cfg.cn = [
-
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-md-' + this.labelWidth,
- html : this.fieldLabel
-
- },
- {
- cls : "col-md-" + (12 - this.labelWidth),
- cn: [
- inputblock
- ]
- }
-
- ];
- } else if ( this.fieldLabel.length) {
- Roo.log(" label");
- cfg.cn = [
-
- {
- tag: 'label',
- 'for': id,
- cls: 'control-label box-input-label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
-
- inputblock
-
- ];
-
- } else {
+ while(prevMonth.valueOf() < nextMonth) {
+ var clsName = '';
- Roo.log(" no label && no align");
- cfg.cn = [
+ if (prevMonth.getUTCDay() === this.weekStart) {
+ if(fillMonths){
+ this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
+ }
- inputblock
+ fillMonths = {
+ tag: 'tr',
+ cn: []
+ };
+
+ if(this.calendarWeeks){
+ // ISO 8601: First week contains first thursday.
+ // ISO also states week starts on Monday, but we can be more abstract here.
+ var
+ // Start of current week: based on weekstart/current date
+ ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
+ // Thursday of this week
+ th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
+ // First Thursday of year, year from thursday
+ yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
+ // Calendar week: ms between thursdays, div ms per day, div 7 days
+ calWeek = (th - yth) / 864e5 / 7 + 1;
- ];
-
-
- };
-
- if(this.boxLabel){
- cfg.cn.push({
- tag: 'label',
- 'for': id,
- cls: 'box-label',
- html: this.boxLabel
+ fillMonths.cn.push({
+ tag: 'td',
+ cls: 'cw',
+ html: calWeek
+ });
+ }
+ }
+
+ if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
+ clsName += ' old';
+ } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
+ clsName += ' new';
+ }
+ if (this.todayHighlight &&
+ prevMonth.getUTCFullYear() == today.getFullYear() &&
+ prevMonth.getUTCMonth() == today.getMonth() &&
+ prevMonth.getUTCDate() == today.getDate()) {
+ clsName += ' today';
+ }
+
+ if (currentDate && prevMonth.valueOf() === currentDate) {
+ clsName += ' active';
+ }
+
+ if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
+ this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
+ clsName += ' disabled';
+ }
+
+ fillMonths.cn.push({
+ tag: 'td',
+ cls: 'day ' + clsName,
+ html: prevMonth.getDate()
})
+
+ prevMonth.setDate(prevMonth.getDate()+1);
}
+
+ var currentYear = this.date && this.date.getUTCFullYear();
+ var currentMonth = this.date && this.date.getUTCMonth();
- return cfg;
+ this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
- },
- inputEl: function ()
- {
- return this.el.select('input.roo-radio',true).first();
- },
- onClick : function()
- {
- this.setChecked(true);
- },
-
- setChecked : function(state,suppressEvent)
- {
- if(state){
- Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
- v.dom.checked = false;
- });
- }
+ Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
+ v.removeClass('active');
+
+ if(currentYear === year && k === currentMonth){
+ v.addClass('active');
+ }
+
+ if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
+ v.addClass('disabled');
+ }
+
+ });
- this.checked = state;
- this.inputEl().dom.checked = state;
- if(suppressEvent !== true){
- this.fireEvent('check', this, state);
- }
+ year = parseInt(year/10, 10) * 10;
- this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
+ this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
- },
-
- getGroupValue : function()
- {
- var value = ''
- Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
- if(v.dom.checked == true){
- value = v.dom.value;
- }
- });
+ this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
- return value;
+ year -= 1;
+ for (var i = -1; i < 11; i++) {
+ this.picker().select('>.datepicker-years tbody td',true).first().createChild({
+ tag: 'span',
+ cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
+ html: year
+ })
+
+ year += 1;
+ }
},
- /**
- * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
- * @return {Mixed} value The field value
- */
- getValue : function(){
- return this.getGroupValue();
- }
-
-});
-
-
-//<script type="text/javascript">
-
-/*
- * Based Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- * LGPL
- *
- */
-
-/**
- * @class Roo.HtmlEditorCore
- * @extends Roo.Component
- * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
- *
- * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
- */
-
-Roo.HtmlEditorCore = function(config){
-
-
- Roo.HtmlEditorCore.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event initialize
- * Fires when the editor is fully initialized (including the iframe)
- * @param {Roo.HtmlEditorCore} this
- */
- initialize: true,
- /**
- * @event activate
- * Fires when the editor is first receives the focus. Any insertion must wait
- * until after this event.
- * @param {Roo.HtmlEditorCore} this
- */
- activate: true,
- /**
- * @event beforesync
- * Fires before the textarea is updated with content from the editor iframe. Return false
- * to cancel the sync.
- * @param {Roo.HtmlEditorCore} 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 {Roo.HtmlEditorCore} this
- * @param {String} html
- */
- beforepush: true,
- /**
- * @event sync
- * Fires when the textarea is updated with content from the editor iframe.
- * @param {Roo.HtmlEditorCore} this
- * @param {String} html
- */
- sync: true,
- /**
- * @event push
- * Fires when the iframe editor is updated with content from the textarea.
- * @param {Roo.HtmlEditorCore} this
- * @param {String} html
- */
- push: true,
-
- /**
- * @event editorevent
- * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
- * @param {Roo.HtmlEditorCore} this
- */
- editorevent: true
- });
-
-};
-
-
-Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
-
-
- /**
- * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
- */
-
- owner : false,
-
- /**
- * @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: 500,
-
- /**
- * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
- *
- */
- stylesheets: false,
-
- // id of frame..
- frameId: false,
-
- // private properties
- validationEvent : false,
- deferHeight: true,
- initialized : false,
- activated : false,
- sourceEditMode : false,
- onFocus : Roo.emptyFn,
- iframePad:3,
- hideMode:'offsets',
-
- clearUp: true,
-
-
+ showMode: function(dir) {
+ if (dir) {
+ this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
+ }
+ Roo.each(this.picker().select('>div',true).elements, function(v){
+ v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+ v.hide();
+ });
+ this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
+ },
-
- /**
- * Protected method that will not generally be called directly. It
- * is called when the editor initializes the iframe with HTML contents. Override this method if you
- * want to change the initialization markup of the iframe (e.g. to add stylesheets).
- */
- getDocMarkup : function(){
- // body styles..
- var st = '';
- Roo.log(this.stylesheets);
+ place: function()
+ {
+ if(this.isInline) return;
- // inherit styels from page...??
- if (this.stylesheets === false) {
-
- Roo.get(document.head).select('style').each(function(node) {
- st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
- });
-
- Roo.get(document.head).select('link').each(function(node) {
- st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
- });
+ this.picker().removeClass(['bottom', 'top']);
+
+ if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
+ /*
+ * place to the top of element!
+ *
+ */
- } else if (!this.stylesheets.length) {
- // simple..
- st = '<style type="text/css">' +
- 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
- '</style>';
- } else {
- Roo.each(this.stylesheets, function(s) {
- st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
- });
+ this.picker().addClass('top');
+ this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
+ return;
}
- st += '<style type="text/css">' +
- 'IMG { cursor: pointer } ' +
- '</style>';
-
+ this.picker().addClass('bottom');
- return '<html><head>' + st +
- //<style type="text/css">' +
- //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
- //'</style>' +
- ' </head><body class="roo-htmleditor-body"></body></html>';
+ this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
},
-
- // private
- onRender : function(ct, position)
+
+ parseDate : function(value){
+ if(!value || value instanceof Date){
+ return value;
+ }
+ var v = Date.parseDate(value, this.format);
+ if (!v && this.useIso) {
+ v = Date.parseDate(value, 'Y-m-d');
+ }
+ if(!v && this.altFormats){
+ if(!this.altFormatsArray){
+ this.altFormatsArray = this.altFormats.split("|");
+ }
+ for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
+ v = Date.parseDate(value, this.altFormatsArray[i]);
+ }
+ }
+ return v;
+ },
+
+ formatDate : function(date, fmt){
+ return (!date || !(date instanceof Date)) ?
+ date : date.dateFormat(fmt || this.format);
+ },
+
+ onFocus : function()
{
- var _t = this;
- //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
- this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
-
-
- this.el.dom.style.border = '0 none';
- this.el.dom.setAttribute('tabIndex', -1);
- this.el.addClass('x-hidden hide');
-
+ Roo.bootstrap.DateField.superclass.onFocus.call(this);
+ this.show();
+ },
+
+ onBlur : function()
+ {
+ Roo.bootstrap.DateField.superclass.onBlur.call(this);
+ this.hide();
+ },
+
+ show : function()
+ {
+ this.picker().show();
+ this.update();
+ this.place();
+ this.fireEvent('show', this, this.date);
+ },
+
+ hide : function()
+ {
+ if(this.isInline) return;
+ this.picker().hide();
+ this.viewMode = this.startViewMode;
+ this.showMode();
- if(Roo.isIE){ // fix IE 1px bogus margin
- this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
- }
-
+ this.fireEvent('hide', this, this.date);
- this.frameId = Roo.id();
+ },
+
+ onMousedown: function(e){
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ keyup: function(e){
+ Roo.bootstrap.DateField.superclass.keyup.call(this);
+ this.update();
-
+ },
+
+ setValue: function(v){
+ Roo.bootstrap.DateField.superclass.setValue.call(this, v);
- var iframe = this.owner.wrap.createChild({
- tag: 'iframe',
- cls: 'form-control', // bootstrap..
- id: this.frameId,
- name: this.frameId,
- frameBorder : 'no',
- 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
- }, this.el
- );
+ this.fireEvent('select', this, this.date);
+ },
+
+ fireKey: function(e){
+ if (!this.picker().isVisible()){
+ if (e.keyCode == 27) // allow escape to hide and re-show picker
+ this.show();
+ return;
+ }
+ var dateChanged = false,
+ dir, day, month,
+ newDate, newViewDate;
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ e.preventDefault();
+ break;
+ case 37: // left
+ case 39: // right
+ if (!this.keyboardNavigation) break;
+ dir = e.keyCode == 37 ? -1 : 1;
+
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue(this.formatDate(this.date));
+ this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 38: // up
+ case 40: // down
+ if (!this.keyboardNavigation) break;
+ dir = e.keyCode == 38 ? -1 : 1;
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue(this.formatDate(this.date));
+ this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 13: // enter
+ this.setValue(this.formatDate(this.date));
+ this.hide();
+ e.preventDefault();
+ break;
+ case 9: // tab
+ this.setValue(this.formatDate(this.date));
+ this.hide();
+ break;
+ }
+ },
+
+
+ onClick: function(e) {
+ e.stopPropagation();
+ e.preventDefault();
- this.iframe = iframe.dom;
-
- this.assignDocWin();
+ var target = e.getTarget();
- this.doc.designMode = 'on';
-
- this.doc.open();
- this.doc.write(this.getDocMarkup());
- this.doc.close();
-
+ if(target.nodeName.toLowerCase() === 'i'){
+ target = Roo.get(target).dom.parentNode;
+ }
- var task = { // must defer to wait for browser to be ready
- run : function(){
- //console.log("run task?" + this.doc.readyState);
- this.assignDocWin();
- if(this.doc.body || this.doc.readyState == 'complete'){
- try {
- this.doc.designMode="on";
- } catch (e) {
- return;
+ var nodeName = target.nodeName;
+ var className = target.className;
+ var html = target.innerHTML;
+
+ switch(nodeName.toLowerCase()) {
+ case 'th':
+ switch(className) {
+ case 'switch':
+ this.showMode(1);
+ break;
+ case 'prev':
+ case 'next':
+ var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
+ switch(this.viewMode){
+ case 0:
+ this.viewDate = this.moveMonth(this.viewDate, dir);
+ break;
+ case 1:
+ case 2:
+ this.viewDate = this.moveYear(this.viewDate, dir);
+ break;
+ }
+ this.fill();
+ break;
+ case 'today':
+ var date = new Date();
+ this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+ this.fill()
+ this.setValue(this.formatDate(this.date));
+ this.hide();
+ break;
+ }
+ break;
+ case 'span':
+ if (className.indexOf('disabled') === -1) {
+ this.viewDate.setUTCDate(1);
+ if (className.indexOf('month') !== -1) {
+ this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
+ } else {
+ var year = parseInt(html, 10) || 0;
+ this.viewDate.setUTCFullYear(year);
+
}
- Roo.TaskMgr.stop(task);
- this.initEditor.defer(10, this);
+ this.showMode(-1);
+ this.fill();
}
- },
- interval : 10,
- duration: 10000,
- scope: this
- };
- Roo.TaskMgr.start(task);
-
-
-
- },
+ break;
+
+ case 'td':
+ if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
+ var day = parseInt(html, 10) || 1;
+ var year = this.viewDate.getUTCFullYear(),
+ month = this.viewDate.getUTCMonth();
- // private
- onResize : function(w, h)
- {
- Roo.log('resize: ' +w + ',' + h );
- //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
- if(!this.iframe){
- return;
- }
- if(typeof w == 'number'){
-
- this.iframe.style.width = w + 'px';
+ if (className.indexOf('old') !== -1) {
+ if(month === 0 ){
+ month = 11;
+ year -= 1;
+ }else{
+ month -= 1;
+ }
+ } else if (className.indexOf('new') !== -1) {
+ if (month == 11) {
+ month = 0;
+ year += 1;
+ } else {
+ month += 1;
+ }
+ }
+ this.date = this.UTCDate(year, month, day,0,0,0,0);
+ this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
+ this.fill();
+ this.setValue(this.formatDate(this.date));
+ this.hide();
+ }
+ break;
}
- if(typeof h == 'number'){
-
- this.iframe.style.height = h + 'px';
- if(this.doc){
- (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
- }
+ },
+
+ setStartDate: function(startDate){
+ this.startDate = startDate || -Infinity;
+ if (this.startDate !== -Infinity) {
+ this.startDate = this.parseDate(this.startDate);
}
-
+ this.update();
+ this.updateNavArrows();
},
- /**
- * Toggles the editor between standard and source edit mode.
- * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
- */
- toggleSourceEdit : function(sourceEditMode){
-
- this.sourceEditMode = sourceEditMode === true;
-
- if(this.sourceEditMode){
-
- Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
-
- }else{
- Roo.get(this.iframe).removeClass(['x-hidden','hide']);
- //this.iframe.className = '';
- this.deferFocus();
+ setEndDate: function(endDate){
+ this.endDate = endDate || Infinity;
+ if (this.endDate !== Infinity) {
+ this.endDate = this.parseDate(this.endDate);
}
- //this.setSize(this.owner.wrap.getSize());
- //this.fireEvent('editmodechange', this, this.sourceEditMode);
+ this.update();
+ this.updateNavArrows();
},
-
-
-
- /**
- * Protected method that will not generally be called directly. If you need/want
- * custom HTML cleanup, this is the method you should override.
- * @param {String} html The HTML to be cleaned
- * return {String} The cleaned HTML
- */
- cleanHtml : function(html){
- html = String(html);
- if(html.length > 5){
- if(Roo.isSafari){ // strip safari nonsense
- html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
- }
- }
- if(html == ' '){
- html = '';
+ setDaysOfWeekDisabled: function(daysOfWeekDisabled){
+ this.daysOfWeekDisabled = daysOfWeekDisabled || [];
+ if (typeof(this.daysOfWeekDisabled) !== 'object') {
+ this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
}
- return html;
+ this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
+ return parseInt(d, 10);
+ });
+ this.update();
+ this.updateNavArrows();
},
+
+ updateNavArrows: function() {
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth();
+
+ Roo.each(this.picker().select('.prev', true).elements, function(v){
+ v.show();
+ switch (this.viewMode) {
+ case 0:
- /**
- * HTML Editor -> Textarea
- * Protected method that will not generally be called directly. Syncs the contents
- * of the editor iframe with the textarea.
- */
- syncValue : function(){
- if(this.initialized){
- var bd = (this.doc.body || this.doc.documentElement);
- //this.cleanUpPaste(); -- this is done else where and causes havoc..
- var html = bd.innerHTML;
- if(Roo.isSafari){
- var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
- var m = bs ? bs.match(/text-align:(.*?);/i) : false;
- if(m && m[1]){
- html = '<div style="'+m[0]+'">' + html + '</div>';
- }
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
+ v.hide();
+ }
+ break;
+ case 1:
+ case 2:
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
+ v.hide();
+ }
+ break;
}
- html = this.cleanHtml(html);
- // fix up the special chars.. normaly like back quotes in word...
- // however we do not want to do this with chinese..
- html = html.replace(/([\x80-\uffff])/g, function (a, b) {
- var cc = b.charCodeAt();
- if (
- (cc >= 0x4E00 && cc < 0xA000 ) ||
- (cc >= 0x3400 && cc < 0x4E00 ) ||
- (cc >= 0xf900 && cc < 0xfb00 )
- ) {
- return b;
- }
- return "&#"+cc+";"
- });
- if(this.owner.fireEvent('beforesync', this, html) !== false){
- this.el.dom.value = html;
- this.owner.fireEvent('sync', this, html);
+ });
+
+ Roo.each(this.picker().select('.next', true).elements, function(v){
+ v.show();
+ switch (this.viewMode) {
+ case 0:
+
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
+ v.hide();
+ }
+ break;
+ case 1:
+ case 2:
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
+ v.hide();
+ }
+ break;
+ }
+ })
+ },
+
+ moveMonth: function(date, dir){
+ if (!dir) return date;
+ var new_date = new Date(date.valueOf()),
+ day = new_date.getUTCDate(),
+ month = new_date.getUTCMonth(),
+ mag = Math.abs(dir),
+ new_month, test;
+ dir = dir > 0 ? 1 : -1;
+ if (mag == 1){
+ test = dir == -1
+ // If going back one month, make sure month is not current month
+ // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
+ ? function(){
+ return new_date.getUTCMonth() == month;
}
+ // If going forward one month, make sure month is as expected
+ // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
+ : function(){
+ return new_date.getUTCMonth() != new_month;
+ };
+ new_month = month + dir;
+ new_date.setUTCMonth(new_month);
+ // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
+ if (new_month < 0 || new_month > 11)
+ new_month = (new_month + 12) % 12;
+ } else {
+ // For magnitudes >1, move one month at a time...
+ for (var i=0; i<mag; i++)
+ // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
+ new_date = this.moveMonth(new_date, dir);
+ // ...then reset the day, keeping it in the new month
+ new_month = new_date.getUTCMonth();
+ new_date.setUTCDate(day);
+ test = function(){
+ return new_month != new_date.getUTCMonth();
+ };
+ }
+ // Common date-resetting loop -- if date is beyond end of month, make it
+ // end of month
+ while (test()){
+ new_date.setUTCDate(--day);
+ new_date.setUTCMonth(new_month);
}
+ return new_date;
},
- /**
- * Protected method that will not generally be called directly. Pushes the value of the textarea
- * into the iframe editor.
- */
- pushValue : function(){
- if(this.initialized){
- var v = this.el.dom.value.trim();
-
-// if(v.length < 1){
-// v = ' ';
-// }
-
- if(this.owner.fireEvent('beforepush', this, v) !== false){
- var d = (this.doc.body || this.doc.documentElement);
- d.innerHTML = v;
- this.cleanUpPaste();
- this.el.dom.value = d.innerHTML;
- this.owner.fireEvent('push', this, v);
- }
- }
+ moveYear: function(date, dir){
+ return this.moveMonth(date, dir*12);
},
- // private
- deferFocus : function(){
- this.focus.defer(10, this);
+ dateWithinRange: function(date){
+ return date >= this.startDate && date <= this.endDate;
},
- // doc'ed in Field
- focus : function(){
- if(this.win && !this.sourceEditMode){
- this.win.focus();
- }else{
- this.el.focus();
+
+ remove: function() {
+ this.picker().remove();
+ }
+
+});
+
+Roo.apply(Roo.bootstrap.DateField, {
+
+ head : {
+ tag: 'thead',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'th',
+ cls: 'prev',
+ html: '<i class="fa fa-arrow-left"/>'
+ },
+ {
+ tag: 'th',
+ cls: 'switch',
+ colspan: '5'
+ },
+ {
+ tag: 'th',
+ cls: 'next',
+ html: '<i class="fa fa-arrow-right"/>'
+ }
+
+ ]
}
+ ]
},
- assignDocWin: function()
- {
- var iframe = this.iframe;
-
- if(Roo.isIE){
- this.doc = iframe.contentWindow.document;
- this.win = iframe.contentWindow;
- } else {
- if (!Roo.get(this.frameId)) {
- return;
+ content : {
+ tag: 'tbody',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ colspan: '7'
}
- this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
- this.win = Roo.get(this.frameId).dom.contentWindow;
+ ]
}
+ ]
},
- // private
- initEditor : function(){
- //console.log("INIT EDITOR");
- this.assignDocWin();
-
-
-
- this.doc.designMode="on";
- this.doc.open();
- this.doc.write(this.getDocMarkup());
- this.doc.close();
-
- var dbody = (this.doc.body || this.doc.documentElement);
- //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
- // this copies styles from the containing element into thsi one..
- // not sure why we need all of this..
- //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
-
- //var ss = this.el.getStyles( 'background-image', 'background-repeat');
- //ss['background-attachment'] = 'fixed'; // w3c
- dbody.bgProperties = 'fixed'; // ie
- //Roo.DomHelper.applyStyles(dbody, ss);
- Roo.EventManager.on(this.doc, {
- //'mousedown': this.onEditorEvent,
- 'mouseup': this.onEditorEvent,
- 'dblclick': this.onEditorEvent,
- 'click': this.onEditorEvent,
- 'keyup': this.onEditorEvent,
- buffer:100,
- scope: this
- });
- if(Roo.isGecko){
- Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
- }
- if(Roo.isIE || Roo.isSafari || Roo.isOpera){
- Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
+ footer : {
+ tag: 'tfoot',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'th',
+ colspan: '7',
+ cls: 'today'
+ }
+
+ ]
}
- this.initialized = true;
-
- this.owner.fireEvent('initialize', this);
- this.pushValue();
+ ]
},
-
- // private
- 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();
+
+ dates:{
+ en: {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today"
}
},
-
- // private
- onFirstFocus : function(){
-
- this.assignDocWin();
-
-
- this.activated = true;
-
- if(Roo.isGecko){ // prevent silly gecko errors
- this.win.focus();
- var s = this.win.getSelection();
- if(!s.focusNode || s.focusNode.nodeType != 3){
- var r = s.getRangeAt(0);
- r.selectNodeContents((this.doc.body || this.doc.documentElement));
- r.collapse(true);
- this.deferFocus();
+ modes: [
+ {
+ clsName: 'days',
+ navFnc: 'Month',
+ navStep: 1
+ },
+ {
+ clsName: 'months',
+ navFnc: 'FullYear',
+ navStep: 1
+ },
+ {
+ clsName: 'years',
+ navFnc: 'FullYear',
+ navStep: 10
+ }]
+});
+
+Roo.apply(Roo.bootstrap.DateField, {
+
+ template : {
+ tag: 'div',
+ cls: 'datepicker dropdown-menu',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'datepicker-days',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.DateField.head,
+ {
+ tag: 'tbody'
+ },
+ Roo.bootstrap.DateField.footer
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'div',
+ cls: 'datepicker-months',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.DateField.head,
+ Roo.bootstrap.DateField.content,
+ Roo.bootstrap.DateField.footer
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'div',
+ cls: 'datepicker-years',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.DateField.head,
+ Roo.bootstrap.DateField.content,
+ Roo.bootstrap.DateField.footer
+ ]
}
- try{
- this.execCmd('useCSS', true);
- this.execCmd('styleWithCSS', false);
- }catch(e){}
- }
- this.owner.fireEvent('activate', this);
- },
-
- // private
- adjustFont: function(btn){
- var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
- //if(Roo.isSafari){ // safari
- // adjust *= 2;
- // }
- var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
- if(Roo.isSafari){ // safari
- var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
- v = (v < 10) ? 10 : v;
- v = (v > 48) ? 48 : v;
- v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
-
+ ]
}
-
-
- v = Math.max(1, v+adjust);
-
- this.execCmd('FontSize', v );
- },
+ ]
+ }
+});
- onEditorEvent : function(e){
- this.owner.fireEvent('editorevent', this, e);
- // this.updateToolbar();
- this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
- },
+
- insertTag : function(tg)
- {
- // could be a bit smarter... -> wrap the current selected tRoo..
- if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
-
- range = this.createRange(this.getSelection());
- var wrappingNode = this.doc.createElement(tg.toLowerCase());
- wrappingNode.appendChild(range.extractContents());
- range.insertNode(wrappingNode);
+ /*
+ * - LGPL
+ *
+ * TimeField
+ *
+ */
- return;
-
-
-
- }
- this.execCmd("formatblock", tg);
-
- },
-
- insertText : function(txt)
- {
-
-
- var range = this.createRange();
- range.deleteContents();
- //alert(Sender.getAttribute('label'));
-
- range.insertNode(this.doc.createTextNode(txt));
- } ,
-
-
+/**
+ * @class Roo.bootstrap.TimeField
+ * @extends Roo.bootstrap.Input
+ * Bootstrap DateField class
+ *
+ *
+ * @constructor
+ * Create a new TimeField
+ * @param {Object} config The config object
+ */
- /**
- * Executes a Midas editor command on the editor document and performs necessary focus and
- * toolbar updates. <b>This should only be called after the editor is initialized.</b>
- * @param {String} cmd The Midas command
- * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
- */
- relayCmd : function(cmd, value){
- this.win.focus();
- this.execCmd(cmd, value);
- this.owner.fireEvent('editorevent', this);
- //this.updateToolbar();
- this.owner.deferFocus();
- },
+Roo.bootstrap.TimeField = function(config){
+ Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event show
+ * Fires when this field show.
+ * @param {Roo.bootstrap.DateField} this
+ * @param {Mixed} date The date value
+ */
+ show : true,
+ /**
+ * @event show
+ * Fires when this field hide.
+ * @param {Roo.bootstrap.DateField} this
+ * @param {Mixed} date The date value
+ */
+ hide : true,
+ /**
+ * @event select
+ * Fires when select a date.
+ * @param {Roo.bootstrap.DateField} this
+ * @param {Mixed} date The date value
+ */
+ select : true
+ });
+};
+Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
+
/**
- * Executes a Midas editor command directly on the editor document.
- * For visual commands, you should use {@link #relayCmd} instead.
- * <b>This should only be called after the editor is initialized.</b>
- * @param {String} cmd The Midas command
- * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
- */
- execCmd : function(cmd, value){
- this.doc.execCommand(cmd, false, value === undefined ? null : value);
- this.syncValue();
- },
-
-
-
- /**
- * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
- * to insert tRoo.
- * @param {String} text | dom node..
+ * @cfg {String} format
+ * The default time format string which can be overriden for localization support. The format must be
+ * valid according to {@link Date#parseDate} (defaults to 'H:i').
*/
- insertAtCursor : function(text)
+ format : "H:i",
+
+ onRender: function(ct, position)
{
+ Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
+
+ this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
+ this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- if(!this.activated){
- return;
- }
- /*
- if(Roo.isIE){
- this.win.focus();
- var r = this.doc.selection.createRange();
- if(r){
- r.collapse(true);
- r.pasteHTML(text);
- this.syncValue();
- this.deferFocus();
-
- }
- return;
- }
- */
- if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
- this.win.focus();
-
-
- // from jquery ui (MIT licenced)
- var range, node;
- var win = this.win;
-
- if (win.getSelection && win.getSelection().getRangeAt) {
- range = win.getSelection().getRangeAt(0);
- node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
- range.insertNode(node);
- } else if (win.document.selection && win.document.selection.createRange) {
- // no firefox support
- var txt = typeof(text) == 'string' ? text : text.outerHTML;
- win.document.selection.createRange().pasteHTML(txt);
- } else {
- // no firefox support
- var txt = typeof(text) == 'string' ? text : text.outerHTML;
- this.execCmd('InsertHTML', txt);
- }
-
- this.syncValue();
+ this.pop = this.picker().select('>.datepicker-time',true).first();
+ this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
+
+ this.picker().on('mousedown', this.onMousedown, this);
+ this.picker().on('click', this.onClick, this);
+
+ this.picker().addClass('datepicker-dropdown');
+
+ this.fillTime();
+ this.update();
- this.deferFocus();
- }
- },
- // private
- mozKeyPress : function(e){
- if(e.ctrlKey){
- var c = e.getCharCode(), cmd;
-
- if(c > 0){
- c = String.fromCharCode(c).toLowerCase();
- switch(c){
- case 'b':
- cmd = 'bold';
- break;
- case 'i':
- cmd = 'italic';
- break;
-
- case 'u':
- cmd = 'underline';
- break;
-
- case 'v':
- this.cleanUpPaste.defer(100, this);
- return;
-
- }
- if(cmd){
- this.win.focus();
- this.execCmd(cmd);
- this.deferFocus();
- e.preventDefault();
- }
-
- }
- }
+ this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
+ this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
+ this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
+ this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
+ this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
+ this.pop.select('button.ok', true).first().on('click', this.setTime, this);
+
},
+
+ fireKey: function(e){
+ if (!this.picker().isVisible()){
+ if (e.keyCode == 27) // allow escape to hide and re-show picker
+ this.show();
+ return;
+ }
- // private
- fixKeys : function(){ // load time branching for fastest keydown performance
- if(Roo.isIE){
- return function(e){
- var k = e.getKey(), r;
- if(k == e.TAB){
- e.stopEvent();
- r = this.doc.selection.createRange();
- if(r){
- r.collapse(true);
- r.pasteHTML('    ');
- this.deferFocus();
- }
- return;
- }
-
- if(k == e.ENTER){
- r = this.doc.selection.createRange();
- if(r){
- var target = r.parentElement();
- if(!target || target.tagName.toLowerCase() != 'li'){
- e.stopEvent();
- r.pasteHTML('<br />');
- r.collapse(false);
- r.select();
+ e.preventDefault();
+
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ break;
+ case 37: // left
+ case 39: // right
+ this.onTogglePeriod();
+ break;
+ case 38: // up
+ this.onIncrementMinutes();
+ break;
+ case 40: // down
+ this.onDecrementMinutes();
+ break;
+ case 13: // enter
+ case 9: // tab
+ this.setTime();
+ break;
+ }
+ },
+
+ onClick: function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ picker : function()
+ {
+ return this.el.select('.datepicker', true).first();
+ },
+
+ fillTime: function()
+ {
+ var time = this.pop.select('tbody', true).first();
+
+ time.dom.innerHTML = '';
+
+ time.createChild({
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'btn',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'hours-up glyphicon glyphicon-chevron-up'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
+ },
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'btn',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'minutes-up glyphicon glyphicon-chevron-up'
+ }
+ ]
}
- }
- }
- if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
- this.cleanUpPaste.defer(100, this);
- return;
- }
-
-
- };
- }else if(Roo.isOpera){
- return function(e){
- var k = e.getKey();
- if(k == e.TAB){
- e.stopEvent();
- this.win.focus();
- this.execCmd('InsertHTML','    ');
- this.deferFocus();
- }
- if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
- this.cleanUpPaste.defer(100, this);
- return;
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
}
-
- };
- }else if(Roo.isSafari){
- return function(e){
- var k = e.getKey();
-
- if(k == e.TAB){
- e.stopEvent();
- this.execCmd('InsertText','\t');
- this.deferFocus();
- return;
+ ]
+ });
+
+ time.createChild({
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'timepicker-hour',
+ html: '00'
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator',
+ html: ':'
+ },
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'timepicker-minute',
+ html: '00'
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
+ },
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'button',
+ type: 'button',
+ cls: 'btn btn-primary period',
+ html: 'AM'
+
+ }
+ ]
}
- if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
- this.cleanUpPaste.defer(100, this);
- return;
+ ]
+ });
+
+ time.createChild({
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'btn',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'hours-down glyphicon glyphicon-chevron-down'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
+ },
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'btn',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'minutes-down glyphicon glyphicon-chevron-down'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
}
-
- };
- }
- }(),
+ ]
+ });
+
+ },
+
+ update: function()
+ {
+
+ this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
+
+ this.fill();
+ },
- getAllAncestors: function()
+ fill: function()
{
- var p = this.getSelectedNode();
- var a = [];
- if (!p) {
- a.push(p); // push blank onto stack..
- p = this.getParentElement();
+ var hours = this.time.getHours();
+ var minutes = this.time.getMinutes();
+ var period = 'AM';
+
+ if(hours > 11){
+ period = 'PM';
+ }
+
+ if(hours == 0){
+ hours = 12;
}
- while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
- a.push(p);
- p = p.parentNode;
+ if(hours > 12){
+ hours = hours - 12;
}
- a.push(this.doc.body);
- return a;
+
+ if(hours < 10){
+ hours = '0' + hours;
+ }
+
+ if(minutes < 10){
+ minutes = '0' + minutes;
+ }
+
+ this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
+ this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
+ this.pop.select('button', true).first().dom.innerHTML = period;
+
+ },
+
+ place: function()
+ {
+ this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
+
+ var cls = ['bottom'];
+
+ if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
+ cls.pop();
+ cls.push('top');
+ }
+
+ cls.push('right');
+
+ if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
+ cls.pop();
+ cls.push('left');
+ }
+
+ this.picker().addClass(cls.join('-'));
+
+ var _this = this;
+
+ Roo.each(cls, function(c){
+ if(c == 'bottom'){
+ _this.picker().setTop(_this.inputEl().getHeight());
+ return;
+ }
+ if(c == 'top'){
+ _this.picker().setTop(0 - _this.picker().getHeight());
+ return;
+ }
+
+ if(c == 'left'){
+ _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
+ return;
+ }
+ if(c == 'right'){
+ _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
+ return;
+ }
+ });
+
+ },
+
+ onFocus : function()
+ {
+ Roo.bootstrap.TimeField.superclass.onFocus.call(this);
+ this.show();
+ },
+
+ onBlur : function()
+ {
+ Roo.bootstrap.TimeField.superclass.onBlur.call(this);
+ this.hide();
+ },
+
+ show : function()
+ {
+ this.picker().show();
+ this.pop.show();
+ this.update();
+ this.place();
+
+ this.fireEvent('show', this, this.date);
+ },
+
+ hide : function()
+ {
+ this.picker().hide();
+ this.pop.hide();
+
+ this.fireEvent('hide', this, this.date);
+ },
+
+ setTime : function()
+ {
+ this.hide();
+ this.setValue(this.time.format(this.format));
+
+ this.fireEvent('select', this, this.date);
+
+
+ },
+
+ onMousedown: function(e){
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ onIncrementHours: function()
+ {
+ Roo.log('onIncrementHours');
+ this.time = this.time.add(Date.HOUR, 1);
+ this.update();
+
+ },
+
+ onDecrementHours: function()
+ {
+ Roo.log('onDecrementHours');
+ this.time = this.time.add(Date.HOUR, -1);
+ this.update();
+ },
+
+ onIncrementMinutes: function()
+ {
+ Roo.log('onIncrementMinutes');
+ this.time = this.time.add(Date.MINUTE, 1);
+ this.update();
+ },
+
+ onDecrementMinutes: function()
+ {
+ Roo.log('onDecrementMinutes');
+ this.time = this.time.add(Date.MINUTE, -1);
+ this.update();
+ },
+
+ onTogglePeriod: function()
+ {
+ Roo.log('onTogglePeriod');
+ this.time = this.time.add(Date.HOUR, 12);
+ this.update();
+ }
+
+
+});
+
+Roo.apply(Roo.bootstrap.TimeField, {
+
+ content : {
+ tag: 'tbody',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ colspan: '7'
+ }
+ ]
+ }
+ ]
},
- lastSel : false,
- lastSelNode : false,
+ footer : {
+ tag: 'tfoot',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'th',
+ colspan: '7',
+ cls: '',
+ cn: [
+ {
+ tag: 'button',
+ cls: 'btn btn-info ok',
+ html: 'OK'
+ }
+ ]
+ }
+
+ ]
+ }
+ ]
+ }
+});
+
+Roo.apply(Roo.bootstrap.TimeField, {
+
+ template : {
+ tag: 'div',
+ cls: 'datepicker dropdown-menu',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'datepicker-time',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.TimeField.content,
+ Roo.bootstrap.TimeField.footer
+ ]
+ }
+ ]
+ }
+ ]
+ }
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * CheckBox
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.CheckBox
+ * @extends Roo.bootstrap.Input
+ * Bootstrap CheckBox class
+ *
+ * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
+ * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
+ * @cfg {String} boxLabel The text that appears beside the checkbox
+ * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
+ * @cfg {Boolean} checked initnal the element
+ *
+ *
+ * @constructor
+ * Create a new CheckBox
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.CheckBox = function(config){
+ Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ /**
+ * @event check
+ * Fires when the element is checked or unchecked.
+ * @param {Roo.bootstrap.CheckBox} this This input
+ * @param {Boolean} checked The new checked value
+ */
+ check : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
- getSelection : function()
- {
- this.assignDocWin();
- return Roo.isIE ? this.doc.selection : this.win.getSelection();
- },
+ inputType: 'checkbox',
+ inputValue: 1,
+ valueOff: 0,
+ boxLabel: false,
+ checked: false,
+ weight : false,
- getSelectedNode: function()
+ getAutoCreate : function()
{
- // this may only work on Gecko!!!
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
- // should we cache this!!!!
+ var id = Roo.id();
+ var cfg = {};
+ cfg.cls = 'form-group checkbox' //input-group
-
- var range = this.createRange(this.getSelection()).cloneRange();
- if (Roo.isIE) {
- var parent = range.parentElement();
- while (true) {
- var testRange = range.duplicate();
- testRange.moveToElementText(parent);
- if (testRange.inRange(range)) {
- break;
- }
- if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
- break;
- }
- parent = parent.parentElement;
- }
- return parent;
+
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ value : (!this.checked) ? this.valueOff : this.inputValue,
+ cls : 'roo-checkbox', //'form-box',
+ placeholder : this.placeholder || ''
+
+ };
+
+ if (this.weight) { // Validity check?
+ cfg.cls += " checkbox-" + this.weight;
}
- // is ancestor a text element.
- var ac = range.commonAncestorContainer;
- if (ac.nodeType == 3) {
- ac = ac.parentNode;
+ if (this.disabled) {
+ input.disabled=true;
}
- var ar = ac.childNodes;
-
- var nodes = [];
- var other_nodes = [];
- var has_other_nodes = false;
- for (var i=0;i<ar.length;i++) {
- if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
- continue;
- }
- // fullly contained node.
-
- if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
- nodes.push(ar[i]);
- continue;
- }
-
- // probably selected..
- if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
- other_nodes.push(ar[i]);
- continue;
- }
- // outer..
- if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
- continue;
- }
-
-
- has_other_nodes = true;
+ if(this.checked){
+ input.checked = this.checked;
}
- if (!nodes.length && other_nodes.length) {
- nodes= other_nodes;
+
+ if (this.name) {
+ input.name = this.name;
}
- if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
- return false;
+
+ if (this.size) {
+ input.cls += ' input-' + this.size;
}
- return nodes[0];
- },
- createRange: function(sel)
- {
- // this has strange effects when using with
- // top toolbar - not sure if it's a great idea.
- //this.editor.contentWindow.focus();
- if (typeof sel != "undefined") {
- try {
- return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
- } catch(e) {
- return this.doc.createRange();
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
}
- } else {
- return this.doc.createRange();
- }
- },
- getParentElement: function()
- {
+ });
- this.assignDocWin();
- var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
+
- var range = this.createRange(sel);
-
- try {
- var p = range.commonAncestorContainer;
- while (p.nodeType == 3) { // text node
- p = p.parentNode;
+ var inputblock = input;
+
+
+
+
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
+ };
+ if (this.before) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.before
+ });
}
- return p;
- } catch (e) {
- return null;
- }
-
- },
- /***
- *
- * Range intersection.. the hard stuff...
- * '-1' = before
- * '0' = hits..
- * '1' = after.
- * [ -- selected range --- ]
- * [fail] [fail]
- *
- * basically..
- * if end is before start or hits it. fail.
- * if start is after end or hits it fail.
- *
- * if either hits (but other is outside. - then it's not
- *
- *
- **/
-
-
- // @see http://www.thismuchiknow.co.uk/?p=64.
- rangeIntersectsNode : function(range, node)
- {
- var nodeRange = node.ownerDocument.createRange();
- try {
- nodeRange.selectNode(node);
- } catch (e) {
- nodeRange.selectNodeContents(node);
- }
-
- var rangeStartRange = range.cloneRange();
- rangeStartRange.collapse(true);
-
- var rangeEndRange = range.cloneRange();
- rangeEndRange.collapse(false);
-
- var nodeStartRange = nodeRange.cloneRange();
- nodeStartRange.collapse(true);
-
- var nodeEndRange = nodeRange.cloneRange();
- nodeEndRange.collapse(false);
-
- return rangeStartRange.compareBoundaryPoints(
- Range.START_TO_START, nodeEndRange) == -1 &&
- rangeEndRange.compareBoundaryPoints(
- Range.START_TO_START, nodeStartRange) == 1;
+ inputblock.cn.push(input);
+ if (this.after) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.after
+ });
+ }
+
+ };
-
- },
- rangeCompareNode : function(range, node)
- {
- var nodeRange = node.ownerDocument.createRange();
- try {
- nodeRange.selectNode(node);
- } catch (e) {
- nodeRange.selectNodeContents(node);
+ if (align ==='left' && this.fieldLabel.length) {
+ Roo.log("left and has label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-md-' + this.labelWidth,
+ html : this.fieldLabel
+
+ },
+ {
+ cls : "col-md-" + (12 - this.labelWidth),
+ cn: [
+ inputblock
+ ]
+ }
+
+ ];
+ } else if ( this.fieldLabel.length) {
+ Roo.log(" label");
+ cfg.cn = [
+
+ {
+ tag: this.boxLabel ? 'span' : 'label',
+ 'for': id,
+ cls: 'control-label box-input-label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+
+ },
+
+ inputblock
+
+ ];
+
+ } else {
+
+ Roo.log(" no label && no align");
+ cfg.cn = [ inputblock ] ;
+
+
+ };
+ if(this.boxLabel){
+ cfg.cn.push( {
+ tag: 'label',
+ 'for': id,
+ cls: 'box-label',
+ html: this.boxLabel
+
+ });
}
- range.collapse(true);
-
- nodeRange.collapse(true);
-
- var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
- var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
-
- //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
-
- var nodeIsBefore = ss == 1;
- var nodeIsAfter = ee == -1;
-
- if (nodeIsBefore && nodeIsAfter)
- return 0; // outer
- if (!nodeIsBefore && nodeIsAfter)
- return 1; //right trailed.
+
+ return cfg;
- if (nodeIsBefore && !nodeIsAfter)
- return 2; // left trailed.
- // fully contined.
- return 3;
},
-
- // private? - in a new class?
- cleanUpPaste : function()
+
+ /**
+ * return the real input element.
+ */
+ inputEl: function ()
{
- // cleans up the whole document..
- Roo.log('cleanuppaste');
-
- this.cleanUpChildren(this.doc.body);
- var clean = this.cleanWordChars(this.doc.body.innerHTML);
- if (clean != this.doc.body.innerHTML) {
- this.doc.body.innerHTML = clean;
- }
-
+ return this.el.select('input.roo-checkbox',true).first();
},
- cleanWordChars : function(input) {// change the chars to hex code
- var he = Roo.HtmlEditorCore;
+ label: function()
+ {
+ return this.el.select('label.control-label',true).first();
+ },
+
+ initEvents : function()
+ {
+// Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
- var output = input;
- Roo.each(he.swapCodes, function(sw) {
- var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
-
- output = output.replace(swapper, sw[1]);
- });
+ this.inputEl().on('click', this.onClick, this);
- return output;
},
+ onClick : function()
+ {
+ this.setChecked(!this.checked);
+ },
- cleanUpChildren : function (n)
+ setChecked : function(state,suppressEvent)
{
- if (!n.childNodes.length) {
- return;
- }
- for (var i = n.childNodes.length-1; i > -1 ; i--) {
- this.cleanUpChild(n.childNodes[i]);
+ this.checked = state;
+
+ this.inputEl().dom.checked = state;
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, state);
}
+
+ this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
+
},
+ setValue : function(v,suppressEvent)
+ {
+ this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
+ }
-
+});
+
+
+/*
+ * - LGPL
+ *
+ * Radio
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.Radio
+ * @extends Roo.bootstrap.CheckBox
+ * Bootstrap Radio class
+
+ * @constructor
+ * Create a new Radio
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Radio = function(config){
+ Roo.bootstrap.Radio.superclass.constructor.call(this, config);
+
+};
+
+Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
- cleanUpChild : function (node)
+ inputType: 'radio',
+ inputValue: '',
+ valueOff: '',
+
+ getAutoCreate : function()
{
- var ed = this;
- //console.log(node);
- if (node.nodeName == "#text") {
- // clean up silly Windows -- stuff?
- return;
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+
+ var id = Roo.id();
+
+ var cfg = {};
+
+ cfg.cls = 'form-group radio' //input-group
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ value : (!this.checked) ? this.valueOff : this.inputValue,
+ cls : 'roo-radio',
+ placeholder : this.placeholder || ''
+
+ };
+ if (this.weight) { // Validity check?
+ cfg.cls += " radio-" + this.weight;
}
- if (node.nodeName == "#comment") {
- node.parentNode.removeChild(node);
- // clean up silly Windows -- stuff?
- return;
+ if (this.disabled) {
+ input.disabled=true;
}
- if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
- // remove node.
- node.parentNode.removeChild(node);
- return;
+ if(this.checked){
+ input.checked = this.checked;
+ }
+
+ if (this.name) {
+ input.name = this.name;
+ }
+
+ if (this.size) {
+ input.cls += ' input-' + this.size;
+ }
+
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ var inputblock = input;
+
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
+ };
+ if (this.before) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.before
+ });
+ }
+ inputblock.cn.push(input);
+ if (this.after) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.after
+ });
+ }
+
+ };
+
+ if (align ==='left' && this.fieldLabel.length) {
+ Roo.log("left and has label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-md-' + this.labelWidth,
+ html : this.fieldLabel
+
+ },
+ {
+ cls : "col-md-" + (12 - this.labelWidth),
+ cn: [
+ inputblock
+ ]
+ }
+
+ ];
+ } else if ( this.fieldLabel.length) {
+ Roo.log(" label");
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ 'for': id,
+ cls: 'control-label box-input-label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+
+ },
+
+ inputblock
+
+ ];
+
+ } else {
+ Roo.log(" no label && no align");
+ cfg.cn = [
+
+ inputblock
+
+ ];
+
+
+ };
+
+ if(this.boxLabel){
+ cfg.cn.push({
+ tag: 'label',
+ 'for': id,
+ cls: 'box-label',
+ html: this.boxLabel
+ })
}
- var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
+ return cfg;
- // remove <a name=....> as rendering on yahoo mailer is borked with this.
- // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
+ },
+ inputEl: function ()
+ {
+ return this.el.select('input.roo-radio',true).first();
+ },
+ onClick : function()
+ {
+ this.setChecked(true);
+ },
+
+ setChecked : function(state,suppressEvent)
+ {
+ if(state){
+ Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
+ v.dom.checked = false;
+ });
+ }
- //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
- // remove_keep_children = true;
- //}
+ this.checked = state;
+ this.inputEl().dom.checked = state;
- if (remove_keep_children) {
- this.cleanUpChildren(node);
- // inserts everything just before this node...
- while (node.childNodes.length) {
- var cn = node.childNodes[0];
- node.removeChild(cn);
- node.parentNode.insertBefore(cn, node);
- }
- node.parentNode.removeChild(node);
- return;
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, state);
}
- if (!node.attributes || !node.attributes.length) {
- this.cleanUpChildren(node);
- return;
- }
+ this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
- function cleanAttr(n,v)
- {
-
- if (v.match(/^\./) || v.match(/^\//)) {
- return;
- }
- if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
- return;
- }
- if (v.match(/^#/)) {
- return;
+ },
+
+ getGroupValue : function()
+ {
+ var value = ''
+ Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
+ if(v.dom.checked == true){
+ value = v.dom.value;
}
-// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
- node.removeAttribute(n);
-
- }
+ });
- function cleanStyle(n,v)
- {
- if (v.match(/expression/)) { //XSS?? should we even bother..
- node.removeAttribute(n);
- return;
- }
- var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
- var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
-
-
- var parts = v.split(/;/);
- var clean = [];
-
- Roo.each(parts, function(p) {
- p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
- if (!p.length) {
- return true;
- }
- var l = p.split(':').shift().replace(/\s+/g,'');
- l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
-
- if ( cblack.indexOf(l) > -1) {
-// Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
- //node.removeAttribute(n);
- return true;
- }
- //Roo.log()
- // only allow 'c whitelisted system attributes'
- if ( cwhite.length && cwhite.indexOf(l) < 0) {
-// Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
- //node.removeAttribute(n);
- return true;
- }
-
-
-
-
- clean.push(p);
- return true;
- });
- if (clean.length) {
- node.setAttribute(n, clean.join(';'));
- } else {
- node.removeAttribute(n);
- }
-
- }
+ return value;
+ },
+
+ /**
+ * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
+ * @return {Mixed} value The field value
+ */
+ getValue : function(){
+ return this.getGroupValue();
+ }
+
+});
+
+
+//<script type="text/javascript">
+
+/*
+ * Based Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ * LGPL
+ *
+ */
+
+/**
+ * @class Roo.HtmlEditorCore
+ * @extends Roo.Component
+ * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
+ *
+ * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
+ */
+
+Roo.HtmlEditorCore = function(config){
+
+
+ Roo.HtmlEditorCore.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event initialize
+ * Fires when the editor is fully initialized (including the iframe)
+ * @param {Roo.HtmlEditorCore} this
+ */
+ initialize: true,
+ /**
+ * @event activate
+ * Fires when the editor is first receives the focus. Any insertion must wait
+ * until after this event.
+ * @param {Roo.HtmlEditorCore} this
+ */
+ activate: true,
+ /**
+ * @event beforesync
+ * Fires before the textarea is updated with content from the editor iframe. Return false
+ * to cancel the sync.
+ * @param {Roo.HtmlEditorCore} 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 {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ beforepush: true,
+ /**
+ * @event sync
+ * Fires when the textarea is updated with content from the editor iframe.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ sync: true,
+ /**
+ * @event push
+ * Fires when the iframe editor is updated with content from the textarea.
+ * @param {Roo.HtmlEditorCore} this
+ * @param {String} html
+ */
+ push: true,
+ /**
+ * @event editorevent
+ * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
+ * @param {Roo.HtmlEditorCore} this
+ */
+ editorevent: true
+ });
+
+};
+
+
+Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
+
+
+ /**
+ * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
+ */
+
+ owner : false,
+
+ /**
+ * @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: 500,
+
+ /**
+ * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+ *
+ */
+ stylesheets: false,
+
+ // id of frame..
+ frameId: false,
+
+ // private properties
+ validationEvent : false,
+ deferHeight: true,
+ initialized : false,
+ activated : false,
+ sourceEditMode : false,
+ onFocus : Roo.emptyFn,
+ iframePad:3,
+ hideMode:'offsets',
+
+ clearUp: true,
+
+
+
+
+ /**
+ * Protected method that will not generally be called directly. It
+ * is called when the editor initializes the iframe with HTML contents. Override this method if you
+ * want to change the initialization markup of the iframe (e.g. to add stylesheets).
+ */
+ getDocMarkup : function(){
+ // body styles..
+ var st = '';
+ Roo.log(this.stylesheets);
- for (var i = node.attributes.length-1; i > -1 ; i--) {
- var a = node.attributes[i];
- //console.log(a);
-
- if (a.name.toLowerCase().substr(0,2)=='on') {
- node.removeAttribute(a.name);
- continue;
- }
- if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
- node.removeAttribute(a.name);
- continue;
- }
- if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
- cleanAttr(a.name,a.value); // fixme..
- continue;
- }
- if (a.name == 'style') {
- cleanStyle(a.name,a.value);
- continue;
- }
- /// clean up MS crap..
- // tecnically this should be a list of valid class'es..
+ // inherit styels from page...??
+ if (this.stylesheets === false) {
+ Roo.get(document.head).select('style').each(function(node) {
+ st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+ });
- if (a.name == 'class') {
- if (a.value.match(/^Mso/)) {
- node.className = '';
- }
-
- if (a.value.match(/body/)) {
- node.className = '';
- }
- continue;
- }
+ Roo.get(document.head).select('link').each(function(node) {
+ st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
+ });
- // style cleanup!?
- // class cleanup?
+ } else if (!this.stylesheets.length) {
+ // simple..
+ st = '<style type="text/css">' +
+ 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+ '</style>';
+ } else {
+ Roo.each(this.stylesheets, function(s) {
+ st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
+ });
}
+ st += '<style type="text/css">' +
+ 'IMG { cursor: pointer } ' +
+ '</style>';
+
- this.cleanUpChildren(node);
-
-
+ return '<html><head>' + st +
+ //<style type="text/css">' +
+ //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+ //'</style>' +
+ ' </head><body class="roo-htmleditor-body"></body></html>';
},
- /**
- * Clean up MS wordisms...
- */
- cleanWord : function(node)
+
+ // private
+ onRender : function(ct, position)
{
var _t = this;
- var cleanWordChildren = function()
- {
- if (!node.childNodes.length) {
- return;
- }
- for (var i = node.childNodes.length-1; i > -1 ; i--) {
- _t.cleanWord(node.childNodes[i]);
- }
- }
+ //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
+ this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
- if (!node) {
- this.cleanWord(this.doc.body);
- return;
- }
- if (node.nodeName == "#text") {
- // clean up silly Windows -- stuff?
- return;
- }
- if (node.nodeName == "#comment") {
- node.parentNode.removeChild(node);
- // clean up silly Windows -- stuff?
- return;
- }
+ this.el.dom.style.border = '0 none';
+ this.el.dom.setAttribute('tabIndex', -1);
+ this.el.addClass('x-hidden hide');
- if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
- node.parentNode.removeChild(node);
- return;
- }
- // remove - but keep children..
- if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
- while (node.childNodes.length) {
- var cn = node.childNodes[0];
- node.removeChild(cn);
- node.parentNode.insertBefore(cn, node);
- }
- node.parentNode.removeChild(node);
- cleanWordChildren();
- return;
- }
- // clean styles
- if (node.className.length) {
-
- 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(Roo.isIE){ // fix IE 1px bogus margin
+ this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
}
+
- 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.frameId = Roo.id();
- cleanWordChildren();
+
+ var iframe = this.owner.wrap.createChild({
+ tag: 'iframe',
+ cls: 'form-control', // bootstrap..
+ id: this.frameId,
+ name: this.frameId,
+ frameBorder : 'no',
+ 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
+ }, this.el
+ );
- },
- domToHTML : function(currentElement, depth, nopadtext) {
- depth = depth || 0;
- nopadtext = nopadtext || false;
+ this.iframe = iframe.dom;
+
+ this.assignDocWin();
- 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 currentElement.nodeValue;
- }
-
-
- 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);
- if (!nopad && toadd.length > 80) {
- innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
+ this.doc.designMode = 'on';
+
+ this.doc.open();
+ this.doc.write(this.getDocMarkup());
+ this.doc.close();
+
+
+ var task = { // must defer to wait for browser to be ready
+ run : function(){
+ //console.log("run task?" + this.doc.readyState);
+ this.assignDocWin();
+ if(this.doc.body || this.doc.readyState == 'complete'){
+ try {
+ this.doc.designMode="on";
+ } catch (e) {
+ return;
}
- innerHTML += toadd;
-
- i++;
- currentElementChild = currentElement.childNodes.item(i);
- lastNode = '';
- continue;
+ Roo.TaskMgr.stop(task);
+ this.initEditor.defer(10, this);
}
- 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( " " );
- }
+ },
+ interval : 10,
+ duration: 10000,
+ scope: this
+ };
+ Roo.TaskMgr.start(task);
+
+
+
+ },
+
+ // private
+ onResize : function(w, h)
+ {
+ Roo.log('resize: ' +w + ',' + h );
+ //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
+ if(!this.iframe){
+ return;
+ }
+ if(typeof w == 'number'){
+ this.iframe.style.width = w + 'px';
+ }
+ if(typeof h == 'number'){
- if (tagName) {
- ret+= "</"+tagName+">";
+ this.iframe.style.height = h + 'px';
+ if(this.doc){
+ (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
}
- return ret;
-
}
-
- // 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
+ * Toggles the editor between standard and source edit mode.
+ * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
*/
+ toggleSourceEdit : function(sourceEditMode){
+
+ this.sourceEditMode = sourceEditMode === true;
+
+ if(this.sourceEditMode){
+
+ Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
+
+ }else{
+ Roo.get(this.iframe).removeClass(['x-hidden','hide']);
+ //this.iframe.className = '';
+ this.deferFocus();
+ }
+ //this.setSize(this.owner.wrap.getSize());
+ //this.fireEvent('editmodechange', this, this.sourceEditMode);
+ },
+
+
+
+
/**
- * @cfg {String} invalidText @hide
+ * Protected method that will not generally be called directly. If you need/want
+ * custom HTML cleanup, this is the method you should override.
+ * @param {String} html The HTML to be cleaned
+ * return {String} The cleaned HTML
*/
+ cleanHtml : function(html){
+ html = String(html);
+ if(html.length > 5){
+ if(Roo.isSafari){ // strip safari nonsense
+ html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
+ }
+ }
+ if(html == ' '){
+ html = '';
+ }
+ return html;
+ },
+
/**
- * @cfg {String} msgFx @hide
+ * HTML Editor -> Textarea
+ * Protected method that will not generally be called directly. Syncs the contents
+ * of the editor iframe with the textarea.
*/
+ syncValue : function(){
+ if(this.initialized){
+ var bd = (this.doc.body || this.doc.documentElement);
+ //this.cleanUpPaste(); -- this is done else where and causes havoc..
+ var html = bd.innerHTML;
+ if(Roo.isSafari){
+ var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
+ var m = bs ? bs.match(/text-align:(.*?);/i) : false;
+ if(m && m[1]){
+ html = '<div style="'+m[0]+'">' + html + '</div>';
+ }
+ }
+ html = this.cleanHtml(html);
+ // fix up the special chars.. normaly like back quotes in word...
+ // however we do not want to do this with chinese..
+ html = html.replace(/([\x80-\uffff])/g, function (a, b) {
+ var cc = b.charCodeAt();
+ if (
+ (cc >= 0x4E00 && cc < 0xA000 ) ||
+ (cc >= 0x3400 && cc < 0x4E00 ) ||
+ (cc >= 0xf900 && cc < 0xfb00 )
+ ) {
+ return b;
+ }
+ return "&#"+cc+";"
+ });
+ if(this.owner.fireEvent('beforesync', this, html) !== false){
+ this.el.dom.value = html;
+ this.owner.fireEvent('sync', this, html);
+ }
+ }
+ },
+
/**
- * @cfg {String} validateOnBlur @hide
+ * Protected method that will not generally be called directly. Pushes the value of the textarea
+ * into the iframe editor.
*/
-});
-
-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'
-];
+ pushValue : function(){
+ if(this.initialized){
+ var v = this.el.dom.value.trim();
+
+// if(v.length < 1){
+// v = ' ';
+// }
+
+ if(this.owner.fireEvent('beforepush', this, v) !== false){
+ var d = (this.doc.body || this.doc.documentElement);
+ d.innerHTML = v;
+ this.cleanUpPaste();
+ this.el.dom.value = d.innerHTML;
+ this.owner.fireEvent('push', this, v);
+ }
+ }
+ },
+ // private
+ deferFocus : function(){
+ this.focus.defer(10, this);
+ },
-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..
+ // doc'ed in Field
+ focus : function(){
+ if(this.win && !this.sourceEditMode){
+ this.win.focus();
+ }else{
+ this.el.focus();
+ }
+ },
+
+ assignDocWin: function()
+ {
+ var iframe = this.iframe;
+
+ if(Roo.isIE){
+ this.doc = iframe.contentWindow.document;
+ this.win = iframe.contentWindow;
+ } else {
+ if (!Roo.get(this.frameId)) {
+ return;
+ }
+ this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
+ this.win = Roo.get(this.frameId).dom.contentWindow;
+ }
+ },
+
+ // private
+ initEditor : function(){
+ //console.log("INIT EDITOR");
+ this.assignDocWin();
+
+
+
+ this.doc.designMode="on";
+ this.doc.open();
+ this.doc.write(this.getDocMarkup());
+ this.doc.close();
+
+ var dbody = (this.doc.body || this.doc.documentElement);
+ //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
+ // this copies styles from the containing element into thsi one..
+ // not sure why we need all of this..
+ //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
+
+ //var ss = this.el.getStyles( 'background-image', 'background-repeat');
+ //ss['background-attachment'] = 'fixed'; // w3c
+ dbody.bgProperties = 'fixed'; // ie
+ //Roo.DomHelper.applyStyles(dbody, ss);
+ Roo.EventManager.on(this.doc, {
+ //'mousedown': this.onEditorEvent,
+ 'mouseup': this.onEditorEvent,
+ 'dblclick': this.onEditorEvent,
+ 'click': this.onEditorEvent,
+ 'keyup': this.onEditorEvent,
+ buffer:100,
+ scope: this
+ });
+ if(Roo.isGecko){
+ Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
+ }
+ if(Roo.isIE || Roo.isSafari || Roo.isOpera){
+ Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
+ }
+ this.initialized = true;
-Roo.HtmlEditorCore.ablack = [
- 'on'
-];
-
-Roo.HtmlEditorCore.aclean = [
- 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
-];
+ this.owner.fireEvent('initialize', this);
+ this.pushValue();
+ },
-// protocols..
-Roo.HtmlEditorCore.pwhite= [
- 'http', 'https', 'mailto'
-];
+ // private
+ 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();
+ }
+ },
-// white listed style attributes.
-Roo.HtmlEditorCore.cwhite= [
- // 'text-align', /// default is to allow most things..
-
+ // private
+ onFirstFocus : function(){
+
+ this.assignDocWin();
+
+
+ this.activated = true;
-// '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, "..." ]
-];
+
+ if(Roo.isGecko){ // prevent silly gecko errors
+ this.win.focus();
+ var s = this.win.getSelection();
+ if(!s.focusNode || s.focusNode.nodeType != 3){
+ var r = s.getRangeAt(0);
+ r.selectNodeContents((this.doc.body || this.doc.documentElement));
+ r.collapse(true);
+ this.deferFocus();
+ }
+ try{
+ this.execCmd('useCSS', true);
+ this.execCmd('styleWithCSS', false);
+ }catch(e){}
+ }
+ this.owner.fireEvent('activate', this);
+ },
- /*
- * - LGPL
- *
- * HtmlEditor
- *
- */
+ // private
+ adjustFont: function(btn){
+ var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
+ //if(Roo.isSafari){ // safari
+ // adjust *= 2;
+ // }
+ var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
+ if(Roo.isSafari){ // safari
+ var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
+ v = (v < 10) ? 10 : v;
+ v = (v > 48) ? 48 : v;
+ v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
+
+ }
+
+
+ v = Math.max(1, v+adjust);
+
+ this.execCmd('FontSize', v );
+ },
-/**
- * @class Roo.bootstrap.HtmlEditor
- * @extends Roo.bootstrap.TextArea
- * Bootstrap HtmlEditor class
+ onEditorEvent : function(e){
+ this.owner.fireEvent('editorevent', this, e);
+ // this.updateToolbar();
+ this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
+ },
- * @constructor
- * Create a new HtmlEditor
- * @param {Object} config The config object
- */
+ insertTag : function(tg)
+ {
+ // could be a bit smarter... -> wrap the current selected tRoo..
+ if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
+
+ range = this.createRange(this.getSelection());
+ var wrappingNode = this.doc.createElement(tg.toLowerCase());
+ wrappingNode.appendChild(range.extractContents());
+ range.insertNode(wrappingNode);
-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
- });
-};
+ return;
+
+
+
+ }
+ this.execCmd("formatblock", tg);
+
+ },
+
+ insertText : function(txt)
+ {
+
+
+ var range = this.createRange();
+ range.deleteContents();
+ //alert(Sender.getAttribute('label'));
+
+ range.insertNode(this.doc.createTextNode(txt));
+ } ,
+
+
+ /**
+ * Executes a Midas editor command on the editor document and performs necessary focus and
+ * toolbar updates. <b>This should only be called after the editor is initialized.</b>
+ * @param {String} cmd The Midas command
+ * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
+ */
+ relayCmd : function(cmd, value){
+ this.win.focus();
+ this.execCmd(cmd, value);
+ this.owner.fireEvent('editorevent', this);
+ //this.updateToolbar();
+ this.owner.deferFocus();
+ },
-Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
-
-
- /**
- * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
+ /**
+ * Executes a Midas editor command directly on the editor document.
+ * For visual commands, you should use {@link #relayCmd} instead.
+ * <b>This should only be called after the editor is initialized.</b>
+ * @param {String} cmd The Midas command
+ * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
*/
- toolbars : false,
+ execCmd : function(cmd, value){
+ this.doc.execCommand(cmd, false, value === undefined ? null : value);
+ this.syncValue();
+ },
+
+
- /**
- * @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.
- *
+ * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
+ * to insert tRoo.
+ * @param {String} text | dom node..
*/
- stylesheets: false,
-
- // id of frame..
- frameId: false,
-
- // private properties
- validationEvent : false,
- deferHeight: true,
- initialized : false,
- activated : false,
-
- onFocus : Roo.emptyFn,
- iframePad:3,
- hideMode:'offsets',
-
-
- tbContainer : false,
-
- toolbarContainer :function() {
- return this.wrap.select('.x-html-editor-tb',true).first();
+ insertAtCursor : function(text)
+ {
+
+
+
+ if(!this.activated){
+ return;
+ }
+ /*
+ if(Roo.isIE){
+ this.win.focus();
+ var r = this.doc.selection.createRange();
+ if(r){
+ r.collapse(true);
+ r.pasteHTML(text);
+ this.syncValue();
+ this.deferFocus();
+
+ }
+ return;
+ }
+ */
+ if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
+ this.win.focus();
+
+
+ // from jquery ui (MIT licenced)
+ var range, node;
+ var win = this.win;
+
+ if (win.getSelection && win.getSelection().getRangeAt) {
+ range = win.getSelection().getRangeAt(0);
+ node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
+ range.insertNode(node);
+ } else if (win.document.selection && win.document.selection.createRange) {
+ // no firefox support
+ var txt = typeof(text) == 'string' ? text : text.outerHTML;
+ win.document.selection.createRange().pasteHTML(txt);
+ } else {
+ // no firefox support
+ var txt = typeof(text) == 'string' ? text : text.outerHTML;
+ this.execCmd('InsertHTML', txt);
+ }
+
+ this.syncValue();
+
+ this.deferFocus();
+ }
+ },
+ // private
+ mozKeyPress : function(e){
+ if(e.ctrlKey){
+ var c = e.getCharCode(), cmd;
+
+ if(c > 0){
+ c = String.fromCharCode(c).toLowerCase();
+ switch(c){
+ case 'b':
+ cmd = 'bold';
+ break;
+ case 'i':
+ cmd = 'italic';
+ break;
+
+ case 'u':
+ cmd = 'underline';
+ break;
+
+ case 'v':
+ this.cleanUpPaste.defer(100, this);
+ return;
+
+ }
+ if(cmd){
+ this.win.focus();
+ this.execCmd(cmd);
+ this.deferFocus();
+ e.preventDefault();
+ }
+
+ }
+ }
},
- /**
- * 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(){
+ // private
+ fixKeys : function(){ // load time branching for fastest keydown performance
+ if(Roo.isIE){
+ return function(e){
+ var k = e.getKey(), r;
+ if(k == e.TAB){
+ e.stopEvent();
+ r = this.doc.selection.createRange();
+ if(r){
+ r.collapse(true);
+ r.pasteHTML('    ');
+ this.deferFocus();
+ }
+ return;
+ }
+
+ if(k == e.ENTER){
+ r = this.doc.selection.createRange();
+ if(r){
+ var target = r.parentElement();
+ if(!target || target.tagName.toLowerCase() != 'li'){
+ e.stopEvent();
+ r.pasteHTML('<br />');
+ r.collapse(false);
+ r.select();
+ }
+ }
+ }
+ if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+ this.cleanUpPaste.defer(100, this);
+ return;
+ }
+
+
+ };
+ }else if(Roo.isOpera){
+ return function(e){
+ var k = e.getKey();
+ if(k == e.TAB){
+ e.stopEvent();
+ this.win.focus();
+ this.execCmd('InsertHTML','    ');
+ this.deferFocus();
+ }
+ if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+ this.cleanUpPaste.defer(100, this);
+ return;
+ }
+
+ };
+ }else if(Roo.isSafari){
+ return function(e){
+ var k = e.getKey();
+
+ if(k == e.TAB){
+ e.stopEvent();
+ this.execCmd('InsertText','\t');
+ this.deferFocus();
+ return;
+ }
+ if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+ this.cleanUpPaste.defer(100, this);
+ return;
+ }
+
+ };
+ }
+ }(),
+
+ getAllAncestors: function()
+ {
+ var p = this.getSelectedNode();
+ var a = [];
+ if (!p) {
+ a.push(p); // push blank onto stack..
+ p = this.getParentElement();
+ }
- Roo.log("create toolbars");
- this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
- this.toolbars[0].render(this.toolbarContainer());
+ while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
+ a.push(p);
+ p = p.parentNode;
+ }
+ a.push(this.doc.body);
+ return a;
+ },
+ lastSel : false,
+ lastSelNode : false,
+
+
+ getSelection : function()
+ {
+ this.assignDocWin();
+ return Roo.isIE ? this.doc.selection : this.win.getSelection();
+ },
+
+ getSelectedNode: function()
+ {
+ // this may only work on Gecko!!!
+
+ // should we cache this!!!!
- return;
-// 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 range = this.createRange(this.getSelection()).cloneRange();
+
+ if (Roo.isIE) {
+ var parent = range.parentElement();
+ while (true) {
+ var testRange = range.duplicate();
+ testRange.moveToElementText(parent);
+ if (testRange.inRange(range)) {
+ break;
}
- });
-
+ if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
+ break;
+ }
+ parent = parent.parentElement;
+ }
+ return parent;
}
- this.createToolbar(this);
-
- if(!this.width && this.resizable){
- this.setSize(this.wrap.getSize());
+ // is ancestor a text element.
+ var ac = range.commonAncestorContainer;
+ if (ac.nodeType == 3) {
+ ac = ac.parentNode;
}
- if (this.resizeEl) {
- this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
- // should trigger onReize..
+
+ var ar = ac.childNodes;
+
+ var nodes = [];
+ var other_nodes = [];
+ var has_other_nodes = false;
+ for (var i=0;i<ar.length;i++) {
+ if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
+ continue;
+ }
+ // fullly contained node.
+
+ if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
+ nodes.push(ar[i]);
+ continue;
+ }
+
+ // probably selected..
+ if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
+ other_nodes.push(ar[i]);
+ continue;
+ }
+ // outer..
+ if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
+ continue;
+ }
+
+
+ has_other_nodes = true;
+ }
+ if (!nodes.length && other_nodes.length) {
+ nodes= other_nodes;
+ }
+ if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
+ return false;
}
+ return nodes[0];
},
-
- // private
- onResize : function(w, h)
+ createRange: function(sel)
{
- Roo.log('resize: ' +w + ',' + h );
- Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
- var ew = false;
- var eh = false;
-
- 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;
+ // this has strange effects when using with
+ // top toolbar - not sure if it's a great idea.
+ //this.editor.contentWindow.focus();
+ if (typeof sel != "undefined") {
+ try {
+ return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
+ } catch(e) {
+ return this.doc.createRange();
}
+ } else {
+ return this.doc.createRange();
}
- Roo.log('onResize:' + [w,h,ew,eh].join(',') );
- this.editorcore.onResize(ew,eh);
-
},
-
- /**
- * Toggles the editor between standard and source edit mode.
- * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
- */
- toggleSourceEdit : function(sourceEditMode)
+ getParentElement: function()
{
- this.editorcore.toggleSourceEdit(sourceEditMode);
- if(this.editorcore.sourceEditMode){
- Roo.log('editor - showing textarea');
-
-// Roo.log('in');
-// Roo.log(this.syncValue());
- this.syncValue();
- this.inputEl().removeClass('hide');
- 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');
- this.inputEl().dom.setAttribute('tabIndex', -1);
- //this.deferFocus();
+ this.assignDocWin();
+ var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
+
+ var range = this.createRange(sel);
+
+ try {
+ var p = range.commonAncestorContainer;
+ while (p.nodeType == 3) { // text node
+ p = p.parentNode;
+ }
+ return p;
+ } catch (e) {
+ return null;
+ }
+
+ },
+ /***
+ *
+ * Range intersection.. the hard stuff...
+ * '-1' = before
+ * '0' = hits..
+ * '1' = after.
+ * [ -- selected range --- ]
+ * [fail] [fail]
+ *
+ * basically..
+ * if end is before start or hits it. fail.
+ * if start is after end or hits it fail.
+ *
+ * if either hits (but other is outside. - then it's not
+ *
+ *
+ **/
+
+
+ // @see http://www.thismuchiknow.co.uk/?p=64.
+ rangeIntersectsNode : function(range, node)
+ {
+ var nodeRange = node.ownerDocument.createRange();
+ try {
+ nodeRange.selectNode(node);
+ } catch (e) {
+ nodeRange.selectNodeContents(node);
}
+
+ var rangeStartRange = range.cloneRange();
+ rangeStartRange.collapse(true);
+
+ var rangeEndRange = range.cloneRange();
+ rangeEndRange.collapse(false);
+
+ var nodeStartRange = nodeRange.cloneRange();
+ nodeStartRange.collapse(true);
+
+ var nodeEndRange = nodeRange.cloneRange();
+ nodeEndRange.collapse(false);
+
+ return rangeStartRange.compareBoundaryPoints(
+ Range.START_TO_START, nodeEndRange) == -1 &&
+ rangeEndRange.compareBoundaryPoints(
+ Range.START_TO_START, nodeStartRange) == 1;
+
- if(this.resizable){
- this.setSize(this.wrap.getSize());
+ },
+ rangeCompareNode : function(range, node)
+ {
+ var nodeRange = node.ownerDocument.createRange();
+ try {
+ nodeRange.selectNode(node);
+ } catch (e) {
+ nodeRange.selectNodeContents(node);
}
- 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(){
+ range.collapse(true);
+
+ nodeRange.collapse(true);
+
+ var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
+ var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
+
+ //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
+ var nodeIsBefore = ss == 1;
+ var nodeIsAfter = ee == -1;
+ if (nodeIsBefore && nodeIsAfter)
+ return 0; // outer
+ if (!nodeIsBefore && nodeIsAfter)
+ return 1; //right trailed.
- 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();
- }
+ if (nodeIsBefore && !nodeIsAfter)
+ return 2; // left trailed.
+ // fully contined.
+ return 3;
},
- // private
- onFirstFocus : function(){
- //Roo.log("onFirstFocus");
- this.editorcore.onFirstFocus();
- for (var i =0; i < this.toolbars.length;i++) {
- this.toolbars[i].onFirstFocus();
+ // private? - in a new class?
+ cleanUpPaste : function()
+ {
+ // cleans up the whole document..
+ Roo.log('cleanuppaste');
+
+ this.cleanUpChildren(this.doc.body);
+ var clean = this.cleanWordChars(this.doc.body.innerHTML);
+ if (clean != this.doc.body.innerHTML) {
+ this.doc.body.innerHTML = clean;
}
},
- // private
- syncValue : function()
- {
- this.editorcore.syncValue();
+ cleanWordChars : function(input) {// change the chars to hex code
+ var he = Roo.HtmlEditorCore;
+
+ var output = input;
+ Roo.each(he.swapCodes, function(sw) {
+ var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
+
+ output = output.replace(swapper, sw[1]);
+ });
+
+ return output;
},
- pushValue : function()
- {
- this.editorcore.pushValue();
- }
-
-
- // 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.namespace('Roo.bootstrap.htmleditor');
-/**
- * @class Roo.bootstrap.HtmlEditorToolbar1
- * Basic Toolbar
- *
- * 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,
+ cleanUpChildren : function (n)
+ {
+ if (!n.childNodes.length) {
+ return;
+ }
+ for (var i = n.childNodes.length-1; i > -1 ; i--) {
+ this.cleanUpChild(n.childNodes[i]);
+ }
+ },
- formats : [
- "p" ,
- "h1","h2","h3","h4","h5","h6",
- "pre", "code",
- "abbr", "acronym", "address", "cite", "samp", "var",
- 'div','span'
- ],
+
- onRender : function(ct, position)
+ cleanUpChild : function (node)
{
- // Roo.log("Call onRender: " + this.xtype);
+ var ed = this;
+ //console.log(node);
+ if (node.nodeName == "#text") {
+ // clean up silly Windows -- stuff?
+ return;
+ }
+ if (node.nodeName == "#comment") {
+ node.parentNode.removeChild(node);
+ // clean up silly Windows -- stuff?
+ return;
+ }
+
+ if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
+ // remove node.
+ node.parentNode.removeChild(node);
+ return;
+
+ }
+
+ var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
- 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){
-
- var event = toggle ? 'toggle' : 'click';
-
- var a = {
- size : 'sm',
- xtype: 'Button',
- xns: Roo.bootstrap,
- glyphicon : id,
- cmd : id || cmd,
- enableToggle:toggle !== false,
- //html : 'submit'
- pressed : toggle ? false : null,
- listeners : {}
+ // remove <a name=....> as rendering on yahoo mailer is borked with this.
+ // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
+
+ //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
+ // remove_keep_children = true;
+ //}
+
+ if (remove_keep_children) {
+ this.cleanUpChildren(node);
+ // inserts everything just before this node...
+ while (node.childNodes.length) {
+ var cn = node.childNodes[0];
+ node.removeChild(cn);
+ node.parentNode.insertBefore(cn, node);
}
- a.listeners[toggle ? 'toggle' : 'click'] = function() {
- handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
+ node.parentNode.removeChild(node);
+ return;
+ }
+
+ if (!node.attributes || !node.attributes.length) {
+ this.cleanUpChildren(node);
+ return;
+ }
+
+ function cleanAttr(n,v)
+ {
+
+ if (v.match(/^\./) || v.match(/^\//)) {
+ return;
}
- children.push(a);
- return a;
- }
+ if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
+ return;
+ }
+ if (v.match(/^#/)) {
+ return;
+ }
+// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
+ node.removeAttribute(n);
+
+ }
- var style = {
- xtype: 'Button',
- size : 'sm',
- xns: Roo.bootstrap,
- glyphicon : 'font',
- //html : 'submit'
- menu : {
- xtype: 'Menu',
- xns: Roo.bootstrap,
- items: []
+ function cleanStyle(n,v)
+ {
+ if (v.match(/expression/)) { //XSS?? should we even bother..
+ node.removeAttribute(n);
+ return;
+ }
+ var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
+ var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
+
+
+ var parts = v.split(/;/);
+ var clean = [];
+
+ Roo.each(parts, function(p) {
+ p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
+ if (!p.length) {
+ return 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();
- }
+ var l = p.split(':').shift().replace(/\s+/g,'');
+ l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
+
+ if ( cblack.indexOf(l) > -1) {
+// Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
+ //node.removeAttribute(n);
+ return true;
}
+ //Roo.log()
+ // only allow 'c whitelisted system attributes'
+ if ( cwhite.length && cwhite.indexOf(l) < 0) {
+// Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
+ //node.removeAttribute(n);
+ return true;
+ }
+
+
+
+ clean.push(p);
+ return true;
});
- });
- children.push(style);
+ if (clean.length) {
+ node.setAttribute(n, clean.join(';'));
+ } else {
+ node.removeAttribute(n);
+ }
+ }
+
+
+ for (var i = node.attributes.length-1; i > -1 ; i--) {
+ var a = node.attributes[i];
+ //console.log(a);
- 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 (a.name.toLowerCase().substr(0,2)=='on') {
+ node.removeAttribute(a.name);
+ continue;
}
- }),
- btn('list','insertunorderedlist',true);
- btn('pencil', false,true, function(btn){
- Roo.log(this);
-
- this.toggleSourceEdit(btn.pressed);
- });
- /*
- var cog = {
- xtype: 'Button',
- size : 'sm',
- xns: Roo.bootstrap,
- glyphicon : 'cog',
- //html : 'submit'
- menu : {
- xtype: 'Menu',
- xns: Roo.bootstrap,
- items: []
+ if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
+ node.removeAttribute(a.name);
+ continue;
+ }
+ if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
+ cleanAttr(a.name,a.value); // fixme..
+ continue;
+ }
+ if (a.name == 'style') {
+ cleanStyle(a.name,a.value);
+ continue;
+ }
+ /// clean up MS crap..
+ // tecnically this should be a list of valid class'es..
+
+
+ if (a.name == 'class') {
+ if (a.value.match(/^Mso/)) {
+ node.className = '';
}
- };
-
- cog.menu.items.push({
- xtype :'MenuItem',
- xns: Roo.bootstrap,
- html : Clean styles,
- tagname : f,
- listeners : {
- click : function()
- {
- editorcore.insertTag(this.tagname);
- editor.focus();
+
+ if (a.value.match(/body/)) {
+ node.className = '';
}
+ continue;
}
- });
- */
-
-
- this.xtype = 'NavSimplebar';
-
- for(var i=0;i< children.length;i++) {
-
- this.buttons.add(this.addxtypeChild(children[i]));
+ // style cleanup!?
+ // class cleanup?
}
- editor.on('editorevent', this.updateToolbar, this);
- },
- onBtnClick : function(id)
- {
- this.editorcore.relayCmd(id);
- this.editorcore.focus();
+
+ this.cleanUpChildren(node);
+
+
},
-
/**
- * 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.
+ * Clean up MS wordisms...
*/
- updateToolbar: function(){
-
- if(!this.editorcore.activated){
- this.editor.onFirstFocus(); // is this neeed?
+ cleanWord : function(node)
+ {
+ var _t = this;
+ var cleanWordChildren = function()
+ {
+ if (!node.childNodes.length) {
+ return;
+ }
+ for (var i = node.childNodes.length-1; i > -1 ; i--) {
+ _t.cleanWord(node.childNodes[i]);
+ }
+ }
+
+
+ if (!node) {
+ this.cleanWord(this.doc.body);
+ return;
+ }
+ if (node.nodeName == "#text") {
+ // clean up silly Windows -- stuff?
+ return;
+ }
+ if (node.nodeName == "#comment") {
+ node.parentNode.removeChild(node);
+ // clean up silly Windows -- stuff?
+ return;
+ }
+
+ if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
+ node.parentNode.removeChild(node);
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'));
+ // remove - but keep children..
+ if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
+ while (node.childNodes.length) {
+ var cn = node.childNodes[0];
+ node.removeChild(cn);
+ node.parentNode.insertBefore(cn, node);
+ }
+ node.parentNode.removeChild(node);
+ cleanWordChildren();
+ return;
+ }
+ // clean styles
+ if (node.className.length) {
+
+ 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");
+ }
+ }
- //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
- btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
- /*
+ if (node.hasAttribute("lang")) {
+ node.removeAttribute("lang");
+ }
- var ans = this.editorcore.getAllAncestors();
- if (this.formatCombo) {
-
+ if (node.hasAttribute("style")) {
- 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;
+ 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');
}
}
+ cleanWordChildren();
- // 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){
+ domToHTML : function(currentElement, depth, nopadtext) {
-
- if(sourceEditMode){
- Roo.log("disabling buttons");
- this.buttons.each( function(item){
- if(item.cmd != 'pencil'){
- item.disable();
+ 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 currentElement.nodeValue;
+ }
+
+
+ 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{
- Roo.log("enabling buttons");
- if(this.editorcore.initialized){
- this.buttons.each( function(item){
- item.enable();
- });
+ } 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);
+ 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;
+
}
- Roo.log("calling toggole on editor");
- // tell the editor that it's been pressed..
- this.editor.toggleSourceEdit(sourceEditMode);
+
+ // 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'
+];
-/*
- * 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.
+// 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
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * HtmlEditor
+ *
*/
-
/**
- * @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.
+ * @class Roo.bootstrap.HtmlEditor
+ * @extends Roo.bootstrap.TextArea
+ * Bootstrap HtmlEditor class
+
* @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 = {};
+ * Create a new HtmlEditor
+ * @param {Object} config The config object
+ */
- // 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++){
- var c = config[i];
- if(typeof c.dataIndex == "undefined"){
- c.dataIndex = i;
- }
- if(typeof c.renderer == "string"){
- c.renderer = Roo.util.Format[c.renderer];
- }
- if(typeof c.id == "undefined"){
- c.id = Roo.id();
- }
- if(c.editor && c.editor.xtype){
- c.editor = Roo.factory(c.editor, Roo.grid);
- }
- if(c.editor && c.editor.isFormField){
- c.editor = new Roo.grid.GridEditor(c.editor);
- }
- this.lookup[c.id] = c;
+Roo.bootstrap.HtmlEditor = function(config){
+ Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
+ if (!this.toolbars) {
+ this.toolbars = [];
}
-
- /**
- * The width of columns which have no width specified (defaults to 100)
- * @type Number
- */
- this.defaultWidth = 100;
-
- /**
- * Default sortable of columns which have no sortable specified (defaults to false)
- * @type Boolean
- */
- this.defaultSortable = false;
-
+ this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
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);
+ /**
+ * @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.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 uses the raw data value.
- */
- /**
- * @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.
- */
-
- /**
- * 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;
- },
- /**
- * Returns the column for a specified id.
- * @param {String} id The column id
- * @return {Object} the column
- */
- getColumnById : function(id){
- return this.lookup[id];
- },
+Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
- /**
- * Returns the column 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
+ /**
+ * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
*/
- getIndexById : function(id){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(this.config[i].id == id){
- return i;
- }
- }
- return -1;
- },
+ toolbars : false,
+
+ /**
+ * @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,
/**
- * Returns the index for a specified column dataIndex.
- * @param {String} dataIndex The column dataIndex
- * @return {Number} the index, or -1 if not found
+ * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+ *
*/
+ stylesheets: false,
- findColumnIndex : function(dataIndex){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(this.config[i].dataIndex == dataIndex){
- return i;
- }
- }
- return -1;
- },
+ // id of frame..
+ frameId: false,
+ // private properties
+ validationEvent : false,
+ deferHeight: true,
+ initialized : false,
+ activated : false,
- 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);
+ onFocus : Roo.emptyFn,
+ iframePad:3,
+ hideMode:'offsets',
+
+
+ tbContainer : false,
+
+ toolbarContainer :function() {
+ return this.wrap.select('.x-html-editor-tb',true).first();
},
- isLocked : function(colIndex){
- return this.config[colIndex].locked === true;
+ /**
+ * 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("create toolbars");
+
+ this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
+ this.toolbars[0].render(this.toolbarContainer());
+
+ return;
+
+// 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);
+// }
},
- setLocked : function(colIndex, value, suppressEvent){
- if(this.isLocked(colIndex) == value){
- return;
+
+ // 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
+ }
+ }
+ });
+
}
- this.config[colIndex].locked = value;
- if(!suppressEvent){
- this.fireEvent("columnlockchange", this, colIndex, value);
+ this.createToolbar(this);
+
+
+ if(!this.width && this.resizable){
+ this.setSize(this.wrap.getSize());
}
- },
-
- 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);
- }
+ if (this.resizeEl) {
+ this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
+ // should trigger onReize..
}
- return totalWidth;
+
},
- getLockedCount : function(){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(!this.isLocked(i)){
- return i;
+ // private
+ onResize : function(w, h)
+ {
+ Roo.log('resize: ' +w + ',' + h );
+ Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
+ var ew = false;
+ var eh = false;
+
+ 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);
+
},
/**
- * Returns the number of columns.
- * @return {Number}
+ * Toggles the editor between standard and source edit mode.
+ * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
*/
- 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;
+ toggleSourceEdit : function(sourceEditMode)
+ {
+ this.editorcore.toggleSourceEdit(sourceEditMode);
+
+ if(this.editorcore.sourceEditMode){
+ Roo.log('editor - showing textarea');
+
+// Roo.log('in');
+// Roo.log(this.syncValue());
+ this.syncValue();
+ this.inputEl().removeClass('hide');
+ 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');
+ this.inputEl().dom.setAttribute('tabIndex', -1);
+ //this.deferFocus();
}
- return this.config.length;
+
+ if(this.resizable){
+ this.setSize(this.wrap.getSize());
+ }
+
+ this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
},
+
+ // private (for BoxComponent)
+ adjustSize : Roo.BoxComponent.prototype.adjustSize,
- /**
- * 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;
- }
- }
- return r;
+ // private (for BoxComponent)
+ getResizeEl : function(){
+ return this.wrap;
},
- /**
- * 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;
- }
- return this.config[col].sortable;
+ // private (for BoxComponent)
+ getPositionEl : function(){
+ return this.wrap;
},
- /**
- * 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;
- }
- return this.config[col].renderer;
+ // private
+ initEvents : function(){
+ this.originalValue = this.getValue();
},
- /**
- * 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;
+// /**
+// * 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();
},
- /**
- * Returns the width for the specified column.
- * @param {Number} col The column index
- * @return {Number}
- */
- getColumnWidth : function(col){
- return this.config[col].width * 1 || this.defaultWidth;
+
+ // private
+ deferFocus : function(){
+ this.focus.defer(10, this);
},
- /**
- * 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);
- }
+ // doc'ed in Field
+ focus : function(){
+ this.editorcore.focus();
+
},
+
- /**
- * 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);
- }
+ // private
+ 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();
}
- return this.totalWidth;
},
- /**
- * Returns the header for the specified column.
- * @param {Number} col The column index
- * @return {String}
- */
- getColumnHeader : function(col){
- return this.config[col].header;
+ // private
+ onFirstFocus : function(){
+ //Roo.log("onFirstFocus");
+ this.editorcore.onFirstFocus();
+ for (var i =0; i < this.toolbars.length;i++) {
+ this.toolbars[i].onFirstFocus();
+ }
+
},
-
- /**
- * 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);
+
+ // private
+ syncValue : function()
+ {
+ this.editorcore.syncValue();
},
-
+
+ pushValue : function()
+ {
+ this.editorcore.pushValue();
+ }
+
+
+ // hide stuff that is not compatible
/**
- * Returns the tooltip for the specified column.
- * @param {Number} col The column index
- * @return {String}
+ * @event blur
+ * @hide
*/
- 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
+ * @event change
+ * @hide
*/
- setColumnTooltip : function(col, tooltip){
- this.config[col].tooltip = tooltip;
- },
-
/**
- * Returns the dataIndex for the specified column.
- * @param {Number} col The column index
- * @return {Number}
+ * @event focus
+ * @hide
*/
- 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
+ * @event specialkey
+ * @hide
*/
- setDataIndex : function(col, dataIndex){
- this.config[col].dataIndex = dataIndex;
- },
-
-
-
/**
- * Returns true if the cell is editable.
- * @param {Number} colIndex The column index
- * @param {Number} rowIndex The row index
- * @return {Boolean}
+ * @cfg {String} fieldClass @hide
*/
- 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}
+ * @cfg {String} focusClass @hide
*/
- 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
+ * @cfg {String} autoCreate @hide
*/
- setEditable : function(col, editable){
- this.config[col].editable = editable;
- },
-
-
/**
- * Returns true if the column is hidden.
- * @param {Number} colIndex The column index
- * @return {Boolean}
+ * @cfg {String} inputType @hide
*/
- isHidden : function(colIndex){
- return this.config[colIndex].hidden;
- },
-
-
/**
- * Returns true if the column width cannot be changed
+ * @cfg {String} invalidClass @hide
*/
- isFixed : function(colIndex){
- return this.config[colIndex].fixed;
- },
-
/**
- * Returns true if the column can be resized
- * @return {Boolean}
+ * @cfg {String} invalidText @hide
*/
- 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
+ * @cfg {String} msgFx @hide
*/
- setHidden : function(colIndex, hidden){
- this.config[colIndex].hidden = hidden;
- this.totalWidth = null;
- this.fireEvent("hiddenchange", this, colIndex, hidden);
+ /**
+ * @cfg {String} validateOnBlur @hide
+ */
+});
+
+
+
+
+
+
+Roo.namespace('Roo.bootstrap.htmleditor');
+/**
+ * @class Roo.bootstrap.HtmlEditorToolbar1
+ * Basic Toolbar
+ *
+ * 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)
+ {
+ // 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){
+
+ var event = toggle ? 'toggle' : 'click';
+
+ var a = {
+ size : 'sm',
+ xtype: 'Button',
+ xns: Roo.bootstrap,
+ glyphicon : id,
+ cmd : id || cmd,
+ enableToggle:toggle !== false,
+ //html : 'submit'
+ 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 style = {
+ xtype: 'Button',
+ size : 'sm',
+ xns: Roo.bootstrap,
+ glyphicon : '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);
+ });
+ /*
+ 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();
+ },
+
/**
- * Sets the editor for a column.
- * @param {Number} col The column index
- * @param {Object} editor The editor object
+ * 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.
*/
- setEditor : function(col, editor){
- this.config[col].editor = 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;
+ }
+ }
+ }
+
+
+
+ // 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{
+ 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);
+
}
});
-Roo.grid.ColumnModel.defaultRenderer = function(value){
- if(typeof value == "string" && value.length < 1){
- return " ";
- }
- return value;
-};
-// Alias for backwards compatibility
-Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
+
+
/**
* @class Roo.bootstrap.Table.AbstractSelectionModel
// old args format still supported... - xtype is prefered..
// created from xtype...
var ds = config.dataSource;
- var items = [];
+ this.toolbarItems = [];
if (config.items) {
- items = config.items;
- config.items = [];
+ this.toolbarItems = config.items;
+// config.items = [];
}
Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
- // supprot items array.
-
- Roo.each(items, function(e) {
- this.add(Roo.factory(e));
- },this);
-
-
-
-
-
};
Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
*/
refreshText : "Refresh",
+ buttons : false,
// private
onRender : function(ct, position)
{
this.navgroup.onRender(this.el, null);
// add the buttons to the navgroup
+ if(this.displayInfo){
+ Roo.log(this.el.select('ul.navbar-nav',true).first());
+ this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
+ this.displayEl = this.el.select('.x-paging-info', true).first();
+// var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
+// this.displayEl = navel.el.select('span',true).first();
+ }
+
+ var _this = this;
+
+ if(this.buttons){
+ Roo.each(_this.buttons, function(e){
+ Roo.factory(e).onRender(_this.el, null);
+ });
+ }
+
+ Roo.each(_this.toolbarItems, function(e) {
+ _this.navgroup.addItem(e);
+ });
+
this.first = this.navgroup.addItem({
tooltip: this.firstText,
cls: "prev",
this.field.on("focus", function(){this.dom.select();});
- this.afterTextEl = field.el.select('.x-paging-after').first();
+ this.afterTextEl = field.el.select('.x-paging-after',true).first();
//this.field.setHeight(18);
//this.addSeparator();
this.next = this.navgroup.addItem({
listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
});
- if(this.displayInfo){
- var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info' } );
- this.displayEl = navel.el.select('a',true).first();
- }
-
},
// private
// private
onLoad : function(ds, r, o){
this.cursor = o.params ? o.params.start : 0;
- var d = this.getPageData(), ap = d.activePage, ps = d.pages;
-
- this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
+ var d = this.getPageData(),
+ ap = d.activePage,
+ ps = d.pages;
+
+ this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
this.field.dom.value = ap;
this.first.setDisabled(ap == 1);
this.prev.setDisabled(ap == 1);
-
\ No newline at end of file
+ /*
+ * - LGPL
+ *
+ * Graph
+ *
+ */
+
+
+/**
+ * @class Roo.bootstrap.Graph
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Graph class
+> Prameters
+ -sm {number} sm 4
+ -md {number} md 5
+ -graphtype {String} graphtype bar | vbar | pie
+ -g_x {number} x coodinator | centre x (pie)
+ -g_y {number} y coodinator | centre y (pie)
+ -g_r {number} radius (pie)
+ -g_height {number} height of the chart (respected by all elements in the set)
+ -g_width {number} width of the chart (respected by all elements in the set)
+ -{Array} values
+ -opts (object) options for the chart
+ o {
+ o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
+ o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
+ o vgutter (number)
+ o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
+ o stacked (boolean) whether or not to tread values as in a stacked bar chart
+ o to
+ o stretch (boolean)
+ o }
+ -opts (object) options for the pie
+ o{
+ o cut
+ o startAngle (number)
+ o endAngle (number)
+ }
+ *
+ * @constructor
+ * Create a new Input
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Graph = function(config){
+ Roo.bootstrap.Graph.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ // img events
+ /**
+ * @event click
+ * The img click event for the img.
+ * @param {Roo.EventObject} e
+ */
+ "click" : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
+
+ sm: 4,
+ md: 5,
+ graphtype: 'bar',
+ g_height: 250,
+ g_width: 650,
+ g_x: 50,
+ g_y: 50,
+ g_r: 30,
+ opts:{
+ //g_colors: this.colors,
+ g_type: 'soft',
+ g_gutter: '20%'
+
+ },
+
+ getAutoCreate : function(){
+
+ var cfg = {
+ tag: 'div',
+ html : null
+ }
+
+
+ return cfg;
+ },
+
+ onRender : function(ct,position){
+ Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
+ var r = {};
+ this.raphael = Raphael(this.el.dom),
+ data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
+ data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
+ data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
+ txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
+ /*
+ r.text(160, 10, "Single Series Chart").attr(txtattr);
+ r.text(480, 10, "Multiline Series Chart").attr(txtattr);
+ r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
+ r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
+
+ r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
+ r.barchart(330, 10, 300, 220, data1);
+ r.barchart(10, 250, 300, 220, data2, {stacked: true});
+ r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
+ */
+
+ // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
+ // r.barchart(30, 30, 560, 250, xdata, {
+ // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
+ // axis : "0 0 1 1",
+ // axisxlabels : xdata
+ // //yvalues : cols,
+
+ // });
+
+ },
+
+ load : function(graphtype,xdata,opts){
+ this.raphael.clear();
+ if(!graphtype) {
+ graphtype = this.graphtype;
+ }
+ if(!opts){
+ opts = this.opts;
+ }
+ switch(graphtype){
+ case 'bar':
+ this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts);
+ break;
+ case 'hbar':
+ this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts);
+ break;
+ case 'pie':
+ this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts);
+ break;
+
+ }
+ },
+
+ initEvents: function() {
+
+ if(!this.href){
+ this.el.on('click', this.onClick, this);
+ }
+ },
+
+ onClick : function(e)
+ {
+ Roo.log('img onclick');
+ this.fireEvent('click', this, e);
+ }
+
+});
+
+
+/*
+ * - LGPL
+ *
+ * numberBox
+ *
+ */
+
+
+/**
+ * @class Roo.bootstrap.dash.NumberBox
+ * @extends Roo.bootstrap.Component
+ * Bootstrap NumberBox class
+ * @cfg {number} sm 4
+ * @cfg {number} md 5
+ * @cfg {String} headline
+ * @cfg {String} title
+ * @cfg {String} more info url
+ * @cfg {String} more info text
+ * @cfg {Array} opts values
+ *
+ * @constructor
+ * Create a new Input
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.dash.NumberBox = function(config){
+ Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ // img events
+ /**
+ * @event click
+ * The img click event for the img.
+ * @param {Roo.EventObject} e
+ */
+ "click" : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
+
+ width: 200,
+ height: 150,
+ headline: '',
+ title: 'Title',
+ more_url: '',
+ more_text: '',
+
+ getAutoCreate : function(){
+
+ var cfg = {
+ tag: 'div',
+ cls: '',
+ html : null,
+ cn: [
+ {
+ tag: 'h',
+ cls: '',
+ html: this.headline ? this.headline : 'Headline'
+ },
+ {
+ tag: 'p',
+ cls: '',
+ html: this.title ? this.title : 'Title'
+ },
+ {
+ tag: 'div',
+ cls: '',
+ html : null,
+ cn: [{
+ tag: 'a',
+ href: this.more_url,
+ cls: '',
+ html: this.more_text
+ }]
+
+ }]
+ }
+
+
+ return cfg;
+ },
+
+ // onRender : function(ct,position){
+ // Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
+
+ // },
+
+ // load : function(graphtype,xdata){
+ // this.raphael.clear();
+ // if(!graphtype) {
+ // graphtype = this.graphtype;
+ // }
+ // switch(graphtype){
+ // case 'bar':
+ // this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,this.opts);
+ // break;
+ // case 'hbar':
+ // this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,this.opts);
+ // break;
+ // case 'pie':
+ // this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,this.opts);
+ // break;
+
+ // }
+ // },
+
+ initEvents: function() {
+
+ if(!this.href){
+ this.el.on('click', this.onClick, this);
+ }
+ },
+
+ onClick : function(e)
+ {
+ Roo.log('img onclick');
+ this.fireEvent('click', this, e);
+ }
+
+});
+
+