}
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);
}
unmask : function()
{
this.maskEl.hide();
- }
+ }
+
},
+ addItem : function(cfg)
+ {
+ var cn = new Roo.bootstrap.NavItem(cfg);
+ this.register(cn);
+ cn.parentId = this.id;
+ cn.onRender(this.el, null);
+ return cn;
+ },
register : function(item)
{
- this.navItems.push( item);
- item.navId = this.navId;
+ this.navItems.push( item);
+ item.navId = this.navId;
},
getNavItem: function(tabId)
});
return ret;
}
+
+
+
+
});
*
*/
-/**
- * @class Roo.bootstrap.Navbar.Item
- * @extends Roo.bootstrap.Component
- * Bootstrap Navbar.Button class
- * @cfg {String} href link to
- * @cfg {String} html content of button
- * @cfg {String} badge text inside badge
- * @cfg {String} glyphicon name of glyphicon
- * @cfg {String} icon name of font awesome icon
- * @cfg {Boolean} active Is item active
- * @cfg {Boolean} preventDefault (true | false) default false
- * @cfg {String} tabId the tab that this item activates.
-
- * @constructor
- * Create a new Navbar Button
- * @param {Object} config The config object
- */
-Roo.bootstrap.Navbar.Item = function(config){
- Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
- this.addEvents({
- // raw events
- /**
- * @event click
- * The raw click event for the entire grid.
- * @param {Roo.EventObject} e
- */
- "click" : true,
- /**
- * @event changed
- * Fires when the active item active state changes
- * @param {Roo.bootstrap.Navbar.Item} this
- * @param {boolean} state the new state
-
- */
- 'changed': true
- });
-
-};
-
-Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
-
- href: false,
- html: '',
- badge: '',
- icon: false,
- glyphicon: false,
- active: false,
- preventDefault : false,
- tabId : false,
-
- getAutoCreate : function(){
-
- var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
-
- if (this.parent().parent().sidebar === true) {
- cfg = {
- tag: 'li',
- cls: '',
- cn: [
- {
- tag: 'p',
- cls: ''
- }
- ]
- }
-
- if (this.html) {
- cfg.cn[0].html = this.html;
- }
-
- if (this.active) {
- this.cls += ' active';
- }
-
- if (this.menu) {
- cfg.cn[0].cls += ' dropdown-toggle';
- cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
- }
-
- if (this.href) {
- cfg.cn[0].tag = 'a',
- cfg.cn[0].href = this.href;
- }
-
- if (this.glyphicon) {
- cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
- }
-
- if (this.icon) {
- cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
- }
-
- return cfg;
- }
-
- cfg = {
- tag: 'li',
- cls: 'nav-item'
- }
-
- if (this.active) {
- cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
- }
-
- cfg.cn = [
- {
- tag: 'p',
- html: 'Text'
- }
- ];
-
- if (this.glyphicon) {
- if(cfg.html){cfg.html = ' ' + this.html};
- cfg.cn=[
- {
- tag: 'span',
- cls: 'glyphicon glyphicon-' + this.glyphicon
- }
- ];
- }
-
- cfg.cn[0].html = this.html || cfg.cn[0].html ;
-
- if (this.menu) {
- cfg.cn[0].tag='a';
- cfg.cn[0].href='#';
- cfg.cn[0].html += " <span class='caret'></span>";
- //}else if (!this.href) {
- // cfg.cn[0].tag='p';
- // cfg.cn[0].cls='navbar-text';
- } else {
- cfg.cn[0].tag='a';
- cfg.cn[0].href=this.href||'#';
- cfg.cn[0].html=this.html;
- }
-
- if (this.badge !== '') {
-
- cfg.cn[0].cn=[
- cfg.cn[0].html + ' ',
- {
- tag: 'span',
- cls: 'badge',
- html: this.badge
- }
- ];
- cfg.cn[0].html=''
- }
-
- if (this.icon) {
- cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
- }
-
- return cfg;
- },
- initEvents: function() {
- // Roo.log('init events?');
- // Roo.log(this.el.dom);
- this.el.select('a',true).on('click', this.onClick, this);
- // at this point parent should be available..
- this.parent().register(this);
- },
-
- onClick : function(e)
- {
- if(this.preventDefault){
- e.preventDefault();
- }
-
- if (typeof (this.menu) != 'undefined') {
- this.menu.parentType = this.xtype;
- this.menu.triggerEl = this.el;
- this.addxtype(Roo.apply({}, this.menu));
- }
-
- if(this.fireEvent('click', this, e) === false){
- return;
- };
-
- if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
- if (typeof(this.parent().setActiveItem) !== 'undefined') {
- this.parent().setActiveItem(this);
- }
-
- }
- },
-
- isActive: function () {
- return this.active
- },
- setActive : function(state, fire)
- {
- this.active = state;
- if (!state ) {
- this.el.removeClass('active');
- } else if (!this.el.hasClass('active')) {
- this.el.addClass('active');
- }
- if (fire) {
- this.fireEvent('changed', this, state);
- }
-
-
- }
- // this should not be here...
-
-});
-
-
- /*
- * - LGPL
- *
- * row
- *
- */
-
/**
* @class Roo.bootstrap.NavItem
* @extends Roo.bootstrap.Component
* @cfg {String} glyphicon name of glyphicon
* @cfg {String} icon name of font awesome icon
* @cfg {Boolean} active Is item active
+ * @cfg {Boolean} disabled Is item disabled
+
* @cfg {Boolean} preventDefault (true | false) default false
* @cfg {String} tabId the tab that this item activates.
+ * @cfg {String} tagtype (a|span) render as a href or span?
* @constructor
* Create a new Navbar Item
active: false,
preventDefault : false,
tabId : false,
+ tagtype : 'a',
+ disabled : false,
getAutoCreate : function(){
cls: 'nav-item',
cn : [
{
- tag: 'a',
+ tag: this.tagtype,
href : this.href || "#",
html: this.html || ''
}
// glyphicon and icon go before content..
if (this.glyphicon || this.icon) {
if (this.icon) {
- cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html + '</span>'
+ cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
} else {
- cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span>' + cfg.cn[0].html;
+ cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
}
}
cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
}
-
+ if (this.disabled) {
+ cfg.cls += ' disabled';
+ }
return cfg;
initEvents: function() {
// Roo.log('init events?');
// Roo.log(this.el.dom);
- if (typeof (this.menu) != 'undefined') {
+ if (typeof (this.menu) != 'undefined') {
this.menu.parentType = this.xtype;
this.menu.triggerEl = this.el;
this.addxtype(Roo.apply({}, this.menu));
onClick : function(e)
{
+
if(this.preventDefault){
e.preventDefault();
}
-
+ if (this.disabled) {
+ return;
+ }
+ Roo.log("fire event clicked");
if(this.fireEvent('click', this, e) === false){
return;
};
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);
+ }
}
- }
+ },
// this should not be here...
-
+ setDisabled : function(state)
+ {
+ this.disabled = state;
+ if (!state ) {
+ this.el.removeClass('disabled');
+ } else if (!this.el.hasClass('disabled')) {
+ this.el.addClass('disabled');
+ }
+
+ }
});
});
/*
- * - 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
- *
-
+ * @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
- */
+ * @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.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.bootstrap.Table.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 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;
}
+
+ /**
+ * 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];
+ },
-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,
+ /**
+ * 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;
+ },
- 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';
+ /**
+ * 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;
+ }
}
- if (this.hover) {
- cfg.cls += ' table-hover';
+ return -1;
+ },
+
+ /**
+ * 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;
+ }
}
- if (this.bordered) {
- cfg.cls += ' table-bordered';
+ return -1;
+ },
+
+
+ moveColumn : function(oldIndex, newIndex){
+ var c = this.config[oldIndex];
+ this.config.splice(oldIndex, 1);
+ this.config.splice(newIndex, 0, c);
+ this.dataMap = null;
+ this.fireEvent("columnmoved", this, oldIndex, newIndex);
+ },
+
+ isLocked : function(colIndex){
+ return this.config[colIndex].locked === true;
+ },
+
+ setLocked : function(colIndex, value, suppressEvent){
+ if(this.isLocked(colIndex) == value){
+ return;
}
- 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;
+ this.config[colIndex].locked = value;
+ if(!suppressEvent){
+ this.fireEvent("columnlockchange", this, colIndex, value);
}
- if (this.frame) {
- cfg.frame=this.frame;
+ },
+
+ 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.rules) {
- cfg.rules=this.rules;
+ return totalWidth;
+ },
+
+ getLockedCount : function(){
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(!this.isLocked(i)){
+ return i;
+ }
}
- if (this.sortable) {
- cfg.sortable=this.sortable;
+ },
+
+ /**
+ * 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++;
+ }
+ }
+ return c;
}
- if (this.summary) {
- cfg.summary=this.summary;
+ 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;
+ }
}
- if (this.width) {
- cfg.width=this.width;
+ return r;
+ },
+
+ /**
+ * 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;
}
-
- if(this.store || this.cm){
- cfg.cn.push(this.renderHeader());
- cfg.cn.push(this.renderBody());
- cfg.cn.push(this.renderFooter());
-
- cfg.cls+= ' TableGrid';
+ return this.config[col].sortable;
+ },
+
+ /**
+ * Returns the rendering (formatting) function defined for the column.
+ * @param {Number} col The column index.
+ * @return {Function} The function used to render the cell. See {@link #setRenderer}.
+ */
+ getRenderer : function(col){
+ if(!this.config[col].renderer){
+ return Roo.grid.ColumnModel.defaultRenderer;
}
-
- return cfg;
+ return this.config[col].renderer;
},
-//
-// 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;
+
+ /**
+ * 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;
+ },
+
+ /**
+ * 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);
}
-
- 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');
-
- 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"
- }
-
- ]
+ },
+
+ /**
+ * 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.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();
-
-
-
+ return this.totalWidth;
},
-
- 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 header for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnHeader : function(col){
+ return this.config[col].header;
},
-
- 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';
- }
-
- header.cn.push(c)
- }
-
- return header;
+
+ /**
+ * Sets the header for a column.
+ * @param {Number} col The column index
+ * @param {String} header The new header
+ */
+ setColumnHeader : function(col, header){
+ this.config[col].header = header;
+ this.fireEvent("headerchange", this, col, header);
},
-
- renderBody : function()
- {
- var body = {
- tag: 'tbody',
- cn : []
- };
-
- return body;
+
+ /**
+ * Returns the tooltip for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnTooltip : function(col){
+ return this.config[col].tooltip;
},
-
- renderFooter : function()
- {
- var footer = {
- tag: 'tfoot',
- cn : []
- };
-
- return footer;
+ /**
+ * 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;
},
-
- 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']);
- }
-
- if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
- e.addClass(['glyphicon', 'glyphicon-arrow-down']);
- }
- });
-
- 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);
-
- }
-
- tbody.createChild(row);
-
- });
- }
-
-
- if(renders.length){
- var _this = this;
- Roo.each(renders, function(r){
- _this.renderColumn(r);
- })
- }
- if(this.loadMask){
- this.maskEl.hide();
- }
+ /**
+ * Returns the dataIndex for the specified column.
+ * @param {Number} col The column index
+ * @return {Number}
+ */
+ getDataIndex : function(col){
+ return this.config[col].dataIndex;
},
-
- onBeforeLoad : function()
- {
- Roo.log('ds onBeforeLoad');
-
- this.clear();
-
- if(this.loadMask){
- this.maskEl.show();
- }
+
+ /**
+ * 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;
},
+
- 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;
+ /**
+ * 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;
},
-
- 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);
- })
- }
- }
-
-});
-
+ /**
+ * 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;
+ },
- /*
- * - LGPL
- *
- * table cell
- *
- */
+ /**
+ * 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;
+ },
-/**
- * @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
- *
+
+ /**
+ * 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;
+};
+
+// Alias for backwards compatibility
+Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.LoadMask
+ * A simple utility class for generically masking elements while loading data. If the element being masked has
+ * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
+ * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
+ * element's UpdateManager load indicator and will be destroyed after the initial load.
* @constructor
- * Create a new TableCell
+ * 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.TableCell = function(config){
- Roo.bootstrap.TableCell.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.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,
-
+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.TableCell.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag: 'td'
- }
+ onLoadException : function()
+ {
+ Roo.log(arguments);
- if(this.tag){
- cfg.tag = this.tag;
+ 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.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;
- }
-
-});
+ 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);
+ }
+ },
- /*
+ // 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);
+ }
+ }
+};/*
* - LGPL
*
- * table row
+ * table
*
*/
/**
- * @class Roo.bootstrap.TableRow
+ * @class Roo.bootstrap.Table
* @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
+ * 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 TableRow
+ * Create a new Table
* @param {Object} config The config object
*/
-Roo.bootstrap.TableRow = function(config){
- Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
+Roo.bootstrap.Table = function(config){
+ Roo.bootstrap.Table.superclass.constructor.call(this, config);
- cls: false,
- align: false,
- bgcolor: false,
- charoff: false,
- valign: false,
-
- getAutoCreate : function(){
- var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag: 'tr'
- }
-
- if(this.cls){
- cfg.cls = this.cls;
- }
- if(this.align){
- cfg.align = this.align;
- }
- if(this.bgcolor){
- cfg.bgcolor = this.bgcolor;
- }
- if(this.charoff){
- cfg.charoff = this.charoff;
- }
- if(this.valign){
- cfg.valign = this.valign;
- }
-
- return cfg;
+ if (this.sm) {
+ this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
+ this.sm = this.selModel;
+ this.sm.xmodule = this.xmodule || false;
}
-
-});
-
-
-
- /*
- * - LGPL
- *
- * table body
- *
- */
-
-/**
- * @class Roo.bootstrap.TableBody
- * @extends Roo.bootstrap.Component
- * Bootstrap TableBody class
- * @cfg {String} cls element class
- * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
- * @cfg {String} align Aligns the content inside the element
- * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
- * @cfg {String} valign Vertical aligns the content inside the <tbody> element
- *
- * @constructor
- * Create a new TableBody
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TableBody = function(config){
- Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+ 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.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
+ cfg.cls+= ' ' +this.cls;
}
- if(this.tag){
- cfg.tag = this.tag;
+
+ // this lot should be simplifed...
+
+ if (this.align) {
+ cfg.align=this.align;
}
-
- if(this.align){
- cfg.align = this.align;
+ if (this.bgcolor) {
+ cfg.bgcolor=this.bgcolor;
}
- if(this.charoff){
- cfg.charoff = this.charoff;
+ if (this.border) {
+ cfg.border=this.border;
}
- if(this.valign){
- cfg.valign = this.valign;
+ 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 + ';'));
}
- return cfg;
- }
+ 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 { cn : [ cfg ] };
+ },
+ 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);
+
+ },
-// 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){
+ 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;
- 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;
+ if(this.CellSelection){
+ this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
}
- 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.RowSelection){
+ this.fireEvent('rowclick', this, row, rowIndex, e);
}
- 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);
+
+ onDblClick : 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('celldblclick', this, cell, rowIndex, cellIndex, e);
+ }
+
+ if(this.RowSelection){
+ this.fireEvent('rowdblclick', this, row, rowIndex, e);
}
- 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()
+ sort : function(e,el)
{
- if (!this.form.progressUrl) {
+ var col = Roo.get(el)
+
+ if(!col.hasClass('sortable')){
return;
}
- if (!this.haveProgress) {
- Roo.MessageBox.progress("Uploading", "Uploading");
- }
- if (this.uploadComplete) {
- Roo.MessageBox.hide();
- return;
+ var sort = col.attr('sort');
+ var dir = 'ASC';
+
+ if(col.hasClass('glyphicon-arrow-up')){
+ dir = 'DESC';
}
- this.haveProgress = true;
-
- var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
+ this.store.sortInfo = {field : sort, direction : dir};
- 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
- });
-
+ if (this.footer) {
+ Roo.log("calling footer first");
+ this.footer.onClick('first');
+ } else {
+
+ this.store.load({ params : { start : 0 } });
+ }
},
-
- run : function()
+ renderHeader : function()
{
- // run get Values on the form, so it syncs any secondary forms.
- this.form.getValues();
+ var header = {
+ tag: 'thead',
+ cn : []
+ };
- var o = this.options;
- var method = this.getMethod();
- var isPost = method == 'POST';
- if(o.clientValidation === false || this.form.isValid()){
+ var cm = this.cm;
+
+ 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',
+ var tbody = this.el.select('tbody', true).first();
+
+ 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 : ''
+ }
- run : function(){
+ _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);
+
+ });
+ }
- Roo.Ajax.request(Roo.apply(
- this.createCallback(), {
- method:this.getMethod(),
- url:this.getUrl(false),
- params:this.getParams()
- }));
- },
+
+ if(renders.length){
+ var _this = this;
+ Roo.each(renders, function(r){
+ _this.renderColumn(r);
+ })
+ }
- success : function(response){
+ //if(this.loadMask){
+ // this.maskEl.hide();
+ //}
+ },
+
+ onBeforeLoad : function()
+ {
+ //Roo.log('ds onBeforeLoad');
- 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.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();
}
- this.form.clearInvalid();
- this.form.setValues(result.data);
- this.form.afterAction(this, true);
+ return this.selModel;
},
-
- 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
- };
+
+ 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
-
- *
- * @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
- });
-
-};
+ * Bootstrap TableCell class
+ * @cfg {String} html cell contain text
+ * @cfg {String} cls cell class
+ * @cfg {String} tag cell tag (td|th) default td
+ * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
+ * @cfg {String} align Aligns the content in a cell
+ * @cfg {String} axis Categorizes cells
+ * @cfg {String} bgcolor Specifies the background color of a cell
+ * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
+ * @cfg {Number} colspan Specifies the number of columns a cell should span
+ * @cfg {String} headers Specifies one or more header cells a cell is related to
+ * @cfg {Number} height Sets the height of a cell
+ * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
+ * @cfg {Number} rowspan Sets the number of rows a cell should span
+ * @cfg {String} scope Defines a way to associate header cells and data cells in a table
+ * @cfg {String} valign Vertical aligns the content in a cell
+ * @cfg {Number} width Specifies the width of a cell
+ *
+ * @constructor
+ * Create a new TableCell
+ * @param {Object} config The config object
+ */
-Roo.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',
+Roo.bootstrap.TableCell = function(config){
+ Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
+};
- // 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
+ }
+ 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 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...');
+
+ return cfg;
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * table row
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.TableRow
+ * @extends Roo.bootstrap.Component
+ * Bootstrap TableRow class
+ * @cfg {String} cls row class
+ * @cfg {String} align Aligns the content in a table row
+ * @cfg {String} bgcolor Specifies a background color for a table row
+ * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
+ * @cfg {String} valign Vertical aligns the content in a table row
+ *
+ * @constructor
+ * Create a new TableRow
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.TableRow = function(config){
+ Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
+
+ cls: false,
+ align: false,
+ bgcolor: false,
+ charoff: false,
+ valign: false,
+
+ getAutoCreate : function(){
+ var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
+
+ cfg = {
+ tag: 'tr'
+ }
+
+ if(this.cls){
+ cfg.cls = this.cls;
+ }
+ if(this.align){
+ cfg.align = this.align;
+ }
+ if(this.bgcolor){
+ cfg.bgcolor = this.bgcolor;
+ }
+ if(this.charoff){
+ cfg.charoff = this.charoff;
+ }
+ if(this.valign){
+ cfg.valign = this.valign;
+ }
+
+ return cfg;
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * table body
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.TableBody
+ * @extends Roo.bootstrap.Component
+ * Bootstrap TableBody class
+ * @cfg {String} cls element class
+ * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
+ * @cfg {String} align Aligns the content inside the element
+ * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
+ * @cfg {String} valign Vertical aligns the content inside the <tbody> element
+ *
+ * @constructor
+ * Create a new TableBody
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.TableBody = function(config){
+ Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
+
+ cls: false,
+ tag: false,
+ align: false,
+ charoff: false,
+ valign: false,
+
+ getAutoCreate : function(){
+ var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
+
+ cfg = {
+ tag: 'tbody'
+ }
+
+ 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;
+ }
+
+ 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){
+
+ 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.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;
+ }
+ }
+ 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);
+ }
+ 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.haveProgress) {
+ Roo.MessageBox.progress("Uploading", "Uploading");
+ }
+ if (this.uploadComplete) {
+ Roo.MessageBox.hide();
+ return;
+ }
+
+ this.haveProgress = true;
+
+ var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
+
+ 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()){
+
+ 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);
+ }
+ },
+
+ success : function(response)
+ {
+ this.uploadComplete= true;
+ if (this.haveProgress) {
+ Roo.MessageBox.hide();
+ }
+
+
+ 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);
+ },
+ failure : function(response)
+ {
+ this.uploadComplete= true;
+ if (this.haveProgress) {
+ Roo.MessageBox.hide();
+ }
+
+ 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
+ };
+ }
+ 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()
+ }));
+ },
+
+ 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, {
+
+ /**
+ * @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,
+
+
+
+ /**
+ * 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 = {
+ 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.labelAlign == 'left' ) {
+ cfg.cls += ' form-horizontal';
+ }
+
+
+ return cfg;
+ },
+ initEvents : function()
+ {
+ this.el.on('submit', this.onSubmit, this);
+ this.el.on('keypress', function(e) {
+ if (e.getCharCode() != 13) {
+ return true;
+ }
+ e.preventDefault();
+ return false;
+ });
+
+ },
+ // 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.fireEvent('beforeaction', this, action) !== false){
+ this.beforeAction(action);
+ action.run.defer(100, action);
+ }
+ 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...');
// }
},
* @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
labelWidth : 2,
labelAlign : false,
readOnly : false,
+ align : false,
parentLabelAlign : function()
{
};
+ 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;
}
* @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
hasQuery: false,
append: false,
loadNext: false,
+ autoFocus : true,
item: [],
// element that contains real text value.. (when hidden is used..)
},
"enter" : function(e){
- this.onViewClick();
+// this.onViewClick();
//return true;
+ this.collapse();
+
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
},
"esc" : function(e){
if(this.editable){
this.inputEl().dom.select();
}
- if(!this.selectByValue(this.value, true)){
+ if(!this.selectByValue(this.value, true) && this.autoFocus){
this.select(0, true);
}
}else{
- this.selectNext();
+ if(this.autoFocus){
+ this.selectNext();
+ }
if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
this.taTask.delay(this.typeAheadDelay);
}
}
this.store.load(options);
- this.expand();
+ /*
+ * 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;
cn: [
{
tag: "img",
- src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
+ src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
},
{
tag: "span",
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();
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],
ev.els = [];
ev.rows = rows;
ev.cells = cells;
- ev.rendered = false;
-// for (var i = 0; i < cells.length;i++) {
-// cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
-//
-// }
+
+ cells[0].events.push(ev);
this.calevents.push(ev);
},
}
Roo.each(this.cells.elements, function(c){
- c.rows = [];
+ c.row = 0;
+ c.events = [];
c.more = [];
});
renderEvents: function()
{
- // first make sure there is enough space..
- this.cells.each(function(c) {
- c.rows = [];
- c.more = [];
- });
+ var _this = this;
- for (var e = 0; e < this.calevents.length; e++) {
+ this.cells.each(function(c) {
- var ev = this.calevents[e];
- var cells = ev.cells;
- var rows = ev.rows;
+ if(c.row < 5){
+ return;
+ }
- 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);
+ var ev = c.events;
+
+ var r = 4;
+ if(c.row != c.events.length){
+ r = 4 - (4 - (c.row - c.events.length));
}
- }
-
- var _this = this;
-
+
+ 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);
+
+ });
+
this.cells.each(function(c) {
- if(c.more.length && c.more.length == 1){
- c.rows.push(c.more.pop());
- }
- var r = (c.more.length) ? c.rows.length + 1 : c.rows.length;
- c.select('.fc-day-content div',true).first().setHeight(Math.max(34, r * 20));
+ c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
- for (var e = 0; e < c.rows.length; e++){
- var ev = c.rows[e];
-
- if(ev.rendered){
- continue;
- }
-
- var cells = ev.cells;
+ 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++) {
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)]);
+ 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);
cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
ev.els.push(cg);
- ev.rendered = true;
}
}
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.rows.length * 20)]);
+ 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);
{
tag: 'th',
cls: 'prev',
- html: '<i class="icon-arrow-left"/>'
+ html: '<i class="fa fa-arrow-left"/>'
},
{
tag: 'th',
{
tag: 'th',
cls: 'next',
- html: '<i class="icon-arrow-right"/>'
+ html: '<i class="fa fa-arrow-right"/>'
}
]
place: function()
{
- this.picker().removeClass(['bottom', 'top']);
+ this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
- 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;
+ 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');
}
- this.picker().addClass('bottom');
+ 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;
+ }
+ });
- this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
},
onFocus : function()
});
};
-Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
-
- inputType: 'checkbox',
- inputValue: 1,
- valueOff: 0,
- boxLabel: false,
- checked: false,
- weight : false,
-
- getAutoCreate : 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 || ''
-
- };
-
- if (this.weight) { // Validity check?
- cfg.cls += " checkbox-" + this.weight;
- }
-
- if (this.disabled) {
- input.disabled=true;
- }
-
- 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: 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
-
- });
- }
-
-
-
- return cfg;
-
- },
-
- /**
- * return the real input element.
- */
- inputEl: function ()
- {
- return this.el.select('input.roo-checkbox',true).first();
- },
-
- label: function()
- {
- return this.el.select('label.control-label',true).first();
- },
-
- initEvents : function()
- {
-// Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
-
- this.inputEl().on('click', this.onClick, this);
-
- },
-
- onClick : function()
- {
- this.setChecked(!this.checked);
- },
-
- setChecked : function(state,suppressEvent)
- {
- 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, {
+Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
- inputType: 'radio',
- inputValue: '',
- valueOff: '',
+ inputType: 'checkbox',
+ inputValue: 1,
+ valueOff: 0,
+ boxLabel: false,
+ checked: false,
+ weight : false,
getAutoCreate : function()
{
var cfg = {};
- cfg.cls = 'form-group radio' //input-group
+ 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-radio',
+ cls : 'roo-checkbox', //'form-box',
placeholder : this.placeholder || ''
};
- if (this.weight) { // Validity check?
- cfg.cls += " radio-" + this.weight;
+
+ if (this.weight) { // Validity check?
+ cfg.cls += " checkbox-" + this.weight;
}
+
if (this.disabled) {
input.disabled=true;
}
}
});
+
+
var inputblock = input;
+
+
+
if (this.before || this.after) {
inputblock = {
];
} else if ( this.fieldLabel.length) {
Roo.log(" label");
- cfg.cn = [
+ cfg.cn = [
{
- tag: 'label',
+ tag: this.boxLabel ? 'span' : 'label',
'for': id,
cls: 'control-label box-input-label',
//cls : 'input-group-addon',
} else {
- Roo.log(" no label && no align");
- cfg.cn = [
-
- inputblock
-
- ];
+ Roo.log(" no label && no align");
+ cfg.cn = [ inputblock ] ;
};
-
- if(this.boxLabel){
- cfg.cn.push({
+ if(this.boxLabel){
+ cfg.cn.push( {
tag: 'label',
'for': id,
cls: 'box-label',
html: this.boxLabel
- })
+
+ });
}
+
+
return cfg;
},
- inputEl: function ()
- {
- return this.el.select('input.roo-radio',true).first();
- },
- onClick : function()
- {
- this.setChecked(true);
- },
- setChecked : function(state,suppressEvent)
+ /**
+ * return the real input element.
+ */
+ inputEl: function ()
{
- if(state){
- Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
- v.dom.checked = false;
- });
- }
-
- 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;
-
+ return this.el.select('input.roo-checkbox',true).first();
},
- getGroupValue : function()
+ label: 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;
- }
- });
-
- return value;
+ return this.el.select('label.control-label',true).first();
},
- /**
- * 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);
-
- // 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);
- });
-
- } 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 +'" />'
- });
-
+ initEvents : function()
+ {
+// Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
+
+ this.inputEl().on('click', this.onClick, this);
+
+ },
+
+ onClick : function()
+ {
+ this.setChecked(!this.checked);
+ },
+
+ setChecked : function(state,suppressEvent)
+ {
+ this.checked = state;
+
+ this.inputEl().dom.checked = state;
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, state);
}
- st += '<style type="text/css">' +
- 'IMG { cursor: pointer } ' +
- '</style>';
-
+ this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
- 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>';
},
+
+ setValue : function(v,suppressEvent)
+ {
+ this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
+ }
+
+});
- // private
- onRender : function(ct, position)
+
+/*
+ * - 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 _t = this;
- //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
- this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
-
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
- this.el.dom.style.border = '0 none';
- this.el.dom.setAttribute('tabIndex', -1);
- this.el.addClass('x-hidden hide');
+ var id = Roo.id();
+ var cfg = {};
+ cfg.cls = 'form-group radio' //input-group
- if(Roo.isIE){ // fix IE 1px bogus margin
- this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
+ 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;
}
-
- this.frameId = Roo.id();
+ if(this.checked){
+ input.checked = this.checked;
+ }
-
+ if (this.name) {
+ input.name = this.name;
+ }
- 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
- );
+ 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];
+ }
+ });
- this.iframe = iframe.dom;
-
- this.assignDocWin();
+ var inputblock = input;
- this.doc.designMode = 'on';
-
- this.doc.open();
- this.doc.write(this.getDocMarkup());
- this.doc.close();
-
+ 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 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;
+ 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
+ ]
}
- Roo.TaskMgr.stop(task);
- this.initEditor.defer(10, this);
- }
- },
- interval : 10,
- duration: 10000,
- scope: this
- };
- Roo.TaskMgr.start(task);
+
+ ];
+ } 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
+ })
+ }
+
+ return cfg;
+
+ },
+ 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;
+ });
+ }
+
+ 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;
-
},
-
- // private
- onResize : function(w, h)
+
+ getGroupValue : function()
{
- 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'){
-
- this.iframe.style.height = h + 'px';
- if(this.doc){
- (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
+ 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;
}
- }
+ });
+ return value;
},
-
+
/**
- * Toggles the editor between standard and source edit mode.
- * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
+ * 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
*/
- toggleSourceEdit : function(sourceEditMode){
-
- this.sourceEditMode = sourceEditMode === true;
-
- if(this.sourceEditMode){
+ getValue : function(){
+ return this.getGroupValue();
+ }
+
+});
+
- 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);
- },
+//<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
+ });
+
+};
- /**
- * 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;
- },
- /**
- * 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);
- }
- }
- },
+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,
+
/**
- * Protected method that will not generally be called directly. Pushes the value of the textarea
- * into the iframe editor.
+ * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+ *
*/
- 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);
- },
-
- // doc'ed in Field
- focus : function(){
- if(this.win && !this.sourceEditMode){
- this.win.focus();
- }else{
- this.el.focus();
- }
- },
+ 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',
- assignDocWin: function()
- {
- var iframe = this.iframe;
+ 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);
- if(Roo.isIE){
- this.doc = iframe.contentWindow.document;
- this.win = iframe.contentWindow;
+ // 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);
+ });
+
+ } else if (!this.stylesheets.length) {
+ // simple..
+ st = '<style type="text/css">' +
+ 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
+ '</style>';
} 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;
+ Roo.each(this.stylesheets, function(s) {
+ st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
+ });
+
}
- },
-
- // 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');
- 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;
+ st += '<style type="text/css">' +
+ 'IMG { cursor: pointer } ' +
+ '</style>';
- this.owner.fireEvent('initialize', this);
- this.pushValue();
+
+ 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>';
},
// private
- onDestroy : function(){
-
+ onRender : function(ct, position)
+ {
+ var _t = this;
+ //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
+ this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
- 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();
- }
- },
-
- // private
- onFirstFocus : function(){
+ this.el.dom.style.border = '0 none';
+ this.el.dom.setAttribute('tabIndex', -1);
+ this.el.addClass('x-hidden hide');
- 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();
- }
- 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];
-
+ if(Roo.isIE){ // fix IE 1px bogus margin
+ this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
}
+
+ this.frameId = Roo.id();
- 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);
-
- return;
-
-
-
- }
- this.execCmd("formatblock", tg);
+
- },
-
- insertText : function(txt)
- {
+ 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
+ );
- 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();
- },
+ this.iframe = iframe.dom;
- /**
- * 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..
- */
- insertAtCursor : function(text)
- {
+ this.assignDocWin();
+ 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;
+ }
+ Roo.TaskMgr.stop(task);
+ this.initEditor.defer(10, this);
+ }
+ },
+ interval : 10,
+ duration: 10000,
+ scope: this
+ };
+ Roo.TaskMgr.start(task);
+
- if(!this.activated){
+
+ },
+
+ // private
+ onResize : function(w, h)
+ {
+ Roo.log('resize: ' +w + ',' + h );
+ //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
+ if(!this.iframe){
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();
+ if(typeof w == 'number'){
- }
- return;
+ this.iframe.style.width = w + 'px';
}
- */
- 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();
+ if(typeof h == 'number'){
- 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.iframe.style.height = h + 'px';
+ if(this.doc){
+ (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
}
}
+
},
- // 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();
- }
+ /**
+ * 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;
- while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
- a.push(p);
- p = p.parentNode;
+ 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();
}
- a.push(this.doc.body);
- return a;
+ //this.setSize(this.owner.wrap.getSize());
+ //this.fireEvent('editmodechange', this, this.sourceEditMode);
},
- lastSel : false,
- lastSelNode : false,
-
+
- getSelection : function()
- {
- this.assignDocWin();
- return Roo.isIE ? this.doc.selection : this.win.getSelection();
+
+
+ /**
+ * 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;
},
-
- getSelectedNode: function()
- {
- // this may only work on Gecko!!!
-
- // should we cache this!!!!
-
-
-
-
- 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;
+
+ /**
+ * 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 ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
- 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;
}
- parent = parent.parentElement;
+ return "&#"+cc+";"
+ });
+ if(this.owner.fireEvent('beforesync', this, html) !== false){
+ this.el.dom.value = html;
+ this.owner.fireEvent('sync', this, html);
}
- return parent;
- }
-
- // is ancestor a text element.
- var ac = range.commonAncestorContainer;
- if (ac.nodeType == 3) {
- ac = ac.parentNode;
}
-
- 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.
+ },
+
+ /**
+ * 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 (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
- nodes.push(ar[i]);
- continue;
- }
+// if(v.length < 1){
+// v = ' ';
+// }
- // 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;
+ 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);
}
-
-
- 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];
},
- 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();
- }
- } else {
- return this.doc.createRange();
- }
+
+ // private
+ deferFocus : function(){
+ this.focus.defer(10, this);
},
- 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;
- }
- return p;
- } catch (e) {
- return null;
+
+ // doc'ed in Field
+ focus : function(){
+ if(this.win && !this.sourceEditMode){
+ this.win.focus();
+ }else{
+ this.el.focus();
}
-
},
- /***
- *
- * 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)
+ assignDocWin: function()
{
- 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;
+ var iframe = this.iframe;
-
- },
- rangeCompareNode : function(range, node)
- {
- var nodeRange = node.ownerDocument.createRange();
- try {
- nodeRange.selectNode(node);
- } catch (e) {
- nodeRange.selectNodeContents(node);
+ 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;
}
-
-
- 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;
+ // private
+ initEditor : function(){
+ //console.log("INIT EDITOR");
+ this.assignDocWin();
- if (nodeIsBefore && nodeIsAfter)
- return 0; // outer
- if (!nodeIsBefore && nodeIsAfter)
- return 1; //right trailed.
- if (nodeIsBefore && !nodeIsAfter)
- return 2; // left trailed.
- // fully contined.
- return 3;
- },
-
- // 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;
- }
+ this.doc.designMode="on";
+ this.doc.open();
+ this.doc.write(this.getDocMarkup());
+ this.doc.close();
- },
-
- cleanWordChars : function(input) {// change the chars to hex code
- var he = Roo.HtmlEditorCore;
+ 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 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]);
+ //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
});
-
- return output;
- },
-
-
- cleanUpChildren : function (n)
- {
- if (!n.childNodes.length) {
- return;
+ if(Roo.isGecko){
+ Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
}
- for (var i = n.childNodes.length-1; i > -1 ; i--) {
- this.cleanUpChild(n.childNodes[i]);
+ if(Roo.isIE || Roo.isSafari || Roo.isOpera){
+ Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
}
+ this.initialized = true;
+
+ this.owner.fireEvent('initialize', this);
+ this.pushValue();
},
-
-
+
+ // private
+ onDestroy : function(){
-
- cleanUpChild : function (node)
- {
- 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;
+
+ 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();
}
+ },
+
+ // private
+ onFirstFocus : function(){
- var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
-
- // 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..
+ this.assignDocWin();
- //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);
+ 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();
}
- node.parentNode.removeChild(node);
- return;
- }
-
- if (!node.attributes || !node.attributes.length) {
- this.cleanUpChildren(node);
- return;
+ try{
+ this.execCmd('useCSS', true);
+ this.execCmd('styleWithCSS', false);
+ }catch(e){}
}
-
- function cleanAttr(n,v)
- {
-
- if (v.match(/^\./) || v.match(/^\//)) {
- return;
- }
- if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
- return;
- }
- if (v.match(/^#/)) {
- return;
- }
-// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
- node.removeAttribute(n);
+ 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];
}
- 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);
- }
-
- }
+ v = Math.max(1, v+adjust);
- 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..
+ 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);
+
+ return;
- if (a.name == 'class') {
- if (a.value.match(/^Mso/)) {
- node.className = '';
- }
-
- if (a.value.match(/body/)) {
- node.className = '';
- }
- continue;
- }
- // style cleanup!?
- // class cleanup?
}
+ this.execCmd("formatblock", tg);
-
- this.cleanUpChildren(node);
+ },
+
+ 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();
},
+
/**
- * Clean up MS wordisms...
+ * 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..
*/
- cleanWord : function(node)
+ insertAtCursor : function(text)
{
- 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);
+ if(!this.activated){
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);
+ /*
+ if(Roo.isIE){
+ this.win.focus();
+ var r = this.doc.selection.createRange();
+ if(r){
+ r.collapse(true);
+ r.pasteHTML(text);
+ this.syncValue();
+ this.deferFocus();
+
}
- node.parentNode.removeChild(node);
- cleanWordChildren();
return;
}
- // clean styles
- if (node.className.length) {
+ */
+ if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
+ this.win.focus();
- var cn = node.className.split(/\W+/);
- var cna = [];
- Roo.each(cn, function(cls) {
- if (cls.match(/Mso[a-zA-Z]+/)) {
- return;
+
+ // 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;
+
}
- cna.push(cls);
- });
- node.className = cna.length ? cna.join(' ') : '';
- if (!cna.length) {
- node.removeAttribute("class");
+ if(cmd){
+ this.win.focus();
+ this.execCmd(cmd);
+ this.deferFocus();
+ e.preventDefault();
+ }
+
}
}
-
- if (node.hasAttribute("lang")) {
- node.removeAttribute("lang");
- }
-
- if (node.hasAttribute("style")) {
-
- var styles = node.getAttribute("style").split(";");
- var nstyle = [];
- Roo.each(styles, function(s) {
- if (!s.match(/:/)) {
+ },
+
+ // 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;
}
- var kv = s.split(":");
- if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
+
+ 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;
}
- // what ever is left... we allow.
- nstyle.push(s);
- });
- node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
- if (!nstyle.length) {
- node.removeAttribute('style');
- }
+
+
+ };
+ }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();
}
-
- cleanWordChildren();
+ while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
+ a.push(p);
+ p = p.parentNode;
+ }
+ a.push(this.doc.body);
+ return a;
},
- domToHTML : function(currentElement, depth, nopadtext) {
+ 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!!!!
+
+
+
+
+ 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;
+ }
- depth = depth || 0;
- nopadtext = nopadtext || false;
+ // is ancestor a text element.
+ var ac = range.commonAncestorContainer;
+ if (ac.nodeType == 3) {
+ ac = ac.parentNode;
+ }
- if (!currentElement) {
- return this.domToHTML(this.doc.body);
+ 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.
- //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;
+ if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
+ nodes.push(ar[i]);
+ continue;
}
-
- 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;
+ // probably selected..
+ if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
+ other_nodes.push(ar[i]);
+ continue;
}
-
-
- // 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);
+ // outer..
+ if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
+ continue;
}
- ret += innerHTML;
- if (!allText) {
- // The remaining code is mostly for formatting the tree
- ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
+ 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];
+ },
+ 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();
}
-
-
- if (tagName) {
- ret+= "</"+tagName+">";
+ } 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;
}
- return ret;
-
+ return p;
+ } catch (e) {
+ return null;
}
- // 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'
-];
+ },
+ /***
+ *
+ * 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);
+ }
-Roo.HtmlEditorCore.aclean = [
- 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
-];
-
-// protocols..
-Roo.HtmlEditorCore.pwhite= [
- 'http', 'https', 'mailto'
-];
-
-// white listed style attributes.
-Roo.HtmlEditorCore.cwhite= [
- // 'text-align', /// default is to allow most things..
-
-
-// 'font-size'//??
-];
-
-// black listed style attributes.
-Roo.HtmlEditorCore.cblack= [
- // 'font-size' -- this can be set by the project
-];
-
-
-Roo.HtmlEditorCore.swapCodes =[
- [ 8211, "--" ],
- [ 8212, "--" ],
- [ 8216, "'" ],
- [ 8217, "'" ],
- [ 8220, '"' ],
- [ 8221, '"' ],
- [ 8226, "*" ],
- [ 8230, "..." ]
-];
-
- /*
- * - LGPL
- *
- * HtmlEditor
- *
- */
-
-/**
- * @class Roo.bootstrap.HtmlEditor
- * @extends Roo.bootstrap.TextArea
- * Bootstrap HtmlEditor class
-
- * @constructor
- * Create a new HtmlEditor
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.HtmlEditor = function(config){
- Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
- if (!this.toolbars) {
- this.toolbars = [];
- }
- this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
- this.addEvents({
- /**
- * @event initialize
- * Fires when the editor is fully initialized (including the iframe)
- * @param {HtmlEditor} this
- */
- initialize: true,
- /**
- * @event activate
- * Fires when the editor is first receives the focus. Any insertion must wait
- * until after this event.
- * @param {HtmlEditor} this
- */
- activate: true,
- /**
- * @event beforesync
- * Fires before the textarea is updated with content from the editor iframe. Return false
- * to cancel the sync.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- beforesync: true,
- /**
- * @event beforepush
- * Fires before the iframe editor is updated with content from the textarea. Return false
- * to cancel the push.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- beforepush: true,
- /**
- * @event sync
- * Fires when the textarea is updated with content from the editor iframe.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- sync: true,
- /**
- * @event push
- * Fires when the iframe editor is updated with content from the textarea.
- * @param {HtmlEditor} this
- * @param {String} html
- */
- push: true,
- /**
- * @event editmodechange
- * Fires when the editor switches edit modes
- * @param {HtmlEditor} this
- * @param {Boolean} sourceEdit True if source edit, false if standard editing.
- */
- editmodechange: true,
- /**
- * @event editorevent
- * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
- * @param {HtmlEditor} this
- */
- editorevent: true,
- /**
- * @event firstfocus
- * Fires when on first focus - needed by toolbars..
- * @param {HtmlEditor} this
- */
- firstfocus: true,
- /**
- * @event autosave
- * Auto save the htmlEditor value as a file into Events
- * @param {HtmlEditor} this
- */
- autosave: true,
- /**
- * @event savedpreview
- * preview the saved version of htmlEditor
- * @param {HtmlEditor} this
- */
- savedpreview: true
- });
-};
-
-
-Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
+ var rangeStartRange = range.cloneRange();
+ rangeStartRange.collapse(true);
+ var rangeEndRange = range.cloneRange();
+ rangeEndRange.collapse(false);
- /**
- * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
- */
- 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,
+ var nodeStartRange = nodeRange.cloneRange();
+ nodeStartRange.collapse(true);
- /**
- * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
- *
- */
- stylesheets: false,
+ var nodeEndRange = nodeRange.cloneRange();
+ nodeEndRange.collapse(false);
- // id of frame..
- frameId: false,
+ return rangeStartRange.compareBoundaryPoints(
+ Range.START_TO_START, nodeEndRange) == -1 &&
+ rangeEndRange.compareBoundaryPoints(
+ Range.START_TO_START, nodeStartRange) == 1;
+
+
+ },
+ rangeCompareNode : function(range, node)
+ {
+ var nodeRange = node.ownerDocument.createRange();
+ try {
+ nodeRange.selectNode(node);
+ } catch (e) {
+ nodeRange.selectNodeContents(node);
+ }
+
+
+ range.collapse(true);
- // private properties
- validationEvent : false,
- deferHeight: true,
- initialized : false,
- activated : false,
+ 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 (nodeIsBefore && !nodeIsAfter)
+ return 2; // left trailed.
+ // fully contined.
+ return 3;
+ },
+
+ // 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;
+ }
+
+ },
- onFocus : Roo.emptyFn,
- iframePad:3,
- hideMode:'offsets',
+ 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;
+ },
+
+
+ cleanUpChildren : function (n)
+ {
+ if (!n.childNodes.length) {
+ return;
+ }
+ for (var i = n.childNodes.length-1; i > -1 ; i--) {
+ this.cleanUpChild(n.childNodes[i]);
+ }
+ },
- tbContainer : false,
+
- toolbarContainer :function() {
- return this.wrap.select('.x-html-editor-tb',true).first();
- },
-
- /**
- * Protected method that will not generally be called directly. It
- * is called when the editor creates its toolbar. Override this method if you need to
- * add custom toolbar buttons.
- * @param {HtmlEditor} editor
- */
- createToolbar : function(){
+ cleanUpChild : function (node)
+ {
+ 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;
+ }
- Roo.log("create toolbars");
+ if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
+ // remove node.
+ node.parentNode.removeChild(node);
+ return;
+
+ }
- this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
- this.toolbars[0].render(this.toolbarContainer());
+ var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
+
+ // 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);
+ }
+ 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;
+ }
+ if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
+ return;
+ }
+ if (v.match(/^#/)) {
+ return;
+ }
+// 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);
+ }
+
+ }
+
+
+ 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..
+
+
+ if (a.name == 'class') {
+ if (a.value.match(/^Mso/)) {
+ node.className = '';
+ }
+
+ if (a.value.match(/body/)) {
+ node.className = '';
+ }
+ continue;
+ }
+
+ // style cleanup!?
+ // class cleanup?
+
+ }
+
+
+ this.cleanUpChildren(node);
- 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)
+ /**
+ * Clean up MS wordisms...
+ */
+ cleanWord : function(node)
{
- // 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'}
- });
+ var cleanWordChildren = function()
+ {
+ if (!node.childNodes.length) {
+ return;
+ }
+ for (var i = node.childNodes.length-1; i > -1 ; i--) {
+ _t.cleanWord(node.childNodes[i]);
+ }
+ }
- 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
- }
+
+ 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;
+ }
+
+ // 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");
+ }
}
- this.createToolbar(this);
-
- if(!this.width && this.resizable){
- this.setSize(this.wrap.getSize());
- }
- if (this.resizeEl) {
- this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
- // should trigger onReize..
+ if (node.hasAttribute("lang")) {
+ node.removeAttribute("lang");
}
- },
-
- // 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();
- //}
+ if (node.hasAttribute("style")) {
+
+ var styles = node.getAttribute("style").split(";");
+ var nstyle = [];
+ Roo.each(styles, function(s) {
+ if (!s.match(/:/)) {
+ return;
}
-
-
-
-
-
- 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;
+ 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');
}
}
- 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)
- {
- 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();
- }
-
- if(this.resizable){
- this.setSize(this.wrap.getSize());
- }
+ cleanWordChildren();
- 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(){
-
+ domToHTML : function(currentElement, depth, nopadtext) {
+ depth = depth || 0;
+ nopadtext = nopadtext || false;
- if(this.rendered){
+ if (!currentElement) {
+ return this.domToHTML(this.doc.body);
+ }
- for (var i =0; i < this.toolbars.length;i++) {
- // fixme - ask toolbars for heights?
- this.toolbars[i].onDestroy();
+ //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( " " );
+ }
+ 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;
- this.wrap.dom.innerHTML = '';
- this.wrap.remove();
- }
- },
-
- // private
- onFirstFocus : function(){
- //Roo.log("onFirstFocus");
- this.editorcore.onFirstFocus();
- for (var i =0; i < this.toolbars.length;i++) {
- this.toolbars[i].onFirstFocus();
}
-
- },
-
- // private
- syncValue : function()
- {
- this.editorcore.syncValue();
- },
-
- pushValue : function()
- {
- this.editorcore.pushValue();
- }
-
// hide stuff that is not compatible
/**
* @cfg {String} validateOnBlur @hide
*/
});
-
+
+Roo.HtmlEditorCore.white = [
+ 'area', 'br', 'img', 'input', 'hr', 'wbr',
+
+ 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
+ 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
+ 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
+ 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
+ 'table', 'ul', 'xmp',
+
+ 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
+ 'thead', 'tr',
+
+ 'dir', 'menu', 'ol', 'ul', 'dl',
+
+ 'embed', 'object'
+];
+
+
+Roo.HtmlEditorCore.black = [
+ // 'embed', 'object', // enable - backend responsiblity to clean thiese
+ 'applet', //
+ 'base', 'basefont', 'bgsound', 'blink', 'body',
+ 'frame', 'frameset', 'head', 'html', 'ilayer',
+ 'iframe', 'layer', 'link', 'meta', 'object',
+ 'script', 'style' ,'title', 'xml' // clean later..
+];
+Roo.HtmlEditorCore.clean = [
+ 'script', 'style', 'title', 'xml'
+];
+Roo.HtmlEditorCore.remove = [
+ 'font'
+];
+// attributes..
+
+Roo.HtmlEditorCore.ablack = [
+ 'on'
+];
-
-
-
+Roo.HtmlEditorCore.aclean = [
+ 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
+];
+
+// protocols..
+Roo.HtmlEditorCore.pwhite= [
+ 'http', 'https', 'mailto'
+];
+
+// white listed style attributes.
+Roo.HtmlEditorCore.cwhite= [
+ // 'text-align', /// default is to allow most things..
+
+// 'font-size'//??
+];
-/**
- * @class Roo.bootstrap.HtmlEditorToolbar1
- * Basic Toolbar
- *
- * Usage:
+// black listed style attributes.
+Roo.HtmlEditorCore.cblack= [
+ // 'font-size' -- this can be set by the project
+];
+
+
+Roo.HtmlEditorCore.swapCodes =[
+ [ 8211, "--" ],
+ [ 8212, "--" ],
+ [ 8216, "'" ],
+ [ 8217, "'" ],
+ [ 8220, '"' ],
+ [ 8221, '"' ],
+ [ 8226, "*" ],
+ [ 8230, "..." ]
+];
+
+ /*
+ * - LGPL
*
- new Roo.bootstrap.HtmlEditor({
- ....
- toolbars : [
- new Roo.bootstrap.HtmlEditorToolbar1({
- disable : { fonts: 1 , format: 1, ..., ... , ...],
- btns : [ .... ]
- })
- }
-
- *
- * @cfg {Object} disable List of elements to disable..
- * @cfg {Array} btns List of additional buttons.
- *
+ * HtmlEditor
*
- * NEEDS Extra CSS?
- * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
*/
-
-Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
-{
-
- 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);
+
+/**
+ * @class Roo.bootstrap.HtmlEditor
+ * @extends Roo.bootstrap.TextArea
+ * Bootstrap HtmlEditor class
+
+ * @constructor
+ * Create a new HtmlEditor
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.HtmlEditor = function(config){
+ Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
+ if (!this.toolbars) {
+ this.toolbars = [];
+ }
+ this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
+ this.addEvents({
+ /**
+ * @event initialize
+ * Fires when the editor is fully initialized (including the iframe)
+ * @param {HtmlEditor} this
+ */
+ initialize: true,
+ /**
+ * @event activate
+ * Fires when the editor is first receives the focus. Any insertion must wait
+ * until after this event.
+ * @param {HtmlEditor} this
+ */
+ activate: true,
+ /**
+ * @event beforesync
+ * Fires before the textarea is updated with content from the editor iframe. Return false
+ * to cancel the sync.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ beforesync: true,
+ /**
+ * @event beforepush
+ * Fires before the iframe editor is updated with content from the textarea. Return false
+ * to cancel the push.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ beforepush: true,
+ /**
+ * @event sync
+ * Fires when the textarea is updated with content from the editor iframe.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ sync: true,
+ /**
+ * @event push
+ * Fires when the iframe editor is updated with content from the textarea.
+ * @param {HtmlEditor} this
+ * @param {String} html
+ */
+ push: true,
+ /**
+ * @event editmodechange
+ * Fires when the editor switches edit modes
+ * @param {HtmlEditor} this
+ * @param {Boolean} sourceEdit True if source edit, false if standard editing.
+ */
+ editmodechange: true,
+ /**
+ * @event editorevent
+ * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
+ * @param {HtmlEditor} this
+ */
+ editorevent: true,
+ /**
+ * @event firstfocus
+ * Fires when on first focus - needed by toolbars..
+ * @param {HtmlEditor} this
+ */
+ firstfocus: true,
+ /**
+ * @event autosave
+ * Auto save the htmlEditor value as a file into Events
+ * @param {HtmlEditor} this
+ */
+ autosave: true,
+ /**
+ * @event savedpreview
+ * preview the saved version of htmlEditor
+ * @param {HtmlEditor} this
+ */
+ savedpreview: true
+ });
+};
+
+
+Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
- this.editor = config.editor;
- this.editorcore = config.editor.editorcore;
- this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
+ /**
+ * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
+ */
+ 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,
- //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, {
+ /**
+ * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
+ *
+ */
+ stylesheets: false,
+ // id of frame..
+ frameId: false,
- bar : true,
+ // private properties
+ validationEvent : false,
+ deferHeight: true,
+ initialized : false,
+ activated : false,
- editor : false,
- editorcore : false,
+ onFocus : Roo.emptyFn,
+ iframePad:3,
+ hideMode:'offsets',
- formats : [
- "p" ,
- "h1","h2","h3","h4","h5","h6",
- "pre", "code",
- "abbr", "acronym", "address", "cite", "samp", "var",
- 'div','span'
- ],
+ tbContainer : false,
+ toolbarContainer :function() {
+ return this.wrap.select('.x-html-editor-tb',true).first();
+ },
+
+ /**
+ * Protected method that will not generally be called directly. It
+ * is called when the editor creates its toolbar. Override this method if you need to
+ * add custom toolbar buttons.
+ * @param {HtmlEditor} editor
+ */
+ createToolbar : function(){
+
+ Roo.log("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);
+// }
+ },
+
+
+ // 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'}
+ });
- 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,
+ 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 : {
- click : function()
- {
- editorcore.insertTag(this.tagname);
- editor.focus();
+ resize : function(r, w, h) {
+ _t.onResize(w,h); // -something
}
}
-
});
- });
- 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';
+ }
+ this.createToolbar(this);
+
- for(var i=0;i< children.length;i++) {
-
- this.buttons.add(this.addxtypeChild(children[i]));
-
+ if(!this.width && this.resizable){
+ this.setSize(this.wrap.getSize());
+ }
+ if (this.resizeEl) {
+ this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
+ // should trigger onReize..
}
- editor.on('editorevent', this.updateToolbar, this);
},
- onBtnClick : function(id)
- {
- this.editorcore.relayCmd(id);
- this.editorcore.focus();
- },
-
- /**
- * 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.
- */
- 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'));
- /*
+ // private
+ onResize : function(w, h)
+ {
+ Roo.log('resize: ' +w + ',' + h );
+ Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
+ var ew = false;
+ var eh = false;
- 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;
+ 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);
-
-
- // 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){
+
+ /**
+ * Toggles the editor between standard and source edit mode.
+ * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
+ */
+ toggleSourceEdit : function(sourceEditMode)
+ {
+ this.editorcore.toggleSourceEdit(sourceEditMode);
-
- if(sourceEditMode){
- Roo.log("disabling buttons");
- this.buttons.each( function(item){
- if(item.cmd != 'pencil'){
- item.disable();
- }
- });
-
+ 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("enabling buttons");
- if(this.editorcore.initialized){
- this.buttons.each( function(item){
- item.enable();
- });
- }
+ 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();
}
- Roo.log("calling toggole on editor");
- // tell the editor that it's been pressed..
- this.editor.toggleSourceEdit(sourceEditMode);
-
- }
-});
-
-
+
+ if(this.resizable){
+ this.setSize(this.wrap.getSize());
+ }
+
+ 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;
+ },
-/**
- * @class Roo.bootstrap.Table.AbstractSelectionModel
- * @extends Roo.util.Observable
- * Abstract base class for grid SelectionModels. It provides the interface that should be
- * implemented by descendant classes. This class should not be directly instantiated.
- * @constructor
- */
-Roo.bootstrap.Table.AbstractSelectionModel = function(){
- this.locked = false;
- Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
-};
+ // 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,
-Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
- /** @ignore Called by the grid automatically. Do not call directly. */
- init : function(grid){
- this.grid = grid;
- this.initEvents();
+ setValue : function(v){
+ Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
+ this.editorcore.pushValue();
},
- /**
- * Locks the selections.
- */
- lock : function(){
- this.locked = true;
+
+ // private
+ deferFocus : function(){
+ this.focus.defer(10, this);
},
- /**
- * Unlocks the selections.
- */
- unlock : function(){
- this.locked = false;
+ // doc'ed in Field
+ focus : function(){
+ this.editorcore.focus();
+
},
+
- /**
- * Returns true if the selections are locked.
- * @return {Boolean}
- */
- isLocked : function(){
- return this.locked;
- }
-});
-/**
- * @class Roo.bootstrap.Table.ColumnModel
- * @extends Roo.util.Observable
- * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
- * the columns in the table.
-
- * @constructor
- * @param {Object} config An Array of column config objects. See this class's
- * config objects for details.
-*/
-Roo.bootstrap.Table.ColumnModel = function(config){
- /**
- * The config passed into the constructor
- */
- this.config = config;
- this.lookup = {};
-
- // if no id, create one
- // if the column does not have a dataIndex mapping,
- // map it to the order it is in the config
- for(var i = 0, len = config.length; i < len; i++){
- 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();
+ // 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();
}
-// 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;
- }
-
- /**
- * 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.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
-};
-Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
+ },
+
+ // private
+ onFirstFocus : function(){
+ //Roo.log("onFirstFocus");
+ this.editorcore.onFirstFocus();
+ for (var i =0; i < this.toolbars.length;i++) {
+ this.toolbars[i].onFirstFocus();
+ }
+
+ },
+
+ // private
+ syncValue : function()
+ {
+ this.editorcore.syncValue();
+ },
+
+ pushValue : function()
+ {
+ this.editorcore.pushValue();
+ }
+
+
+ // hide stuff that is not compatible
/**
- * @cfg {String} header The header text to display in the Grid view.
+ * @event blur
+ * @hide
*/
/**
- * @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.
+ * @event change
+ * @hide
*/
/**
- * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
- * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
+ * @event focus
+ * @hide
*/
/**
- * @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}.
+ * @event specialkey
+ * @hide
*/
/**
- * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
+ * @cfg {String} fieldClass @hide
*/
/**
- * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
+ * @cfg {String} focusClass @hide
*/
/**
- * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
+ * @cfg {String} autoCreate @hide
*/
/**
- * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
+ * @cfg {String} inputType @hide
*/
/**
- * @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 {String} invalidClass @hide
*/
/**
- * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
+ * @cfg {String} invalidText @hide
*/
-
/**
- * Returns the id of the column at the specified index.
- * @param {Number} index The column index
- * @return {String} the id
+ * @cfg {String} msgFx @hide
*/
- 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
+ * @cfg {String} validateOnBlur @hide
*/
- getColumnById : function(id){
- return this.lookup[id];
- },
-
+});
+
- /**
- * 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;
- },
+
+
+
+
+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)
+{
- /**
- * Returns the index for a specified column id.
- * @param {String} id The column id
- * @return {Number} the index, or -1 if not found
- */
- getIndexById : function(id){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(this.config[i].id == id){
- return i;
- }
- }
- return -1;
- },
+ Roo.apply(this, config);
- /**
- * Returns the index for a specified column dataIndex.
- * @param {String} dataIndex The column dataIndex
- * @return {Number} the index, or -1 if not found
- */
+ // 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);
- findColumnIndex : function(dataIndex){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(this.config[i].dataIndex == dataIndex){
- return i;
- }
- }
- return -1;
- },
+ this.editor = config.editor;
+ this.editorcore = config.editor.editorcore;
+ this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
- 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);
+ //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 : {}
}
- }
- return totalWidth;
- },
-
- getLockedCount : function(){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(!this.isLocked(i)){
- return i;
+ a.listeners[toggle ? 'toggle' : 'click'] = function() {
+ handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
}
- }
- },
-
- /**
- * 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++;
+ 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);
}
- return c;
- }
- 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;
+ }),
+ 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]));
+
}
- return r;
- },
-
- /**
- * 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;
- },
-
- /**
- * 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.bootstrap.Table.ColumnModel.defaultRenderer;
- }
- return this.config[col].renderer;
+
+ editor.on('editorevent', this.updateToolbar, this);
},
-
- /**
- * 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;
+ onBtnClick : function(id)
+ {
+ this.editorcore.relayCmd(id);
+ this.editorcore.focus();
},
-
+
/**
- * Returns the width for the specified column.
- * @param {Number} col The column index
- * @return {Number}
+ * 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.
*/
- getColumnWidth : function(col){
- return this.config[col].width * 1 || this.defaultWidth;
- },
+ updateToolbar: function(){
- /**
- * Sets the width for a column.
- * @param {Number} col The column index
- * @param {Number} width The new width
- */
- setColumnWidth : function(col, width, suppressEvent){
- this.config[col].width = width;
- this.totalWidth = null;
- if(!suppressEvent){
- this.fireEvent("widthchange", this, col, width);
+ if(!this.editorcore.activated){
+ this.editor.onFirstFocus(); // is this neeed?
+ return;
}
- },
- /**
- * Returns the total width of all columns.
- * @param {Boolean} includeHidden True to include hidden column widths
- * @return {Number}
- */
- getTotalWidth : function(includeHidden){
- if(!this.totalWidth){
- this.totalWidth = 0;
- for(var i = 0, len = this.config.length; i < len; i++){
- if(includeHidden || !this.isHidden(i)){
- this.totalWidth += this.getColumnWidth(i);
+ var 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;
}
}
}
- 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;
- },
-
- /**
- * Sets the header for a column.
- * @param {Number} col The column index
- * @param {String} header The new header
- */
- setColumnHeader : function(col, header){
- this.config[col].header = header;
- this.fireEvent("headerchange", this, col, header);
- },
-
- /**
- * Returns the tooltip for the specified column.
- * @param {Number} col The column index
- * @return {String}
- */
- getColumnTooltip : function(col){
- return this.config[col].tooltip;
- },
- /**
- * Sets the tooltip for a column.
- * @param {Number} col The column index
- * @param {String} tooltip The new tooltip
- */
- setColumnTooltip : function(col, tooltip){
- this.config[col].tooltip = tooltip;
- },
-
- /**
- * Returns the dataIndex for the specified column.
- * @param {Number} col The column index
- * @return {Number}
- */
- getDataIndex : function(col){
- return this.config[col].dataIndex;
- },
-
- /**
- * Sets the dataIndex for a column.
- * @param {Number} col The column index
- * @param {Number} dataIndex The new dataIndex
- */
- setDataIndex : function(col, dataIndex){
- this.config[col].dataIndex = dataIndex;
+
+
+
+ // hides menus... - so this cant be on a menu...
+ Roo.bootstrap.MenuMgr.hideAll();
+ */
+ Roo.bootstrap.MenuMgr.hideAll();
+ //this.editorsyncValue();
},
-
-
-
- /**
- * 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;
+ 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);
+
+ }
+});
- /**
- * 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;
- },
+
+/**
+ * @class Roo.bootstrap.Table.AbstractSelectionModel
+ * @extends Roo.util.Observable
+ * Abstract base class for grid SelectionModels. It provides the interface that should be
+ * implemented by descendant classes. This class should not be directly instantiated.
+ * @constructor
+ */
+Roo.bootstrap.Table.AbstractSelectionModel = function(){
+ this.locked = false;
+ Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
+};
- /**
- * Returns true if the column width cannot be changed
- */
- isFixed : function(colIndex){
- return this.config[colIndex].fixed;
+Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
+ /** @ignore Called by the grid automatically. Do not call directly. */
+ init : function(grid){
+ this.grid = grid;
+ this.initEvents();
},
/**
- * Returns true if the column can be resized
- * @return {Boolean}
+ * Locks the selections.
*/
- isResizable : function(colIndex){
- return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
+ lock : function(){
+ this.locked = true;
},
+
/**
- * Sets if a column is hidden.
- * @param {Number} colIndex The column index
- * @param {Boolean} hidden True if the column is hidden
+ * Unlocks the selections.
*/
- setHidden : function(colIndex, hidden){
- this.config[colIndex].hidden = hidden;
- this.totalWidth = null;
- this.fireEvent("hiddenchange", this, colIndex, hidden);
+ unlock : function(){
+ this.locked = false;
},
/**
- * Sets the editor for a column.
- * @param {Number} col The column index
- * @param {Object} editor The editor object
+ * Returns true if the selections are locked.
+ * @return {Boolean}
*/
- setEditor : function(col, editor){
- this.config[col].editor = editor;
+ isLocked : function(){
+ return this.locked;
}
});
-
-Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
- if(typeof value == "string" && value.length < 1){
- return " ";
- }
- return value;
-};
-
-// Alias for backwards compatibility
-Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
-
/**
* @extends Roo.bootstrap.Table.AbstractSelectionModel
* @class Roo.bootstrap.Table.RowSelectionModel
},
/**
- * Selects a row.
- * @param {Number} row The index of the row to select
- * @param {Boolean} keepExisting (optional) True to keep existing selections
+ * Selects a row.
+ * @param {Number} row The index of the row to select
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
+ */
+ selectRow : function(index, keepExisting, preventViewNotify){
+ if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
+ if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
+ if(!keepExisting || this.singleSelect){
+ this.clearSelections();
+ }
+ var r = this.grid.dataSource.getAt(index);
+ this.selections.add(r);
+ this.last = this.lastActive = index;
+ if(!preventViewNotify){
+ this.grid.getView().onRowSelect(index);
+ }
+ this.fireEvent("rowselect", this, index, r);
+ this.fireEvent("selectionchange", this);
+ }
+ },
+
+ /**
+ * Deselects a row.
+ * @param {Number} row The index of the row to deselect
+ */
+ deselectRow : function(index, preventViewNotify){
+ if(this.locked) return;
+ if(this.last == index){
+ this.last = false;
+ }
+ if(this.lastActive == index){
+ this.lastActive = false;
+ }
+ var r = this.grid.dataSource.getAt(index);
+ this.selections.remove(r);
+ if(!preventViewNotify){
+ this.grid.getView().onRowDeselect(index);
+ }
+ this.fireEvent("rowdeselect", this, index);
+ this.fireEvent("selectionchange", this);
+ },
+
+ // private
+ restoreLast : function(){
+ if(this._last){
+ this.last = this._last;
+ }
+ },
+
+ // private
+ acceptsNav : function(row, col, cm){
+ return !cm.isHidden(col) && cm.isCellEditable(col, row);
+ },
+
+ // private
+ onEditorKey : function(field, e){
+ var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
+ if(k == e.TAB){
+ e.stopEvent();
+ ed.completeEdit();
+ if(e.shiftKey){
+ newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
+ }else{
+ newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
+ }
+ }else if(k == e.ENTER && !e.ctrlKey){
+ e.stopEvent();
+ ed.completeEdit();
+ if(e.shiftKey){
+ newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
+ }else{
+ newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
+ }
+ }else if(k == e.ESC){
+ ed.cancelEdit();
+ }
+ if(newCell){
+ g.startEditing(newCell[0], newCell[1]);
+ }
+ }
+});/*
+ * 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.bootstrap.PagingToolbar
+ * @extends Roo.Row
+ * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
+ * @constructor
+ * Create a new PagingToolbar
+ * @param {Object} config The config object
+ */
+Roo.bootstrap.PagingToolbar = function(config)
+{
+ // old args format still supported... - xtype is prefered..
+ // created from xtype...
+ var ds = config.dataSource;
+ this.toolbarItems = [];
+ if (config.items) {
+ this.toolbarItems = config.items;
+// config.items = [];
+ }
+
+ Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
+ this.ds = ds;
+ this.cursor = 0;
+ if (ds) {
+ this.bind(ds);
+ }
+
+ this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
+
+};
+
+Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
+ /**
+ * @cfg {Roo.data.Store} dataSource
+ * The underlying data store providing the paged data
+ */
+ /**
+ * @cfg {String/HTMLElement/Element} container
+ * container The id or element that will contain the toolbar
+ */
+ /**
+ * @cfg {Boolean} displayInfo
+ * True to display the displayMsg (defaults to false)
+ */
+ /**
+ * @cfg {Number} pageSize
+ * The number of records to display per page (defaults to 20)
+ */
+ pageSize: 20,
+ /**
+ * @cfg {String} displayMsg
+ * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
+ */
+ displayMsg : 'Displaying {0} - {1} of {2}',
+ /**
+ * @cfg {String} emptyMsg
+ * The message to display when no records are found (defaults to "No data to display")
+ */
+ emptyMsg : 'No data to display',
+ /**
+ * Customizable piece of the default paging text (defaults to "Page")
+ * @type String
+ */
+ beforePageText : "Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "of %0")
+ * @type String
+ */
+ afterPageText : "of {0}",
+ /**
+ * Customizable piece of the default paging text (defaults to "First Page")
+ * @type String
+ */
+ firstText : "First Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "Previous Page")
+ * @type String
+ */
+ prevText : "Previous Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "Next Page")
+ * @type String
+ */
+ nextText : "Next Page",
+ /**
+ * Customizable piece of the default paging text (defaults to "Last Page")
+ * @type String
*/
- selectRow : function(index, keepExisting, preventViewNotify){
- if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
- if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
- if(!keepExisting || this.singleSelect){
- this.clearSelections();
- }
- var r = this.grid.dataSource.getAt(index);
- this.selections.add(r);
- this.last = this.lastActive = index;
- if(!preventViewNotify){
- this.grid.getView().onRowSelect(index);
- }
- this.fireEvent("rowselect", this, index, r);
- this.fireEvent("selectionchange", this);
- }
- },
-
+ lastText : "Last Page",
/**
- * Deselects a row.
- * @param {Number} row The index of the row to deselect
+ * Customizable piece of the default paging text (defaults to "Refresh")
+ * @type String
*/
- deselectRow : function(index, preventViewNotify){
- if(this.locked) return;
- if(this.last == index){
- this.last = false;
- }
- if(this.lastActive == index){
- this.lastActive = false;
+ refreshText : "Refresh",
+
+ buttons : false,
+ // private
+ onRender : function(ct, position)
+ {
+ Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
+ this.navgroup.parentId = this.id;
+ 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 r = this.grid.dataSource.getAt(index);
- this.selections.remove(r);
- if(!preventViewNotify){
- this.grid.getView().onRowDeselect(index);
+
+ var _this = this;
+
+ if(this.buttons){
+ Roo.each(_this.buttons, function(e){
+ Roo.factory(e).onRender(_this.el, null);
+ });
}
- this.fireEvent("rowdeselect", this, index);
- this.fireEvent("selectionchange", this);
+
+ Roo.each(_this.toolbarItems, function(e) {
+ _this.navgroup.addItem(e);
+ });
+
+ this.first = this.navgroup.addItem({
+ tooltip: this.firstText,
+ cls: "prev",
+ icon : 'fa fa-backward',
+ disabled: true,
+ listeners : { click : this.onClick.createDelegate(this, ["first"]) }
+ });
+
+ this.prev = this.navgroup.addItem({
+ tooltip: this.prevText,
+ cls: "prev",
+ icon : 'fa fa-step-backward',
+ disabled: true,
+ listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
+ });
+ //this.addSeparator();
+
+
+ var field = this.navgroup.addItem( {
+ tagtype : 'span',
+ cls : 'x-paging-position',
+
+ html : this.beforePageText +
+ '<input type="text" size="3" value="1" class="x-grid-page-number">' +
+ '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
+ } ); //?? escaped?
+
+ this.field = field.el.select('input', true).first();
+ this.field.on("keydown", this.onPagingKeydown, this);
+ this.field.on("focus", function(){this.dom.select();});
+
+
+ this.afterTextEl = field.el.select('.x-paging-after',true).first();
+ //this.field.setHeight(18);
+ //this.addSeparator();
+ this.next = this.navgroup.addItem({
+ tooltip: this.nextText,
+ cls: "next",
+ html : ' <i class="fa fa-step-forward">',
+ disabled: true,
+ listeners : { click : this.onClick.createDelegate(this, ["next"]) }
+ });
+ this.last = this.navgroup.addItem({
+ tooltip: this.lastText,
+ icon : 'fa fa-forward',
+ cls: "next",
+ disabled: true,
+ listeners : { click : this.onClick.createDelegate(this, ["last"]) }
+ });
+ //this.addSeparator();
+ this.loading = this.navgroup.addItem({
+ tooltip: this.refreshText,
+ icon: 'fa fa-refresh',
+
+ listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
+ });
+
},
// private
- restoreLast : function(){
- if(this._last){
- this.last = this._last;
+ updateInfo : function(){
+ if(this.displayEl){
+ var count = this.ds.getCount();
+ var msg = count == 0 ?
+ this.emptyMsg :
+ String.format(
+ this.displayMsg,
+ this.cursor+1, this.cursor+count, this.ds.getTotalCount()
+ );
+ this.displayEl.update(msg);
}
},
// private
- acceptsNav : function(row, col, cm){
- return !cm.isHidden(col) && cm.isCellEditable(col, row);
+ 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.dom.innerHTML = String.format(this.afterPageText, d.pages);
+ this.field.dom.value = ap;
+ this.first.setDisabled(ap == 1);
+ this.prev.setDisabled(ap == 1);
+ this.next.setDisabled(ap == ps);
+ this.last.setDisabled(ap == ps);
+ this.loading.enable();
+ this.updateInfo();
},
// private
- onEditorKey : function(field, e){
- var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
- if(k == e.TAB){
- e.stopEvent();
- ed.completeEdit();
- if(e.shiftKey){
- newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
- }else{
- newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
+ getPageData : function(){
+ var total = this.ds.getTotalCount();
+ return {
+ total : total,
+ activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
+ pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
+ };
+ },
+
+ // private
+ onLoadError : function(){
+ this.loading.enable();
+ },
+
+ // private
+ onPagingKeydown : function(e){
+ var k = e.getKey();
+ var d = this.getPageData();
+ if(k == e.RETURN){
+ var v = this.field.dom.value, pageNum;
+ if(!v || isNaN(pageNum = parseInt(v, 10))){
+ this.field.dom.value = d.activePage;
+ return;
}
- }else if(k == e.ENTER && !e.ctrlKey){
+ pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
+ this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
e.stopEvent();
- ed.completeEdit();
- if(e.shiftKey){
- newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
- }else{
- newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
- }
- }else if(k == e.ESC){
- ed.cancelEdit();
}
- if(newCell){
- g.startEditing(newCell[0], newCell[1]);
+ else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
+ {
+ var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
+ this.field.dom.value = pageNum;
+ this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
+ e.stopEvent();
+ }
+ else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
+ {
+ var v = this.field.dom.value, pageNum;
+ var increment = (e.shiftKey) ? 10 : 1;
+ if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
+ increment *= -1;
+ if(!v || isNaN(pageNum = parseInt(v, 10))) {
+ this.field.dom.value = d.activePage;
+ return;
+ }
+ else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
+ {
+ this.field.dom.value = parseInt(v, 10) + increment;
+ pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
+ this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
+ }
+ e.stopEvent();
+ }
+ },
+
+ // private
+ beforeLoad : function(){
+ if(this.loading){
+ this.loading.disable();
+ }
+ },
+
+ // private
+ onClick : function(which){
+ var ds = this.ds;
+ if (!ds) {
+ return;
}
+ switch(which){
+ case "first":
+ ds.load({params:{start: 0, limit: this.pageSize}});
+ break;
+ case "prev":
+ ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
+ break;
+ case "next":
+ ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
+ break;
+ case "last":
+ var total = ds.getTotalCount();
+ var extra = total % this.pageSize;
+ var lastStart = extra ? (total - extra) : total-this.pageSize;
+ ds.load({params:{start: lastStart, limit: this.pageSize}});
+ break;
+ case "refresh":
+ ds.load({params:{start: this.cursor, limit: this.pageSize}});
+ break;
+ }
+ },
+
+ /**
+ * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
+ * @param {Roo.data.Store} store The data store to unbind
+ */
+ unbind : function(ds){
+ ds.un("beforeload", this.beforeLoad, this);
+ ds.un("load", this.onLoad, this);
+ ds.un("loadexception", this.onLoadError, this);
+ ds.un("remove", this.updateInfo, this);
+ ds.un("add", this.updateInfo, this);
+ this.ds = undefined;
+ },
+
+ /**
+ * Binds the paging toolbar to the specified {@link Roo.data.Store}
+ * @param {Roo.data.Store} store The data store to bind
+ */
+ bind : function(ds){
+ ds.on("beforeload", this.beforeLoad, this);
+ ds.on("load", this.onLoad, this);
+ ds.on("loadexception", this.onLoadError, this);
+ ds.on("remove", this.updateInfo, this);
+ ds.on("add", this.updateInfo, this);
+ this.ds = ds;
}
});/*
* - LGPL
-
\ 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);
+ }
+
+});
+
+