-/**
+Roo.bootstrap = {};/**
* set the version of bootstrap based on the stylesheet...
*
*/
-Roo.bootstrap.version = (
- function() {
- var ret=3;
- Roo.each(document.styleSheets[0], function(s) {
- if (s.href.match(/css-bootstrap4/)) {
- ret=4;
- }
- });
- return ret;
-})();/*
+Roo.bootstrap.version = ( function() {
+ var ret=3;
+ Roo.each(document.styleSheets, function(s) {
+ if ( s.href && s.href.match(/css-bootstrap4/)) {
+ ret=4;
+ }
+ });
+ if (ret > 3) {
+ Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
+ }
+ return ret;
+})(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
+Roo.bootstrap.nav = {};
+
+Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
+ * 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.Shadow
+ * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
+ * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
+ * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
+ * @constructor
+ * Create a new Shadow
+ * @param {Object} config The config object
+ */
+Roo.Shadow = function(config){
+ Roo.apply(this, config);
+ if(typeof this.mode != "string"){
+ this.mode = this.defaultMode;
+ }
+ var o = this.offset, a = {h: 0};
+ var rad = Math.floor(this.offset/2);
+ switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
+ case "drop":
+ a.w = 0;
+ a.l = a.t = o;
+ a.t -= 1;
+ if(Roo.isIE){
+ a.l -= this.offset + rad;
+ a.t -= this.offset + rad;
+ a.w -= rad;
+ a.h -= rad;
+ a.t += 1;
+ }
+ break;
+ case "sides":
+ a.w = (o*2);
+ a.l = -o;
+ a.t = o-1;
+ if(Roo.isIE){
+ a.l -= (this.offset - rad);
+ a.t -= this.offset + rad;
+ a.l += 1;
+ a.w -= (this.offset - rad)*2;
+ a.w -= rad + 1;
+ a.h -= 1;
+ }
+ break;
+ case "frame":
+ a.w = a.h = (o*2);
+ a.l = a.t = -o;
+ a.t += 1;
+ a.h -= 2;
+ if(Roo.isIE){
+ a.l -= (this.offset - rad);
+ a.t -= (this.offset - rad);
+ a.l += 1;
+ a.w -= (this.offset + rad + 1);
+ a.h -= (this.offset + rad);
+ a.h += 1;
+ }
+ break;
+ };
+
+ this.adjusts = a;
+};
+
+Roo.Shadow.prototype = {
+ /**
+ * @cfg {String} mode
+ * The shadow display mode. Supports the following options:<br />
+ * sides: Shadow displays on both sides and bottom only<br />
+ * frame: Shadow displays equally on all four sides<br />
+ * drop: Traditional bottom-right drop shadow (default)
+ */
+ mode: false,
+ /**
+ * @cfg {String} offset
+ * The number of pixels to offset the shadow from the element (defaults to 4)
+ */
+ offset: 4,
+
+ // private
+ defaultMode: "drop",
+
+ /**
+ * Displays the shadow under the target element
+ * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
+ */
+ show : function(target){
+ target = Roo.get(target);
+ if(!this.el){
+ this.el = Roo.Shadow.Pool.pull();
+ if(this.el.dom.nextSibling != target.dom){
+ this.el.insertBefore(target);
+ }
+ }
+ this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
+ if(Roo.isIE){
+ this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
+ }
+ this.realign(
+ target.getLeft(true),
+ target.getTop(true),
+ target.getWidth(),
+ target.getHeight()
+ );
+ this.el.dom.style.display = "block";
+ },
+
+ /**
+ * Returns true if the shadow is visible, else false
+ */
+ isVisible : function(){
+ return this.el ? true : false;
+ },
+
+ /**
+ * Direct alignment when values are already available. Show must be called at least once before
+ * calling this method to ensure it is initialized.
+ * @param {Number} left The target element left position
+ * @param {Number} top The target element top position
+ * @param {Number} width The target element width
+ * @param {Number} height The target element height
+ */
+ realign : function(l, t, w, h){
+ if(!this.el){
+ return;
+ }
+ var a = this.adjusts, d = this.el.dom, s = d.style;
+ var iea = 0;
+ s.left = (l+a.l)+"px";
+ s.top = (t+a.t)+"px";
+ var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
+
+ if(s.width != sws || s.height != shs){
+ s.width = sws;
+ s.height = shs;
+ if(!Roo.isIE){
+ var cn = d.childNodes;
+ var sww = Math.max(0, (sw-12))+"px";
+ cn[0].childNodes[1].style.width = sww;
+ cn[1].childNodes[1].style.width = sww;
+ cn[2].childNodes[1].style.width = sww;
+ cn[1].style.height = Math.max(0, (sh-12))+"px";
+ }
+ }
+ },
+
+ /**
+ * Hides this shadow
+ */
+ hide : function(){
+ if(this.el){
+ this.el.dom.style.display = "none";
+ Roo.Shadow.Pool.push(this.el);
+ delete this.el;
+ }
+ },
+
+ /**
+ * Adjust the z-index of this shadow
+ * @param {Number} zindex The new z-index
+ */
+ setZIndex : function(z){
+ this.zIndex = z;
+ if(this.el){
+ this.el.setStyle("z-index", z);
+ }
+ }
+};
+
+// Private utility class that manages the internal Shadow cache
+Roo.Shadow.Pool = function(){
+ var p = [];
+ var markup = Roo.isIE ?
+ '<div class="x-ie-shadow"></div>' :
+ '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
+ return {
+ pull : function(){
+ var sh = p.shift();
+ if(!sh){
+ sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
+ sh.autoBoxAdjust = false;
+ }
+ return sh;
+ },
+
+ push : function(sh){
+ p.push(sh);
+ }
+ };
+}();/*
* - LGPL
*
* base class for bootstrap elements.
/**
* @class Roo.bootstrap.Component
* @extends Roo.Component
+ * @abstract
+ * @children Roo.bootstrap.Component
* Bootstrap Component base class
* @cfg {String} cls css class
* @cfg {String} style any extra css
}
if (this.cls) {
- cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
+ cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
}
if (this.style) { // fixme needs to support more complex style data.
- cfg.style = this.style;
+ cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
}
if(this.name){
{
return this.el;
},
+ getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
+ {
+ return Roo.get(document.body);
+ },
+
/**
* Fetch the element to display the tooltip on.
* @return {Roo.Element} defaults to this.el
}
});
+ /*
+ * - LGPL
+ *
+ * element
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.Element
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
+ * Bootstrap Element class (basically a DIV used to make random stuff )
+ *
+ * @cfg {String} html contents of the element
+ * @cfg {String} tag tag of the element
+ * @cfg {String} cls class of the element
+ * @cfg {Boolean} preventDefault (true|false) default false
+ * @cfg {Boolean} clickable (true|false) default false
+ * @cfg {String} role default blank - set to button to force cursor pointer
+
+ *
+ * @constructor
+ * Create a new Element
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Element = function(config){
+ Roo.bootstrap.Element.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ // raw events
+ /**
+ * @event click
+ * When a element is chick
+ * @param {Roo.bootstrap.Element} this
+ * @param {Roo.EventObject} e
+ */
+ "click" : true
+
+
+ });
+};
+
+Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
+
+ tag: 'div',
+ cls: '',
+ html: '',
+ preventDefault: false,
+ clickable: false,
+ tapedTwice : false,
+ role : false,
+
+ getAutoCreate : function(){
+
+ var cfg = {
+ tag: this.tag,
+ // cls: this.cls, double assign in parent class Component.js :: onRender
+ html: this.html
+ };
+ if (this.role !== false) {
+ cfg.role = this.role;
+ }
+
+ return cfg;
+ },
+
+ initEvents: function()
+ {
+ Roo.bootstrap.Element.superclass.initEvents.call(this);
+
+ if(this.clickable){
+ this.el.on('click', this.onClick, this);
+ }
+
+
+ },
+
+ onClick : function(e)
+ {
+ if(this.preventDefault){
+ e.preventDefault();
+ }
+
+ this.fireEvent('click', this, e); // why was this double click before?
+ },
+
+
+
+
+
+
+ getValue : function()
+ {
+ return this.el.dom.innerHTML;
+ },
+
+ setValue : function(value)
+ {
+ this.el.dom.innerHTML = value;
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * dropable area
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.DropTarget
+ * @extends Roo.bootstrap.Element
+ * Bootstrap DropTarget class
+
+ * @cfg {string} name dropable name
+ *
+ * @constructor
+ * Create a new Dropable Area
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.DropTarget = function(config){
+ Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ // raw events
+ /**
+ * @event click
+ * When a element is chick
+ * @param {Roo.bootstrap.Element} this
+ * @param {Roo.EventObject} e
+ */
+ "drop" : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
+
+
+ getAutoCreate : function(){
+
+
+ },
+
+ initEvents: function()
+ {
+ Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
+ this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
+ ddGroup: this.name,
+ listeners : {
+ drop : this.dragDrop.createDelegate(this),
+ enter : this.dragEnter.createDelegate(this),
+ out : this.dragOut.createDelegate(this),
+ over : this.dragOver.createDelegate(this)
+ }
+
+ });
+ this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
+ },
+
+ dragDrop : function(source,e,data)
+ {
+ // user has to decide how to impliment this.
+ Roo.log('drop');
+ Roo.log(this);
+ //this.fireEvent('drop', this, source, e ,data);
+ return false;
+ },
+
+ dragEnter : function(n, dd, e, data)
+ {
+ // probably want to resize the element to match the dropped element..
+ Roo.log("enter");
+ this.originalSize = this.el.getSize();
+ this.el.setSize( n.el.getSize());
+ this.dropZone.DDM.refreshCache(this.name);
+ Roo.log([n, dd, e, data]);
+ },
+
+ dragOut : function(value)
+ {
+ // resize back to normal
+ Roo.log("out");
+ this.el.setSize(this.originalSize);
+ this.dropZone.resetConstraints();
+ },
+
+ dragOver : function()
+ {
+ // ??? do nothing?
+ }
+
+});
+
+
+
/*
* - LGPL
*
/**
* @class Roo.bootstrap.Body
* @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
+ * @parent none builder
* Bootstrap Body class
*
* @constructor
* @class Roo.bootstrap.ButtonGroup
* @extends Roo.bootstrap.Component
* Bootstrap ButtonGroup class
+ * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
+ *
* @cfg {String} size lg | sm | xs (default empty normal)
* @cfg {String} align vertical | justified (default none)
* @cfg {String} direction up | down (default down)
* @extends Roo.bootstrap.Component
* Bootstrap Button class
* @cfg {String} html The button content
- * @cfg {String} weight (default | primary | secondary | success | info | warning | danger | link ) default
- * @cfg {String} badge_weight (default | primary | secondary | success | info | warning | danger | link ) default (same as button)
+ * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
+ * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
* @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
- * @cfg {String} size ( lg | sm | xs)
- * @cfg {String} tag ( a | input | submit)
+ * @cfg {String} size (lg|sm|xs)
+ * @cfg {String} tag (a|input|submit)
* @cfg {String} href empty or href
* @cfg {Boolean} disabled default false;
* @cfg {Boolean} isClose default false;
* @cfg {String} theme (default|glow)
* @cfg {Boolean} inverse dark themed version
* @cfg {Boolean} toggle is it a slidy toggle button
- * @cfg {Boolean} pressed (true|false) default null - if the button ahs active state
+ * @cfg {Boolean} pressed default null - if the button ahs active state
* @cfg {String} ontext text for on slidy toggle state
* @cfg {String} offtext text for off slidy toggle state
* @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
* @cfg {Boolean} removeClass remove the standard class..
- * @cfg {String} target target for a href. (_self|_blank|_parent|_top| other)
- *
+ * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
+ * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
+ * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
+
* @constructor
* Create a new button
* @param {Object} config The config object
Roo.bootstrap.Button = function(config){
Roo.bootstrap.Button.superclass.constructor.call(this, config);
- this.weightClass = ["btn-default btn-outline-secondary",
- "btn-primary",
- "btn-success",
- "btn-info",
- "btn-warning",
- "btn-danger",
- "btn-link"
- ],
+
this.addEvents({
// raw events
/**
* @event click
- * When a butotn is pressed
+ * When a button is pressed
* @param {Roo.bootstrap.Button} btn
* @param {Roo.EventObject} e
*/
"click" : true,
+ /**
+ * @event dblclick
+ * When a button is double clicked
+ * @param {Roo.bootstrap.Button} btn
+ * @param {Roo.EventObject} e
+ */
+ "dblclick" : true,
/**
* @event toggle
* After the button has been toggles
removeClass: false,
name: false,
target: false,
+ group : false,
pressed : null,
}
]
};
-
- if (['default', 'secondary' , 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
- cfg.cls += ' '+this.weight;
+ // why are we validating the weights?
+ if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
+ cfg.cls += ' ' + this.weight;
}
return cfg;
return cfg;
}
+
-
if (this.theme==='default') {
cfg.cls = 'btn roo-button';
//if (this.parentType != 'Navbar') {
this.weight = this.weight.length ? this.weight : 'default';
//}
- if (['default', 'primary', 'secondary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
+ if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
var weight = this.weight == 'default' ? 'secondary' : this.weight;
cfg.tag = 'a';
cfg.cls = 'btn-glow roo-button';
- if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
+ if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
cfg.cls += ' ' + this.weight;
}
}
- if (this.el.hasClass('roo-button')) {
+ if (this.el.hasClass('roo-button')) {
+ this.el.on('click', this.onClick, this);
+ this.el.on('dblclick', this.onDblClick, this);
+ } else {
+ this.el.select('.roo-button').on('click', this.onClick, this);
+ this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
+
+ }
+ // why?
+ if(this.removeClass){
this.el.on('click', this.onClick, this);
- } else {
- this.el.select('.roo-button').on('click', this.onClick, this);
- }
-
- if(this.removeClass){
- this.el.on('click', this.onClick, this);
- }
-
- this.el.enableDisplayMode();
+ }
+
+ if (this.group === true) {
+ if (this.pressed === false || this.pressed === true) {
+ // nothing
+ } else {
+ this.pressed = false;
+ this.setActive(this.pressed);
+ }
+
+ }
+
+ this.el.enableDisplayMode();
},
onClick : function(e)
}
Roo.log('button on click ');
- if(this.preventDefault){
+ if(this.href === '' || this.preventDefault){
e.preventDefault();
}
+ if (this.group) {
+ if (this.pressed) {
+ // do nothing -
+ return;
+ }
+ this.setActive(true);
+ var pi = this.parent().items;
+ for (var i = 0;i < pi.length;i++) {
+ if (this == pi[i]) {
+ continue;
+ }
+ if (pi[i].el.hasClass('roo-button')) {
+ pi[i].setActive(false);
+ }
+ }
+ this.fireEvent('click', this, e);
+ return;
+ }
+
if (this.pressed === true || this.pressed === false) {
this.toggleActive(e);
}
this.fireEvent('click', this, e);
},
-
+ onDblClick: function(e)
+ {
+ if (this.disabled) {
+ return;
+ }
+ if(this.preventDefault){
+ e.preventDefault();
+ }
+ this.fireEvent('dblclick', this, e);
+ },
/**
* Enables this button
*/
{
this.disabled = false;
this.el.removeClass('disabled');
+ this.el.dom.removeAttribute("disabled");
},
/**
{
this.disabled = true;
this.el.addClass('disabled');
+ this.el.attr("disabled", "disabled")
},
/**
* sets the active state on/off,
*/
toggleActive : function(e)
{
- this.setActive(!this.pressed);
- this.fireEvent('toggle', this, e, !this.pressed);
+ this.setActive(!this.pressed); // this modifies pressed...
+ this.fireEvent('toggle', this, e, this.pressed);
},
/**
* get the current active state
setWeight : function(str)
{
- this.el.removeClass(this.weightClass);
+ this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
+ this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
this.weight = str;
var outline = this.outline ? 'outline-' : '';
if (str == 'default') {
});
-
- /*
+// fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
+
+Roo.bootstrap.Button.weights = [
+ 'default',
+ 'secondary' ,
+ 'primary',
+ 'success',
+ 'info',
+ 'warning',
+ 'danger',
+ 'link',
+ 'light',
+ 'dark'
+
+];/*
* - LGPL
*
* column
/**
* @class Roo.bootstrap.Column
* @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
* Bootstrap Column class
* @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
* @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
};
var settings=this;
- ['xs','sm','md','lg'].map(function(size){
+ var sizes = ['xs','sm','md','lg'];
+ sizes.map(function(size ,ix){
//Roo.log( size + ':' + settings[size]);
if (settings[size+'off'] !== false) {
}
if (!settings[size]) { // 0 = hidden
- cfg.cls += ' hidden-' + size;
+ cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
+ // bootsrap4
+ for (var i = ix; i > -1; i--) {
+ cfg.cls += ' d-' + sizes[i] + '-none';
+ }
+
+
return;
}
- cfg.cls += ' col-' + size + '-' + settings[size];
+ cfg.cls += ' col-' + size + '-' + settings[size] + (
+ size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
+ );
});
/**
* @class Roo.bootstrap.Container
* @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
+ * @parent builder
* Bootstrap Container class
* @cfg {Boolean} jumbotron is it a jumbotron element
* @cfg {String} html content of element
}
});
- /*
- * - LGPL
+ /**
+ * @class Roo.bootstrap.Card
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
+ * @licence LGPL
+ * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
*
- * image
+ *
+ * possible... may not be implemented..
+ * @cfg {String} header_image src url of image.
+ * @cfg {String|Object} header
+ * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
+ * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
*
- */
-
-
-/**
- * @class Roo.bootstrap.Img
- * @extends Roo.bootstrap.Component
- * Bootstrap Img class
- * @cfg {Boolean} imgResponsive false | true
- * @cfg {String} border rounded | circle | thumbnail
- * @cfg {String} src image source
- * @cfg {String} alt image alternative text
- * @cfg {String} href a tag href
- * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
- * @cfg {String} xsUrl xs image source
- * @cfg {String} smUrl sm image source
- * @cfg {String} mdUrl md image source
- * @cfg {String} lgUrl lg image source
+ * @cfg {String} title
+ * @cfg {String} subtitle
+ * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
+ * @cfg {String} footer
+
+ * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
+ *
+ * @cfg {String} margin (0|1|2|3|4|5|auto)
+ * @cfg {String} margin_top (0|1|2|3|4|5|auto)
+ * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
+ * @cfg {String} margin_left (0|1|2|3|4|5|auto)
+ * @cfg {String} margin_right (0|1|2|3|4|5|auto)
+ * @cfg {String} margin_x (0|1|2|3|4|5|auto)
+ * @cfg {String} margin_y (0|1|2|3|4|5|auto)
+ *
+ * @cfg {String} padding (0|1|2|3|4|5)
+ * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
+ * @cfg {String} padding_bottom (0|1|2|3|4|5)
+ * @cfg {String} padding_left (0|1|2|3|4|5)
+ * @cfg {String} padding_right (0|1|2|3|4|5)
+ * @cfg {String} padding_x (0|1|2|3|4|5)
+ * @cfg {String} padding_y (0|1|2|3|4|5)
+ *
+ * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
+ * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
+ * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
+ * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
+ * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
+
+ * @config {Boolean} dragable if this card can be dragged.
+ * @config {String} drag_group group for drag
+ * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
+ * @config {String} drop_group group for drag
+ *
+ * @config {Boolean} collapsable can the body be collapsed.
+ * @config {Boolean} collapsed is the body collapsed when rendered...
+ * @config {Boolean} rotateable can the body be rotated by clicking on it..
+ * @config {Boolean} rotated is the body rotated when rendered...
*
* @constructor
- * Create a new Input
+ * Create a new Container
* @param {Object} config The config object
*/
-Roo.bootstrap.Img = function(config){
- Roo.bootstrap.Img.superclass.constructor.call(this, config);
+Roo.bootstrap.Card = function(config){
+ Roo.bootstrap.Card.superclass.constructor.call(this, config);
this.addEvents({
- // img events
+ // raw events
/**
- * @event click
- * The img click event for the img.
- * @param {Roo.EventObject} e
+ * @event drop
+ * When a element a card is dropped
+ * @param {Roo.bootstrap.Card} this
+ *
+ *
+ * @param {Roo.bootstrap.Card} move_card the card being dropped?
+ * @param {String} position 'above' or 'below'
+ * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
+
*/
- "click" : true
+ 'drop' : true,
+ /**
+ * @event rotate
+ * When a element a card is rotate
+ * @param {Roo.bootstrap.Card} this
+ * @param {Roo.Element} n the node being dropped?
+ * @param {Boolean} rotate status
+ */
+ 'rotate' : true,
+ /**
+ * @event cardover
+ * When a card element is dragged over ready to drop (return false to block dropable)
+ * @param {Roo.bootstrap.Card} this
+ * @param {Object} data from dragdrop
+ */
+ 'cardover' : true
+
});
};
-Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
+
+Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
- imgResponsive: true,
- border: '',
- src: 'about:blank',
- href: false,
- target: false,
- xsUrl: '',
- smUrl: '',
- mdUrl: '',
- lgUrl: '',
+
+ weight : '',
+
+ margin: '', /// may be better in component?
+ margin_top: '',
+ margin_bottom: '',
+ margin_left: '',
+ margin_right: '',
+ margin_x: '',
+ margin_y: '',
+
+ padding : '',
+ padding_top: '',
+ padding_bottom: '',
+ padding_left: '',
+ padding_right: '',
+ padding_x: '',
+ padding_y: '',
+
+ display: '',
+ display_xs: '',
+ display_sm: '',
+ display_lg: '',
+ display_xl: '',
+
+ header_image : '',
+ header : '',
+ header_size : 0,
+ title : '',
+ subtitle : '',
+ html : '',
+ footer: '',
- getAutoCreate : function()
- {
- if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
- return this.createSingleImg();
+ collapsable : false,
+ collapsed : false,
+ rotateable : false,
+ rotated : false,
+
+ dragable : false,
+ drag_group : false,
+ dropable : false,
+ drop_group : false,
+ childContainer : false,
+ dropEl : false, /// the dom placeholde element that indicates drop location.
+ containerEl: false, // body container
+ bodyEl: false, // card-body
+ headerContainerEl : false, //
+ headerEl : false,
+ header_imageEl : false,
+
+
+ layoutCls : function()
+ {
+ var cls = '';
+ var t = this;
+ Roo.log(this.margin_bottom.length);
+ ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
+ // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
+
+ if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
+ cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
+ }
+ if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
+ cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
+ }
+ });
+
+ ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
+ if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
+ cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
+ }
+ });
+
+ // more generic support?
+ if (this.hidden) {
+ cls += ' d-none';
}
+ return cls;
+ },
+
+ // Roo.log("Call onRender: " + this.xtype);
+ /* We are looking at something like this.
+<div class="card">
+ <img src="..." class="card-img-top" alt="...">
+ <div class="card-body">
+ <h5 class="card-title">Card title</h5>
+ <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
+
+ >> this bit is really the body...
+ <div> << we will ad dthis in hopefully it will not break shit.
+
+ ** card text does not actually have any styling...
+
+ <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
+
+ </div> <<
+ <a href="#" class="card-link">Card link</a>
+
+ </div>
+ <div class="card-footer">
+ <small class="text-muted">Last updated 3 mins ago</small>
+ </div>
+</div>
+ */
+ getAutoCreate : function(){
+
var cfg = {
- tag: 'div',
- cls: 'roo-image-responsive-group',
- cn: []
+ tag : 'div',
+ cls : 'card',
+ cn : [ ]
};
- var _this = this;
- Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
-
- if(!_this[size + 'Url']){
- return;
- }
-
- var img = {
- tag: 'img',
- cls: (_this.imgResponsive) ? 'img-responsive' : '',
- html: _this.html || cfg.html,
- src: _this[size + 'Url']
+ if (this.weight.length && this.weight != 'light') {
+ cfg.cls += ' text-white';
+ } else {
+ cfg.cls += ' text-dark'; // need as it's nested..
+ }
+ if (this.weight.length) {
+ cfg.cls += ' bg-' + this.weight;
+ }
+
+ cfg.cls += ' ' + this.layoutCls();
+
+ var hdr = false;
+ var hdr_ctr = false;
+ if (this.header.length) {
+ hdr = {
+ tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
+ cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
+ cn : []
};
+ cfg.cn.push(hdr);
+ hdr_ctr = hdr;
+ } else {
+ hdr = {
+ tag : 'div',
+ cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
+ cn : []
+ };
+ cfg.cn.push(hdr);
+ hdr_ctr = hdr;
+ }
+ if (this.collapsable) {
+ hdr_ctr = {
+ tag : 'a',
+ cls : 'd-block user-select-none',
+ cn: [
+ {
+ tag: 'i',
+ cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
+ }
+
+ ]
+ };
+ hdr.cn.push(hdr_ctr);
+ }
+
+ hdr_ctr.cn.push( {
+ tag: 'span',
+ cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
+ html : this.header
+ });
+
+
+ if (this.header_image.length) {
+ cfg.cn.push({
+ tag : 'img',
+ cls : 'card-img-top',
+ src: this.header_image // escape?
+ });
+ } else {
+ cfg.cn.push({
+ tag : 'div',
+ cls : 'card-img-top d-none'
+ });
+ }
- img.cls += ' roo-image-responsive-' + size;
-
- var s = ['xs', 'sm', 'md', 'lg'];
+ var body = {
+ tag : 'div',
+ cls : 'card-body' + (this.html === false ? ' d-none' : ''),
+ cn : []
+ };
+ var obody = body;
+ if (this.collapsable || this.rotateable) {
+ obody = {
+ tag: 'div',
+ cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
+ cn : [ body ]
+ };
+ }
+
+ cfg.cn.push(obody);
+
+ if (this.title.length) {
+ body.cn.push({
+ tag : 'div',
+ cls : 'card-title',
+ src: this.title // escape?
+ });
+ }
+
+ if (this.subtitle.length) {
+ body.cn.push({
+ tag : 'div',
+ cls : 'card-title',
+ src: this.subtitle // escape?
+ });
+ }
+
+ body.cn.push({
+ tag : 'div',
+ cls : 'roo-card-body-ctr'
+ });
+
+ if (this.html.length) {
+ body.cn.push({
+ tag: 'div',
+ html : this.html
+ });
+ }
+ // fixme ? handle objects?
+
+ if (this.footer.length) {
+
+ cfg.cn.push({
+ cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
+ html : this.footer
+ });
- s.splice(s.indexOf(size), 1);
+ } else {
+ cfg.cn.push({cls : 'card-footer d-none'});
+ }
+
+ // footer...
+
+ return cfg;
+ },
+
+
+ getCardHeader : function()
+ {
+ var ret = this.el.select('.card-header',true).first();
+ if (ret.hasClass('d-none')) {
+ ret.removeClass('d-none');
+ }
+
+ return ret;
+ },
+ getCardFooter : function()
+ {
+ var ret = this.el.select('.card-footer',true).first();
+ if (ret.hasClass('d-none')) {
+ ret.removeClass('d-none');
+ }
+
+ return ret;
+ },
+ getCardImageTop : function()
+ {
+ var ret = this.header_imageEl;
+ if (ret.hasClass('d-none')) {
+ ret.removeClass('d-none');
+ }
- Roo.each(s, function(ss){
- img.cls += ' hidden-' + ss;
+ return ret;
+ },
+
+ getChildContainer : function()
+ {
+
+ if(!this.el){
+ return false;
+ }
+ return this.el.select('.roo-card-body-ctr',true).first();
+ },
+
+ initEvents: function()
+ {
+ this.bodyEl = this.el.select('.card-body',true).first();
+ this.containerEl = this.getChildContainer();
+ if(this.dragable){
+ this.dragZone = new Roo.dd.DragZone(this.getEl(), {
+ containerScroll: true,
+ ddGroup: this.drag_group || 'default_card_drag_group'
});
+ this.dragZone.getDragData = this.getDragData.createDelegate(this);
+ }
+ if (this.dropable) {
+ this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
+ containerScroll: true,
+ ddGroup: this.drop_group || 'default_card_drag_group'
+ });
+ this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
+ this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
+ this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
+ this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
+ this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
+ }
+
+ if (this.collapsable) {
+ this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
+ }
+ if (this.rotateable) {
+ this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
+ }
+ this.collapsableEl = this.el.select('.roo-collapsable',true).first();
+
+ this.footerEl = this.el.select('.card-footer',true).first();
+ this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
+ this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
+ this.headerEl = this.el.select('.card-header',true).first();
+
+ if (this.rotated) {
+ this.el.addClass('roo-card-rotated');
+ this.fireEvent('rotate', this, true);
+ }
+ this.header_imageEl = this.el.select('.card-img-top',true).first();
+ this.header_imageEl.on('load', this.onHeaderImageLoad, this );
+
+ },
+ getDragData : function(e)
+ {
+ var target = this.getEl();
+ if (target) {
+ //this.handleSelection(e);
- if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
- cfg.cls += ' img-' + _this.border;
- }
-
- if(_this.alt){
- cfg.alt = _this.alt;
- }
+ var dragData = {
+ source: this,
+ copy: false,
+ nodes: this.getEl(),
+ records: []
+ };
- if(_this.href){
- var a = {
- tag: 'a',
- href: _this.href,
- cn: [
- img
- ]
- };
-
- if(this.target){
- a.target = _this.target;
- }
- }
- cfg.cn.push((_this.href) ? a : img);
+ dragData.ddel = target.dom ; // the div element
+ Roo.log(target.getWidth( ));
+ dragData.ddel.style.width = target.getWidth() + 'px';
- });
-
- return cfg;
+ return dragData;
+ }
+ return false;
},
-
- createSingleImg : function()
+ /**
+ * Part of the Roo.dd.DropZone interface. If no target node is found, the
+ * whole Element becomes the target, and this causes the drop gesture to append.
+ *
+ * Returns an object:
+ * {
+
+ position : 'below' or 'above'
+ card : relateive to card OBJECT (or true for no cards listed)
+ items_n : relative to nth item in list
+ card_n : relative to nth card in list
+ }
+ *
+ *
+ */
+ getTargetFromEvent : function(e, dragged_card_el)
{
- var cfg = {
- tag: 'img',
- cls: (this.imgResponsive) ? 'img-responsive' : '',
- html : null,
- src : 'about:blank' // just incase src get's set to undefined?!?
+ var target = e.getTarget();
+ while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
+ target = target.parentNode;
+ }
+
+ var ret = {
+ position: '',
+ cards : [],
+ card_n : -1,
+ items_n : -1,
+ card : false
};
- cfg.html = this.html || cfg.html;
+ //Roo.log([ 'target' , target ? target.id : '--nothing--']);
+ // see if target is one of the 'cards'...
- cfg.src = this.src || cfg.src;
- if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
- cfg.cls += ' img-' + this.border;
+ //Roo.log(this.items.length);
+ var pos = false;
+
+ var last_card_n = 0;
+ var cards_len = 0;
+ for (var i = 0;i< this.items.length;i++) {
+
+ if (!this.items[i].el.hasClass('card')) {
+ continue;
+ }
+ pos = this.getDropPoint(e, this.items[i].el.dom);
+
+ cards_len = ret.cards.length;
+ //Roo.log(this.items[i].el.dom.id);
+ ret.cards.push(this.items[i]);
+ last_card_n = i;
+ if (ret.card_n < 0 && pos == 'above') {
+ ret.position = cards_len > 0 ? 'below' : pos;
+ ret.items_n = i > 0 ? i - 1 : 0;
+ ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
+ ret.card = ret.cards[ret.card_n];
+ }
+ }
+ if (!ret.cards.length) {
+ ret.card = true;
+ ret.position = 'below';
+ ret.items_n;
+ return ret;
+ }
+ // could not find a card.. stick it at the end..
+ if (ret.card_n < 0) {
+ ret.card_n = last_card_n;
+ ret.card = ret.cards[last_card_n];
+ ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
+ ret.position = 'below';
}
- if(this.alt){
- cfg.alt = this.alt;
+ if (this.items[ret.items_n].el == dragged_card_el) {
+ return false;
}
- if(this.href){
- var a = {
- tag: 'a',
- href: this.href,
- cn: [
- cfg
- ]
- };
+ if (ret.position == 'below') {
+ var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
- if(this.target){
- a.target = this.target;
+ if (card_after && card_after.el == dragged_card_el) {
+ return false;
}
-
+ return ret;
}
- return (this.href) ? a : cfg;
+ // its's after ..
+ var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
+
+ if (card_before && card_before.el == dragged_card_el) {
+ return false;
+ }
+
+ return ret;
},
- initEvents: function()
+ onNodeEnter : function(n, dd, e, data){
+ return false;
+ },
+ onNodeOver : function(n, dd, e, data)
{
- if(!this.href){
- this.el.on('click', this.onClick, this);
+
+ var target_info = this.getTargetFromEvent(e,data.source.el);
+ if (target_info === false) {
+ this.dropPlaceHolder('hide');
+ return false;
+ }
+ Roo.log(['getTargetFromEvent', target_info ]);
+
+
+ if (this.fireEvent('cardover', this, [ data ]) === false) {
+ return false;
}
+ this.dropPlaceHolder('show', target_info,data);
+
+ return false;
+ },
+ onNodeOut : function(n, dd, e, data){
+ this.dropPlaceHolder('hide');
+
},
+ onNodeDrop : function(n, dd, e, data)
+ {
+
+ // call drop - return false if
+
+ // this could actually fail - if the Network drops..
+ // we will ignore this at present..- client should probably reload
+ // the whole set of cards if stuff like that fails.
+
+
+ var info = this.getTargetFromEvent(e,data.source.el);
+ if (info === false) {
+ return false;
+ }
+ this.dropPlaceHolder('hide');
+
+
- onClick : function(e)
+ this.acceptCard(data.source, info.position, info.card, info.items_n);
+ return true;
+
+ },
+ firstChildCard : function()
{
- Roo.log('img onclick');
- this.fireEvent('click', this, e);
+ for (var i = 0;i< this.items.length;i++) {
+
+ if (!this.items[i].el.hasClass('card')) {
+ continue;
+ }
+ return this.items[i];
+ }
+ return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
},
/**
- * Sets the url of the image - used to update it
- * @param {String} url the url of the image
+ * accept card
+ *
+ * - card.acceptCard(move_card, info.position, info.card, info.items_n);
*/
-
- setSrc : function(url)
+ acceptCard : function(move_card, position, next_to_card )
{
- this.src = url;
+ if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
+ return false;
+ }
- if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
- this.el.dom.src = url;
+ var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
+
+ move_card.parent().removeCard(move_card);
+
+
+ var dom = move_card.el.dom;
+ dom.style.width = ''; // clear with - which is set by drag.
+
+ if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
+ var cardel = next_to_card.el.dom;
+
+ if (position == 'above' ) {
+ cardel.parentNode.insertBefore(dom, cardel);
+ } else if (cardel.nextSibling) {
+ cardel.parentNode.insertBefore(dom,cardel.nextSibling);
+ } else {
+ cardel.parentNode.append(dom);
+ }
+ } else {
+ // card container???
+ this.containerEl.dom.append(dom);
+ }
+
+ //FIXME HANDLE card = true
+
+ // add this to the correct place in items.
+
+ // remove Card from items.
+
+
+ if (this.items.length) {
+ var nitems = [];
+ //Roo.log([info.items_n, info.position, this.items.length]);
+ for (var i =0; i < this.items.length; i++) {
+ if (i == to_items_n && position == 'above') {
+ nitems.push(move_card);
+ }
+ nitems.push(this.items[i]);
+ if (i == to_items_n && position == 'below') {
+ nitems.push(move_card);
+ }
+ }
+ this.items = nitems;
+ Roo.log(this.items);
+ } else {
+ this.items.push(move_card);
+ }
+
+ move_card.parentId = this.id;
+
+ return true;
+
+
+ },
+ removeCard : function(c)
+ {
+ this.items = this.items.filter(function(e) { return e != c });
+
+ var dom = c.el.dom;
+ dom.parentNode.removeChild(dom);
+ dom.style.width = ''; // clear with - which is set by drag.
+ c.parentId = false;
+
+ },
+
+ /** Decide whether to drop above or below a View node. */
+ getDropPoint : function(e, n, dd)
+ {
+ if (dd) {
+ return false;
+ }
+ if (n == this.containerEl.dom) {
+ return "above";
+ }
+ var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
+ var c = t + (b - t) / 2;
+ var y = Roo.lib.Event.getPageY(e);
+ if(y <= c) {
+ return "above";
+ }else{
+ return "below";
+ }
+ },
+ onToggleCollapse : function(e)
+ {
+ if (this.collapsed) {
+ this.el.select('.roo-collapse-toggle').removeClass('collapsed');
+ this.collapsableEl.addClass('show');
+ this.collapsed = false;
return;
}
+ this.el.select('.roo-collapse-toggle').addClass('collapsed');
+ this.collapsableEl.removeClass('show');
+ this.collapsed = true;
- this.el.select('img', true).first().dom.src = url;
- }
+ },
-
-});
-
- /*
- * - LGPL
- *
- * image
- *
- */
-
-
-/**
- * @class Roo.bootstrap.Link
- * @extends Roo.bootstrap.Component
- * Bootstrap Link Class
- * @cfg {String} alt image alternative text
- * @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 {String} anchor name for the anchor link
- * @cfg {String} fa - favicon
-
- * @cfg {Boolean} preventDefault (true | false) default false
-
- *
- * @constructor
- * Create a new Input
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Link = function(config){
- Roo.bootstrap.Link.superclass.constructor.call(this, config);
+ onToggleRotate : function(e)
+ {
+ this.collapsableEl.removeClass('show');
+ this.footerEl.removeClass('d-none');
+ this.el.removeClass('roo-card-rotated');
+ this.el.removeClass('d-none');
+ if (this.rotated) {
+
+ this.collapsableEl.addClass('show');
+ this.rotated = false;
+ this.fireEvent('rotate', this, this.rotated);
+ return;
+ }
+ this.el.addClass('roo-card-rotated');
+ this.footerEl.addClass('d-none');
+ this.el.select('.roo-collapsable').removeClass('show');
+
+ this.rotated = true;
+ this.fireEvent('rotate', this, this.rotated);
- this.addEvents({
- // img events
- /**
- * @event click
- * The img click event for the img.
- * @param {Roo.EventObject} e
- */
- "click" : true
- });
-};
-
-Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
+ },
- href: false,
- target: false,
- preventDefault: false,
- anchor : false,
- alt : false,
- fa: false,
-
-
- getAutoCreate : function()
+ dropPlaceHolder: function (action, info, data)
{
- var html = this.html || '';
-
- if (this.fa !== false) {
- html = '<i class="fa fa-' + this.fa + '"></i>';
+ if (this.dropEl === false) {
+ this.dropEl = Roo.DomHelper.append(this.containerEl, {
+ cls : 'd-none'
+ },true);
}
- var cfg = {
- tag: 'a'
- };
- // anchor's do not require html/href...
- if (this.anchor === false) {
- cfg.html = html;
- cfg.href = this.href || '#';
- } else {
- cfg.name = this.anchor;
- if (this.html !== false || this.fa !== false) {
- cfg.html = html;
- }
- if (this.href !== false) {
- cfg.href = this.href;
- }
+ this.dropEl.removeClass(['d-none', 'd-block']);
+ if (action == 'hide') {
+
+ this.dropEl.addClass('d-none');
+ return;
}
+ // FIXME - info.card == true!!!
+ this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
- if(this.alt !== false){
- cfg.alt = this.alt;
+ if (info.card !== true) {
+ var cardel = info.card.el.dom;
+
+ if (info.position == 'above') {
+ cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
+ } else if (cardel.nextSibling) {
+ cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
+ } else {
+ cardel.parentNode.append(this.dropEl.dom);
+ }
+ } else {
+ // card container???
+ this.containerEl.dom.append(this.dropEl.dom);
}
+ this.dropEl.addClass('d-block roo-card-dropzone');
- if(this.target !== false) {
- cfg.target = this.target;
- }
+ this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
- return cfg;
- },
-
- initEvents: function() {
- if(!this.href || this.preventDefault){
- this.el.on('click', this.onClick, this);
+
+
+
+ },
+ setHeaderText: function(html)
+ {
+ this.header = html;
+ if (this.headerContainerEl) {
+ this.headerContainerEl.dom.innerHTML = html;
}
},
-
- onClick : function(e)
+ onHeaderImageLoad : function(ev, he)
{
- if(this.preventDefault){
- e.preventDefault();
+ if (!this.header_image_fit_square) {
+ return;
}
- //Roo.log('img onclick');
- this.fireEvent('click', this, e);
+
+ var hw = he.naturalHeight / he.naturalWidth;
+ // wide image = < 0
+ // tall image = > 1
+ //var w = he.dom.naturalWidth;
+ var ww = he.width;
+ he.style.left = 0;
+ he.style.position = 'relative';
+ if (hw > 1) {
+ var nw = (ww * (1/hw));
+ Roo.get(he).setSize( ww * (1/hw), ww);
+ he.style.left = ((ww - nw)/ 2) + 'px';
+ he.style.position = 'relative';
+ }
+
}
-
+
+
});
- /*
+/*
* - LGPL
*
- * header
+ * Card header - holder for the card header elements.
*
*/
/**
- * @class Roo.bootstrap.Header
- * @extends Roo.bootstrap.Component
- * Bootstrap Header class
- * @cfg {String} html content of header
- * @cfg {Number} level (1|2|3|4|5|6) default 1
- *
+ * @class Roo.bootstrap.CardHeader
+ * @extends Roo.bootstrap.Element
+ * @parent Roo.bootstrap.Card
+ * @children Roo.bootstrap.Component
+ * Bootstrap CardHeader class
* @constructor
- * Create a new Header
+ * Create a new Card Header - that you can embed children into
* @param {Object} config The config object
*/
-
-Roo.bootstrap.Header = function(config){
- Roo.bootstrap.Header.superclass.constructor.call(this, config);
+Roo.bootstrap.CardHeader = function(config){
+ Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
- //href : false,
- html : false,
- level : 1,
+ container_method : 'getCardHeader'
+
+
- getAutoCreate : function(){
-
-
-
- var cfg = {
- tag: 'h' + (1 *this.level),
- html: this.html || ''
- } ;
-
- return cfg;
- }
});
/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
+ * - LGPL
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * Card footer - holder for the card footer elements.
+ *
*/
-
+
/**
- * @class Roo.bootstrap.MenuMgr
- * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
- * @singleton
+ * @class Roo.bootstrap.CardFooter
+ * @extends Roo.bootstrap.Element
+ * @parent Roo.bootstrap.Card
+ * @children Roo.bootstrap.Component
+ * Bootstrap CardFooter class
+ *
+ * @constructor
+ * Create a new Card Footer - that you can embed children into
+ * @param {Object} config The config object
*/
-Roo.bootstrap.MenuMgr = function(){
- var menus, active, groups = {}, attached = false, lastShow = new Date();
-
- // private - called when first menu is created
- function init(){
- menus = {};
- active = new Roo.util.MixedCollection();
- Roo.get(document).addKeyListener(27, function(){
- if(active.length > 0){
- hideAll();
- }
- });
- }
-
- // private
- function hideAll(){
- if(active && active.length > 0){
- var c = active.clone();
- c.each(function(m){
- m.hide();
- });
- }
- }
-
- // private
- function onHide(m){
- active.remove(m);
- if(active.length < 1){
- Roo.get(document).un("mouseup", onMouseDown);
-
- attached = false;
- }
- }
-
- // private
- function onShow(m){
- var last = active.last();
- lastShow = new Date();
- active.add(m);
- if(!attached){
- Roo.get(document).on("mouseup", onMouseDown);
-
- attached = true;
- }
- if(m.parentMenu){
- //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
- m.parentMenu.activeChild = m;
- }else if(last && last.isVisible()){
- //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
- }
- }
-
- // private
- function onBeforeHide(m){
- if(m.activeChild){
- m.activeChild.hide();
- }
- if(m.autoHideTimer){
- clearTimeout(m.autoHideTimer);
- delete m.autoHideTimer;
- }
- }
-
- // private
- function onBeforeShow(m){
- var pm = m.parentMenu;
- if(!pm && !m.allowOtherMenus){
- hideAll();
- }else if(pm && pm.activeChild && active != m){
- pm.activeChild.hide();
- }
- }
-
- // private this should really trigger on mouseup..
- function onMouseDown(e){
- Roo.log("on Mouse Up");
-
- if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
- Roo.log("MenuManager hideAll");
- hideAll();
- e.stopEvent();
- }
-
-
- }
-
- // private
- function onBeforeCheck(mi, state){
- if(state){
- var g = groups[mi.group];
- for(var i = 0, l = g.length; i < l; i++){
- if(g[i] != mi){
- g[i].setChecked(false);
- }
- }
- }
- }
-
- return {
- /**
- * Hides all menus that are currently visible
- */
- hideAll : function(){
- hideAll();
- },
-
- // private
- register : function(menu){
- if(!menus){
- init();
- }
- menus[menu.id] = menu;
- menu.on("beforehide", onBeforeHide);
- menu.on("hide", onHide);
- menu.on("beforeshow", onBeforeShow);
- menu.on("show", onShow);
- var g = menu.group;
- if(g && menu.events["checkchange"]){
- if(!groups[g]){
- groups[g] = [];
- }
- groups[g].push(menu);
- menu.on("checkchange", onCheck);
- }
- },
-
- /**
- * Returns a {@link Roo.menu.Menu} object
- * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
- * be used to generate and return a new Menu instance.
- */
- get : function(menu){
- if(typeof menu == "string"){ // menu id
- return menus[menu];
- }else if(menu.events){ // menu instance
- return menu;
- }
- /*else if(typeof menu.length == 'number'){ // array of menu items?
- return new Roo.bootstrap.Menu({items:menu});
- }else{ // otherwise, must be a config
- return new Roo.bootstrap.Menu(menu);
- }
- */
- return false;
- },
+Roo.bootstrap.CardFooter = function(config){
+ Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
+};
- // private
- unregister : function(menu){
- delete menus[menu.id];
- menu.un("beforehide", onBeforeHide);
- menu.un("hide", onHide);
- menu.un("beforeshow", onBeforeShow);
- menu.un("show", onShow);
- var g = menu.group;
- if(g && menu.events["checkchange"]){
- groups[g].remove(menu);
- menu.un("checkchange", onCheck);
- }
- },
+Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
+
+
+ container_method : 'getCardFooter'
+
+
+
+
+
+});
- // private
- registerCheckable : function(menuItem){
- var g = menuItem.group;
- if(g){
- if(!groups[g]){
- groups[g] = [];
- }
- groups[g].push(menuItem);
- menuItem.on("beforecheckchange", onBeforeCheck);
- }
- },
+
- // private
- unregisterCheckable : function(menuItem){
- var g = menuItem.group;
- if(g){
- groups[g].remove(menuItem);
- menuItem.un("beforecheckchange", onBeforeCheck);
- }
- }
- };
-}();/*
+ /*
* - LGPL
*
- * menu
+ * Card header - holder for the card header elements.
*
*/
/**
- * @class Roo.bootstrap.Menu
- * @extends Roo.bootstrap.Component
- * Bootstrap Menu class - container for MenuItems
- * @cfg {String} type (dropdown|treeview|submenu) type of menu
- * @cfg {bool} hidden if the menu should be hidden when rendered.
- * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
- * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
+ * @class Roo.bootstrap.CardImageTop
+ * @extends Roo.bootstrap.Element
+ * @parent Roo.bootstrap.Card
+ * @children Roo.bootstrap.Component
+ * Bootstrap CardImageTop class
*
* @constructor
- * Create a new Menu
+ * Create a new Card Image Top container
* @param {Object} config The config object
*/
+Roo.bootstrap.CardImageTop = function(config){
+ Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
+};
-Roo.bootstrap.Menu = function(config){
- Roo.bootstrap.Menu.superclass.constructor.call(this, config);
- if (this.registerMenu && this.type != 'treeview') {
- Roo.bootstrap.MenuMgr.register(this);
- }
+Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
+
+ container_method : 'getCardImageTop'
- this.addEvents({
- /**
- * @event beforeshow
- * Fires before this menu is displayed
- * @param {Roo.menu.Menu} this
- */
- beforeshow : true,
- /**
- * @event beforehide
- * Fires before this menu is hidden
- * @param {Roo.menu.Menu} this
- */
- beforehide : true,
- /**
- * @event show
- * Fires after this menu is displayed
- * @param {Roo.menu.Menu} this
- */
- show : true,
- /**
- * @event hide
- * Fires after this menu is hidden
- * @param {Roo.menu.Menu} this
- */
- hide : true,
- /**
- * @event click
- * Fires when this menu is clicked (or when the enter key is pressed while it is active)
- * @param {Roo.menu.Menu} this
- * @param {Roo.menu.Item} menuItem The menu item that was clicked
- * @param {Roo.EventObject} e
- */
- click : true,
- /**
- * @event mouseover
- * Fires when the mouse is hovering over this menu
- * @param {Roo.menu.Menu} this
- * @param {Roo.EventObject} e
- * @param {Roo.menu.Item} menuItem The menu item that was clicked
- */
- mouseover : true,
+
+
+
+});
+
+
+
+
+/*
+* Licence: LGPL
+*/
+
+/**
+ * @class Roo.bootstrap.ButtonUploader
+ * @extends Roo.bootstrap.Button
+ * Bootstrap Button Uploader class - it's a button which when you add files to it
+ *
+ *
+ * @cfg {Number} errorTimeout default 3000
+ * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
+ * @cfg {Array} html The button text.
+ * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
+ *
+ * @constructor
+ * Create a new CardUploader
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.ButtonUploader = function(config){
+
+
+
+ Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
+
+
+ this.addEvents({
+ // raw events
/**
- * @event mouseout
- * Fires when the mouse exits this menu
- * @param {Roo.menu.Menu} this
- * @param {Roo.EventObject} e
- * @param {Roo.menu.Item} menuItem The menu item that was clicked
+ * @event beforeselect
+ * When button is pressed, before show upload files dialog is shown
+ * @param {Roo.bootstrap.UploaderButton} this
+ *
*/
- mouseout : true,
- /**
- * @event itemclick
- * Fires when a menu item contained in this menu is clicked
- * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
- * @param {Roo.EventObject} e
+ 'beforeselect' : true,
+ /**
+ * @event fired when files have been selected,
+ * When a the download link is clicked
+ * @param {Roo.bootstrap.UploaderButton} this
+ * @param {Array} Array of files that have been uploaded
*/
- itemclick: true
+ 'uploaded' : true
+
});
- this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
};
-
-Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
+
+Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
- /// html : false,
- //align : '',
- triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
- type: false,
- /**
- * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
- */
- registerMenu : true,
+
+ errorTimeout : 3000,
+
+ images : false,
+
+ fileCollection : false,
+ allowBlank : true,
- menuItems :false, // stores the menu items..
+ multiple : true,
- hidden:true,
+ getAutoCreate : function()
+ {
+
- parentMenu : false,
-
- stopEvent : true,
-
- isLink : false,
-
- getChildContainer : function() {
- return this.el;
+ return {
+ cls :'div' ,
+ cn : [
+ Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
+ ]
+ };
+
+
},
-
- getAutoCreate : function(){
-
- //if (['right'].indexOf(this.align)!==-1) {
- // cfg.cn[1].cls += ' pull-right'
- //}
-
+
+
+ initEvents : function()
+ {
- var cfg = {
- tag : 'ul',
- cls : 'dropdown-menu' ,
- style : 'z-index:1000'
-
- };
-
- if (this.type === 'submenu') {
- cfg.cls = 'submenu active';
- }
- if (this.type === 'treeview') {
- cfg.cls = 'treeview-menu';
- }
+ Roo.bootstrap.Button.prototype.initEvents.call(this);
- return cfg;
- },
- initEvents : function() {
- // Roo.log("ADD event");
- // Roo.log(this.triggerEl.dom);
- this.triggerEl.on('click', this.onTriggerClick, this);
- this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
-
- if (this.triggerEl.hasClass('nav-item')) {
- // dropdown toggle on the 'a' in BS4?
- this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
- } else {
- this.triggerEl.addClass('dropdown-toggle');
- }
- if (Roo.isTouch) {
- this.el.on('touchstart' , this.onTouch, this);
+ this.urlAPI = (window.createObjectURL && window) ||
+ (window.URL && URL.revokeObjectURL && URL) ||
+ (window.webkitURL && webkitURL);
+
+ var im = {
+ tag: 'input',
+ type : 'file',
+ cls : 'd-none roo-card-upload-selector'
+
+ };
+ if (this.multiple) {
+ im.multiple = 'multiple';
}
- this.el.on('click' , this.onClick, this);
-
- this.el.on("mouseover", this.onMouseOver, this);
- this.el.on("mouseout", this.onMouseOut, this);
+ this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
+
+ //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
+ this.selectorEl.on('change', this.onFileSelected, this);
+
+
+
},
- findTargetItem : function(e)
+
+ onClick : function(e)
{
- var t = e.getTarget(".dropdown-menu-item", this.el, true);
- if(!t){
- return false;
- }
- //Roo.log(t); Roo.log(t.id);
- if(t && t.id){
- //Roo.log(this.menuitems);
- return this.menuitems.get(t.id);
-
- //return this.items.get(t.menuItemId);
- }
+ e.preventDefault();
- return false;
- },
-
- onTouch : function(e)
- {
- Roo.log("menu.onTouch");
- //e.stopEvent(); this make the user popdown broken
- this.onClick(e);
+ if ( this.fireEvent('beforeselect', this) === false) {
+ return;
+ }
+
+ this.selectorEl.dom.click();
+
},
- onClick : function(e)
+ onFileSelected : function(e)
{
- Roo.log("menu.onClick");
+ e.preventDefault();
- var t = this.findTargetItem(e);
- if(!t || t.isContainer){
- return;
- }
- Roo.log(e);
- /*
- if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
- if(t == this.activeItem && t.shouldDeactivate(e)){
- this.activeItem.deactivate();
- delete this.activeItem;
- return;
- }
- if(t.canActivate){
- this.setActiveItem(t, true);
- }
+ if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
return;
-
-
- }
- */
-
- Roo.log('pass click event');
-
- t.onClick(e);
-
- this.fireEvent("click", this, t, e);
-
- var _this = this;
-
- if(!t.href.length || t.href == '#'){
- (function() { _this.hide(); }).defer(100);
}
+ var files = Array.prototype.slice.call(this.selectorEl.dom.files);
+ this.selectorEl.dom.value = '';// hopefully reset..
- },
-
- onMouseOver : function(e){
- var t = this.findTargetItem(e);
- //Roo.log(t);
- //if(t){
- // if(t.canActivate && !t.disabled){
- // this.setActiveItem(t, true);
- // }
- //}
-
- this.fireEvent("mouseover", this, e, t);
- },
- isVisible : function(){
- return !this.hidden;
- },
- onMouseOut : function(e){
- var t = this.findTargetItem(e);
+ this.fireEvent('uploaded', this, files );
- //if(t ){
- // if(t == this.activeItem && t.shouldDeactivate(e)){
- // this.activeItem.deactivate();
- // delete this.activeItem;
- // }
- //}
- this.fireEvent("mouseout", this, e, t);
},
+
+
/**
- * Displays this menu relative to another element
- * @param {String/HTMLElement/Roo.Element} element The element to align to
- * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
- * the element (defaults to this.defaultAlign)
- * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
- */
- show : function(el, pos, parentMenu){
- this.parentMenu = parentMenu;
- if(!this.el){
- this.render();
- }
- this.fireEvent("beforeshow", this);
- this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
- },
- /**
- * Displays this menu at a specific xy position
- * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
- * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
- */
- showAt : function(xy, parentMenu, /* private: */_e){
- this.parentMenu = parentMenu;
- if(!this.el){
- this.render();
- }
- if(_e !== false){
- this.fireEvent("beforeshow", this);
- //xy = this.el.adjustForConstraints(xy);
+ * addCard - add an Attachment to the uploader
+ * @param data - the data about the image to upload
+ *
+ * {
+ id : 123
+ title : "Title of file",
+ is_uploaded : false,
+ src : "http://.....",
+ srcfile : { the File upload object },
+ mimetype : file.type,
+ preview : false,
+ is_deleted : 0
+ .. any other data...
}
-
- //this.el.show();
- this.hideMenuItems();
- this.hidden = false;
- this.triggerEl.addClass('open');
- this.el.addClass('show');
-
- // reassign x when hitting right
- if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
- xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
- }
-
- // reassign y when hitting bottom
- if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
- xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
+ *
+ *
+ */
+
+ reset: function()
+ {
+
+ this.selectorEl
+ }
+
+
+
+
+});
+ /*
+ * - LGPL
+ *
+ * image
+ *
+ */
+
+
+/**
+ * @class Roo.bootstrap.Img
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Img class
+ * @cfg {Boolean} imgResponsive false | true
+ * @cfg {String} border rounded | circle | thumbnail
+ * @cfg {String} src image source
+ * @cfg {String} alt image alternative text
+ * @cfg {String} href a tag href
+ * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
+ * @cfg {String} xsUrl xs image source
+ * @cfg {String} smUrl sm image source
+ * @cfg {String} mdUrl md image source
+ * @cfg {String} lgUrl lg image source
+ * @cfg {Boolean} backgroundContain (use style background and contain image in content)
+ *
+ * @constructor
+ * Create a new Input
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Img = function(config){
+ Roo.bootstrap.Img.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ // img events
+ /**
+ * @event click
+ * The img click event for the img.
+ * @param {Roo.EventObject} e
+ */
+ "click" : true,
+ /**
+ * @event load
+ * The when any image loads
+ * @param {Roo.EventObject} e
+ */
+ "load" : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
+
+ imgResponsive: true,
+ border: '',
+ src: 'about:blank',
+ href: false,
+ target: false,
+ xsUrl: '',
+ smUrl: '',
+ mdUrl: '',
+ lgUrl: '',
+ backgroundContain : false,
+
+ getAutoCreate : function()
+ {
+ if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
+ return this.createSingleImg();
}
- // but the list may align on trigger left or trigger top... should it be a properity?
+ var cfg = {
+ tag: 'div',
+ cls: 'roo-image-responsive-group',
+ cn: []
+ };
+ var _this = this;
- if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
- this.el.setXY(xy);
- }
+ Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
+
+ if(!_this[size + 'Url']){
+ return;
+ }
+
+ var img = {
+ tag: 'img',
+ cls: (_this.imgResponsive) ? 'img-responsive' : '',
+ html: _this.html || cfg.html,
+ src: _this[size + 'Url']
+ };
+
+ img.cls += ' roo-image-responsive-' + size;
+
+ var s = ['xs', 'sm', 'md', 'lg'];
+
+ s.splice(s.indexOf(size), 1);
+
+ Roo.each(s, function(ss){
+ img.cls += ' hidden-' + ss;
+ });
+
+ if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
+ cfg.cls += ' img-' + _this.border;
+ }
+
+ if(_this.alt){
+ cfg.alt = _this.alt;
+ }
+
+ if(_this.href){
+ var a = {
+ tag: 'a',
+ href: _this.href,
+ cn: [
+ img
+ ]
+ };
+
+ if(this.target){
+ a.target = _this.target;
+ }
+ }
+
+ cfg.cn.push((_this.href) ? a : img);
+
+ });
- this.focus();
- this.fireEvent("show", this);
+ return cfg;
},
- focus : function(){
- return;
- if(!this.hidden){
- this.doFocus.defer(50, this);
- }
- },
-
- doFocus : function(){
- if(!this.hidden){
- this.focusEl.focus();
- }
- },
-
- /**
- * Hides this menu and optionally all parent menus
- * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
- */
- hide : function(deep)
+ createSingleImg : function()
{
+ var cfg = {
+ tag: 'img',
+ cls: (this.imgResponsive) ? 'img-responsive' : '',
+ html : null,
+ src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
+ };
- this.hideMenuItems();
- if(this.el && this.isVisible()){
- this.fireEvent("beforehide", this);
- if(this.activeItem){
- this.activeItem.deactivate();
- this.activeItem = null;
- }
- this.triggerEl.removeClass('open');;
- this.el.removeClass('show');
- this.hidden = true;
- this.fireEvent("hide", this);
+ if (this.backgroundContain) {
+ cfg.cls += ' background-contain';
}
- if(deep === true && this.parentMenu){
- this.parentMenu.hide(true);
+
+ cfg.html = this.html || cfg.html;
+
+ if (this.backgroundContain) {
+ cfg.style="background-image: url(" + this.src + ')';
+ } else {
+ cfg.src = this.src || cfg.src;
}
- },
-
- onTriggerClick : function(e)
- {
- Roo.log('trigger click');
- var target = e.getTarget();
+ if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
+ cfg.cls += ' img-' + this.border;
+ }
- Roo.log(target.nodeName.toLowerCase());
+ if(this.alt){
+ cfg.alt = this.alt;
+ }
- if(target.nodeName.toLowerCase() === 'i'){
- e.preventDefault();
+ if(this.href){
+ var a = {
+ tag: 'a',
+ href: this.href,
+ cn: [
+ cfg
+ ]
+ };
+
+ if(this.target){
+ a.target = this.target;
+ }
+
}
+ return (this.href) ? a : cfg;
},
- onTriggerPress : function(e)
+ initEvents: function()
{
- Roo.log('trigger press');
- //Roo.log(e.getTarget());
- // Roo.log(this.triggerEl.dom);
-
- // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
- var pel = Roo.get(e.getTarget());
- if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
- Roo.log('is treeview or dropdown?');
- return;
- }
-
- if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
- return;
+ if(!this.href){
+ this.el.on('click', this.onClick, this);
}
-
- if (this.isVisible()) {
- Roo.log('hide');
- this.hide();
+ if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
+ this.el.on('load', this.onImageLoad, this);
} else {
- Roo.log('show');
- this.show(this.triggerEl, '?', false);
- }
-
- if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
- e.stopEvent();
+ // not sure if this works.. not tested
+ this.el.select('img', true).on('load', this.onImageLoad, this);
}
},
-
- hideMenuItems : function()
+ onClick : function(e)
{
- Roo.log("hide Menu Items");
- if (!this.el) {
- return;
- }
- //$(backdrop).remove()
- this.el.select('.open',true).each(function(aa) {
-
- aa.removeClass('open');
- //var parent = getParent($(this))
- //var relatedTarget = { relatedTarget: this }
-
- //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
- //if (e.isDefaultPrevented()) return
- //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
- });
- },
- addxtypeChild : function (tree, cntr) {
- var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
-
- this.menuitems.add(comp);
- return comp;
-
+ Roo.log('img onclick');
+ this.fireEvent('click', this, e);
},
- getEl : function()
+ onImageLoad: function(e)
{
- Roo.log(this.el);
- return this.el;
+ Roo.log('img load');
+ this.fireEvent('load', this, e);
},
- clear : function()
+ /**
+ * Sets the url of the image - used to update it
+ * @param {String} url the url of the image
+ */
+
+ setSrc : function(url)
{
- this.getEl().dom.innerHTML = '';
- this.menuitems.clear();
+ this.src = url;
+
+ if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
+ if (this.backgroundContain) {
+ this.el.dom.style.backgroundImage = 'url(' + url + ')';
+ } else {
+ this.el.dom.src = url;
+ }
+ return;
+ }
+
+ this.el.select('img', true).first().dom.src = url;
}
+
+
+
});
-
/*
* - LGPL
*
- * menu item
+ * image
*
*/
/**
- * @class Roo.bootstrap.MenuItem
+ * @class Roo.bootstrap.Link
* @extends Roo.bootstrap.Component
- * Bootstrap MenuItem class
- * @cfg {String} html the menu label
- * @cfg {String} href the link
- * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
- * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
- * @cfg {Boolean} active used on sidebars to highlight active itesm
- * @cfg {String} fa favicon to show on left of menu item.
- * @cfg {Roo.bootsrap.Menu} menu the child menu.
- *
+ * @children Roo.bootstrap.Component
+ * Bootstrap Link Class (eg. '<a href>')
+
+ * @cfg {String} alt image alternative text
+ * @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 {String} anchor name for the anchor link
+ * @cfg {String} fa - favicon
+
+ * @cfg {Boolean} preventDefault (true | false) default false
+
*
* @constructor
- * Create a new MenuItem
+ * Create a new Input
* @param {Object} config The config object
*/
-
-Roo.bootstrap.MenuItem = function(config){
- Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
+Roo.bootstrap.Link = function(config){
+ Roo.bootstrap.Link.superclass.constructor.call(this, config);
+
this.addEvents({
- // raw events
+ // img events
/**
* @event click
- * The raw click event for the entire grid.
- * @param {Roo.bootstrap.MenuItem} this
+ * The img click event for the img.
* @param {Roo.EventObject} e
*/
"click" : true
});
};
-Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
- href : false,
- html : false,
+ href: false,
+ target: false,
preventDefault: false,
- isContainer : false,
- active : false,
+ anchor : false,
+ alt : false,
fa: false,
-
- getAutoCreate : function(){
-
- if(this.isContainer){
- return {
- tag: 'li',
- cls: 'dropdown-menu-item '
- };
- }
- var ctag = {
- tag: 'span',
- html: 'Link'
- };
-
- var anc = {
- tag : 'a',
- cls : 'dropdown-item',
- href : '#',
- cn : [ ]
- };
+
+
+ getAutoCreate : function()
+ {
+ var html = this.html || '';
if (this.fa !== false) {
- anc.cn.push({
- tag : 'i',
- cls : 'fa fa-' + this.fa
- });
+ html = '<i class="fa fa-' + this.fa + '"></i>';
}
-
- anc.cn.push(ctag);
-
-
- var cfg= {
- tag: 'li',
- cls: 'dropdown-menu-item',
- cn: [ anc ]
+ var cfg = {
+ tag: 'a'
};
- if (this.parent().type == 'treeview') {
- cfg.cls = 'treeview-menu';
+ // anchor's do not require html/href...
+ if (this.anchor === false) {
+ cfg.html = html;
+ cfg.href = this.href || '#';
+ } else {
+ cfg.name = this.anchor;
+ if (this.html !== false || this.fa !== false) {
+ cfg.html = html;
+ }
+ if (this.href !== false) {
+ cfg.href = this.href;
+ }
}
- if (this.active) {
- cfg.cls += ' active';
+
+ if(this.alt !== false){
+ cfg.alt = this.alt;
}
+ if(this.target !== false) {
+ cfg.target = this.target;
+ }
- anc.href = this.href || cfg.cn[0].href ;
- ctag.html = this.html || cfg.cn[0].html ;
return cfg;
},
- initEvents: function()
- {
- if (this.parent().type == 'treeview') {
- this.el.select('a').on('click', this.onClick, this);
- }
+ initEvents: function() {
- if (this.menu) {
- this.menu.parentType = this.xtype;
- this.menu.triggerEl = this.el;
- this.menu = this.addxtype(Roo.apply({}, this.menu));
+ if(!this.href || this.preventDefault){
+ this.el.on('click', this.onClick, this);
}
-
},
+
onClick : function(e)
{
- Roo.log('item on click ');
-
if(this.preventDefault){
e.preventDefault();
}
- //this.parent().hideMenuItems();
-
+ //Roo.log('img onclick');
this.fireEvent('click', this, e);
- },
- getEl : function()
- {
- return this.el;
- }
+ }
+
});
-
-
/*
* - LGPL
*
- * menu separator
+ * header
*
*/
-
/**
- * @class Roo.bootstrap.MenuSeparator
+ * @class Roo.bootstrap.Header
* @extends Roo.bootstrap.Component
- * Bootstrap MenuSeparator class
+ * @children Roo.bootstrap.Component
+ * Bootstrap Header class
+ *
+ *
+ * @cfg {String} html content of header
+ * @cfg {Number} level (1|2|3|4|5|6) default 1
*
* @constructor
- * Create a new MenuItem
+ * Create a new Header
* @param {Object} config The config object
*/
-Roo.bootstrap.MenuSeparator = function(config){
- Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
+Roo.bootstrap.Header = function(config){
+ Roo.bootstrap.Header.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
+
+ //href : false,
+ html : false,
+ level : 1,
+
+
getAutoCreate : function(){
+
+
+
var cfg = {
- cls: 'divider',
- tag : 'li'
- };
+ tag: 'h' + (1 *this.level),
+ html: this.html || ''
+ } ;
return cfg;
}
-
-/*
-* Licence: LGPL
-*/
-
-/**
- * @class Roo.bootstrap.Modal
- * @extends Roo.bootstrap.Component
- * Bootstrap Modal class
- * @cfg {String} title Title of dialog
- * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
- * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
- * @cfg {Boolean} specificTitle default false
- * @cfg {Array} buttons Array of buttons or standard button set..
- * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
- * @cfg {Boolean} animate default true
- * @cfg {Boolean} allow_close default true
- * @cfg {Boolean} fitwindow default false
- * @cfg {String} size (sm|lg) default empty
- * @cfg {Number} max_width set the max width of modal
- *
- *
- * @constructor
- * Create a new Modal Dialog
- * @param {Object} config The config object
+ /**
+ * @class Roo.bootstrap.MenuMgr
+ * @licence LGPL
+ * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
+ * @static
*/
+Roo.bootstrap.menu.Manager = function(){
+ var menus, active, groups = {}, attached = false, lastShow = new Date();
-Roo.bootstrap.Modal = function(config){
- Roo.bootstrap.Modal.superclass.constructor.call(this, config);
- this.addEvents({
- // raw events
- /**
- * @event btnclick
- * The raw btnclick event for the button
- * @param {Roo.EventObject} e
- */
- "btnclick" : true,
- /**
- * @event resize
- * Fire when dialog resize
- * @param {Roo.bootstrap.Modal} this
- * @param {Roo.EventObject} e
- */
- "resize" : true
- });
- this.buttons = this.buttons || [];
-
- if (this.tmpl) {
- this.tmpl = Roo.factory(this.tmpl);
- }
-
-};
+ // private - called when first menu is created
+ function init(){
+ menus = {};
+ active = new Roo.util.MixedCollection();
+ Roo.get(document).addKeyListener(27, function(){
+ if(active.length > 0){
+ hideAll();
+ }
+ });
+ }
-Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
+ // private
+ function hideAll(){
+ if(active && active.length > 0){
+ var c = active.clone();
+ c.each(function(m){
+ m.hide();
+ });
+ }
+ }
- title : 'test dialog',
-
- buttons : false,
-
- // set on load...
-
- html: false,
-
- tmp: false,
-
- specificTitle: false,
-
- buttonPosition: 'right',
-
- allow_close : true,
-
- animate : true,
-
- fitwindow: false,
-
- // private
- dialogEl: false,
- bodyEl: false,
- footerEl: false,
- titleEl: false,
- closeEl: false,
-
- size: '',
-
- max_width: 0,
-
- max_height: 0,
-
- fit_content: false,
+ // private
+ function onHide(m){
+ active.remove(m);
+ if(active.length < 1){
+ Roo.get(document).un("mouseup", onMouseDown);
+
+ attached = false;
+ }
+ }
- onRender : function(ct, position)
- {
- Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
+ // private
+ function onShow(m){
+ var last = active.last();
+ lastShow = new Date();
+ active.add(m);
+ if(!attached){
+ Roo.get(document).on("mouseup", onMouseDown);
+
+ attached = true;
+ }
+ if(m.parentMenu){
+ //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
+ m.parentMenu.activeChild = m;
+ }else if(last && last.isVisible()){
+ //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
+ }
+ }
- if(!this.el){
- var cfg = Roo.apply({}, this.getAutoCreate());
- cfg.id = Roo.id();
- //if(!cfg.name){
- // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
- //}
- //if (!cfg.name.length) {
- // delete cfg.name;
- // }
- if (this.cls) {
- cfg.cls += ' ' + this.cls;
- }
- if (this.style) {
- cfg.style = this.style;
- }
- this.el = Roo.get(document.body).createChild(cfg, position);
- }
- //var type = this.el.dom.type;
+ // private
+ function onBeforeHide(m){
+ if(m.activeChild){
+ m.activeChild.hide();
+ }
+ if(m.autoHideTimer){
+ clearTimeout(m.autoHideTimer);
+ delete m.autoHideTimer;
+ }
+ }
+ // private
+ function onBeforeShow(m){
+ var pm = m.parentMenu;
+ if(!pm && !m.allowOtherMenus){
+ hideAll();
+ }else if(pm && pm.activeChild && active != m){
+ pm.activeChild.hide();
+ }
+ }
- if(this.tabIndex !== undefined){
- this.el.dom.setAttribute('tabIndex', this.tabIndex);
+ // private this should really trigger on mouseup..
+ function onMouseDown(e){
+ Roo.log("on Mouse Up");
+
+ if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
+ Roo.log("MenuManager hideAll");
+ hideAll();
+ e.stopEvent();
}
-
- this.dialogEl = this.el.select('.modal-dialog',true).first();
- this.bodyEl = this.el.select('.modal-body',true).first();
- this.closeEl = this.el.select('.modal-header .close', true).first();
- this.headerEl = this.el.select('.modal-header',true).first();
- this.titleEl = this.el.select('.modal-title',true).first();
- this.footerEl = this.el.select('.modal-footer',true).first();
-
- this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
- //this.el.addClass("x-dlg-modal");
+
+ }
- if (this.buttons.length) {
- Roo.each(this.buttons, function(bb) {
- var b = Roo.apply({}, bb);
- b.xns = b.xns || Roo.bootstrap;
- b.xtype = b.xtype || 'Button';
- if (typeof(b.listeners) == 'undefined') {
- b.listeners = { click : this.onButtonClick.createDelegate(this) };
- }
+ // private
+ function onBeforeCheck(mi, state){
+ if(state){
+ var g = groups[mi.group];
+ for(var i = 0, l = g.length; i < l; i++){
+ if(g[i] != mi){
+ g[i].setChecked(false);
+ }
+ }
+ }
+ }
- var btn = Roo.factory(b);
+ return {
- btn.render(this.getButtonContainer());
+ /**
+ * Hides all menus that are currently visible
+ */
+ hideAll : function(){
+ hideAll();
+ },
- },this);
- }
- // render the children.
- var nitems = [];
+ // private
+ register : function(menu){
+ if(!menus){
+ init();
+ }
+ menus[menu.id] = menu;
+ menu.on("beforehide", onBeforeHide);
+ menu.on("hide", onHide);
+ menu.on("beforeshow", onBeforeShow);
+ menu.on("show", onShow);
+ var g = menu.group;
+ if(g && menu.events["checkchange"]){
+ if(!groups[g]){
+ groups[g] = [];
+ }
+ groups[g].push(menu);
+ menu.on("checkchange", onCheck);
+ }
+ },
- if(typeof(this.items) != 'undefined'){
- var items = this.items;
- delete this.items;
+ /**
+ * Returns a {@link Roo.menu.Menu} object
+ * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
+ * be used to generate and return a new Menu instance.
+ */
+ get : function(menu){
+ if(typeof menu == "string"){ // menu id
+ return menus[menu];
+ }else if(menu.events){ // menu instance
+ return menu;
+ }
+ /*else if(typeof menu.length == 'number'){ // array of menu items?
+ return new Roo.bootstrap.Menu({items:menu});
+ }else{ // otherwise, must be a config
+ return new Roo.bootstrap.Menu(menu);
+ }
+ */
+ return false;
+ },
- for(var i =0;i < items.length;i++) {
- nitems.push(this.addxtype(Roo.apply({}, items[i])));
- }
- }
+ // private
+ unregister : function(menu){
+ delete menus[menu.id];
+ menu.un("beforehide", onBeforeHide);
+ menu.un("hide", onHide);
+ menu.un("beforeshow", onBeforeShow);
+ menu.un("show", onShow);
+ var g = menu.group;
+ if(g && menu.events["checkchange"]){
+ groups[g].remove(menu);
+ menu.un("checkchange", onCheck);
+ }
+ },
- this.items = nitems;
+ // private
+ registerCheckable : function(menuItem){
+ var g = menuItem.group;
+ if(g){
+ if(!groups[g]){
+ groups[g] = [];
+ }
+ groups[g].push(menuItem);
+ menuItem.on("beforecheckchange", onBeforeCheck);
+ }
+ },
- // where are these used - they used to be body/close/footer
+ // private
+ unregisterCheckable : function(menuItem){
+ var g = menuItem.group;
+ if(g){
+ groups[g].remove(menuItem);
+ menuItem.un("beforecheckchange", onBeforeCheck);
+ }
+ }
+ };
+}();
+/**
+ * @class Roo.bootstrap.menu.Menu
+ * @extends Roo.bootstrap.Component
+ * @licence LGPL
+ * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
+ * @parent none
+ * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
+ *
+ * @cfg {String} type (dropdown|treeview|submenu) type of menu
+ * @cfg {bool} hidden if the menu should be hidden when rendered.
+ * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
+ * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
+* @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
+* @cfg {String} align default tl-bl? == below - how the menu should be aligned.
+
+ * @constructor
+ * Create a new Menu
+ * @param {Object} config The config objectQ
+ */
- this.initEvents();
- //this.el.addClass([this.fieldClass, this.cls]);
+Roo.bootstrap.menu.Menu = function(config){
+
+ if (config.type == 'treeview') {
+ // normally menu's are drawn attached to the document to handle layering etc..
+ // however treeview (used by the docs menu is drawn into the parent element)
+ this.container_method = 'getChildContainer';
+ }
+
+ Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
+ if (this.registerMenu && this.type != 'treeview') {
+ Roo.bootstrap.menu.Manager.register(this);
+ }
+
+
+ this.addEvents({
+ /**
+ * @event beforeshow
+ * Fires before this menu is displayed (return false to block)
+ * @param {Roo.menu.Menu} this
+ */
+ beforeshow : true,
+ /**
+ * @event beforehide
+ * Fires before this menu is hidden (return false to block)
+ * @param {Roo.menu.Menu} this
+ */
+ beforehide : true,
+ /**
+ * @event show
+ * Fires after this menu is displayed
+ * @param {Roo.menu.Menu} this
+ */
+ show : true,
+ /**
+ * @event hide
+ * Fires after this menu is hidden
+ * @param {Roo.menu.Menu} this
+ */
+ hide : true,
+ /**
+ * @event click
+ * Fires when this menu is clicked (or when the enter key is pressed while it is active)
+ * @param {Roo.menu.Menu} this
+ * @param {Roo.menu.Item} menuItem The menu item that was clicked
+ * @param {Roo.EventObject} e
+ */
+ click : true,
+ /**
+ * @event mouseover
+ * Fires when the mouse is hovering over this menu
+ * @param {Roo.menu.Menu} this
+ * @param {Roo.EventObject} e
+ * @param {Roo.menu.Item} menuItem The menu item that was clicked
+ */
+ mouseover : true,
+ /**
+ * @event mouseout
+ * Fires when the mouse exits this menu
+ * @param {Roo.menu.Menu} this
+ * @param {Roo.EventObject} e
+ * @param {Roo.menu.Item} menuItem The menu item that was clicked
+ */
+ mouseout : true,
+ /**
+ * @event itemclick
+ * Fires when a menu item contained in this menu is clicked
+ * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
+ * @param {Roo.EventObject} e
+ */
+ itemclick: true
+ });
+ this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
+};
+Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
+
+ /// html : false,
+
+ triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
+ type: false,
+ /**
+ * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
+ */
+ registerMenu : true,
+
+ menuItems :false, // stores the menu items..
+
+ hidden:true,
+
+ parentMenu : false,
+
+ stopEvent : true,
+
+ isLink : false,
+
+ container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
+
+ hideTrigger : false,
+
+ align : 'tl-bl?',
+
+
+ getChildContainer : function() {
+ return this.el;
},
-
- getAutoCreate : function()
- {
- var bdy = {
- cls : 'modal-body',
- html : this.html || ''
- };
-
- var title = {
- tag: 'h4',
- cls : 'modal-title',
- html : this.title
+
+ getAutoCreate : function(){
+
+ //if (['right'].indexOf(this.align)!==-1) {
+ // cfg.cn[1].cls += ' pull-right'
+ //}
+
+ var cfg = {
+ tag : 'ul',
+ cls : 'dropdown-menu shadow' ,
+ style : 'z-index:1000'
+
};
-
- if(this.specificTitle){
- title = this.title;
-
- }
-
- var header = [];
- if (this.allow_close && Roo.bootstrap.version == 3) {
- header.push({
- tag: 'button',
- cls : 'close',
- html : '×'
- });
+
+ if (this.type === 'submenu') {
+ cfg.cls = 'submenu active';
}
-
- header.push(title);
-
- if (this.allow_close && Roo.bootstrap.version == 4) {
- header.push({
- tag: 'button',
- cls : 'close',
- html : '×'
- });
+ if (this.type === 'treeview') {
+ cfg.cls = 'treeview-menu';
}
- var size = '';
-
- if(this.size.length){
- size = 'modal-' + this.size;
- }
+ return cfg;
+ },
+ initEvents : function() {
- var footer = Roo.bootstrap.version == 3 ?
- {
- cls : 'modal-footer',
- cn : [
- {
- tag: 'div',
- cls: 'btn-' + this.buttonPosition
- }
- ]
-
- } :
- { // BS4 uses mr-auto on left buttons....
- cls : 'modal-footer'
- };
-
+ // Roo.log("ADD event");
+ // Roo.log(this.triggerEl.dom);
+ if (this.triggerEl) {
-
-
-
- var modal = {
- cls: "modal",
- cn : [
- {
- cls: "modal-dialog " + size,
- cn : [
- {
- cls : "modal-content",
- cn : [
- {
- cls : 'modal-header',
- cn : header
- },
- bdy,
- footer
- ]
-
- }
- ]
-
+ this.triggerEl.on('click', this.onTriggerClick, this);
+
+ this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
+
+ if (!this.hideTrigger) {
+ if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
+ // dropdown toggle on the 'a' in BS4?
+ this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
+ } else {
+ this.triggerEl.addClass('dropdown-toggle');
}
- ]
- };
-
- if(this.animate){
- modal.cls += ' fade';
+ }
}
-
- return modal;
-
- },
- getChildContainer : function() {
-
- return this.bodyEl;
-
- },
- getButtonContainer : function() {
- return Roo.bootstrap.version == 4 ?
- this.el.select('.modal-footer',true).first()
- : this.el.select('.modal-footer div',true).first();
+ if (Roo.isTouch) {
+ this.el.on('touchstart' , this.onTouch, this);
+ }
+ this.el.on('click' , this.onClick, this);
+ this.el.on("mouseover", this.onMouseOver, this);
+ this.el.on("mouseout", this.onMouseOut, this);
+
},
- initEvents : function()
+
+ findTargetItem : function(e)
{
- if (this.allow_close) {
- this.closeEl.on('click', this.hide, this);
+ var t = e.getTarget(".dropdown-menu-item", this.el, true);
+ if(!t){
+ return false;
}
- Roo.EventManager.onWindowResize(this.resize, this, true);
-
-
+ //Roo.log(t); Roo.log(t.id);
+ if(t && t.id){
+ //Roo.log(this.menuitems);
+ return this.menuitems.get(t.id);
+
+ //return this.items.get(t.menuItemId);
+ }
+
+ return false;
},
-
-
- resize : function()
+
+ onTouch : function(e)
{
- this.maskEl.setSize(
- Roo.lib.Dom.getViewWidth(true),
- Roo.lib.Dom.getViewHeight(true)
- );
+ Roo.log("menu.onTouch");
+ //e.stopEvent(); this make the user popdown broken
+ this.onClick(e);
+ },
+
+ onClick : function(e)
+ {
+ Roo.log("menu.onClick");
- if (this.fitwindow) {
-
-
- this.setSize(
- this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
- this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
- );
+ var t = this.findTargetItem(e);
+ if(!t || t.isContainer){
return;
}
-
- if(this.max_width !== 0) {
-
- var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
-
- if(this.height) {
- this.setSize(w, this.height);
+ Roo.log(e);
+ /*
+ if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
+ if(t == this.activeItem && t.shouldDeactivate(e)){
+ this.activeItem.deactivate();
+ delete this.activeItem;
return;
}
-
- if(this.max_height) {
- this.setSize(w,Math.min(
- this.max_height,
- Roo.lib.Dom.getViewportHeight(true) - 60
- ));
-
- return;
+ if(t.canActivate){
+ this.setActiveItem(t, true);
}
+ return;
- if(!this.fit_content) {
- this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
- return;
- }
- this.setSize(w, Math.min(
- 60 +
- this.headerEl.getHeight() +
- this.footerEl.getHeight() +
- this.getChildHeight(this.bodyEl.dom.childNodes),
- Roo.lib.Dom.getViewportHeight(true) - 60)
- );
+ }
+ */
+
+ Roo.log('pass click event');
+
+ t.onClick(e);
+
+ this.fireEvent("click", this, t, e);
+
+ var _this = this;
+
+ if(!t.href.length || t.href == '#'){
+ (function() { _this.hide(); }).defer(100);
}
},
-
- setSize : function(w,h)
+
+ onMouseOver : function(e){
+ var t = this.findTargetItem(e);
+ //Roo.log(t);
+ //if(t){
+ // if(t.canActivate && !t.disabled){
+ // this.setActiveItem(t, true);
+ // }
+ //}
+
+ this.fireEvent("mouseover", this, e, t);
+ },
+ isVisible : function(){
+ return !this.hidden;
+ },
+ onMouseOut : function(e){
+ var t = this.findTargetItem(e);
+
+ //if(t ){
+ // if(t == this.activeItem && t.shouldDeactivate(e)){
+ // this.activeItem.deactivate();
+ // delete this.activeItem;
+ // }
+ //}
+ this.fireEvent("mouseout", this, e, t);
+ },
+
+
+ /**
+ * Displays this menu relative to another element
+ * @param {String/HTMLElement/Roo.Element} element The element to align to
+ * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
+ * the element (defaults to this.defaultAlign)
+ * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
+ */
+ show : function(el, pos, parentMenu)
{
- if (!w && !h) {
- return;
+ if (false === this.fireEvent("beforeshow", this)) {
+ Roo.log("show canceled");
+ return;
+ }
+ this.parentMenu = parentMenu;
+ if(!this.el){
+ this.render();
+ }
+ this.el.addClass('show'); // show otherwise we do not know how big we are..
+
+ var xy = this.el.getAlignToXY(el, pos);
+
+ // bl-tl << left align below
+ // tl-bl << left align
+
+ if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
+ // if it goes to far to the right.. -> align left.
+ xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
+ }
+ if(xy[0] < 0){
+ // was left align - go right?
+ xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
+ }
+
+ // goes down the bottom
+ if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
+ xy[1] < 0 ){
+ var a = this.align.replace('?', '').split('-');
+ xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
+
}
- this.resizeTo(w,h);
+ this.showAt( xy , parentMenu, false);
},
-
- show : function() {
-
- if (!this.rendered) {
+ /**
+ * Displays this menu at a specific xy position
+ * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
+ * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
+ */
+ showAt : function(xy, parentMenu, /* private: */_e){
+ this.parentMenu = parentMenu;
+ if(!this.el){
this.render();
}
-
- //this.el.setStyle('display', 'block');
- this.el.removeClass('hideing');
- this.el.dom.style.display='block';
-
- Roo.get(document.body).addClass('modal-open');
-
- if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
-
- (function(){
- this.el.addClass('show');
- this.el.addClass('in');
- }).defer(50, this);
- }else{
- this.el.addClass('show');
- this.el.addClass('in');
+ if(_e !== false){
+ this.fireEvent("beforeshow", this);
+ //xy = this.el.adjustForConstraints(xy);
}
-
- // not sure how we can show data in here..
- //if (this.tmpl) {
- // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
- //}
-
- Roo.get(document.body).addClass("x-body-masked");
- this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
- this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
- this.maskEl.dom.style.display = 'block';
- this.maskEl.addClass('show');
+ //this.el.show();
+ this.hideMenuItems();
+ this.hidden = false;
+ if (this.triggerEl) {
+ this.triggerEl.addClass('open');
+ }
+ this.el.addClass('show');
- this.resize();
+
+
+ // reassign x when hitting right
- this.fireEvent('show', this);
-
- // set zindex here - otherwise it appears to be ignored...
- this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
-
- (function () {
- this.items.forEach( function(e) {
- e.layout ? e.layout() : false;
-
- });
- }).defer(100,this);
-
- },
- hide : function()
- {
- if(this.fireEvent("beforehide", this) !== false){
-
- this.maskEl.removeClass('show');
-
- this.maskEl.dom.style.display = '';
- Roo.get(document.body).removeClass("x-body-masked");
- this.el.removeClass('in');
- this.el.select('.modal-dialog', true).first().setStyle('transform','');
-
- if(this.animate){ // why
- this.el.addClass('hideing');
- this.el.removeClass('show');
- (function(){
- if (!this.el.hasClass('hideing')) {
- return; // it's been shown again...
- }
-
- this.el.dom.style.display='';
-
- Roo.get(document.body).removeClass('modal-open');
- this.el.removeClass('hideing');
- }).defer(150,this);
-
- }else{
- this.el.removeClass('show');
- this.el.dom.style.display='';
- Roo.get(document.body).removeClass('modal-open');
-
- }
- this.fireEvent('hide', this);
- }
- },
- isVisible : function()
- {
+ // reassign y when hitting bottom
- return this.el.hasClass('show') && !this.el.hasClass('hideing');
+ // but the list may align on trigger left or trigger top... should it be a properity?
- },
-
- addButton : function(str, cb)
- {
-
-
- var b = Roo.apply({}, { html : str } );
- b.xns = b.xns || Roo.bootstrap;
- b.xtype = b.xtype || 'Button';
- if (typeof(b.listeners) == 'undefined') {
- b.listeners = { click : cb.createDelegate(this) };
+ if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
+ this.el.setXY(xy);
}
-
- var btn = Roo.factory(b);
-
- btn.render(this.getButtonContainer());
-
- return btn;
-
- },
-
- setDefaultButton : function(btn)
- {
- //this.el.select('.modal-footer').()
- },
-
- resizeTo: function(w,h)
- {
- this.dialogEl.setWidth(w);
-
- var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
-
- this.bodyEl.setHeight(h - diff);
- this.fireEvent('resize', this);
+ this.focus();
+ this.fireEvent("show", this);
},
- setContentSize : function(w, h)
- {
-
- },
- onButtonClick: function(btn,e)
- {
- //Roo.log([a,b,c]);
- this.fireEvent('btnclick', btn.name, e);
+ focus : function(){
+ return;
+ if(!this.hidden){
+ this.doFocus.defer(50, this);
+ }
},
- /**
- * Set the title of the Dialog
- * @param {String} str new Title
- */
- setTitle: function(str) {
- this.titleEl.dom.innerHTML = str;
+
+ doFocus : function(){
+ if(!this.hidden){
+ this.focusEl.focus();
+ }
},
+
/**
- * Set the body of the Dialog
- * @param {String} str new Title
+ * Hides this menu and optionally all parent menus
+ * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
*/
- setBody: function(str) {
- this.bodyEl.dom.innerHTML = str;
+ hide : function(deep)
+ {
+ if (false === this.fireEvent("beforehide", this)) {
+ Roo.log("hide canceled");
+ return;
+ }
+ this.hideMenuItems();
+ if(this.el && this.isVisible()){
+
+ if(this.activeItem){
+ this.activeItem.deactivate();
+ this.activeItem = null;
+ }
+ if (this.triggerEl) {
+ this.triggerEl.removeClass('open');
+ }
+
+ this.el.removeClass('show');
+ this.hidden = true;
+ this.fireEvent("hide", this);
+ }
+ if(deep === true && this.parentMenu){
+ this.parentMenu.hide(true);
+ }
},
- /**
- * Set the body of the Dialog using the template
- * @param {Obj} data - apply this data to the template and replace the body contents.
- */
- applyBody: function(obj)
+
+ onTriggerClick : function(e)
{
- if (!this.tmpl) {
- Roo.log("Error - using apply Body without a template");
- //code
+ Roo.log('trigger click');
+
+ var target = e.getTarget();
+
+ Roo.log(target.nodeName.toLowerCase());
+
+ if(target.nodeName.toLowerCase() === 'i'){
+ e.preventDefault();
}
- this.tmpl.overwrite(this.bodyEl, obj);
+
},
- getChildHeight : function(child_nodes)
+ onTriggerPress : function(e)
{
- if(
- !child_nodes ||
- child_nodes.length == 0
- ) {
+ Roo.log('trigger press');
+ //Roo.log(e.getTarget());
+ // Roo.log(this.triggerEl.dom);
+
+ // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
+ var pel = Roo.get(e.getTarget());
+ if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
+ Roo.log('is treeview or dropdown?');
return;
}
- var child_height = 0;
+ if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
+ return;
+ }
- for(var i = 0; i < child_nodes.length; i++) {
-
- /*
- * for modal with tabs...
- if(child_nodes[i].classList.contains('roo-layout-panel')) {
-
- var layout_childs = child_nodes[i].childNodes;
-
- for(var j = 0; j < layout_childs.length; j++) {
-
- if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
-
- var layout_body_childs = layout_childs[j].childNodes;
-
- for(var k = 0; k < layout_body_childs.length; k++) {
-
- if(layout_body_childs[k].classList.contains('navbar')) {
- child_height += layout_body_childs[k].offsetHeight;
- continue;
- }
-
- if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
-
- var layout_body_tab_childs = layout_body_childs[k].childNodes;
-
- for(var m = 0; m < layout_body_tab_childs.length; m++) {
-
- if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
- child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
- continue;
- }
-
- }
-
- }
-
- }
- }
- }
- continue;
- }
- */
+ if (this.isVisible()) {
+ Roo.log('hide');
+ this.hide();
+ } else {
+ Roo.log('show');
- child_height += child_nodes[i].offsetHeight;
- // Roo.log(child_nodes[i].offsetHeight);
+ this.show(this.triggerEl, this.align, false);
}
- return child_height;
- }
+ if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
+ e.stopEvent();
+ }
+
+ },
+
+
+ hideMenuItems : function()
+ {
+ Roo.log("hide Menu Items");
+ if (!this.el) {
+ return;
+ }
+
+ this.el.select('.open',true).each(function(aa) {
+
+ aa.removeClass('open');
+
+ });
+ },
+ addxtypeChild : function (tree, cntr) {
+ var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
+
+ this.menuitems.add(comp);
+ return comp;
+ },
+ getEl : function()
+ {
+ Roo.log(this.el);
+ return this.el;
+ },
+
+ clear : function()
+ {
+ this.getEl().dom.innerHTML = '';
+ this.menuitems.clear();
+ }
});
+
+ /**
+ * @class Roo.bootstrap.menu.Item
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
+ * @parent Roo.bootstrap.menu.Menu
+ * @licence LGPL
+ * Bootstrap MenuItem class
+ *
+ * @cfg {String} html the menu label
+ * @cfg {String} href the link
+ * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
+ * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
+ * @cfg {Boolean} active used on sidebars to highlight active itesm
+ * @cfg {String} fa favicon to show on left of menu item.
+ * @cfg {Roo.bootsrap.Menu} menu the child menu.
+ *
+ *
+ * @constructor
+ * Create a new MenuItem
+ * @param {Object} config The config object
+ */
-Roo.apply(Roo.bootstrap.Modal, {
- /**
- * Button config that displays a single OK button
- * @type Object
- */
- OK : [{
- name : 'ok',
- weight : 'primary',
- html : 'OK'
- }],
- /**
- * Button config that displays Yes and No buttons
- * @type Object
- */
- YESNO : [
- {
- name : 'no',
- html : 'No'
- },
- {
- name :'yes',
- weight : 'primary',
- html : 'Yes'
- }
- ],
+Roo.bootstrap.menu.Item = function(config){
+ Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
+ this.addEvents({
+ // raw events
/**
- * Button config that displays OK and Cancel buttons
- * @type Object
- */
- OKCANCEL : [
- {
- name : 'cancel',
- html : 'Cancel'
- },
- {
- name : 'ok',
- weight : 'primary',
- html : 'OK'
- }
- ],
- /**
- * Button config that displays Yes, No and Cancel buttons
- * @type Object
+ * @event click
+ * The raw click event for the entire grid.
+ * @param {Roo.bootstrap.menu.Item} this
+ * @param {Roo.EventObject} e
*/
- YESNOCANCEL : [
- {
- name : 'yes',
- weight : 'primary',
- html : 'Yes'
- },
- {
- name : 'no',
- html : 'No'
- },
- {
- name : 'cancel',
- html : 'Cancel'
- }
- ],
+ "click" : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
+
+ href : false,
+ html : false,
+ preventDefault: false,
+ isContainer : false,
+ active : false,
+ fa: false,
+
+ getAutoCreate : function(){
- zIndex : 10001
+ if(this.isContainer){
+ return {
+ tag: 'li',
+ cls: 'dropdown-menu-item '
+ };
+ }
+ var ctag = {
+ tag: 'span',
+ html: 'Link'
+ };
+
+ var anc = {
+ tag : 'a',
+ cls : 'dropdown-item',
+ href : '#',
+ cn : [ ]
+ };
+
+ if (this.fa !== false) {
+ anc.cn.push({
+ tag : 'i',
+ cls : 'fa fa-' + this.fa
+ });
+ }
+
+ anc.cn.push(ctag);
+
+
+ var cfg= {
+ tag: 'li',
+ cls: 'dropdown-menu-item',
+ cn: [ anc ]
+ };
+ if (this.parent().type == 'treeview') {
+ cfg.cls = 'treeview-menu';
+ }
+ if (this.active) {
+ cfg.cls += ' active';
+ }
+
+
+
+ anc.href = this.href || cfg.cn[0].href ;
+ ctag.html = this.html || cfg.cn[0].html ;
+ return cfg;
+ },
+
+ initEvents: function()
+ {
+ if (this.parent().type == 'treeview') {
+ this.el.select('a').on('click', this.onClick, this);
+ }
+
+ if (this.menu) {
+ this.menu.parentType = this.xtype;
+ this.menu.triggerEl = this.el;
+ this.menu = this.addxtype(Roo.apply({}, this.menu));
+ }
+
+ },
+ onClick : function(e)
+ {
+ //Roo.log('item on click ');
+
+ if(this.href === false || this.preventDefault){
+ e.preventDefault();
+ }
+ //this.parent().hideMenuItems();
+
+ this.fireEvent('click', this, e);
+ },
+ getEl : function()
+ {
+ return this.el;
+ }
});
-/*
- * - LGPL
- *
- * messagebox - can be used as a replace
+
+
+
+
+
+
+/**
+ * @class Roo.bootstrap.menu.Separator
+ * @extends Roo.bootstrap.Component
+ * @licence LGPL
+ * @parent Roo.bootstrap.menu.Menu
+ * Bootstrap Separator class
*
+ * @constructor
+ * Create a new Separator
+ * @param {Object} config The config object
*/
-/**
- * @class Roo.MessageBox
- * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
- * Example usage:
- *<pre><code>
-// Basic alert:
-Roo.Msg.alert('Status', 'Changes saved successfully.');
-// Prompt for user data:
-Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
- if (btn == 'ok'){
- // process text value...
+
+Roo.bootstrap.menu.Separator = function(config){
+ Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
+
+ getAutoCreate : function(){
+ var cfg = {
+ tag : 'li',
+ cls: 'dropdown-divider divider'
+ };
+
+ return cfg;
}
+
});
-// Show a dialog using config options:
-Roo.Msg.show({
- title:'Save Changes?',
- msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
- buttons: Roo.Msg.YESNOCANCEL,
- fn: processResult,
- animEl: 'elId'
-});
-</code></pre>
- * @singleton
+
+
+
+/*
+* Licence: LGPL
+*/
+
+/**
+ * @class Roo.bootstrap.Modal
+ * @extends Roo.bootstrap.Component
+ * @parent none builder
+ * @children Roo.bootstrap.Component
+ * Bootstrap Modal class
+ * @cfg {String} title Title of dialog
+ * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
+ * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
+ * @cfg {Boolean} specificTitle default false
+ * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
+ * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
+ * @cfg {Boolean} animate default true
+ * @cfg {Boolean} allow_close default true
+ * @cfg {Boolean} fitwindow default false
+ * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
+ * @cfg {Number} width fixed width - usefull for chrome extension only really.
+ * @cfg {Number} height fixed height - usefull for chrome extension only really.
+ * @cfg {String} size (sm|lg|xl) default empty
+ * @cfg {Number} max_width set the max width of modal
+ * @cfg {Boolean} editableTitle can the title be edited
+
+ *
+ *
+ * @constructor
+ * Create a new Modal Dialog
+ * @param {Object} config The config object
*/
-Roo.bootstrap.MessageBox = function(){
- var dlg, opt, mask, waitTimer;
- var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
- var buttons, activeTextEl, bwidth;
+Roo.bootstrap.Modal = function(config){
+ Roo.bootstrap.Modal.superclass.constructor.call(this, config);
+ this.addEvents({
+ // raw events
+ /**
+ * @event btnclick
+ * The raw btnclick event for the button
+ * @param {Roo.EventObject} e
+ */
+ "btnclick" : true,
+ /**
+ * @event resize
+ * Fire when dialog resize
+ * @param {Roo.bootstrap.Modal} this
+ * @param {Roo.EventObject} e
+ */
+ "resize" : true,
+ /**
+ * @event titlechanged
+ * Fire when the editable title has been changed
+ * @param {Roo.bootstrap.Modal} this
+ * @param {Roo.EventObject} value
+ */
+ "titlechanged" : true
+
+ });
+ this.buttons = this.buttons || [];
+
+ if (this.tmpl) {
+ this.tmpl = Roo.factory(this.tmpl);
+ }
+
+};
+
+Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
+
+ title : 'test dialog',
+
+ buttons : false,
+
+ // set on load...
+
+ html: false,
+
+ tmp: false,
+
+ specificTitle: false,
+
+ buttonPosition: 'right',
+
+ allow_close : true,
+
+ animate : true,
+
+ fitwindow: false,
- // private
- var handleButton = function(button){
- dlg.hide();
- Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
- };
+ // private
+ dialogEl: false,
+ bodyEl: false,
+ footerEl: false,
+ titleEl: false,
+ closeEl: false,
- // private
- var handleHide = function(){
- if(opt && opt.cls){
- dlg.el.removeClass(opt.cls);
+ size: '',
+
+ max_width: 0,
+
+ max_height: 0,
+
+ fit_content: false,
+ editableTitle : false,
+
+ onRender : function(ct, position)
+ {
+ Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
+
+ if(!this.el){
+ var cfg = Roo.apply({}, this.getAutoCreate());
+ cfg.id = Roo.id();
+ //if(!cfg.name){
+ // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
+ //}
+ //if (!cfg.name.length) {
+ // delete cfg.name;
+ // }
+ if (this.cls) {
+ cfg.cls += ' ' + this.cls;
+ }
+ if (this.style) {
+ cfg.style = this.style;
+ }
+ this.el = Roo.get(document.body).createChild(cfg, position);
}
- //if(waitTimer){
- // Roo.TaskMgr.stop(waitTimer);
- // waitTimer = null;
- //}
- };
+ //var type = this.el.dom.type;
- // private
- var updateButtons = function(b){
- var width = 0;
- if(!b){
- buttons["ok"].hide();
- buttons["cancel"].hide();
- buttons["yes"].hide();
- buttons["no"].hide();
- dlg.footerEl.hide();
-
- return width;
+
+ if(this.tabIndex !== undefined){
+ this.el.dom.setAttribute('tabIndex', this.tabIndex);
}
- dlg.footerEl.show();
- for(var k in buttons){
- if(typeof buttons[k] != "function"){
- if(b[k]){
- buttons[k].show();
- buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
- width += buttons[k].el.getWidth()+15;
- }else{
- buttons[k].hide();
+
+ this.dialogEl = this.el.select('.modal-dialog',true).first();
+ this.bodyEl = this.el.select('.modal-body',true).first();
+ this.closeEl = this.el.select('.modal-header .close', true).first();
+ this.headerEl = this.el.select('.modal-header',true).first();
+ this.titleEl = this.el.select('.modal-title',true).first();
+ this.footerEl = this.el.select('.modal-footer',true).first();
+
+ this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
+
+ //this.el.addClass("x-dlg-modal");
+
+ if (this.buttons.length) {
+ Roo.each(this.buttons, function(bb) {
+ var b = Roo.apply({}, bb);
+ b.xns = b.xns || Roo.bootstrap;
+ b.xtype = b.xtype || 'Button';
+ if (typeof(b.listeners) == 'undefined') {
+ b.listeners = { click : this.onButtonClick.createDelegate(this) };
}
+
+ var btn = Roo.factory(b);
+
+ btn.render(this.getButtonContainer());
+
+ },this);
+ }
+ // render the children.
+ var nitems = [];
+
+ if(typeof(this.items) != 'undefined'){
+ var items = this.items;
+ delete this.items;
+
+ for(var i =0;i < items.length;i++) {
+ // we force children not to montor widnow resize - as we do that for them.
+ items[i].monitorWindowResize = false;
+ nitems.push(this.addxtype(Roo.apply({}, items[i])));
}
}
- return width;
- };
- // private
- var handleEsc = function(d, k, e){
- if(opt && opt.closable !== false){
- dlg.hide();
+ this.items = nitems;
+
+ // where are these used - they used to be body/close/footer
+
+
+ this.initEvents();
+ //this.el.addClass([this.fieldClass, this.cls]);
+
+ },
+
+ getAutoCreate : function()
+ {
+ // we will default to modal-body-overflow - might need to remove or make optional later.
+ var bdy = {
+ cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
+ html : this.html || ''
+ };
+
+ var title = {
+ tag: 'h5',
+ cls : 'modal-title',
+ html : this.title
+ };
+
+ if(this.specificTitle){ // WTF is this?
+ title = this.title;
}
- if(e){
- e.stopEvent();
+
+ var header = [];
+ if (this.allow_close && Roo.bootstrap.version == 3) {
+ header.push({
+ tag: 'button',
+ cls : 'close',
+ html : '×'
+ });
}
- };
- return {
- /**
- * Returns a reference to the underlying {@link Roo.BasicDialog} element
- * @return {Roo.BasicDialog} The BasicDialog element
- */
- getDialog : function(){
- if(!dlg){
- dlg = new Roo.bootstrap.Modal( {
- //draggable: true,
- //resizable:false,
- //constraintoviewport:false,
- //fixedcenter:true,
- //collapsible : false,
- //shim:true,
- //modal: true,
- // width: 'auto',
- // height:100,
- //buttonAlign:"center",
- closeClick : function(){
- if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
- handleButton("no");
- }else{
- handleButton("cancel");
- }
- }
- });
- dlg.render();
- dlg.on("hide", handleHide);
- mask = dlg.mask;
- //dlg.addKeyListener(27, handleEsc);
- buttons = {};
- this.buttons = buttons;
- var bt = this.buttonText;
- buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
- buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
- buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
- buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
- //Roo.log(buttons);
- bodyEl = dlg.bodyEl.createChild({
+ header.push(title);
- html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
- '<textarea class="roo-mb-textarea"></textarea>' +
- '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
- });
- msgEl = bodyEl.dom.firstChild;
- textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
- textboxEl.enableDisplayMode();
- textboxEl.addKeyListener([10,13], function(){
- if(dlg.isVisible() && opt && opt.buttons){
- if(opt.buttons.ok){
- handleButton("ok");
- }else if(opt.buttons.yes){
- handleButton("yes");
- }
+ if (this.editableTitle) {
+ header.push({
+ cls: 'form-control roo-editable-title d-none',
+ tag: 'input',
+ type: 'text'
+ });
+ }
+
+ if (this.allow_close && Roo.bootstrap.version == 4) {
+ header.push({
+ tag: 'button',
+ cls : 'close',
+ html : '×'
+ });
+ }
+
+ var size = '';
+
+ if(this.size.length){
+ size = 'modal-' + this.size;
+ }
+
+ var footer = Roo.bootstrap.version == 3 ?
+ {
+ cls : 'modal-footer',
+ cn : [
+ {
+ tag: 'div',
+ cls: 'btn-' + this.buttonPosition
}
- });
- textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
- textareaEl.enableDisplayMode();
- progressEl = Roo.get(bodyEl.dom.childNodes[4]);
- progressEl.enableDisplayMode();
-
- // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
- var pf = progressEl.dom.firstChild;
- if (pf) {
- pp = Roo.get(pf.firstChild);
- pp.setHeight(pf.offsetHeight);
- }
-
- }
- return dlg;
- },
+ ]
- /**
- * Updates the message box body text
- * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
- * the XHTML-compliant non-breaking space character '&#160;')
- * @return {Roo.MessageBox} This message box
- */
- updateText : function(text)
- {
- if(!dlg.isVisible() && !opt.width){
- dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
- // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
- }
- msgEl.innerHTML = text || ' ';
-
- var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
- //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
- var w = Math.max(
- Math.min(opt.width || cw , this.maxWidth),
- Math.max(opt.minWidth || this.minWidth, bwidth)
- );
- if(opt.prompt){
- activeTextEl.setWidth(w);
- }
- if(dlg.isVisible()){
- dlg.fixedcenter = false;
- }
- // to big, make it scroll. = But as usual stupid IE does not support
- // !important..
-
- if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
- bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
- bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
- } else {
- bodyEl.dom.style.height = '';
- bodyEl.dom.style.overflowY = '';
- }
- if (cw > w) {
- bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
- } else {
- bodyEl.dom.style.overflowX = '';
- }
-
- dlg.setContentSize(w, bodyEl.getHeight());
- if(dlg.isVisible()){
- dlg.fixedcenter = true;
- }
- return this;
- },
+ } :
+ { // BS4 uses mr-auto on left buttons....
+ cls : 'modal-footer'
+ };
- /**
- * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
- * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
- * @param {Number} value Any number between 0 and 1 (e.g., .5)
- * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
- * @return {Roo.MessageBox} This message box
- */
- updateProgress : function(value, text){
- if(text){
- this.updateText(text);
- }
- if (pp) { // weird bug on my firefox - for some reason this is not defined
- pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
- pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
- }
- return this;
- },
- /**
- * Returns true if the message box is currently displayed
- * @return {Boolean} True if the message box is visible, else false
- */
- isVisible : function(){
- return dlg && dlg.isVisible();
- },
+
+
+ var modal = {
+ cls: "modal",
+ cn : [
+ {
+ cls: "modal-dialog " + size,
+ cn : [
+ {
+ cls : "modal-content",
+ cn : [
+ {
+ cls : 'modal-header',
+ cn : header
+ },
+ bdy,
+ footer
+ ]
- /**
- * Hides the message box if it is displayed
- */
- hide : function(){
- if(this.isVisible()){
- dlg.hide();
- }
- },
+ }
+ ]
- /**
- * Displays a new message box, or reinitializes an existing message box, based on the config options
- * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
- * The following config object properties are supported:
- * <pre>
-Property Type Description
----------- --------------- ------------------------------------------------------------------------------------
-animEl String/Element An id or Element from which the message box should animate as it opens and
- closes (defaults to undefined)
-buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
- cancel:'Bar'}), or false to not show any buttons (defaults to false)
-closable Boolean False to hide the top-right close button (defaults to true). Note that
- progress and wait dialogs will ignore this property and always hide the
- close button as they can only be closed programmatically.
-cls String A custom CSS class to apply to the message box element
-defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
- displayed (defaults to 75)
-fn Function A callback function to execute after closing the dialog. The arguments to the
- function will be btn (the name of the button that was clicked, if applicable,
- e.g. "ok"), and text (the value of the active text field, if applicable).
- Progress and wait dialogs will ignore this option since they do not respond to
- user actions and can only be closed programmatically, so any required function
- should be called by the same code after it closes the dialog.
-icon String A CSS class that provides a background image to be used as an icon for
- the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
-maxWidth Number The maximum width in pixels of the message box (defaults to 600)
-minWidth Number The minimum width in pixels of the message box (defaults to 100)
-modal Boolean False to allow user interaction with the page while the message box is
- displayed (defaults to true)
-msg String A string that will replace the existing message box body text (defaults
- to the XHTML-compliant non-breaking space character ' ')
-multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
-progress Boolean True to display a progress bar (defaults to false)
-progressText String The text to display inside the progress bar if progress = true (defaults to '')
-prompt Boolean True to prompt the user to enter single-line text (defaults to false)
-proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
-title String The title text
-value String The string value to set into the active textbox element if displayed
-wait Boolean True to display a progress bar (defaults to false)
-width Number The width of the dialog in pixels
-</pre>
- *
- * Example usage:
- * <pre><code>
-Roo.Msg.show({
- title: 'Address',
- msg: 'Please enter your address:',
- width: 300,
- buttons: Roo.MessageBox.OKCANCEL,
- multiline: true,
- fn: saveAddress,
- animEl: 'addAddressBtn'
-});
-</code></pre>
- * @param {Object} config Configuration options
- * @return {Roo.MessageBox} This message box
- */
- show : function(options)
- {
-
- // this causes nightmares if you show one dialog after another
- // especially on callbacks..
-
- if(this.isVisible()){
-
- this.hide();
- Roo.log("[Roo.Messagebox] Show called while message displayed:" );
- Roo.log("Old Dialog Message:" + msgEl.innerHTML );
- Roo.log("New Dialog Message:" + options.msg )
- //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
- //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
-
- }
- var d = this.getDialog();
- opt = options;
- d.setTitle(opt.title || " ");
- d.closeEl.setDisplayed(opt.closable !== false);
- activeTextEl = textboxEl;
- opt.prompt = opt.prompt || (opt.multiline ? true : false);
- if(opt.prompt){
- if(opt.multiline){
- textboxEl.hide();
- textareaEl.show();
- textareaEl.setHeight(typeof opt.multiline == "number" ?
- opt.multiline : this.defaultTextHeight);
- activeTextEl = textareaEl;
- }else{
- textboxEl.show();
- textareaEl.hide();
- }
- }else{
- textboxEl.hide();
- textareaEl.hide();
- }
- progressEl.setDisplayed(opt.progress === true);
- if (opt.progress) {
- d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
- }
- this.updateProgress(0);
- activeTextEl.dom.value = opt.value || "";
- if(opt.prompt){
- dlg.setDefaultButton(activeTextEl);
- }else{
- var bs = opt.buttons;
- var db = null;
- if(bs && bs.ok){
- db = buttons["ok"];
- }else if(bs && bs.yes){
- db = buttons["yes"];
}
- dlg.setDefaultButton(db);
- }
- bwidth = updateButtons(opt.buttons);
- this.updateText(opt.msg);
- if(opt.cls){
- d.el.addClass(opt.cls);
- }
- d.proxyDrag = opt.proxyDrag === true;
- d.modal = opt.modal !== false;
- d.mask = opt.modal !== false ? mask : false;
- if(!d.isVisible()){
- // force it to the end of the z-index stack so it gets a cursor in FF
- document.body.appendChild(dlg.el.dom);
- d.animateTarget = null;
- d.show(options.animEl);
- }
- return this;
- },
-
- /**
- * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
- * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
- * and closing the message box when the process is complete.
- * @param {String} title The title bar text
- * @param {String} msg The message box body text
- * @return {Roo.MessageBox} This message box
- */
- progress : function(title, msg){
- this.show({
- title : title,
- msg : msg,
- buttons: false,
- progress:true,
- closable:false,
- minWidth: this.minProgressWidth,
- modal : true
- });
- return this;
- },
+ ]
+ };
- /**
- * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
- * If a callback function is passed it will be called after the user clicks the button, and the
- * id of the button that was clicked will be passed as the only parameter to the callback
- * (could also be the top-right close button).
- * @param {String} title The title bar text
- * @param {String} msg The message box body text
- * @param {Function} fn (optional) The callback function invoked after the message box is closed
- * @param {Object} scope (optional) The scope of the callback function
- * @return {Roo.MessageBox} This message box
- */
- alert : function(title, msg, fn, scope)
- {
- this.show({
- title : title,
- msg : msg,
- buttons: this.OK,
- fn: fn,
- closable : false,
- scope : scope,
- modal : true
- });
- return this;
- },
+ if(this.animate){
+ modal.cls += ' fade';
+ }
- /**
- * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
- * interaction while waiting for a long-running process to complete that does not have defined intervals.
- * You are responsible for closing the message box when the process is complete.
- * @param {String} msg The message box body text
- * @param {String} title (optional) The title bar text
- * @return {Roo.MessageBox} This message box
- */
- wait : function(msg, title){
- this.show({
- title : title,
- msg : msg,
- buttons: false,
- closable:false,
- progress:true,
- modal:true,
- width:300,
- wait:true
- });
- waitTimer = Roo.TaskMgr.start({
- run: function(i){
- Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
- },
- interval: 1000
- });
- return this;
- },
+ return modal;
- /**
- * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
- * If a callback function is passed it will be called after the user clicks either button, and the id of the
- * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
- * @param {String} title The title bar text
- * @param {String} msg The message box body text
- * @param {Function} fn (optional) The callback function invoked after the message box is closed
- * @param {Object} scope (optional) The scope of the callback function
- * @return {Roo.MessageBox} This message box
- */
- confirm : function(title, msg, fn, scope){
- this.show({
- title : title,
- msg : msg,
- buttons: this.YESNO,
- fn: fn,
- scope : scope,
- modal : true
- });
- return this;
- },
+ },
+ getChildContainer : function() {
- /**
- * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
- * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
- * is passed it will be called after the user clicks either button, and the id of the button that was clicked
- * (could also be the top-right close button) and the text that was entered will be passed as the two
- * parameters to the callback.
- * @param {String} title The title bar text
- * @param {String} msg The message box body text
- * @param {Function} fn (optional) The callback function invoked after the message box is closed
- * @param {Object} scope (optional) The scope of the callback function
- * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
- * property, or the height in pixels to create the textbox (defaults to false / single-line)
- * @return {Roo.MessageBox} This message box
- */
- prompt : function(title, msg, fn, scope, multiline){
- this.show({
- title : title,
- msg : msg,
- buttons: this.OKCANCEL,
- fn: fn,
- minWidth:250,
- scope : scope,
- prompt:true,
- multiline: multiline,
- modal : true
- });
- return this;
- },
+ return this.bodyEl;
- /**
- * Button config that displays a single OK button
- * @type Object
- */
- OK : {ok:true},
- /**
- * Button config that displays Yes and No buttons
- * @type Object
- */
- YESNO : {yes:true, no:true},
- /**
- * Button config that displays OK and Cancel buttons
- * @type Object
- */
- OKCANCEL : {ok:true, cancel:true},
- /**
- * Button config that displays Yes, No and Cancel buttons
- * @type Object
- */
- YESNOCANCEL : {yes:true, no:true, cancel:true},
+ },
+ getButtonContainer : function() {
+
+ return Roo.bootstrap.version == 4 ?
+ this.el.select('.modal-footer',true).first()
+ : this.el.select('.modal-footer div',true).first();
- /**
- * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
- * @type Number
- */
- defaultTextHeight : 75,
- /**
- * The maximum width in pixels of the message box (defaults to 600)
- * @type Number
- */
- maxWidth : 600,
- /**
- * The minimum width in pixels of the message box (defaults to 100)
- * @type Number
- */
- minWidth : 100,
- /**
- * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
- * for setting a different minimum width than text-only dialogs may need (defaults to 250)
- * @type Number
- */
- minProgressWidth : 250,
- /**
- * An object containing the default button text strings that can be overriden for localized language support.
- * Supported properties are: ok, cancel, yes and no.
- * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
- * @type Object
- */
- buttonText : {
- ok : "OK",
- cancel : "Cancel",
- yes : "Yes",
- no : "No"
+ },
+ initEvents : function()
+ {
+ if (this.allow_close) {
+ this.closeEl.on('click', this.hide, this);
+ }
+ Roo.EventManager.onWindowResize(this.resize, this, true);
+ if (this.editableTitle) {
+ this.headerEditEl = this.headerEl.select('.form-control',true).first();
+ this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
+ this.headerEditEl.on('keyup', function(e) {
+ if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
+ this.toggleHeaderInput(false)
+ }
+ }, this);
+ this.headerEditEl.on('blur', function(e) {
+ this.toggleHeaderInput(false)
+ },this);
}
- };
-}();
-
-/**
- * Shorthand for {@link Roo.MessageBox}
- */
-Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
-Roo.Msg = Roo.Msg || Roo.MessageBox;
-/*
- * - LGPL
- *
- * navbar
- *
- */
-
-/**
- * @class Roo.bootstrap.Navbar
- * @extends Roo.bootstrap.Component
- * Bootstrap Navbar class
-
- * @constructor
- * Create a new Navbar
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Navbar = function(config){
- Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
- this.addEvents({
- // raw events
- /**
- * @event beforetoggle
- * Fire before toggle the menu
- * @param {Roo.EventObject} e
- */
- "beforetoggle" : true
- });
-};
+ },
+
-Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
-
-
-
- // private
- navItems : false,
- loadMask : false,
-
-
- getAutoCreate : function(){
-
+ resize : function()
+ {
+ this.maskEl.setSize(
+ Roo.lib.Dom.getViewWidth(true),
+ Roo.lib.Dom.getViewHeight(true)
+ );
- throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
+ if (this.fitwindow) {
+
+ this.dialogEl.setStyle( { 'max-width' : '100%' });
+ this.setSize(
+ this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
+ this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
+ );
+ return;
+ }
- },
-
- initEvents :function ()
- {
- //Roo.log(this.el.select('.navbar-toggle',true));
- this.el.select('.navbar-toggle',true).on('click', function() {
- if(this.fireEvent('beforetoggle', this) !== false){
- var ce = this.el.select('.navbar-collapse',true).first();
- ce.toggleClass('in'); // old...
- if (ce.hasClass('collapse')) {
- // show it...
- ce.removeClass('collapse');
- ce.addClass('show');
- var h = ce.getHeight();
- Roo.log(h);
- ce.removeClass('show');
- // at this point we should be able to see it..
- ce.addClass('collapsing');
-
- ce.setHeight(0); // resize it ...
- ce.on('transitionend', function() {
- Roo.log('done transition');
- ce.removeClass('collapsing');
- ce.addClass('show');
- ce.removeClass('collapse');
-
- ce.dom.style.height = '';
- }, this, { single: true} );
- ce.setHeight(h);
-
- } else {
- ce.setHeight(ce.getHeight());
- ce.removeClass('show');
- ce.addClass('collapsing');
-
- ce.on('transitionend', function() {
- ce.dom.style.height = '';
- ce.removeClass('collapsing');
- ce.addClass('collapse');
- }, this, { single: true} );
- ce.setHeight(0);
- }
+ if(this.max_width !== 0) {
+
+ var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
+
+ if(this.height) {
+ this.setSize(w, this.height);
+ return;
}
- }, this);
-
- var mark = {
- tag: "div",
- cls:"x-dlg-mask"
- };
-
- this.maskEl = Roo.DomHelper.append(this.el, mark, true);
-
- var size = this.el.getSize();
- this.maskEl.setSize(size.width, size.height);
- this.maskEl.enableDisplayMode("block");
- this.maskEl.hide();
-
- if(this.loadMask){
- this.maskEl.show();
+ if(this.max_height) {
+ this.setSize(w,Math.min(
+ this.max_height,
+ Roo.lib.Dom.getViewportHeight(true) - 60
+ ));
+
+ return;
+ }
+
+ if(!this.fit_content) {
+ this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
+ return;
+ }
+
+ this.setSize(w, Math.min(
+ 60 +
+ this.headerEl.getHeight() +
+ this.footerEl.getHeight() +
+ this.getChildHeight(this.bodyEl.dom.childNodes),
+ Roo.lib.Dom.getViewportHeight(true) - 60)
+ );
}
+
},
-
-
- getChildContainer : function()
+
+ setSize : function(w,h)
{
- if (this.el.select('.collapse').getCount()) {
- return this.el.select('.collapse',true).first();
+ if (!w && !h) {
+ return;
}
- return this.el;
- },
-
- mask : function()
- {
- this.maskEl.show();
- },
-
- unmask : function()
- {
- this.maskEl.hide();
- }
-
-
-
-
-});
-
-
-
-
-
- /*
- * - LGPL
- *
- * navbar
- *
- */
-
-/**
- * @class Roo.bootstrap.NavSimplebar
- * @extends Roo.bootstrap.Navbar
- * Bootstrap Sidebar class
- *
- * @cfg {Boolean} inverse is inverted color
- *
- * @cfg {String} type (nav | pills | tabs)
- * @cfg {Boolean} arrangement stacked | justified
- * @cfg {String} align (left | right) alignment
- *
- * @cfg {Boolean} main (true|false) main nav bar? default false
- * @cfg {Boolean} loadMask (true|false) loadMask on the bar
- *
- * @cfg {String} tag (header|footer|nav|div) default is nav
-
- * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
- *
- *
- * @constructor
- * Create a new Sidebar
- * @param {Object} config The config object
- */
+ this.resizeTo(w,h);
+ // any layout/border etc.. resize..
+ (function () {
+ this.items.forEach( function(e) {
+ e.layout ? e.layout() : false;
+ });
+ }).defer(100,this);
+
+ },
-Roo.bootstrap.NavSimplebar = function(config){
- Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
-};
+ show : function() {
-Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
-
- inverse: false,
-
- type: false,
- arrangement: '',
- align : false,
-
- weight : 'light',
-
- main : false,
-
-
- tag : false,
-
-
- getAutoCreate : function(){
-
-
- var cfg = {
- tag : this.tag || 'div',
- cls : 'navbar navbar-expand-lg roo-navbar-simple'
- };
- if (['light','white'].indexOf(this.weight) > -1) {
- cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
- }
- cfg.cls += ' bg-' + this.weight;
-
- if (this.inverse) {
- cfg.cls += ' navbar-inverse';
-
+ if (!this.rendered) {
+ this.render();
}
-
- // i'm not actually sure these are really used - normally we add a navGroup to a navbar
-
- //if (Roo.bootstrap.version == 4) {
- // return cfg;
- //}
-
- cfg.cn = [
- {
- cls: 'nav',
- tag : 'ul'
- }
- ];
-
-
- this.type = this.type || 'nav';
- if (['tabs','pills'].indexOf(this.type) != -1) {
- cfg.cn[0].cls += ' nav-' + this.type
-
+ this.toggleHeaderInput(false);
+ //this.el.setStyle('display', 'block');
+ this.el.removeClass('hideing');
+ this.el.dom.style.display='block';
- } else {
- if (this.type!=='nav') {
- Roo.log('nav type must be nav/tabs/pills')
- }
- cfg.cn[0].cls += ' navbar-nav'
+ Roo.get(document.body).addClass('modal-open');
+
+ if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
+
+ (function(){
+ this.el.addClass('show');
+ this.el.addClass('in');
+ }).defer(50, this);
+ }else{
+ this.el.addClass('show');
+ this.el.addClass('in');
}
+
+ // not sure how we can show data in here..
+ //if (this.tmpl) {
+ // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
+ //}
+
+ Roo.get(document.body).addClass("x-body-masked");
+ this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
+ this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+ this.maskEl.dom.style.display = 'block';
+ this.maskEl.addClass('show');
+ this.resize();
- if (['stacked','justified'].indexOf(this.arrangement) != -1) {
- cfg.cn[0].cls += ' nav-' + this.arrangement;
- }
+ this.fireEvent('show', this);
+
+ // set zindex here - otherwise it appears to be ignored...
+ this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
- if (this.align === 'right') {
- cfg.cn[0].cls += ' navbar-right';
+ // this is for children that are... layout.Border
+ (function () {
+ this.items.forEach( function(e) {
+ e.layout ? e.layout() : false;
+
+ });
+ }).defer(100,this);
+
+ },
+ hide : function()
+ {
+ if(this.fireEvent("beforehide", this) !== false){
+
+ this.maskEl.removeClass('show');
+
+ this.maskEl.dom.style.display = '';
+ Roo.get(document.body).removeClass("x-body-masked");
+ this.el.removeClass('in');
+ this.el.select('.modal-dialog', true).first().setStyle('transform','');
+
+ if(this.animate){ // why
+ this.el.addClass('hideing');
+ this.el.removeClass('show');
+ (function(){
+ if (!this.el.hasClass('hideing')) {
+ return; // it's been shown again...
+ }
+
+ this.el.dom.style.display='';
+
+ Roo.get(document.body).removeClass('modal-open');
+ this.el.removeClass('hideing');
+ }).defer(150,this);
+
+ }else{
+ this.el.removeClass('show');
+ this.el.dom.style.display='';
+ Roo.get(document.body).removeClass('modal-open');
+
+ }
+ this.fireEvent('hide', this);
}
+ },
+ isVisible : function()
+ {
+ return this.el.hasClass('show') && !this.el.hasClass('hideing');
-
-
- return cfg;
-
-
- }
-
-
-
-});
+ },
+ addButton : function(str, cb)
+ {
-
+ var b = Roo.apply({}, { html : str } );
+ b.xns = b.xns || Roo.bootstrap;
+ b.xtype = b.xtype || 'Button';
+ if (typeof(b.listeners) == 'undefined') {
+ b.listeners = { click : cb.createDelegate(this) };
+ }
-
- /*
- * - LGPL
- *
- * navbar
- * navbar-fixed-top
- * navbar-expand-md fixed-top
- */
+ var btn = Roo.factory(b);
-/**
- * @class Roo.bootstrap.NavHeaderbar
- * @extends Roo.bootstrap.NavSimplebar
- * Bootstrap Sidebar class
- *
- * @cfg {String} brand what is brand
- * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
- * @cfg {String} brand_href href of the brand
- * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
- * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
- * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
- * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
- *
- * @constructor
- * Create a new Sidebar
- * @param {Object} config The config object
- */
+ btn.render(this.getButtonContainer());
+ return btn;
-Roo.bootstrap.NavHeaderbar = function(config){
- Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
-
-};
+ },
-Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
-
- position: '',
- brand: '',
- brand_href: false,
- srButton : true,
- autohide : false,
- desktopCenter : false,
-
-
- getAutoCreate : function(){
+ setDefaultButton : function(btn)
+ {
+ //this.el.select('.modal-footer').()
+ },
+
+ resizeTo: function(w,h)
+ {
+ this.dialogEl.setWidth(w);
- var cfg = {
- tag: this.nav || 'nav',
- cls: 'navbar navbar-expand-md',
- role: 'navigation',
- cn: []
- };
+ var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
+
+ this.bodyEl.setHeight(h - diff);
- var cn = cfg.cn;
- if (this.desktopCenter) {
- cn.push({cls : 'container', cn : []});
- cn = cn[0].cn;
+ this.fireEvent('resize', this);
+ },
+
+ setContentSize : function(w, h)
+ {
+
+ },
+ onButtonClick: function(btn,e)
+ {
+ //Roo.log([a,b,c]);
+ this.fireEvent('btnclick', btn.name, e);
+ },
+ /**
+ * Set the title of the Dialog
+ * @param {String} str new Title
+ */
+ setTitle: function(str) {
+ this.titleEl.dom.innerHTML = str;
+ this.title = str;
+ },
+ /**
+ * Set the body of the Dialog
+ * @param {String} str new Title
+ */
+ setBody: function(str) {
+ this.bodyEl.dom.innerHTML = str;
+ },
+ /**
+ * Set the body of the Dialog using the template
+ * @param {Obj} data - apply this data to the template and replace the body contents.
+ */
+ applyBody: function(obj)
+ {
+ if (!this.tmpl) {
+ Roo.log("Error - using apply Body without a template");
+ //code
}
-
- if(this.srButton){
- var btn = {
- tag: 'button',
- type: 'button',
- cls: 'navbar-toggle navbar-toggler',
- 'data-toggle': 'collapse',
- cn: [
- {
- tag: 'span',
- cls: 'sr-only',
- html: 'Toggle navigation'
- },
- {
- tag: 'span',
- cls: 'icon-bar navbar-toggler-icon'
- },
- {
- tag: 'span',
- cls: 'icon-bar'
- },
- {
- tag: 'span',
- cls: 'icon-bar'
- }
- ]
- };
-
- cn.push( Roo.bootstrap.version == 4 ? btn : {
- tag: 'div',
- cls: 'navbar-header',
- cn: [
- btn
- ]
- });
+ this.tmpl.overwrite(this.bodyEl, obj);
+ },
+
+ getChildHeight : function(child_nodes)
+ {
+ if(
+ !child_nodes ||
+ child_nodes.length == 0
+ ) {
+ return 0;
}
- cn.push({
- tag: 'div',
- cls: 'collapse navbar-collapse',
- cn : []
- });
-
- cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
-
- if (['light','white'].indexOf(this.weight) > -1) {
- cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
- }
- cfg.cls += ' bg-' + this.weight;
-
+ var child_height = 0;
- if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
- cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
+ for(var i = 0; i < child_nodes.length; i++) {
- // tag can override this..
+ /*
+ * for modal with tabs...
+ if(child_nodes[i].classList.contains('roo-layout-panel')) {
+
+ var layout_childs = child_nodes[i].childNodes;
+
+ for(var j = 0; j < layout_childs.length; j++) {
+
+ if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
+
+ var layout_body_childs = layout_childs[j].childNodes;
+
+ for(var k = 0; k < layout_body_childs.length; k++) {
+
+ if(layout_body_childs[k].classList.contains('navbar')) {
+ child_height += layout_body_childs[k].offsetHeight;
+ continue;
+ }
+
+ if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
+
+ var layout_body_tab_childs = layout_body_childs[k].childNodes;
+
+ for(var m = 0; m < layout_body_tab_childs.length; m++) {
+
+ if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
+ child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
+ continue;
+ }
+
+ }
+
+ }
+
+ }
+ }
+ }
+ continue;
+ }
+ */
- cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
- }
-
- if (this.brand !== '') {
- var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
- cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
- tag: 'a',
- href: this.brand_href ? this.brand_href : '#',
- cls: 'navbar-brand',
- cn: [
- this.brand
- ]
- });
- }
-
- if(this.main){
- cfg.cls += ' main-nav';
+ child_height += child_nodes[i].offsetHeight;
+ // Roo.log(child_nodes[i].offsetHeight);
}
-
- return cfg;
-
-
+ return child_height;
},
- getHeaderChildContainer : function()
+ toggleHeaderInput : function(is_edit)
{
- if (this.srButton && this.el.select('.navbar-header').getCount()) {
- return this.el.select('.navbar-header',true).first();
+ if (!this.editableTitle) {
+ return; // not editable.
}
-
- return this.getChildContainer();
- },
-
+ if (is_edit && this.is_header_editing) {
+ return; // already editing..
+ }
+ if (is_edit) {
- initEvents : function()
- {
- Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
-
- if (this.autohide) {
-
- var prevScroll = 0;
- var ft = this.el;
+ this.headerEditEl.dom.value = this.title;
+ this.headerEditEl.removeClass('d-none');
+ this.headerEditEl.dom.focus();
+ this.titleEl.addClass('d-none');
- Roo.get(document).on('scroll',function(e) {
- var ns = Roo.get(document).getScroll().top;
- var os = prevScroll;
- prevScroll = ns;
-
- if(ns > os){
- ft.removeClass('slideDown');
- ft.addClass('slideUp');
- return;
- }
- ft.removeClass('slideUp');
- ft.addClass('slideDown');
-
-
- },this);
+ this.is_header_editing = true;
+ return
}
- }
+ // flip back to not editing.
+ this.title = this.headerEditEl.dom.value;
+ this.headerEditEl.addClass('d-none');
+ this.titleEl.removeClass('d-none');
+ this.titleEl.dom.innerHTML = String.format('{0}', this.title);
+ this.is_header_editing = false;
+ this.fireEvent('titlechanged', this, this.title);
+
+
+ }
+
});
+Roo.apply(Roo.bootstrap.Modal, {
+ /**
+ * Button config that displays a single OK button
+ * @type Object
+ */
+ OK : [{
+ name : 'ok',
+ weight : 'primary',
+ html : 'OK'
+ }],
+ /**
+ * Button config that displays Yes and No buttons
+ * @type Object
+ */
+ YESNO : [
+ {
+ name : 'no',
+ html : 'No'
+ },
+ {
+ name :'yes',
+ weight : 'primary',
+ html : 'Yes'
+ }
+ ],
-
+ /**
+ * Button config that displays OK and Cancel buttons
+ * @type Object
+ */
+ OKCANCEL : [
+ {
+ name : 'cancel',
+ html : 'Cancel'
+ },
+ {
+ name : 'ok',
+ weight : 'primary',
+ html : 'OK'
+ }
+ ],
+ /**
+ * Button config that displays Yes, No and Cancel buttons
+ * @type Object
+ */
+ YESNOCANCEL : [
+ {
+ name : 'yes',
+ weight : 'primary',
+ html : 'Yes'
+ },
+ {
+ name : 'no',
+ html : 'No'
+ },
+ {
+ name : 'cancel',
+ html : 'Cancel'
+ }
+ ],
+
+ zIndex : 10001
+});
- /*
+/*
* - LGPL
*
- * navbar
+ * messagebox - can be used as a replace
*
*/
-
/**
- * @class Roo.bootstrap.NavSidebar
- * @extends Roo.bootstrap.Navbar
- * Bootstrap Sidebar class
- *
- * @constructor
- * Create a new Sidebar
- * @param {Object} config The config object
- */
-
-
-Roo.bootstrap.NavSidebar = function(config){
- Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
-};
+ * @class Roo.MessageBox
+ * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
+ * Example usage:
+ *<pre><code>
+// Basic alert:
+Roo.Msg.alert('Status', 'Changes saved successfully.');
-Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
-
- sidebar : true, // used by Navbar Item and NavbarGroup at present...
-
- getAutoCreate : function(){
-
-
- return {
- tag: 'div',
- cls: 'sidebar sidebar-nav'
- };
-
-
+// Prompt for user data:
+Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
+ if (btn == 'ok'){
+ // process text value...
}
-
-
-
});
-
-
-
-
- /*
- * - LGPL
- *
- * nav group
- *
- */
-
-/**
- * @class Roo.bootstrap.NavGroup
- * @extends Roo.bootstrap.Component
- * Bootstrap NavGroup class
- * @cfg {String} align (left|right)
- * @cfg {Boolean} inverse
- * @cfg {String} type (nav|pills|tab) default nav
- * @cfg {String} navId - reference Id for navbar.
-
- *
- * @constructor
- * Create a new nav group
- * @param {Object} config The config object
+// Show a dialog using config options:
+Roo.Msg.show({
+ title:'Save Changes?',
+ msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
+ buttons: Roo.Msg.YESNOCANCEL,
+ fn: processResult,
+ animEl: 'elId'
+});
+</code></pre>
+ * @static
*/
+Roo.bootstrap.MessageBox = function(){
+ var dlg, opt, mask, waitTimer;
+ var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
+ var buttons, activeTextEl, bwidth;
-Roo.bootstrap.NavGroup = function(config){
- Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
- this.navItems = [];
-
- Roo.bootstrap.NavGroup.register(this);
- this.addEvents({
- /**
- * @event changed
- * Fires when the active item changes
- * @param {Roo.bootstrap.NavGroup} this
- * @param {Roo.bootstrap.Navbar.Item} selected The item selected
- * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
- */
- 'changed': true
- });
-};
+ // private
+ var handleButton = function(button){
+ dlg.hide();
+ Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
+ };
-Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
-
- align: '',
- inverse: false,
- form: false,
- type: 'nav',
- navId : '',
// private
-
- navItems : false,
-
- getAutoCreate : function()
- {
- var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag : 'ul',
- cls: 'nav'
- };
- if (Roo.bootstrap.version == 4) {
- if (['tabs','pills'].indexOf(this.type) != -1) {
- cfg.cls += ' nav-' + this.type;
- } else {
- cfg.cls += ' navbar-nav';
- }
- } else {
- if (['tabs','pills'].indexOf(this.type) != -1) {
- cfg.cls += ' nav-' + this.type
- } else {
- if (this.type !== 'nav') {
- Roo.log('nav type must be nav/tabs/pills')
- }
- cfg.cls += ' navbar-nav'
- }
- }
-
- if (this.parent() && this.parent().sidebar) {
- cfg = {
- tag: 'ul',
- cls: 'dashboard-menu sidebar-menu'
- };
-
- return cfg;
+ var handleHide = function(){
+ if(opt && opt.cls){
+ dlg.el.removeClass(opt.cls);
}
-
- if (this.form === true) {
- cfg = {
- tag: 'form',
- cls: 'navbar-form form-inline'
- };
+ //if(waitTimer){
+ // Roo.TaskMgr.stop(waitTimer);
+ // waitTimer = null;
+ //}
+ };
+
+ // private
+ var updateButtons = function(b){
+ var width = 0;
+ if(!b){
+ buttons["ok"].hide();
+ buttons["cancel"].hide();
+ buttons["yes"].hide();
+ buttons["no"].hide();
+ dlg.footerEl.hide();
- if (this.align === 'right') {
- cfg.cls += ' navbar-right ml-md-auto';
- } else {
- cfg.cls += ' navbar-left';
+ return width;
+ }
+ dlg.footerEl.show();
+ for(var k in buttons){
+ if(typeof buttons[k] != "function"){
+ if(b[k]){
+ buttons[k].show();
+ buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
+ width += buttons[k].el.getWidth()+15;
+ }else{
+ buttons[k].hide();
+ }
}
}
-
- if (this.align === 'right') {
- cfg.cls += ' navbar-right ml-md-auto';
- } else {
- cfg.cls += ' mr-auto';
- }
-
- if (this.inverse) {
- cfg.cls += ' navbar-inverse';
-
+ return width;
+ };
+
+ // private
+ var handleEsc = function(d, k, e){
+ if(opt && opt.closable !== false){
+ dlg.hide();
}
-
-
- return cfg;
- },
- /**
- * sets the active Navigation item
- * @param {Roo.bootstrap.NavItem} the new current navitem
- */
- setActiveItem : function(item)
- {
- var prev = false;
- Roo.each(this.navItems, function(v){
- if (v == item) {
- return ;
- }
- if (v.isActive()) {
- v.setActive(false, true);
- prev = v;
+ if(e){
+ e.stopEvent();
+ }
+ };
+
+ return {
+ /**
+ * Returns a reference to the underlying {@link Roo.BasicDialog} element
+ * @return {Roo.BasicDialog} The BasicDialog element
+ */
+ getDialog : function(){
+ if(!dlg){
+ dlg = new Roo.bootstrap.Modal( {
+ //draggable: true,
+ //resizable:false,
+ //constraintoviewport:false,
+ //fixedcenter:true,
+ //collapsible : false,
+ //shim:true,
+ //modal: true,
+ // width: 'auto',
+ // height:100,
+ //buttonAlign:"center",
+ closeClick : function(){
+ if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
+ handleButton("no");
+ }else{
+ handleButton("cancel");
+ }
+ }
+ });
+ dlg.render();
+ dlg.on("hide", handleHide);
+ mask = dlg.mask;
+ //dlg.addKeyListener(27, handleEsc);
+ buttons = {};
+ this.buttons = buttons;
+ var bt = this.buttonText;
+ buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
+ buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
+ buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
+ buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
+ //Roo.log(buttons);
+ bodyEl = dlg.bodyEl.createChild({
+
+ html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
+ '<textarea class="roo-mb-textarea"></textarea>' +
+ '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
+ });
+ msgEl = bodyEl.dom.firstChild;
+ textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
+ textboxEl.enableDisplayMode();
+ textboxEl.addKeyListener([10,13], function(){
+ if(dlg.isVisible() && opt && opt.buttons){
+ if(opt.buttons.ok){
+ handleButton("ok");
+ }else if(opt.buttons.yes){
+ handleButton("yes");
+ }
+ }
+ });
+ textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
+ textareaEl.enableDisplayMode();
+ progressEl = Roo.get(bodyEl.dom.childNodes[4]);
+ progressEl.enableDisplayMode();
+
+ // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
+ var pf = progressEl.dom.firstChild;
+ if (pf) {
+ pp = Roo.get(pf.firstChild);
+ pp.setHeight(pf.offsetHeight);
+ }
}
-
- });
+ return dlg;
+ },
- item.setActive(true, true);
- this.fireEvent('changed', this, item, prev);
-
-
- },
- /**
- * gets the active Navigation item
- * @return {Roo.bootstrap.NavItem} the current navitem
- */
- getActive : function()
- {
-
- var prev = false;
- Roo.each(this.navItems, function(v){
-
- if (v.isActive()) {
- prev = v;
-
+ /**
+ * Updates the message box body text
+ * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
+ * the XHTML-compliant non-breaking space character '&#160;')
+ * @return {Roo.MessageBox} This message box
+ */
+ updateText : function(text)
+ {
+ if(!dlg.isVisible() && !opt.width){
+ dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
+ // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
}
-
- });
- return prev;
- },
-
- indexOfNav : function()
- {
-
- var prev = false;
- Roo.each(this.navItems, function(v,i){
-
- if (v.isActive()) {
- prev = i;
-
+ msgEl.innerHTML = text || ' ';
+
+ var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
+ //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
+ var w = Math.max(
+ Math.min(opt.width || cw , this.maxWidth),
+ Math.max(opt.minWidth || this.minWidth, bwidth)
+ );
+ if(opt.prompt){
+ activeTextEl.setWidth(w);
}
-
- });
- return prev;
- },
- /**
- * adds a Navigation item
- * @param {Roo.bootstrap.NavItem} the navitem to add
- */
- addItem : function(cfg)
- {
- if (this.form && Roo.bootstrap.version == 4) {
- cfg.tag = 'div';
- }
- var cn = new Roo.bootstrap.NavItem(cfg);
- this.register(cn);
- cn.parentId = this.id;
- cn.onRender(this.el, null);
- return cn;
- },
- /**
- * register a Navigation item
- * @param {Roo.bootstrap.NavItem} the navitem to add
- */
- register : function(item)
- {
- this.navItems.push( item);
- item.navId = this.navId;
-
- },
-
- /**
- * clear all the Navigation item
- */
-
- clearAll : function()
- {
- this.navItems = [];
- this.el.dom.innerHTML = '';
- },
-
- getNavItem: function(tabId)
- {
- var ret = false;
- Roo.each(this.navItems, function(e) {
- if (e.tabId == tabId) {
- ret = e;
- return false;
+ if(dlg.isVisible()){
+ dlg.fixedcenter = false;
}
- return true;
+ // to big, make it scroll. = But as usual stupid IE does not support
+ // !important..
- });
- return ret;
- },
-
- setActiveNext : function()
- {
- var i = this.indexOfNav(this.getActive());
- if (i > this.navItems.length) {
- return;
- }
- this.setActiveItem(this.navItems[i+1]);
- },
- setActivePrev : function()
- {
- var i = this.indexOfNav(this.getActive());
- if (i < 1) {
- return;
- }
- this.setActiveItem(this.navItems[i-1]);
- },
- clearWasActive : function(except) {
- Roo.each(this.navItems, function(e) {
- if (e.tabId != except.tabId && e.was_active) {
- e.was_active = false;
- return false;
+ if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
+ bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
+ bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
+ } else {
+ bodyEl.dom.style.height = '';
+ bodyEl.dom.style.overflowY = '';
}
- return true;
-
- });
- },
- getWasActive : function ()
- {
- var r = false;
- Roo.each(this.navItems, function(e) {
- if (e.was_active) {
- r = e;
- return false;
+ if (cw > w) {
+ bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
+ } else {
+ bodyEl.dom.style.overflowX = '';
}
- return true;
- });
- return r;
- }
-
-
-});
+ dlg.setContentSize(w, bodyEl.getHeight());
+ if(dlg.isVisible()){
+ dlg.fixedcenter = true;
+ }
+ return this;
+ },
-
-Roo.apply(Roo.bootstrap.NavGroup, {
-
- groups: {},
- /**
- * register a Navigation Group
- * @param {Roo.bootstrap.NavGroup} the navgroup to add
- */
- register : function(navgrp)
- {
- this.groups[navgrp.navId] = navgrp;
-
- },
- /**
- * fetch a Navigation Group based on the navigation ID
- * @param {string} the navgroup to add
- * @returns {Roo.bootstrap.NavGroup} the navgroup
- */
- get: function(navId) {
- if (typeof(this.groups[navId]) == 'undefined') {
- return false;
- //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
- }
- return this.groups[navId] ;
- }
-
-
-
-});
+ /**
+ * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
+ * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
+ * @param {Number} value Any number between 0 and 1 (e.g., .5)
+ * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
+ * @return {Roo.MessageBox} This message box
+ */
+ updateProgress : function(value, text){
+ if(text){
+ this.updateText(text);
+ }
+
+ if (pp) { // weird bug on my firefox - for some reason this is not defined
+ pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
+ pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
+ }
+ return this;
+ },
- /*
- * - LGPL
- *
- * row
- *
- */
+ /**
+ * Returns true if the message box is currently displayed
+ * @return {Boolean} True if the message box is visible, else false
+ */
+ isVisible : function(){
+ return dlg && dlg.isVisible();
+ },
-/**
- * @class Roo.bootstrap.NavItem
- * @extends Roo.bootstrap.Component
- * Bootstrap Navbar.NavItem class
- * @cfg {String} href link to
- * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
+ /**
+ * Hides the message box if it is displayed
+ */
+ hide : function(){
+ if(this.isVisible()){
+ dlg.hide();
+ }
+ },
+
+ /**
+ * Displays a new message box, or reinitializes an existing message box, based on the config options
+ * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
+ * The following config object properties are supported:
+ * <pre>
+Property Type Description
+---------- --------------- ------------------------------------------------------------------------------------
+animEl String/Element An id or Element from which the message box should animate as it opens and
+ closes (defaults to undefined)
+buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
+ cancel:'Bar'}), or false to not show any buttons (defaults to false)
+closable Boolean False to hide the top-right close button (defaults to true). Note that
+ progress and wait dialogs will ignore this property and always hide the
+ close button as they can only be closed programmatically.
+cls String A custom CSS class to apply to the message box element
+defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
+ displayed (defaults to 75)
+fn Function A callback function to execute after closing the dialog. The arguments to the
+ function will be btn (the name of the button that was clicked, if applicable,
+ e.g. "ok"), and text (the value of the active text field, if applicable).
+ Progress and wait dialogs will ignore this option since they do not respond to
+ user actions and can only be closed programmatically, so any required function
+ should be called by the same code after it closes the dialog.
+icon String A CSS class that provides a background image to be used as an icon for
+ the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
+maxWidth Number The maximum width in pixels of the message box (defaults to 600)
+minWidth Number The minimum width in pixels of the message box (defaults to 100)
+modal Boolean False to allow user interaction with the page while the message box is
+ displayed (defaults to true)
+msg String A string that will replace the existing message box body text (defaults
+ to the XHTML-compliant non-breaking space character ' ')
+multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
+progress Boolean True to display a progress bar (defaults to false)
+progressText String The text to display inside the progress bar if progress = true (defaults to '')
+prompt Boolean True to prompt the user to enter single-line text (defaults to false)
+proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
+title String The title text
+value String The string value to set into the active textbox element if displayed
+wait Boolean True to display a progress bar (defaults to false)
+width Number The width of the dialog in pixels
+</pre>
+ *
+ * Example usage:
+ * <pre><code>
+Roo.Msg.show({
+ title: 'Address',
+ msg: 'Please enter your address:',
+ width: 300,
+ buttons: Roo.MessageBox.OKCANCEL,
+ multiline: true,
+ fn: saveAddress,
+ animEl: 'addAddressBtn'
+});
+</code></pre>
+ * @param {Object} config Configuration options
+ * @return {Roo.MessageBox} This message box
+ */
+ show : function(options)
+ {
+
+ // this causes nightmares if you show one dialog after another
+ // especially on callbacks..
+
+ if(this.isVisible()){
+
+ this.hide();
+ Roo.log("[Roo.Messagebox] Show called while message displayed:" );
+ Roo.log("Old Dialog Message:" + msgEl.innerHTML );
+ Roo.log("New Dialog Message:" + options.msg )
+ //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
+ //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
+
+ }
+ var d = this.getDialog();
+ opt = options;
+ d.setTitle(opt.title || " ");
+ d.closeEl.setDisplayed(opt.closable !== false);
+ activeTextEl = textboxEl;
+ opt.prompt = opt.prompt || (opt.multiline ? true : false);
+ if(opt.prompt){
+ if(opt.multiline){
+ textboxEl.hide();
+ textareaEl.show();
+ textareaEl.setHeight(typeof opt.multiline == "number" ?
+ opt.multiline : this.defaultTextHeight);
+ activeTextEl = textareaEl;
+ }else{
+ textboxEl.show();
+ textareaEl.hide();
+ }
+ }else{
+ textboxEl.hide();
+ textareaEl.hide();
+ }
+ progressEl.setDisplayed(opt.progress === true);
+ if (opt.progress) {
+ d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
+ }
+ this.updateProgress(0);
+ activeTextEl.dom.value = opt.value || "";
+ if(opt.prompt){
+ dlg.setDefaultButton(activeTextEl);
+ }else{
+ var bs = opt.buttons;
+ var db = null;
+ if(bs && bs.ok){
+ db = buttons["ok"];
+ }else if(bs && bs.yes){
+ db = buttons["yes"];
+ }
+ dlg.setDefaultButton(db);
+ }
+ bwidth = updateButtons(opt.buttons);
+ this.updateText(opt.msg);
+ if(opt.cls){
+ d.el.addClass(opt.cls);
+ }
+ d.proxyDrag = opt.proxyDrag === true;
+ d.modal = opt.modal !== false;
+ d.mask = opt.modal !== false ? mask : false;
+ if(!d.isVisible()){
+ // force it to the end of the z-index stack so it gets a cursor in FF
+ document.body.appendChild(dlg.el.dom);
+ d.animateTarget = null;
+ d.show(options.animEl);
+ }
+ return this;
+ },
+
+ /**
+ * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
+ * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
+ * and closing the message box when the process is complete.
+ * @param {String} title The title bar text
+ * @param {String} msg The message box body text
+ * @return {Roo.MessageBox} This message box
+ */
+ progress : function(title, msg){
+ this.show({
+ title : title,
+ msg : msg,
+ buttons: false,
+ progress:true,
+ closable:false,
+ minWidth: this.minProgressWidth,
+ modal : true
+ });
+ return this;
+ },
+
+ /**
+ * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
+ * If a callback function is passed it will be called after the user clicks the button, and the
+ * id of the button that was clicked will be passed as the only parameter to the callback
+ * (could also be the top-right close button).
+ * @param {String} title The title bar text
+ * @param {String} msg The message box body text
+ * @param {Function} fn (optional) The callback function invoked after the message box is closed
+ * @param {Object} scope (optional) The scope of the callback function
+ * @return {Roo.MessageBox} This message box
+ */
+ alert : function(title, msg, fn, scope)
+ {
+ this.show({
+ title : title,
+ msg : msg,
+ buttons: this.OK,
+ fn: fn,
+ closable : false,
+ scope : scope,
+ modal : true
+ });
+ return this;
+ },
+
+ /**
+ * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
+ * interaction while waiting for a long-running process to complete that does not have defined intervals.
+ * You are responsible for closing the message box when the process is complete.
+ * @param {String} msg The message box body text
+ * @param {String} title (optional) The title bar text
+ * @return {Roo.MessageBox} This message box
+ */
+ wait : function(msg, title){
+ this.show({
+ title : title,
+ msg : msg,
+ buttons: false,
+ closable:false,
+ progress:true,
+ modal:true,
+ width:300,
+ wait:true
+ });
+ waitTimer = Roo.TaskMgr.start({
+ run: function(i){
+ Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
+ },
+ interval: 1000
+ });
+ return this;
+ },
+
+ /**
+ * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
+ * If a callback function is passed it will be called after the user clicks either button, and the id of the
+ * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
+ * @param {String} title The title bar text
+ * @param {String} msg The message box body text
+ * @param {Function} fn (optional) The callback function invoked after the message box is closed
+ * @param {Object} scope (optional) The scope of the callback function
+ * @return {Roo.MessageBox} This message box
+ */
+ confirm : function(title, msg, fn, scope){
+ this.show({
+ title : title,
+ msg : msg,
+ buttons: this.YESNO,
+ fn: fn,
+ scope : scope,
+ modal : true
+ });
+ return this;
+ },
+
+ /**
+ * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
+ * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
+ * is passed it will be called after the user clicks either button, and the id of the button that was clicked
+ * (could also be the top-right close button) and the text that was entered will be passed as the two
+ * parameters to the callback.
+ * @param {String} title The title bar text
+ * @param {String} msg The message box body text
+ * @param {Function} fn (optional) The callback function invoked after the message box is closed
+ * @param {Object} scope (optional) The scope of the callback function
+ * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
+ * property, or the height in pixels to create the textbox (defaults to false / single-line)
+ * @return {Roo.MessageBox} This message box
+ */
+ prompt : function(title, msg, fn, scope, multiline){
+ this.show({
+ title : title,
+ msg : msg,
+ buttons: this.OKCANCEL,
+ fn: fn,
+ minWidth:250,
+ scope : scope,
+ prompt:true,
+ multiline: multiline,
+ modal : true
+ });
+ return this;
+ },
+
+ /**
+ * Button config that displays a single OK button
+ * @type Object
+ */
+ OK : {ok:true},
+ /**
+ * Button config that displays Yes and No buttons
+ * @type Object
+ */
+ YESNO : {yes:true, no:true},
+ /**
+ * Button config that displays OK and Cancel buttons
+ * @type Object
+ */
+ OKCANCEL : {ok:true, cancel:true},
+ /**
+ * Button config that displays Yes, No and Cancel buttons
+ * @type Object
+ */
+ YESNOCANCEL : {yes:true, no:true, cancel:true},
+
+ /**
+ * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
+ * @type Number
+ */
+ defaultTextHeight : 75,
+ /**
+ * The maximum width in pixels of the message box (defaults to 600)
+ * @type Number
+ */
+ maxWidth : 600,
+ /**
+ * The minimum width in pixels of the message box (defaults to 100)
+ * @type Number
+ */
+ minWidth : 100,
+ /**
+ * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
+ * for setting a different minimum width than text-only dialogs may need (defaults to 250)
+ * @type Number
+ */
+ minProgressWidth : 250,
+ /**
+ * An object containing the default button text strings that can be overriden for localized language support.
+ * Supported properties are: ok, cancel, yes and no.
+ * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
+ * @type Object
+ */
+ buttonText : {
+ ok : "OK",
+ cancel : "Cancel",
+ yes : "Yes",
+ no : "No"
+ }
+ };
+}();
+
+/**
+ * Shorthand for {@link Roo.MessageBox}
+ */
+Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
+Roo.Msg = Roo.Msg || Roo.MessageBox;
+/*
+ * - LGPL
+ *
+ * navbar
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.nav.Bar
+ * @extends Roo.bootstrap.Component
+ * @abstract
+ * Bootstrap Navbar class
- * @cfg {String} html content of button
- * @cfg {String} badge text inside badge
- * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
- * @cfg {String} glyphicon DEPRICATED - use fa
- * @cfg {String} icon DEPRICATED - use fa
- * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
- * @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?
- * @cfg {Boolean} animateRef (true|false) link to element default false
-
* @constructor
- * Create a new Navbar Item
+ * Create a new Navbar
* @param {Object} config The config object
*/
-Roo.bootstrap.NavItem = function(config){
- Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
+
+
+Roo.bootstrap.nav.Bar = function(config){
+ Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
this.addEvents({
// raw events
/**
- * @event click
- * The raw click event for the entire grid.
+ * @event beforetoggle
+ * Fire before toggle the menu
* @param {Roo.EventObject} e
*/
- "click" : true,
- /**
- * @event changed
- * Fires when the active item active state changes
- * @param {Roo.bootstrap.NavItem} this
- * @param {boolean} state the new state
-
- */
- 'changed': true,
- /**
- * @event scrollto
- * Fires when scroll to element
- * @param {Roo.bootstrap.NavItem} this
- * @param {Object} options
- * @param {Roo.EventObject} e
-
- */
- 'scrollto': true
+ "beforetoggle" : true
});
-
};
-Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
- href: false,
- html: '',
- badge: '',
- icon: false,
- fa : false,
- glyphicon: false,
- active: false,
- preventDefault : false,
- tabId : false,
- tagtype : 'a',
- tag: 'li',
- disabled : false,
- animateRef : false,
- was_active : false,
- button_weight : '',
- button_outline : false,
- navLink: false,
+
+ // private
+ navItems : false,
+ loadMask : false,
+
getAutoCreate : function(){
-
- var cfg = {
- tag: this.tag,
- cls: 'nav-item'
- };
-
- if (this.active) {
- cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
- }
- if (this.disabled) {
- cfg.cls += ' disabled';
- }
-
- // BS4 only?
- if (this.button_weight.length) {
- cfg.tag = this.href ? 'a' : 'button';
- cfg.html = this.html || '';
- cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
- if (this.href) {
- cfg.href = this.href;
- }
- if (this.fa) {
- cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
- }
-
- // menu .. should add dropdown-menu class - so no need for carat..
-
- if (this.badge !== '') {
-
- cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
- }
- return cfg;
- }
-
- if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
- cfg.cn = [
- {
- tag: this.tagtype,
- href : this.href || "#",
- html: this.html || ''
- }
- ];
- if (this.tagtype == 'a') {
- cfg.cn[0].cls = 'nav-link';
- }
- if (this.icon) {
- cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
- }
- if (this.fa) {
- cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
- }
- if(this.glyphicon) {
- cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
- }
-
- if (this.menu) {
-
- cfg.cn[0].html += " <span class='caret'></span>";
-
- }
-
- if (this.badge !== '') {
-
- cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
- }
- }
+ throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
- return cfg;
- },
- onRender : function(ct, position)
- {
- // Roo.log("Call onRender: " + this.xtype);
- if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
- this.tag = 'div';
- }
-
- var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
- this.navLink = this.el.select('.nav-link',true).first();
- return ret;
},
-
- initEvents: function()
+ initEvents :function ()
{
- if (typeof (this.menu) != 'undefined') {
- this.menu.parentType = this.xtype;
- this.menu.triggerEl = this.el;
- this.menu = this.addxtype(Roo.apply({}, this.menu));
- }
+ //Roo.log(this.el.select('.navbar-toggle',true));
+ this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
+
+ var mark = {
+ tag: "div",
+ cls:"x-dlg-mask"
+ };
- this.el.select('a',true).on('click', this.onClick, this);
+ this.maskEl = Roo.DomHelper.append(this.el, mark, true);
- if(this.tagtype == 'span'){
- this.el.select('span',true).on('click', this.onClick, this);
+ var size = this.el.getSize();
+ this.maskEl.setSize(size.width, size.height);
+ this.maskEl.enableDisplayMode("block");
+ this.maskEl.hide();
+
+ if(this.loadMask){
+ this.maskEl.show();
}
-
- // at this point parent should be available..
- this.parent().register(this);
},
- onClick : function(e)
+
+ getChildContainer : function()
{
- if (e.getTarget('.dropdown-menu-item')) {
- // did you click on a menu itemm.... - then don't trigger onclick..
- return;
+ if (this.el && this.el.select('.collapse').getCount()) {
+ return this.el.select('.collapse',true).first();
}
- if(
- this.preventDefault ||
- this.href == '#'
- ){
- Roo.log("NavItem - prevent Default?");
- e.preventDefault();
- }
+ return this.el;
+ },
+
+ mask : function()
+ {
+ this.maskEl.show();
+ },
+
+ unmask : function()
+ {
+ this.maskEl.hide();
+ },
+ onToggle : function()
+ {
- if (this.disabled) {
+ if(this.fireEvent('beforetoggle', this) === false){
return;
}
+ var ce = this.el.select('.navbar-collapse',true).first();
+
+ if (!ce.hasClass('show')) {
+ this.expand();
+ } else {
+ this.collapse();
+ }
- var tg = Roo.bootstrap.TabGroup.get(this.navId);
- if (tg && tg.transition) {
- Roo.log("waiting for the transitionend");
- return;
- }
-
-
-
- //Roo.log("fire event clicked");
- if(this.fireEvent('click', this, e) === false){
- return;
- };
-
- if(this.tagtype == 'span'){
- return;
- }
-
- //Roo.log(this.href);
- var ael = this.el.select('a',true).first();
- //Roo.log(ael);
-
- if(ael && this.animateRef && this.href.indexOf('#') > -1){
- //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
- if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
- return; // ignore... - it's a 'hash' to another page.
- }
- Roo.log("NavItem - prevent Default?");
- e.preventDefault();
- this.scrollToElement(e);
- }
-
-
- var p = this.parent();
-
- if (['tabs','pills'].indexOf(p.type)!==-1) {
- if (typeof(p.setActiveItem) !== 'undefined') {
- p.setActiveItem(this);
- }
- }
- // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
- if (p.parentType == 'NavHeaderbar' && !this.menu) {
- // remove the collapsed menu expand...
- p.parent().el.select('.navbar-collapse',true).removeClass('in');
- }
- },
- isActive: function () {
- return this.active
},
- setActive : function(state, fire, is_was_active)
+ /**
+ * Expand the navbar pulldown
+ */
+ expand : function ()
{
- if (this.active && !state && this.navId) {
- this.was_active = true;
- var nv = Roo.bootstrap.NavGroup.get(this.navId);
- if (nv) {
- nv.clearWasActive(this);
- }
-
- }
- this.active = state;
-
- if (!state ) {
- this.el.removeClass('active');
- this.navLink ? this.navLink.removeClass('active') : false;
- } else if (!this.el.hasClass('active')) {
-
- this.el.addClass('active');
- if (Roo.bootstrap.version == 4 && this.navLink ) {
- this.navLink.addClass('active');
- }
-
- }
- if (fire) {
- this.fireEvent('changed', this, state);
- }
-
- // show a panel if it's registered and related..
-
- if (!this.navId || !this.tabId || !state || is_was_active) {
- return;
- }
-
- var tg = Roo.bootstrap.TabGroup.get(this.navId);
- if (!tg) {
- return;
- }
- var pan = tg.getPanelByName(this.tabId);
- if (!pan) {
+
+ var ce = this.el.select('.navbar-collapse',true).first();
+ if (ce.hasClass('collapsing')) {
return;
}
- // if we can not flip to new panel - go back to old nav highlight..
- if (false == tg.showPanel(pan)) {
- var nv = Roo.bootstrap.NavGroup.get(this.navId);
- if (nv) {
- var onav = nv.getWasActive();
- if (onav) {
- onav.setActive(true, false, true);
- }
- }
-
- }
-
-
-
- },
- // 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');
- }
+ ce.dom.style.height = '';
+ // show it...
+ ce.addClass('in'); // old...
+ ce.removeClass('collapse');
+ ce.addClass('show');
+ var h = ce.getHeight();
+ Roo.log(h);
+ ce.removeClass('show');
+ // at this point we should be able to see it..
+ ce.addClass('collapsing');
+ ce.setHeight(0); // resize it ...
+ ce.on('transitionend', function() {
+ //Roo.log('done transition');
+ ce.removeClass('collapsing');
+ ce.addClass('show');
+ ce.removeClass('collapse');
+
+ ce.dom.style.height = '';
+ }, this, { single: true} );
+ ce.setHeight(h);
+ ce.dom.scrollTop = 0;
},
-
/**
- * Fetch the element to display the tooltip on.
- * @return {Roo.Element} defaults to this.el
+ * Collapse the navbar pulldown
*/
- tooltipEl : function()
- {
- return this.el.select('' + this.tagtype + '', true).first();
- },
-
- scrollToElement : function(e)
+ collapse : function()
{
- var c = document.body;
-
- /*
- * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
- */
- if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
- c = document.documentElement;
- }
-
- var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
-
- if(!target){
+ var ce = this.el.select('.navbar-collapse',true).first();
+
+ if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
+ // it's collapsed or collapsing..
return;
}
-
- var o = target.calcOffsetsTo(c);
-
- var options = {
- target : target,
- value : o[1]
- };
+ ce.removeClass('in'); // old...
+ ce.setHeight(ce.getHeight());
+ ce.removeClass('show');
+ ce.addClass('collapsing');
- this.fireEvent('scrollto', this, options, e);
-
- Roo.get(c).scrollTo('top', options.value, true);
-
- return;
+ ce.on('transitionend', function() {
+ ce.dom.style.height = '';
+ ce.removeClass('collapsing');
+ ce.addClass('collapse');
+ }, this, { single: true} );
+ ce.setHeight(0);
}
+
+
+
});
+
+
+
/*
* - LGPL
*
- * sidebar item
- *
- * li
- * <span> icon </span>
- * <span> text </span>
- * <span>badge </span>
+ * navbar
+ *
*/
/**
- * @class Roo.bootstrap.NavSidebarItem
- * @extends Roo.bootstrap.NavItem
- * Bootstrap Navbar.NavSidebarItem class
- * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
- * {Boolean} open is the menu open
- * {Boolean} buttonView use button as the tigger el rather that a (default false)
- * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
- * {String} buttonSize (sm|md|lg)the extra classes for the button
- * {Boolean} showArrow show arrow next to the text (default true)
+ * @class Roo.bootstrap.nav.Simplebar
+ * @extends Roo.bootstrap.nav.Bar
+ * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
+ * Bootstrap Sidebar class
+ *
+ * @cfg {Boolean} inverse is inverted color
+ *
+ * @cfg {String} type (nav | pills | tabs)
+ * @cfg {Boolean} arrangement stacked | justified
+ * @cfg {String} align (left | right) alignment
+ *
+ * @cfg {Boolean} main (true|false) main nav bar? default false
+ * @cfg {Boolean} loadMask (true|false) loadMask on the bar
+ *
+ * @cfg {String} tag (header|footer|nav|div) default is nav
+
+ * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
+ *
+ *
* @constructor
- * Create a new Navbar Button
+ * Create a new Sidebar
* @param {Object} config The config object
*/
-Roo.bootstrap.NavSidebarItem = function(config){
- Roo.bootstrap.NavSidebarItem.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.NavSidebarItem} this
- * @param {boolean} state the new state
-
- */
- 'changed': true
- });
-
+
+
+Roo.bootstrap.nav.Simplebar = function(config){
+ Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
+Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
- badgeWeight : 'default',
+ inverse: false,
- open: false,
+ type: false,
+ arrangement: '',
+ align : false,
- buttonView : false,
+ weight : 'light',
- buttonWeight : 'default',
+ main : false,
- buttonSize : 'md',
- showArrow : true,
+ tag : false,
+
getAutoCreate : function(){
- var a = {
- tag: 'a',
- href : this.href || '#',
- cls: '',
- html : '',
- cn : []
- };
-
- if(this.buttonView){
- a = {
- tag: 'button',
- href : this.href || '#',
- cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
- html : this.html,
- cn : []
- };
- }
-
var cfg = {
- tag: 'li',
- cls: '',
- cn: [ a ]
+ tag : this.tag || 'div',
+ cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
};
-
- if (this.active) {
- cfg.cls += ' active';
- }
-
- if (this.disabled) {
- cfg.cls += ' disabled';
- }
- if (this.open) {
- cfg.cls += ' open x-open';
- }
- // left icon..
- if (this.glyphicon || this.icon) {
- var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
- a.cn.push({ tag : 'i', cls : c }) ;
- }
-
- if(!this.buttonView){
- var span = {
- tag: 'span',
- html : this.html || ''
- };
-
- a.cn.push(span);
-
- }
-
- if (this.badge !== '') {
- a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
- }
-
- if (this.menu) {
-
- if(this.showArrow){
- a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
- }
+ if (['light','white'].indexOf(this.weight) > -1) {
+ cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
+ }
+ cfg.cls += ' bg-' + this.weight;
+
+ if (this.inverse) {
+ cfg.cls += ' navbar-inverse';
- a.cls += ' dropdown-toggle treeview' ;
}
-
- return cfg;
- },
+
+ // i'm not actually sure these are really used - normally we add a navGroup to a navbar
+
+ if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
+ return cfg;
+ }
+
+
- initEvents : function()
- {
- if (typeof (this.menu) != 'undefined') {
- this.menu.parentType = this.xtype;
- this.menu.triggerEl = this.el;
- this.menu = this.addxtype(Roo.apply({}, this.menu));
- }
+
+ cfg.cn = [
+ {
+ cls: 'nav nav-' + this.xtype,
+ tag : 'ul'
+ }
+ ];
- this.el.on('click', this.onClick, this);
+
+ this.type = this.type || 'nav';
+ if (['tabs','pills'].indexOf(this.type) != -1) {
+ cfg.cn[0].cls += ' nav-' + this.type
- if(this.badge !== ''){
- this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
- }
- },
-
- onClick : function(e)
- {
- if(this.disabled){
- e.preventDefault();
- return;
+ } else {
+ if (this.type!=='nav') {
+ Roo.log('nav type must be nav/tabs/pills')
+ }
+ cfg.cn[0].cls += ' navbar-nav'
}
- if(this.preventDefault){
- e.preventDefault();
- }
- this.fireEvent('click', this);
- },
-
- disable : function()
- {
- this.setDisabled(true);
- },
-
- enable : function()
- {
- this.setDisabled(false);
- },
-
- setDisabled : function(state)
- {
- if(this.disabled == state){
- return;
- }
- this.disabled = state;
- if (state) {
- this.el.addClass('disabled');
- return;
+ if (['stacked','justified'].indexOf(this.arrangement) != -1) {
+ cfg.cn[0].cls += ' nav-' + this.arrangement;
}
- this.el.removeClass('disabled');
- return;
- },
-
- setActive : function(state)
- {
- if(this.active == state){
- return;
+ if (this.align === 'right') {
+ cfg.cn[0].cls += ' navbar-right';
}
- this.active = state;
- if (state) {
- this.el.addClass('active');
- return;
- }
- this.el.removeClass('active');
- return;
- },
-
- isActive: function ()
- {
- return this.active;
- },
+ return cfg;
- setBadge : function(str)
- {
- if(!this.badgeEl){
- return;
- }
- this.badgeEl.dom.innerHTML = str;
}
-
-
-
-});
-
-
- /*
- * - LGPL
- *
- * row
- *
- */
-
-/**
- * @class Roo.bootstrap.Row
- * @extends Roo.bootstrap.Component
- * Bootstrap Row class (contains columns...)
- *
- * @constructor
- * Create a new Row
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Row = function(config){
- Roo.bootstrap.Row.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
-
- getAutoCreate : function(){
- return {
- cls: 'row clearfix'
- };
- }
});
+
+
- /*
+
+ /*
* - LGPL
*
- * element
- *
+ * navbar
+ * navbar-fixed-top
+ * navbar-expand-md fixed-top
*/
/**
- * @class Roo.bootstrap.Element
- * @extends Roo.bootstrap.Component
- * Bootstrap Element class
- * @cfg {String} html contents of the element
- * @cfg {String} tag tag of the element
- * @cfg {String} cls class of the element
- * @cfg {Boolean} preventDefault (true|false) default false
- * @cfg {Boolean} clickable (true|false) default false
+ * @class Roo.bootstrap.nav.Headerbar
+ * @extends Roo.bootstrap.nav.Simplebar
+ * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
+ * Bootstrap Sidebar class
+ *
+ * @cfg {String} brand what is brand
+ * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
+ * @cfg {String} brand_href href of the brand
+ * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
+ * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
+ * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
+ * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
*
* @constructor
- * Create a new Element
+ * Create a new Sidebar
* @param {Object} config The config object
*/
-Roo.bootstrap.Element = function(config){
- Roo.bootstrap.Element.superclass.constructor.call(this, config);
-
- this.addEvents({
- // raw events
- /**
- * @event click
- * When a element is chick
- * @param {Roo.bootstrap.Element} this
- * @param {Roo.EventObject} e
- */
- "click" : true
- });
+
+Roo.bootstrap.nav.Headerbar = function(config){
+ Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
+
};
-Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
- tag: 'div',
- cls: '',
- html: '',
- preventDefault: false,
- clickable: false,
+ position: '',
+ brand: '',
+ brand_href: false,
+ srButton : true,
+ autohide : false,
+ desktopCenter : false,
+
getAutoCreate : function(){
- var cfg = {
- tag: this.tag,
- // cls: this.cls, double assign in parent class Component.js :: onRender
- html: this.html
+ var cfg = {
+ tag: this.nav || 'nav',
+ cls: 'navbar navbar-expand-md',
+ role: 'navigation',
+ cn: []
};
- return cfg;
- },
-
- initEvents: function()
- {
- Roo.bootstrap.Element.superclass.initEvents.call(this);
-
- if(this.clickable){
- this.el.on('click', this.onClick, this);
- }
-
- },
-
- onClick : function(e)
- {
- if(this.preventDefault){
- e.preventDefault();
+ var cn = cfg.cn;
+ if (this.desktopCenter) {
+ cn.push({cls : 'container', cn : []});
+ cn = cn[0].cn;
}
- this.fireEvent('click', this, e);
- },
-
- getValue : function()
- {
- return this.el.dom.innerHTML;
- },
-
- setValue : function(value)
- {
- this.el.dom.innerHTML = value;
- }
-
+ if(this.srButton){
+ var btn = {
+ tag: 'button',
+ type: 'button',
+ cls: 'navbar-toggle navbar-toggler',
+ 'data-toggle': 'collapse',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'sr-only',
+ html: 'Toggle navigation'
+ },
+ {
+ tag: 'span',
+ cls: 'icon-bar navbar-toggler-icon'
+ },
+ {
+ tag: 'span',
+ cls: 'icon-bar'
+ },
+ {
+ tag: 'span',
+ cls: 'icon-bar'
+ }
+ ]
+ };
+
+ cn.push( Roo.bootstrap.version == 4 ? btn : {
+ tag: 'div',
+ cls: 'navbar-header',
+ cn: [
+ btn
+ ]
+ });
+ }
+
+ cn.push({
+ tag: 'div',
+ cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
+ cn : []
+ });
+
+ cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
+
+ if (['light','white'].indexOf(this.weight) > -1) {
+ cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
+ }
+ cfg.cls += ' bg-' + this.weight;
+
+
+ if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
+ cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
+
+ // tag can override this..
+
+ cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
+ }
+
+ if (this.brand !== '') {
+ var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
+ cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
+ tag: 'a',
+ href: this.brand_href ? this.brand_href : '#',
+ cls: 'navbar-brand',
+ cn: [
+ this.brand
+ ]
+ });
+ }
+
+ if(this.main){
+ cfg.cls += ' main-nav';
+ }
+
+
+ return cfg;
+
+
+ },
+ getHeaderChildContainer : function()
+ {
+ if (this.srButton && this.el.select('.navbar-header').getCount()) {
+ return this.el.select('.navbar-header',true).first();
+ }
+
+ return this.getChildContainer();
+ },
+
+ getChildContainer : function()
+ {
+
+ return this.el.select('.roo-navbar-collapse',true).first();
+
+
+ },
+
+ initEvents : function()
+ {
+ Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
+
+ if (this.autohide) {
+
+ var prevScroll = 0;
+ var ft = this.el;
+
+ Roo.get(document).on('scroll',function(e) {
+ var ns = Roo.get(document).getScroll().top;
+ var os = prevScroll;
+ prevScroll = ns;
+
+ if(ns > os){
+ ft.removeClass('slideDown');
+ ft.addClass('slideUp');
+ return;
+ }
+ ft.removeClass('slideUp');
+ ft.addClass('slideDown');
+
+
+ },this);
+ }
+ }
+
});
+
+
/*
* - LGPL
*
- * pagination
+ * navbar
*
*/
/**
- * @class Roo.bootstrap.Pagination
- * @extends Roo.bootstrap.Component
- * Bootstrap Pagination class
- * @cfg {String} size xs | sm | md | lg
- * @cfg {Boolean} inverse false | true
+ * @class Roo.bootstrap.nav.Sidebar
+ * @extends Roo.bootstrap.nav.Bar
+ * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
+ * Bootstrap Sidebar class
*
* @constructor
- * Create a new Pagination
+ * Create a new Sidebar
* @param {Object} config The config object
*/
-Roo.bootstrap.Pagination = function(config){
- Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
+
+Roo.bootstrap.nav.Sidebar = function(config){
+ Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
- cls: false,
- size: false,
- inverse: false,
+ sidebar : true, // used by Navbar Item and NavbarGroup at present...
getAutoCreate : function(){
- var cfg = {
- tag: 'ul',
- cls: 'pagination'
+
+
+ return {
+ tag: 'div',
+ cls: 'sidebar sidebar-nav'
};
- if (this.inverse) {
- cfg.cls += ' inverse';
- }
- if (this.html) {
- cfg.html=this.html;
- }
- if (this.cls) {
- cfg.cls += " " + this.cls;
- }
- return cfg;
+
+
}
-
+
+
+
});
+
+
/*
* - LGPL
*
- * Pagination item
+ * nav group
*
*/
-
/**
- * @class Roo.bootstrap.PaginationItem
+ * @class Roo.bootstrap.nav.Group
* @extends Roo.bootstrap.Component
- * Bootstrap PaginationItem class
- * @cfg {String} html text
- * @cfg {String} href the link
- * @cfg {Boolean} preventDefault (true | false) default true
- * @cfg {Boolean} active (true | false) default false
- * @cfg {Boolean} disabled default false
- *
+ * @children Roo.bootstrap.nav.Item
+ * Bootstrap NavGroup class
+ * @cfg {String} align (left|right)
+ * @cfg {Boolean} inverse
+ * @cfg {String} type (nav|pills|tab) default nav
+ * @cfg {String} navId - reference Id for navbar.
+ * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
*
* @constructor
- * Create a new PaginationItem
+ * Create a new nav group
* @param {Object} config The config object
*/
-
-Roo.bootstrap.PaginationItem = function(config){
- Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
- this.addEvents({
- // raw events
+Roo.bootstrap.nav.Group = function(config){
+ Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
+ this.navItems = [];
+
+ Roo.bootstrap.nav.Group.register(this);
+ this.addEvents({
/**
- * @event click
- * The raw click event for the entire grid.
- * @param {Roo.EventObject} e
+ * @event changed
+ * Fires when the active item changes
+ * @param {Roo.bootstrap.nav.Group} this
+ * @param {Roo.bootstrap.Navbar.Item} selected The item selected
+ * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
*/
- "click" : true
- });
+ 'changed': true
+ });
+
};
-Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
- href : false,
- html : false,
- preventDefault: true,
- active : false,
- cls : false,
- disabled: false,
+ align: '',
+ inverse: false,
+ form: false,
+ type: 'nav',
+ navId : '',
+ // private
+ pilltype : true,
- getAutoCreate : function(){
- var cfg= {
- tag: 'li',
- cn: [
- {
- tag : 'a',
- href : this.href ? this.href : '#',
- html : this.html ? this.html : ''
- }
- ]
+ navItems : false,
+
+ getAutoCreate : function()
+ {
+ var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
+
+ cfg = {
+ tag : 'ul',
+ cls: 'nav'
};
+ if (Roo.bootstrap.version == 4) {
+ if (['tabs','pills'].indexOf(this.type) != -1) {
+ cfg.cls += ' nav-' + this.type;
+ } else {
+ // trying to remove so header bar can right align top?
+ if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
+ // do not use on header bar...
+ cfg.cls += ' navbar-nav';
+ }
+ }
+
+ } else {
+ if (['tabs','pills'].indexOf(this.type) != -1) {
+ cfg.cls += ' nav-' + this.type
+ } else {
+ if (this.type !== 'nav') {
+ Roo.log('nav type must be nav/tabs/pills')
+ }
+ cfg.cls += ' navbar-nav'
+ }
+ }
- if(this.cls){
- cfg.cls = this.cls;
+ if (this.parent() && this.parent().sidebar) {
+ cfg = {
+ tag: 'ul',
+ cls: 'dashboard-menu sidebar-menu'
+ };
+
+ return cfg;
}
- if(this.disabled){
- cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
+ if (this.form === true) {
+ cfg = {
+ tag: 'form',
+ cls: 'navbar-form form-inline'
+ };
+ //nav navbar-right ml-md-auto
+ if (this.align === 'right') {
+ cfg.cls += ' navbar-right ml-md-auto';
+ } else {
+ cfg.cls += ' navbar-left';
+ }
}
- if(this.active){
- cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
+ if (this.align === 'right') {
+ cfg.cls += ' navbar-right ml-md-auto';
+ } else {
+ cfg.cls += ' mr-auto';
+ }
+
+ if (this.inverse) {
+ cfg.cls += ' navbar-inverse';
+
}
-
+
+
return cfg;
},
-
- initEvents: function() {
+ /**
+ * sets the active Navigation item
+ * @param {Roo.bootstrap.nav.Item} the new current navitem
+ */
+ setActiveItem : function(item)
+ {
+ var prev = false;
+ Roo.each(this.navItems, function(v){
+ if (v == item) {
+ return ;
+ }
+ if (v.isActive()) {
+ v.setActive(false, true);
+ prev = v;
+
+ }
+
+ });
+
+ item.setActive(true, true);
+ this.fireEvent('changed', this, item, prev);
- this.el.on('click', this.onClick, this);
},
- onClick : function(e)
+ /**
+ * gets the active Navigation item
+ * @return {Roo.bootstrap.nav.Item} the current navitem
+ */
+ getActive : function()
{
- Roo.log('PaginationItem on click ');
- if(this.preventDefault){
- e.preventDefault();
- }
- if(this.disabled){
+ var prev = false;
+ Roo.each(this.navItems, function(v){
+
+ if (v.isActive()) {
+ prev = v;
+
+ }
+
+ });
+ return prev;
+ },
+
+ indexOfNav : function()
+ {
+
+ var prev = false;
+ Roo.each(this.navItems, function(v,i){
+
+ if (v.isActive()) {
+ prev = i;
+
+ }
+
+ });
+ return prev;
+ },
+ /**
+ * adds a Navigation item
+ * @param {Roo.bootstrap.nav.Item} the navitem to add
+ */
+ addItem : function(cfg)
+ {
+ if (this.form && Roo.bootstrap.version == 4) {
+ cfg.tag = 'div';
+ }
+ var cn = new Roo.bootstrap.nav.Item(cfg);
+ this.register(cn);
+ cn.parentId = this.id;
+ cn.onRender(this.el, null);
+ return cn;
+ },
+ /**
+ * register a Navigation item
+ * @param {Roo.bootstrap.nav.Item} the navitem to add
+ */
+ register : function(item)
+ {
+ this.navItems.push( item);
+ item.navId = this.navId;
+
+ },
+
+ /**
+ * clear all the Navigation item
+ */
+
+ clearAll : function()
+ {
+ this.navItems = [];
+ this.el.dom.innerHTML = '';
+ },
+
+ getNavItem: function(tabId)
+ {
+ var ret = false;
+ Roo.each(this.navItems, function(e) {
+ if (e.tabId == tabId) {
+ ret = e;
+ return false;
+ }
+ return true;
+
+ });
+ return ret;
+ },
+
+ setActiveNext : function()
+ {
+ var i = this.indexOfNav(this.getActive());
+ if (i > this.navItems.length) {
return;
}
-
- this.fireEvent('click', this, e);
+ this.setActiveItem(this.navItems[i+1]);
+ },
+ setActivePrev : function()
+ {
+ var i = this.indexOfNav(this.getActive());
+ if (i < 1) {
+ return;
+ }
+ this.setActiveItem(this.navItems[i-1]);
+ },
+ clearWasActive : function(except) {
+ Roo.each(this.navItems, function(e) {
+ if (e.tabId != except.tabId && e.was_active) {
+ e.was_active = false;
+ return false;
+ }
+ return true;
+
+ });
+ },
+ getWasActive : function ()
+ {
+ var r = false;
+ Roo.each(this.navItems, function(e) {
+ if (e.was_active) {
+ r = e;
+ return false;
+ }
+ return true;
+
+ });
+ return r;
}
-
+
+
});
+Roo.apply(Roo.bootstrap.nav.Group, {
+
+ groups: {},
+ /**
+ * register a Navigation Group
+ * @param {Roo.bootstrap.nav.Group} the navgroup to add
+ */
+ register : function(navgrp)
+ {
+ this.groups[navgrp.navId] = navgrp;
+
+ },
+ /**
+ * fetch a Navigation Group based on the navigation ID
+ * @param {string} the navgroup to add
+ * @returns {Roo.bootstrap.nav.Group} the navgroup
+ */
+ get: function(navId) {
+ if (typeof(this.groups[navId]) == 'undefined') {
+ return false;
+ //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
+ }
+ return this.groups[navId] ;
+ }
+
+
+
+});
- /*
- * - LGPL
- *
- * slider
- *
- */
-
-
-/**
- * @class Roo.bootstrap.Slider
+ /**
+ * @class Roo.bootstrap.nav.Item
* @extends Roo.bootstrap.Component
- * Bootstrap Slider class
- *
+ * @children Roo.bootstrap.Container Roo.bootstrap.Button
+ * @parent Roo.bootstrap.nav.Group
+ * @licence LGPL
+ * Bootstrap Navbar.NavItem class
+ *
+ * @cfg {String} href link to
+ * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
+ * @cfg {Boolean} button_outline show and outlined button
+ * @cfg {String} html content of button
+ * @cfg {String} badge text inside badge
+ * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
+ * @cfg {String} glyphicon DEPRICATED - use fa
+ * @cfg {String} icon DEPRICATED - use fa
+ * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
+ * @cfg {Boolean} active Is item active
+ * @cfg {Boolean} disabled Is item disabled
+ * @cfg {String} linkcls Link Class
+ * @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?
+ * @cfg {Boolean} animateRef (true|false) link to element default false
+ * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
+
* @constructor
- * Create a new Slider
+ * Create a new Navbar Item
* @param {Object} config The config object
*/
-
-Roo.bootstrap.Slider = function(config){
- Roo.bootstrap.Slider.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
-
- getAutoCreate : function(){
-
- var cfg = {
- tag: 'div',
- cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
- cn: [
- {
- tag: 'a',
- cls: 'ui-slider-handle ui-state-default ui-corner-all'
- }
- ]
- };
-
- return cfg;
- }
-
-});
-
- /*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
-
-
-/**
- * @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
- * @param {Object} config An Array of column config objects. See this class's
- * config objects for details.
-*/
-Roo.grid.ColumnModel = function(config){
- /**
- * The config passed into the constructor
- */
- this.config = config;
- this.lookup = {};
-
- // if no id, create one
- // if the column does not have a dataIndex mapping,
- // map it to the order it is in the config
- for(var i = 0, len = config.length; i < len; i++){
- 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;
-
+Roo.bootstrap.nav.Item = function(config){
+ Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
this.addEvents({
+ // raw events
/**
- * @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
+ * @event click
+ * The raw click event for the entire grid.
+ * @param {Roo.EventObject} e
*/
- "columnmoved" : true,
+ "click" : true,
+ /**
+ * @event changed
+ * Fires when the active item active state changes
+ * @param {Roo.bootstrap.nav.Item} this
+ * @param {boolean} state the new state
+
+ */
+ 'changed': true,
/**
- * @event columlockchange
- * Fires when a column's locked state is changed
- * @param {ColumnModel} this
- * @param {Number} colIndex
- * @param {Boolean} locked true if locked
+ * @event scrollto
+ * Fires when scroll to element
+ * @param {Roo.bootstrap.nav.Item} this
+ * @param {Object} options
+ * @param {Roo.EventObject} e
+
*/
- "columnlockchange" : true
+ 'scrollto': 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 returns the escaped data value. If an object is returned (bootstrap only)
- * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
- */
- /**
- * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
- */
- /**
- * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
- */
- /**
- * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
- */
- /**
- * @cfg {String} cursor (Optional)
- */
- /**
- * @cfg {String} tooltip (Optional)
- */
- /**
- * @cfg {Number} xs (Optional)
- */
- /**
- * @cfg {Number} sm (Optional)
- */
- /**
- * @cfg {Number} md (Optional)
- */
- /**
- * @cfg {Number} lg (Optional)
- */
- /**
- * Returns the id of the column at the specified index.
- * @param {Number} index The column index
- * @return {String} the id
- */
- getColumnId : function(index){
- return this.config[index].id;
- },
-
- /**
- * 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.nav.Item, Roo.bootstrap.Component, {
- /**
- * 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;
- },
+ href: false,
+ html: '',
+ badge: '',
+ icon: false,
+ fa : false,
+ glyphicon: false,
+ active: false,
+ preventDefault : false,
+ tabId : false,
+ tagtype : 'a',
+ tag: 'li',
+ disabled : false,
+ animateRef : false,
+ was_active : false,
+ button_weight : '',
+ button_outline : false,
+ linkcls : '',
+ navLink: false,
- /**
- * Returns the index for a specified column id.
- * @param {String} id The column id
- * @return {Number} the index, or -1 if not found
- */
- getIndexById : function(id){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(this.config[i].id == id){
- return i;
+ getAutoCreate : function(){
+
+ var cfg = {
+ tag: this.tag,
+ cls: 'nav-item'
+ };
+
+ cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
+
+ if (this.active) {
+ cfg.cls += ' active' ;
+ }
+ if (this.disabled) {
+ cfg.cls += ' disabled';
+ }
+
+ // BS4 only?
+ if (this.button_weight.length) {
+ cfg.tag = this.href ? 'a' : 'button';
+ cfg.html = this.html || '';
+ cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
+ if (this.href) {
+ cfg.href = this.href;
+ }
+ if (this.fa) {
+ cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
+ } else {
+ cfg.cls += " nav-html";
+ }
+
+ // menu .. should add dropdown-menu class - so no need for carat..
+
+ if (this.badge !== '') {
+
+ cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
+ }
+ return cfg;
+ }
+
+ if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
+ cfg.cn = [
+ {
+ tag: this.tagtype,
+ href : this.href || "#",
+ html: this.html || '',
+ cls : ''
+ }
+ ];
+ if (this.tagtype == 'a') {
+ cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
+
+ }
+ if (this.icon) {
+ cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
+ } else if (this.fa) {
+ cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
+ } else if(this.glyphicon) {
+ cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
+ } else {
+ cfg.cn[0].cls += " nav-html";
+ }
+
+ if (this.menu) {
+ cfg.cn[0].html += " <span class='caret'></span>";
+
+ }
+
+ if (this.badge !== '') {
+ cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
}
}
- return -1;
+
+
+
+ return cfg;
},
+ onRender : function(ct, position)
+ {
+ // Roo.log("Call onRender: " + this.xtype);
+ if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
+ this.tag = 'div';
+ }
+
+ var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
+ this.navLink = this.el.select('.nav-link',true).first();
+ this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
+ return ret;
+ },
+
- /**
- * 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;
- }
+ initEvents: function()
+ {
+ if (typeof (this.menu) != 'undefined') {
+ this.menu.parentType = this.xtype;
+ this.menu.triggerEl = this.el;
+ this.menu = this.addxtype(Roo.apply({}, this.menu));
}
- return -1;
+
+ this.el.on('click', this.onClick, this);
+
+ //if(this.tagtype == 'span'){
+ // this.el.select('span',true).on('click', this.onClick, this);
+ //}
+
+ // at this point parent should be available..
+ this.parent().register(this);
},
-
- moveColumn : function(oldIndex, newIndex){
- var c = this.config[oldIndex];
- this.config.splice(oldIndex, 1);
- this.config.splice(newIndex, 0, c);
- this.dataMap = null;
- this.fireEvent("columnmoved", this, oldIndex, newIndex);
- },
-
- isLocked : function(colIndex){
- return this.config[colIndex].locked === true;
- },
-
- setLocked : function(colIndex, value, suppressEvent){
- if(this.isLocked(colIndex) == value){
+ onClick : function(e)
+ {
+ if (e.getTarget('.dropdown-menu-item')) {
+ // did you click on a menu itemm.... - then don't trigger onclick..
return;
}
- this.config[colIndex].locked = value;
- if(!suppressEvent){
- this.fireEvent("columnlockchange", this, colIndex, value);
+
+ if(
+ this.preventDefault ||
+ this.href === false ||
+ this.href === '#'
+ ){
+ //Roo.log("NavItem - prevent Default?");
+ e.preventDefault();
}
- },
-
- 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.disabled) {
+ return;
+ }
+
+ var tg = Roo.bootstrap.TabGroup.get(this.navId);
+ if (tg && tg.transition) {
+ Roo.log("waiting for the transitionend");
+ return;
+ }
+
+
+
+ //Roo.log("fire event clicked");
+ if(this.fireEvent('click', this, e) === false){
+ return;
+ };
+
+ if(this.tagtype == 'span'){
+ return;
+ }
+
+ //Roo.log(this.href);
+ var ael = this.el.select('a',true).first();
+ //Roo.log(ael);
+
+ if(ael && this.animateRef && this.href.indexOf('#') > -1){
+ //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
+ if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
+ return; // ignore... - it's a 'hash' to another page.
}
+ Roo.log("NavItem - prevent Default?");
+ e.preventDefault();
+ this.scrollToElement(e);
}
- return totalWidth;
- },
-
- getLockedCount : function(){
- for(var i = 0, len = this.config.length; i < len; i++){
- if(!this.isLocked(i)){
- return i;
+
+
+ var p = this.parent();
+
+ if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
+ if (typeof(p.setActiveItem) !== 'undefined') {
+ p.setActiveItem(this);
}
}
- return this.config.length;
+ // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
+ if (p.parentType == 'NavHeaderbar' && !this.menu) {
+ // remove the collapsed menu expand...
+ p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
+ }
},
-
- /**
- * 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++;
- }
+
+ isActive: function () {
+ return this.active
+ },
+ setActive : function(state, fire, is_was_active)
+ {
+ if (this.active && !state && this.navId) {
+ this.was_active = true;
+ var nv = Roo.bootstrap.nav.Group.get(this.navId);
+ if (nv) {
+ nv.clearWasActive(this);
}
- 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;
+ this.active = state;
+
+ if (!state ) {
+ this.el.removeClass('active');
+ this.navLink ? this.navLink.removeClass('active') : false;
+ } else if (!this.el.hasClass('active')) {
+
+ this.el.addClass('active');
+ if (Roo.bootstrap.version == 4 && this.navLink ) {
+ this.navLink.addClass('active');
+ }
+
+ }
+ if (fire) {
+ this.fireEvent('changed', this, state);
+ }
+
+ // show a panel if it's registered and related..
+
+ if (!this.navId || !this.tabId || !state || is_was_active) {
+ return;
+ }
+
+ var tg = Roo.bootstrap.TabGroup.get(this.navId);
+ if (!tg) {
+ return;
+ }
+ var pan = tg.getPanelByName(this.tabId);
+ if (!pan) {
+ return;
+ }
+ // if we can not flip to new panel - go back to old nav highlight..
+ if (false == tg.showPanel(pan)) {
+ var nv = Roo.bootstrap.nav.Group.get(this.navId);
+ if (nv) {
+ var onav = nv.getWasActive();
+ if (onav) {
+ onav.setActive(true, false, true);
+ }
}
+
}
- 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;
+ // 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');
}
- 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 this.config[col].renderer;
- },
-
- /**
- * Sets the rendering (formatting) function for a column.
- * @param {Number} col The column index
- * @param {Function} fn The function to use to process the cell's raw data
- * to return HTML markup for the grid view. The render function is called with
- * the following parameters:<ul>
- * <li>Data value.</li>
- * <li>Cell metadata. An object in which you may set the following attributes:<ul>
- * <li>css A CSS style string to apply to the table cell.</li>
- * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
- * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
- * <li>Row index</li>
- * <li>Column index</li>
- * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
- */
- setRenderer : function(col, fn){
- this.config[col].renderer = fn;
- },
-
- /**
- * Returns the width for the specified column.
- * @param {Number} col The column index
- * @return {Number}
+ * Fetch the element to display the tooltip on.
+ * @return {Roo.Element} defaults to this.el
*/
- getColumnWidth : function(col){
- return this.config[col].width * 1 || this.defaultWidth;
+ tooltipEl : function()
+ {
+ return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
},
-
- /**
- * 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);
+
+ scrollToElement : function(e)
+ {
+ var c = document.body;
+
+ /*
+ * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
+ */
+ if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
+ c = document.documentElement;
}
- },
-
- /**
- * 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 target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
+
+ if(!target){
+ return;
}
- 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;
+ var o = target.calcOffsetsTo(c);
+
+ var options = {
+ target : target,
+ value : o[1]
+ };
+
+ this.fireEvent('scrollto', this, options, e);
+
+ Roo.get(c).scrollTo('top', options.value, true);
+
+ return;
},
/**
- * Sets the tooltip for a column.
- * @param {Number} col The column index
- * @param {String} tooltip The new tooltip
+ * Set the HTML (text content) of the item
+ * @param {string} html content for the nav item
*/
- setColumnTooltip : function(col, tooltip){
- this.config[col].tooltip = tooltip;
- },
+ setHtml : function(html)
+ {
+ this.html = html;
+ this.htmlEl.dom.innerHTML = html;
+
+ }
+});
+
- /**
- * Returns the dataIndex for the specified column.
- * @param {Number} col The column index
- * @return {Number}
- */
- getDataIndex : function(col){
- return this.config[col].dataIndex;
- },
+ /*
+ * - LGPL
+ *
+ * sidebar item
+ *
+ * li
+ * <span> icon </span>
+ * <span> text </span>
+ * <span>badge </span>
+ */
- /**
- * 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;
- },
+/**
+ * @class Roo.bootstrap.nav.SidebarItem
+ * @extends Roo.bootstrap.nav.Item
+ * Bootstrap Navbar.NavSidebarItem class
+ *
+ * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
+ * {Boolean} open is the menu open
+ * {Boolean} buttonView use button as the tigger el rather that a (default false)
+ * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
+ * {String} buttonSize (sm|md|lg)the extra classes for the button
+ * {Boolean} showArrow show arrow next to the text (default true)
+ * @constructor
+ * Create a new Navbar Button
+ * @param {Object} config The config object
+ */
+Roo.bootstrap.nav.SidebarItem = function(config){
+ Roo.bootstrap.nav.SidebarItem.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.nav.SidebarItem} this
+ * @param {boolean} state the new state
+
+ */
+ 'changed': true
+ });
+
+};
+Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
+ badgeWeight : 'default',
- /**
- * Returns true if the cell is editable.
- * @param {Number} colIndex The column index
- * @param {Number} rowIndex The row index - this is nto actually used..?
- * @return {Boolean}
- */
- isCellEditable : function(colIndex, rowIndex){
- return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
- },
+ open: false,
+
+ buttonView : false,
+
+ buttonWeight : 'default',
+
+ buttonSize : 'md',
+
+ showArrow : true,
+
+ getAutoCreate : function(){
+
+
+ var a = {
+ tag: 'a',
+ href : this.href || '#',
+ cls: '',
+ html : '',
+ cn : []
+ };
+
+ if(this.buttonView){
+ a = {
+ tag: 'button',
+ href : this.href || '#',
+ cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
+ html : this.html,
+ cn : []
+ };
+ }
+
+ var cfg = {
+ tag: 'li',
+ cls: '',
+ cn: [ a ]
+ };
+
+ if (this.active) {
+ cfg.cls += ' active';
+ }
+
+ if (this.disabled) {
+ cfg.cls += ' disabled';
+ }
+ if (this.open) {
+ cfg.cls += ' open x-open';
+ }
+ // left icon..
+ if (this.glyphicon || this.icon) {
+ var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
+ a.cn.push({ tag : 'i', cls : c }) ;
+ }
+
+ if(!this.buttonView){
+ var span = {
+ tag: 'span',
+ html : this.html || ''
+ };
- /**
- * 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;
+ a.cn.push(span);
+
+ }
+
+ if (this.badge !== '') {
+ a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
+ }
+
+ if (this.menu) {
+
+ if(this.showArrow){
+ a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
+ }
+
+ a.cls += ' dropdown-toggle treeview' ;
+ }
+
+ return cfg;
},
-
- /**
- * 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;
+
+ initEvents : function()
+ {
+ if (typeof (this.menu) != 'undefined') {
+ this.menu.parentType = this.xtype;
+ this.menu.triggerEl = this.el;
+ this.menu = this.addxtype(Roo.apply({}, this.menu));
+ }
+
+ this.el.on('click', this.onClick, this);
+
+ if(this.badge !== ''){
+ this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
+ }
+
},
-
-
- /**
- * Returns true if the column is hidden.
- * @param {Number} colIndex The column index
- * @return {Boolean}
- */
- isHidden : function(colIndex){
- return this.config[colIndex].hidden;
+
+ onClick : function(e)
+ {
+ if(this.disabled){
+ e.preventDefault();
+ return;
+ }
+
+ if(this.preventDefault){
+ e.preventDefault();
+ }
+
+ this.fireEvent('click', this, e);
},
-
-
- /**
- * Returns true if the column width cannot be changed
- */
- isFixed : function(colIndex){
- return this.config[colIndex].fixed;
+
+ disable : function()
+ {
+ this.setDisabled(true);
},
-
- /**
- * 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;
+
+ enable : function()
+ {
+ this.setDisabled(false);
},
- /**
- * 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);
+
+ setDisabled : function(state)
+ {
+ if(this.disabled == state){
+ return;
+ }
+
+ this.disabled = state;
+
+ if (state) {
+ this.el.addClass('disabled');
+ return;
+ }
+
+ this.el.removeClass('disabled');
+
+ return;
},
-
- /**
- * 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 == "object") {
- return value;
+
+ setActive : function(state)
+ {
+ if(this.active == state){
+ return;
+ }
+
+ this.active = state;
+
+ if (state) {
+ this.el.addClass('active');
+ return;
+ }
+
+ this.el.removeClass('active');
+
+ return;
+ },
+
+ isActive: function ()
+ {
+ return this.active;
+ },
+
+ setBadge : function(str)
+ {
+ if(!this.badgeEl){
+ return;
+ }
+
+ this.badgeEl.dom.innerHTML = str;
}
- if(typeof value == "string" && value.length < 1){
- return " ";
- }
- return String.format("{0}", value);
-};
+
+
+
+});
+
-// Alias for backwards compatibility
-Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
-/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
+ /*
+ * - LGPL
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * nav progress bar
+ *
*/
-
+
/**
- * @class Roo.LoadMask
- * A simple utility class for generically masking elements while loading data. If the element being masked has
- * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
- * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
- * element's UpdateManager load indicator and will be destroyed after the initial load.
+ * @class Roo.bootstrap.nav.ProgressBar
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.nav.ProgressBarItem
+ * Bootstrap NavProgressBar class
+ *
* @constructor
- * Create a new LoadMask
- * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
+ * Create a new nav progress bar - a bar indicating step along a process
* @param {Object} config The config object
*/
-Roo.LoadMask = function(el, config){
- this.el = Roo.get(el);
- Roo.apply(this, config);
- if(this.store){
- this.store.on('beforeload', this.onBeforeLoad, this);
- this.store.on('load', this.onLoad, this);
- this.store.on('loadexception', this.onLoadException, this);
- this.removeMask = false;
- }else{
- var um = this.el.getUpdateManager();
- um.showLoadIndicator = false; // disable the default indicator
- um.on('beforeupdate', this.onBeforeLoad, this);
- um.on('update', this.onLoad, this);
- um.on('failure', this.onLoad, this);
- this.removeMask = true;
- }
-};
-Roo.LoadMask.prototype = {
- /**
- * @cfg {Boolean} removeMask
- * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
- * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
- */
- /**
- * @cfg {String} msg
- * The text to display in a centered loading message box (defaults to 'Loading...')
- */
- msg : 'Loading...',
- /**
- * @cfg {String} msgCls
- * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
- */
- msgCls : 'x-mask-loading',
+Roo.bootstrap.nav.ProgressBar = function(config){
+ Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
- /**
- * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
- * @type Boolean
- */
- disabled: false,
+ this.bullets = this.bullets || [];
+
+// Roo.bootstrap.nav.ProgressBar.register(this);
+ this.addEvents({
+ /**
+ * @event changed
+ * Fires when the active item changes
+ * @param {Roo.bootstrap.nav.ProgressBar} this
+ * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
+ * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
+ */
+ 'changed': true
+ });
+
+};
+Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
/**
- * Disables the mask to prevent it from being displayed
+ * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
+ * Bullets for the Nav Progress bar for the toolbar
*/
- disable : function(){
- this.disabled = true;
- },
-
- /**
- * Enables the mask so that it can be displayed
- */
- enable : function(){
- this.disabled = false;
- },
+ bullets : [],
+ barItems : [],
- onLoadException : function()
+ getAutoCreate : function()
{
- Roo.log(arguments);
+ var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
- 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) {
+ cfg = {
+ tag : 'div',
+ cls : 'roo-navigation-bar-group',
+ cn : [
+ {
+ tag : 'div',
+ cls : 'roo-navigation-top-bar'
+ },
+ {
+ tag : 'div',
+ cls : 'roo-navigation-bullets-bar',
+ cn : [
+ {
+ tag : 'ul',
+ cls : 'roo-navigation-bar'
+ }
+ ]
+ },
+
+ {
+ tag : 'div',
+ cls : 'roo-navigation-bottom-bar'
+ }
+ ]
- }
- */
-
- (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+ };
+
+ return cfg;
+
},
- // private
- onLoad : function()
+
+ initEvents: function()
{
- (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+
},
-
- // private
- onBeforeLoad : function(){
- if(!this.disabled){
- (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
+
+ onRender : function(ct, position)
+ {
+ Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
+
+ if(this.bullets.length){
+ Roo.each(this.bullets, function(b){
+ this.addItem(b);
+ }, this);
}
+
+ this.format();
+
},
-
- // 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
- *
- */
-
-/**
- * @class Roo.bootstrap.Table
- * @extends Roo.bootstrap.Component
- * Bootstrap Table class
- * @cfg {String} cls table class
- * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
- * @cfg {String} bgcolor Specifies the background color for a table
- * @cfg {Number} border Specifies whether the table cells should have borders or not
- * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
- * @cfg {Number} cellspacing Specifies the space between cells
- * @cfg {String} frame Specifies which parts of the outside borders that should be visible
- * @cfg {String} rules Specifies which parts of the inside borders that should be visible
- * @cfg {String} sortable Specifies that the table should be sortable
- * @cfg {String} summary Specifies a summary of the content of a table
- * @cfg {Number} width Specifies the width of a table
- * @cfg {String} layout table layout (auto | fixed | initial | inherit)
- *
- * @cfg {boolean} striped Should the rows be alternative striped
- * @cfg {boolean} bordered Add borders to the table
- * @cfg {boolean} hover Add hover highlighting
- * @cfg {boolean} condensed Format condensed
- * @cfg {boolean} responsive Format condensed
- * @cfg {Boolean} loadMask (true|false) default false
- * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
- * @cfg {Boolean} headerShow (true|false) generate thead, default true
- * @cfg {Boolean} rowSelection (true|false) default false
- * @cfg {Boolean} cellSelection (true|false) default false
- * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
- * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
- * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
- * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
-
- *
- * @constructor
- * Create a new Table
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Table = function(config){
- Roo.bootstrap.Table.superclass.constructor.call(this, config);
-
-
-
- // BC...
- this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
- this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
- this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
- this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
-
- this.sm = this.sm || {xtype: 'RowSelectionModel'};
- if (this.sm) {
- this.sm.grid = this;
- this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
- this.sm = this.selModel;
- this.sm.xmodule = this.xmodule || false;
- }
-
- if (this.cm && typeof(this.cm.config) == 'undefined') {
- this.colModel = new Roo.grid.ColumnModel(this.cm);
- this.cm = this.colModel;
- this.cm.xmodule = this.xmodule || false;
- }
- if (this.store) {
- this.store= Roo.factory(this.store, Roo.data);
- this.ds = this.store;
- this.ds.xmodule = this.xmodule || false;
-
- }
- if (this.footer && this.store) {
- this.footer.dataSource = this.ds;
- this.footer = Roo.factory(this.footer);
- }
-
- /** @private */
- this.addEvents({
- /**
- * @event cellclick
- * Fires when a cell is clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "cellclick" : true,
- /**
- * @event celldblclick
- * Fires when a cell is double clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "celldblclick" : true,
- /**
- * @event rowclick
- * Fires when a row is clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Roo.EventObject} e
- */
- "rowclick" : true,
- /**
- * @event rowdblclick
- * Fires when a row is double clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Roo.EventObject} e
- */
- "rowdblclick" : true,
- /**
- * @event mouseover
- * Fires when a mouseover occur
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "mouseover" : true,
- /**
- * @event mouseout
- * Fires when a mouseout occur
- * @param {Roo.bootstrap.Table} this
- * @param {Roo.Element} el
- * @param {Number} rowIndex
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "mouseout" : true,
- /**
- * @event rowclass
- * Fires when a row is rendered, so you can change add a style to it.
- * @param {Roo.bootstrap.Table} this
- * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
- */
- 'rowclass' : true,
- /**
- * @event rowsrendered
- * Fires when all the rows have been rendered
- * @param {Roo.bootstrap.Table} this
- */
- 'rowsrendered' : true,
- /**
- * @event contextmenu
- * The raw contextmenu event for the entire grid.
- * @param {Roo.EventObject} e
- */
- "contextmenu" : true,
- /**
- * @event rowcontextmenu
- * Fires when a row is right clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Number} rowIndex
- * @param {Roo.EventObject} e
- */
- "rowcontextmenu" : true,
- /**
- * @event cellcontextmenu
- * Fires when a cell is right clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Number} rowIndex
- * @param {Number} cellIndex
- * @param {Roo.EventObject} e
- */
- "cellcontextmenu" : true,
- /**
- * @event headercontextmenu
- * Fires when a header is right clicked
- * @param {Roo.bootstrap.Table} this
- * @param {Number} columnIndex
- * @param {Roo.EventObject} e
- */
- "headercontextmenu" : true
- });
-};
-
-Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
-
- cls: false,
- align: false,
- bgcolor: false,
- border: false,
- cellpadding: false,
- cellspacing: false,
- frame: false,
- rules: false,
- sortable: false,
- summary: false,
- width: false,
- striped : false,
- scrollBody : false,
- bordered: false,
- hover: false,
- condensed : false,
- responsive : false,
- sm : false,
- cm : false,
- store : false,
- loadMask : false,
- footerShow : true,
- headerShow : true,
-
- rowSelection : false,
- cellSelection : false,
- layout : false,
-
- // Roo.Element - the tbody
- mainBody: false,
- // Roo.Element - thead element
- mainHead: false,
-
- container: false, // used by gridpanel...
-
- lazyLoad : false,
-
- CSS : Roo.util.CSS,
-
- auto_hide_footer : false,
- getAutoCreate : function()
+ addItem : function(cfg)
{
- var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
+ var item = new Roo.bootstrap.nav.ProgressItem(cfg);
- cfg = {
- tag: 'table',
- cls : 'table',
- cn : []
- };
- if (this.scrollBody) {
- cfg.cls += ' table-body-fixed';
- }
- if (this.striped) {
- cfg.cls += ' table-striped';
- }
+ item.parentId = this.id;
+ item.render(this.el.select('.roo-navigation-bar', true).first(), null);
- 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(cfg.html){
+ var top = new Roo.bootstrap.Element({
+ tag : 'div',
+ cls : 'roo-navigation-bar-text'
+ });
+
+ var bottom = new Roo.bootstrap.Element({
+ tag : 'div',
+ cls : 'roo-navigation-bar-text'
+ });
+
+ top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
+ bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
+
+ var topText = new Roo.bootstrap.Element({
+ tag : 'span',
+ html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
+ });
+
+ var bottomText = new Roo.bootstrap.Element({
+ tag : 'span',
+ html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
+ });
+
+ topText.onRender(top.el, null);
+ bottomText.onRender(bottom.el, null);
+
+ item.topEl = top;
+ item.bottomEl = bottom;
}
- if (this.cls) {
- cfg.cls+= ' ' +this.cls;
- }
+ this.barItems.push(item);
- // this lot should be simplifed...
- var _t = this;
- var cp = [
- 'align',
- 'bgcolor',
- 'border',
- 'cellpadding',
- 'cellspacing',
- 'frame',
- 'rules',
- 'sortable',
- 'summary',
- 'width'
- ].forEach(function(k) {
- if (_t[k]) {
- cfg[k] = _t[k];
+ return item;
+ },
+
+ getActive : function()
+ {
+ var active = false;
+
+ Roo.each(this.barItems, function(v){
+
+ if (!v.isActive()) {
+ return;
}
+
+ active = v;
+ return false;
+
});
+ return active;
+ },
+
+ setActiveItem : function(item)
+ {
+ var prev = false;
- if (this.layout) {
- cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
- }
-
- if(this.store || this.cm){
- if(this.headerShow){
- cfg.cn.push(this.renderHeader());
+ Roo.each(this.barItems, function(v){
+ if (v.rid == item.rid) {
+ return ;
}
- cfg.cn.push(this.renderBody());
-
- if(this.footerShow){
- cfg.cn.push(this.renderFooter());
+ if (v.isActive()) {
+ v.setActive(false);
+ prev = v;
}
- // where does this come from?
- //cfg.cls+= ' TableGrid';
- }
+ });
+
+ item.setActive(true);
- return { cn : [ cfg ] };
+ this.fireEvent('changed', this, item, prev);
},
- initEvents : function()
- {
- if(!this.store || !this.cm){
- return;
- }
- if (this.selModel) {
- this.selModel.initEvents();
- }
-
-
- //Roo.log('initEvents with ds!!!!');
-
- this.mainBody = this.el.select('tbody', true).first();
- this.mainHead = this.el.select('thead', true).first();
- this.mainFoot = this.el.select('tfoot', true).first();
-
-
-
- var _this = this;
+ getBarItem: function(rid)
+ {
+ var ret = false;
- Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
- e.on('click', _this.sort, _this);
+ Roo.each(this.barItems, function(e) {
+ if (e.rid != rid) {
+ return;
+ }
+
+ ret = e;
+ return false;
});
- this.mainBody.on("click", this.onClick, this);
- this.mainBody.on("dblclick", this.onDblClick, this);
-
- // why is this done????? = it breaks dialogs??
- //this.parent().el.setStyle('position', 'relative');
-
+ return ret;
+ },
+
+ indexOfItem : function(item)
+ {
+ var index = false;
- if (this.footer) {
- this.footer.parentId = this.id;
- this.footer.onRender(this.el.select('tfoot tr td').first(), null);
+ Roo.each(this.barItems, function(v, i){
- if(this.lazyLoad){
- this.el.select('tfoot tr td').first().addClass('hide');
+ if (v.rid != item.rid) {
+ return;
}
- }
-
- if(this.loadMask) {
- this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
- }
-
- this.store.on('load', this.onLoad, this);
- this.store.on('beforeload', this.onBeforeLoad, this);
- this.store.on('update', this.onUpdate, this);
- this.store.on('add', this.onAdd, this);
- this.store.on("clear", this.clear, this);
-
- this.el.on("contextmenu", this.onContextMenu, this);
-
- this.mainBody.on('scroll', this.onBodyScroll, this);
-
- this.cm.on("headerchange", this.onHeaderChange, this);
-
- this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
+
+ index = i;
+ return false
+ });
+ return index;
},
- onContextMenu : function(e, t)
+ setActiveNext : function()
{
- this.processEvent("contextmenu", e);
+ var i = this.indexOfItem(this.getActive());
+
+ if (i > this.barItems.length) {
+ return;
+ }
+
+ this.setActiveItem(this.barItems[i+1]);
},
- processEvent : function(name, e)
+ setActivePrev : function()
{
- if (name != 'touchstart' ) {
- this.fireEvent(name, e);
- }
-
- var t = e.getTarget();
-
- var cell = Roo.get(t);
+ var i = this.indexOfItem(this.getActive());
- if(!cell){
+ if (i < 1) {
return;
}
- if(cell.findParent('tfoot', false, true)){
+ this.setActiveItem(this.barItems[i-1]);
+ },
+
+ format : function()
+ {
+ if(!this.barItems.length){
return;
}
+
+ var width = 100 / this.barItems.length;
- if(cell.findParent('thead', false, true)){
-
- if(e.getTarget().nodeName.toLowerCase() != 'th'){
- cell = Roo.get(t).findParent('th', false, true);
- if (!cell) {
- Roo.log("failed to find th in thead?");
- Roo.log(e.getTarget());
- return;
- }
- }
-
- var cellIndex = cell.dom.cellIndex;
-
- var ename = name == 'touchstart' ? 'click' : name;
- this.fireEvent("header" + ename, this, cellIndex, e);
-
- return;
- }
+ Roo.each(this.barItems, function(i){
+ i.el.setStyle('width', width + '%');
+ i.topEl.el.setStyle('width', width + '%');
+ i.bottomEl.el.setStyle('width', width + '%');
+ }, this);
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = Roo.get(t).findParent('td', false, true);
- if (!cell) {
- Roo.log("failed to find th in tbody?");
- Roo.log(e.getTarget());
- return;
- }
- }
+ }
+
+});
+/*
+ * - LGPL
+ *
+ * Nav Progress Item
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.nav.ProgressBarItem
+ * @extends Roo.bootstrap.Component
+ * Bootstrap NavProgressBarItem class
+ * @cfg {String} rid the reference id
+ * @cfg {Boolean} active (true|false) Is item active default false
+ * @cfg {Boolean} disabled (true|false) Is item active default false
+ * @cfg {String} html
+ * @cfg {String} position (top|bottom) text position default bottom
+ * @cfg {String} icon show icon instead of number
+ *
+ * @constructor
+ * Create a new NavProgressBarItem
+ * @param {Object} config The config object
+ */
+Roo.bootstrap.nav.ProgressBarItem = function(config){
+ Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
+ this.addEvents({
+ // raw events
+ /**
+ * @event click
+ * The raw click event for the entire grid.
+ * @param {Roo.bootstrap.nav.ProgressBarItem} this
+ * @param {Roo.EventObject} e
+ */
+ "click" : true
+ });
+
+};
+
+Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
+
+ rid : '',
+ active : false,
+ disabled : false,
+ html : '',
+ position : 'bottom',
+ icon : false,
+
+ getAutoCreate : function()
+ {
+ var iconCls = 'roo-navigation-bar-item-icon';
- var row = cell.findParent('tr', false, true);
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = row.dom.rowIndex - 1;
+ iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
- if(row !== false){
-
- this.fireEvent("row" + name, this, rowIndex, e);
-
- if(cell !== false){
-
- this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
- }
+ var cfg = {
+ tag: 'li',
+ cls: 'roo-navigation-bar-item',
+ cn : [
+ {
+ tag : 'i',
+ cls : iconCls
+ }
+ ]
+ };
+
+ if(this.active){
+ cfg.cls += ' active';
+ }
+ if(this.disabled){
+ cfg.cls += ' disabled';
}
+ return cfg;
},
- onMouseover : function(e, el)
+ disable : function()
{
- var cell = Roo.get(el);
-
- if(!cell){
- return;
- }
-
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = cell.findParent('td', false, true);
- }
-
- var row = cell.findParent('tr', false, true);
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = row.dom.rowIndex - 1; // start from 0
-
- this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
-
+ this.setDisabled(true);
},
- onMouseout : function(e, el)
+ enable : function()
{
- var cell = Roo.get(el);
-
- if(!cell){
- return;
- }
-
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = cell.findParent('td', false, true);
- }
-
- var row = cell.findParent('tr', false, true);
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = row.dom.rowIndex - 1; // start from 0
-
- this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
-
+ this.setDisabled(false);
},
- onClick : function(e, el)
+ initEvents: function()
{
- var cell = Roo.get(el);
-
- if(!cell || (!this.cellSelection && !this.rowSelection)){
- return;
- }
+ this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = cell.findParent('td', false, true);
- }
+ this.iconEl.on('click', this.onClick, this);
+ },
+
+ onClick : function(e)
+ {
+ e.preventDefault();
- if(!cell || typeof(cell) == 'undefined'){
+ if(this.disabled){
return;
}
- var row = cell.findParent('tr', false, true);
-
- if(!row || typeof(row) == 'undefined'){
+ if(this.fireEvent('click', this, e) === false){
return;
- }
-
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = this.getRowIndex(row);
-
- // why??? - should these not be based on SelectionModel?
- if(this.cellSelection){
- this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
- }
-
- if(this.rowSelection){
- this.fireEvent('rowclick', this, row, rowIndex, e);
- }
-
+ };
+ this.parent().setActiveItem(this);
},
-
- onDblClick : function(e,el)
+
+ isActive: function ()
{
- var cell = Roo.get(el);
-
- if(!cell || (!this.cellSelection && !this.rowSelection)){
- return;
- }
-
- if(e.getTarget().nodeName.toLowerCase() != 'td'){
- cell = cell.findParent('td', false, true);
- }
-
- if(!cell || typeof(cell) == 'undefined'){
+ return this.active;
+ },
+
+ setActive : function(state)
+ {
+ if(this.active == state){
return;
}
- var row = cell.findParent('tr', false, true);
+ this.active = state;
- if(!row || typeof(row) == 'undefined'){
+ if (state) {
+ this.el.addClass('active');
return;
}
- var cellIndex = cell.dom.cellIndex;
- var rowIndex = this.getRowIndex(row);
-
- if(this.cellSelection){
- this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
- }
+ this.el.removeClass('active');
- if(this.rowSelection){
- this.fireEvent('rowdblclick', this, row, rowIndex, e);
- }
+ return;
},
- sort : function(e,el)
+ setDisabled : function(state)
{
- var col = Roo.get(el);
-
- if(!col.hasClass('sortable')){
+ if(this.disabled == state){
return;
}
- var sort = col.attr('sort');
- var dir = 'ASC';
+ this.disabled = state;
- if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
- dir = 'DESC';
+ if (state) {
+ this.el.addClass('disabled');
+ return;
}
- this.store.sortInfo = {field : sort, direction : dir};
-
- if (this.footer) {
- Roo.log("calling footer first");
- this.footer.onClick('first');
- } else {
-
- this.store.load({ params : { start : 0 } });
- }
+ this.el.removeClass('disabled');
},
- renderHeader : function()
+ tooltipEl : function()
{
- var header = {
- tag: 'thead',
- cn : []
- };
-
- var cm = this.cm;
- this.totalWidth = 0;
-
- for(var i = 0, len = cm.getColumnCount(); i < len; i++){
-
- var config = cm.config[i];
-
- var c = {
- tag: 'th',
- cls : 'x-hcol-' + i,
- style : '',
- html: cm.getColumnHeader(i)
- };
-
- var hh = '';
-
- if(typeof(config.sortable) != 'undefined' && config.sortable){
- c.cls = 'sortable';
- c.html = '<i class="glyphicon"></i>' + c.html;
- }
-
- if(typeof(config.lgHeader) != 'undefined'){
- hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
- }
-
- if(typeof(config.mdHeader) != 'undefined'){
- hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
- }
-
- if(typeof(config.smHeader) != 'undefined'){
- hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
- }
-
- if(typeof(config.xsHeader) != 'undefined'){
- hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
- }
-
- if(hh.length){
- c.html = hh;
- }
-
- if(typeof(config.tooltip) != 'undefined'){
- c.tooltip = config.tooltip;
- }
-
- if(typeof(config.colspan) != 'undefined'){
- c.colspan = config.colspan;
- }
-
- if(typeof(config.hidden) != 'undefined' && config.hidden){
- c.style += ' display:none;';
- }
-
- if(typeof(config.dataIndex) != 'undefined'){
- c.sort = config.dataIndex;
- }
-
-
-
- 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;';
- this.totalWidth += config.width;
- } else {
- this.totalWidth += 100; // assume minimum of 100 per column?
- }
-
- if(typeof(config.cls) != 'undefined'){
- c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
- }
-
- ['xs','sm','md','lg'].map(function(size){
-
- if(typeof(config[size]) == 'undefined'){
- return;
- }
-
- if (!config[size]) { // 0 = hidden
- c.cls += ' hidden-' + size;
- return;
- }
-
- c.cls += ' col-' + size + '-' + config[size];
+ return this.el.select('.roo-navigation-bar-item-icon', true).first();;
+ }
+});
+
- });
-
- header.cn.push(c)
- }
-
- return header;
- },
+ /*
+ * - LGPL
+ *
+ * Breadcrumb Nav
+ *
+ */
+Roo.namespace('Roo.bootstrap.breadcrumb');
+
+
+/**
+ * @class Roo.bootstrap.breadcrumb.Nav
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Breadcrumb Nav Class
+ *
+ * @children Roo.bootstrap.breadcrumb.Item
+ *
+ * @constructor
+ * Create a new breadcrumb.Nav
+ * @param {Object} config The config object
+ */
+
+
+Roo.bootstrap.breadcrumb.Nav = function(config){
+ Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
- renderBody : function()
+
+};
+
+Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
+
+ getAutoCreate : function()
{
- var body = {
- tag: 'tbody',
+
+ var cfg = {
+ tag: 'nav',
cn : [
{
- tag: 'tr',
- cn : [
- {
- tag : 'td',
- colspan : this.cm.getColumnCount()
- }
- ]
+ tag : 'ol',
+ cls : 'breadcrumb'
}
]
+
};
-
- return body;
+
+ return cfg;
},
- renderFooter : function()
+ initEvents: function()
{
- var footer = {
- tag: 'tfoot',
- cn : [
- {
- tag: 'tr',
- cn : [
- {
- tag : 'td',
- colspan : this.cm.getColumnCount()
- }
- ]
- }
- ]
- };
-
- return footer;
+ this.olEl = this.el.select('ol',true).first();
},
+ getChildContainer : function()
+ {
+ return this.olEl;
+ }
+
+});
+
+ /*
+ * - LGPL
+ *
+ * Breadcrumb Item
+ *
+ */
+
+
+/**
+ * @class Roo.bootstrap.breadcrumb.Nav
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
+ * @parent Roo.bootstrap.breadcrumb.Nav
+ * Bootstrap Breadcrumb Nav Class
+ *
+ *
+ * @cfg {String} html the content of the link.
+ * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
+ * @cfg {Boolean} active is it active
+
+ *
+ * @constructor
+ * Create a new breadcrumb.Nav
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.breadcrumb.Item = function(config){
+ Roo.bootstrap.breadcrumb.Item.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.breadcrumb.Item, Roo.bootstrap.Component, {
+ href: false,
+ html : '',
- onLoad : function()
+ getAutoCreate : function()
{
-// Roo.log('ds onload');
- this.clear();
-
- var _this = this;
- var cm = this.cm;
- var ds = this.store;
-
- Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
- e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
- if (_this.store.sortInfo) {
-
- if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
- e.select('i', true).addClass(['glyphicon-arrow-up']);
- }
-
- if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
- e.select('i', true).addClass(['glyphicon-arrow-down']);
- }
- }
- });
-
- var tbody = this.mainBody;
-
- if(ds.getCount() > 0){
- ds.data.each(function(d,rowIndex){
- var row = this.renderRow(cm, ds, rowIndex);
-
- tbody.createChild(row);
-
- var _this = this;
-
- if(row.cellObjects.length){
- Roo.each(row.cellObjects, function(r){
- _this.renderCellObject(r);
- })
- }
-
- }, this);
- }
-
- var tfoot = this.el.select('tfoot', true).first();
-
- if(this.footerShow && this.auto_hide_footer && this.mainFoot){
-
- this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
-
- var total = this.ds.getTotalCount();
-
- if(this.footer.pageSize < total){
- this.mainFoot.show();
- }
+
+ var cfg = {
+ tag: 'li',
+ cls : 'breadcrumb-item' + (this.active ? ' active' : '')
+ };
+ if (this.href !== false) {
+ cfg.cn = [{
+ tag : 'a',
+ href : this.href,
+ html : this.html
+ }];
+ } else {
+ cfg.html = this.html;
}
- Roo.each(this.el.select('tbody td', true).elements, function(e){
- e.on('mouseover', _this.onMouseover, _this);
- });
-
- Roo.each(this.el.select('tbody td', true).elements, function(e){
- e.on('mouseout', _this.onMouseout, _this);
- });
- this.fireEvent('rowsrendered', this);
-
- this.autoSize();
+ return cfg;
},
-
- onUpdate : function(ds,record)
+ initEvents: function()
{
- this.refreshRow(record);
- this.autoSize();
- },
-
- onRemove : function(ds, record, index, isUpdate){
- if(isUpdate !== true){
- this.fireEvent("beforerowremoved", this, index, record);
- }
- var bt = this.mainBody.dom;
-
- var rows = this.el.select('tbody > tr', true).elements;
-
- if(typeof(rows[index]) != 'undefined'){
- bt.removeChild(rows[index].dom);
+ if (this.href) {
+ this.el.select('a', true).first().on('click',this.onClick, this)
}
-// if(bt.rows[index]){
-// bt.removeChild(bt.rows[index]);
-// }
-
- if(isUpdate !== true){
- //this.stripeRows(index);
- //this.syncRowHeights(index, index);
- //this.layout();
- this.fireEvent("rowremoved", this, index, record);
- }
},
-
- onAdd : function(ds, records, rowIndex)
+ onClick : function(e)
{
- //Roo.log('on Add called');
- // - note this does not handle multiple adding very well..
- var bt = this.mainBody.dom;
- for (var i =0 ; i < records.length;i++) {
- //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
- //Roo.log(records[i]);
- //Roo.log(this.store.getAt(rowIndex+i));
- this.insertRow(this.store, rowIndex + i, false);
- return;
- }
-
- },
-
-
- refreshRow : function(record){
- var ds = this.store, index;
- if(typeof record == 'number'){
- index = record;
- record = ds.getAt(index);
- }else{
- index = ds.indexOf(record);
- }
- this.insertRow(ds, index, true);
- this.autoSize();
- this.onRemove(ds, record, index+1, true);
- this.autoSize();
- //this.syncRowHeights(index, index);
- //this.layout();
- this.fireEvent("rowupdated", this, index, record);
- },
+ e.preventDefault();
+ this.fireEvent('click',this, e);
+ }
- insertRow : function(dm, rowIndex, isUpdate){
-
- if(!isUpdate){
- this.fireEvent("beforerowsinserted", this, rowIndex);
- }
- //var s = this.getScrollState();
- var row = this.renderRow(this.cm, this.store, rowIndex);
- // insert before rowIndex..
- var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
-
- var _this = this;
-
- if(row.cellObjects.length){
- Roo.each(row.cellObjects, function(r){
- _this.renderCellObject(r);
- })
- }
-
- if(!isUpdate){
- this.fireEvent("rowsinserted", this, rowIndex);
- //this.syncRowHeights(firstRow, lastRow);
- //this.stripeRows(firstRow);
- //this.layout();
- }
-
- },
+});
+
+ /*
+ * - LGPL
+ *
+ * row
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.Row
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
+ * Bootstrap Row class (contains columns...)
+ *
+ * @constructor
+ * Create a new Row
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Row = function(config){
+ Roo.bootstrap.Row.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
+ getAutoCreate : function(){
+ return {
+ cls: 'row clearfix'
+ };
+ }
- getRowDom : function(rowIndex)
- {
- var rows = this.el.select('tbody > tr', true).elements;
-
- return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
-
- },
- // returns the object tree for a tr..
-
- renderRow : function(cm, ds, rowIndex)
- {
- var d = ds.getAt(rowIndex);
-
- var row = {
- tag : 'tr',
- cls : 'x-row-' + rowIndex,
- cn : []
- };
-
- var cellObjects = [];
-
- for(var i = 0, len = cm.getColumnCount(); i < len; i++){
- var config = cm.config[i];
-
- var renderer = cm.getRenderer(i);
- var value = '';
- var id = false;
-
- if(typeof(renderer) !== 'undefined'){
- value = renderer(d.data[cm.getDataIndex(i)], false, d);
- }
- // if object are returned, then they are expected to be Roo.bootstrap.Component instances
- // and are rendered into the cells after the row is rendered - using the id for the element.
-
- if(typeof(value) === 'object'){
- id = Roo.id();
- cellObjects.push({
- container : id,
- cfg : value
- })
- }
-
- var rowcfg = {
- record: d,
- rowIndex : rowIndex,
- colIndex : i,
- rowClass : ''
- };
+});
- this.fireEvent('rowclass', this, rowcfg);
-
- var td = {
- tag: 'td',
- cls : rowcfg.rowClass + ' x-col-' + i,
- style: '',
- html: (typeof(value) === 'object') ? '' : value
- };
-
- if (id) {
- td.id = id;
- }
-
- if(typeof(config.colspan) != 'undefined'){
- td.colspan = config.colspan;
- }
-
- if(typeof(config.hidden) != 'undefined' && config.hidden){
- td.style += ' display:none;';
- }
-
- if(typeof(config.align) != 'undefined' && config.align.length){
- td.style += ' text-align:' + config.align + ';';
- }
- if(typeof(config.valign) != 'undefined' && config.valign.length){
- td.style += ' vertical-align:' + config.valign + ';';
- }
-
- if(typeof(config.width) != 'undefined'){
- td.style += ' width:' + config.width + 'px;';
- }
-
- if(typeof(config.cursor) != 'undefined'){
- td.style += ' cursor:' + config.cursor + ';';
- }
-
- if(typeof(config.cls) != 'undefined'){
- td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
- }
-
- ['xs','sm','md','lg'].map(function(size){
-
- if(typeof(config[size]) == 'undefined'){
- return;
- }
-
- if (!config[size]) { // 0 = hidden
- td.cls += ' hidden-' + size;
- return;
- }
-
- td.cls += ' col-' + size + '-' + config[size];
+
- });
-
- row.cn.push(td);
-
- }
-
- row.cellObjects = cellObjects;
-
- return row;
-
- },
-
+ /*
+ * - LGPL
+ *
+ * pagination
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.Pagination
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Pagination
+ * Bootstrap Pagination class
+ *
+ * @cfg {String} size (xs|sm|md|lg|xl)
+ * @cfg {Boolean} inverse
+ *
+ * @constructor
+ * Create a new Pagination
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Pagination = function(config){
+ Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
+ cls: false,
+ size: false,
+ inverse: false,
- onBeforeLoad : function()
- {
-
- },
- /**
- * Remove all rows
- */
- clear : function()
- {
- this.el.select('tbody', true).first().dom.innerHTML = '';
- },
- /**
- * Show or hide a row.
- * @param {Number} rowIndex to show or hide
- * @param {Boolean} state hide
- */
- setRowVisibility : function(rowIndex, state)
- {
- var bt = this.mainBody.dom;
-
- var rows = this.el.select('tbody > tr', true).elements;
-
- if(typeof(rows[rowIndex]) == 'undefined'){
- return;
+ getAutoCreate : function(){
+ var cfg = {
+ tag: 'ul',
+ cls: 'pagination'
+ };
+ if (this.inverse) {
+ cfg.cls += ' inverse';
}
- rows[rowIndex].dom.style.display = state ? '' : 'none';
- },
-
-
- getSelectionModel : function(){
- if(!this.selModel){
- this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
+ if (this.html) {
+ cfg.html=this.html;
}
- return this.selModel;
- },
- /*
- * Render the Roo.bootstrap object from renderder
- */
- renderCellObject : function(r)
- {
- var _this = this;
-
- r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
-
- var t = r.cfg.render(r.container);
-
- if(r.cfg.cn){
- Roo.each(r.cfg.cn, function(c){
- var child = {
- container: t.getChildContainer(),
- cfg: c
- };
- _this.renderCellObject(child);
- })
+ if (this.cls) {
+ cfg.cls += " " + this.cls;
}
- },
+ return cfg;
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * Pagination item
+ *
+ */
+
+
+/**
+ * @class Roo.bootstrap.PaginationItem
+ * @extends Roo.bootstrap.Component
+ * Bootstrap PaginationItem class
+ * @cfg {String} html text
+ * @cfg {String} href the link
+ * @cfg {Boolean} preventDefault (true | false) default true
+ * @cfg {Boolean} active (true | false) default false
+ * @cfg {Boolean} disabled default false
+ *
+ *
+ * @constructor
+ * Create a new PaginationItem
+ * @param {Object} config The config object
+ */
+
+
+Roo.bootstrap.PaginationItem = function(config){
+ Roo.bootstrap.PaginationItem.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
+ });
+};
+
+Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
- getRowIndex : function(row)
- {
- var rowIndex = -1;
-
- Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
- if(el != row){
- return;
- }
-
- rowIndex = index;
- });
-
- return rowIndex;
- },
- /**
- * Returns the grid's underlying element = used by panel.Grid
- * @return {Element} The element
- */
- getGridEl : function(){
- return this.el;
- },
- /**
- * Forces a resize - used by panel.Grid
- * @return {Element} The element
- */
- autoSize : function()
- {
- //var ctr = Roo.get(this.container.dom.parentElement);
- var ctr = Roo.get(this.el.dom);
-
- var thd = this.getGridEl().select('thead',true).first();
- var tbd = this.getGridEl().select('tbody', true).first();
- var tfd = this.getGridEl().select('tfoot', true).first();
-
- var cw = ctr.getWidth();
+ href : false,
+ html : false,
+ preventDefault: true,
+ active : false,
+ cls : false,
+ disabled: false,
+
+ getAutoCreate : function(){
+ var cfg= {
+ tag: 'li',
+ cn: [
+ {
+ tag : 'a',
+ href : this.href ? this.href : '#',
+ html : this.html ? this.html : ''
+ }
+ ]
+ };
- if (tbd) {
-
- tbd.setSize(ctr.getWidth(),
- ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
- );
- var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
- cw -= barsize;
+ if(this.cls){
+ cfg.cls = this.cls;
}
- cw = Math.max(cw, this.totalWidth);
- this.getGridEl().select('tr',true).setWidth(cw);
- // resize 'expandable coloumn?
-
- return; // we doe not have a view in this design..
- },
- onBodyScroll: function()
- {
- //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
- if(this.mainHead){
- this.mainHead.setStyle({
- 'position' : 'relative',
- 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
- });
+ if(this.disabled){
+ cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
}
- if(this.lazyLoad){
-
- var scrollHeight = this.mainBody.dom.scrollHeight;
-
- var scrollTop = Math.ceil(this.mainBody.getScroll().top);
-
- var height = this.mainBody.getHeight();
-
- if(scrollHeight - height == scrollTop) {
-
- var total = this.ds.getTotalCount();
-
- if(this.footer.cursor + this.footer.pageSize < total){
-
- this.footer.ds.load({
- params : {
- start : this.footer.cursor + this.footer.pageSize,
- limit : this.footer.pageSize
- },
- add : true
- });
- }
- }
-
+ if(this.active){
+ cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
}
+
+ return cfg;
},
- onHeaderChange : function()
- {
- var header = this.renderHeader();
- var table = this.el.select('table', true).first();
+ initEvents: function() {
+
+ this.el.on('click', this.onClick, this);
- this.mainHead.remove();
- this.mainHead = table.createChild(header, this.mainBody, false);
},
-
- onHiddenChange : function(colModel, colIndex, hidden)
+ onClick : function(e)
{
- var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
- var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
-
- this.CSS.updateRule(thSelector, "display", "");
- this.CSS.updateRule(tdSelector, "display", "");
-
- if(hidden){
- this.CSS.updateRule(thSelector, "display", "none");
- this.CSS.updateRule(tdSelector, "display", "none");
+ Roo.log('PaginationItem on click ');
+ if(this.preventDefault){
+ e.preventDefault();
}
- this.onHeaderChange();
- this.onLoad();
- },
-
- setColumnWidth: function(col_index, width)
- {
- // width = "md-2 xs-2..."
- if(!this.colModel.config[col_index]) {
+ if(this.disabled){
return;
}
- var w = width.split(" ");
-
- var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
-
- var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
-
-
- for(var j = 0; j < w.length; j++) {
-
- if(!w[j]) {
- continue;
- }
-
- var size_cls = w[j].split("-");
-
- if(!Number.isInteger(size_cls[1] * 1)) {
- continue;
- }
-
- if(!this.colModel.config[col_index][size_cls[0]]) {
- continue;
- }
-
- if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
- continue;
- }
-
- h_row[0].classList.replace(
- "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
- "col-"+size_cls[0]+"-"+size_cls[1]
- );
-
- for(var i = 0; i < rows.length; i++) {
-
- var size_cls = w[j].split("-");
-
- if(!Number.isInteger(size_cls[1] * 1)) {
- continue;
- }
-
- if(!this.colModel.config[col_index][size_cls[0]]) {
- continue;
- }
-
- if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
- continue;
- }
-
- rows[i].classList.replace(
- "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
- "col-"+size_cls[0]+"-"+size_cls[1]
- );
- }
-
- this.colModel.config[col_index][size_cls[0]] = size_cls[1];
- }
+ this.fireEvent('click', this, e);
}
+
});
/*
* - LGPL
*
- * table cell
+ * slider
*
*/
+
/**
- * @class Roo.bootstrap.TableCell
+ * @class Roo.bootstrap.Slider
* @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
- *
+ * Bootstrap Slider class
+ *
* @constructor
- * Create a new TableCell
+ * Create a new Slider
* @param {Object} config The config object
*/
-Roo.bootstrap.TableCell = function(config){
- Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
+Roo.bootstrap.Slider = function(config){
+ Roo.bootstrap.Slider.superclass.constructor.call(this, config);
};
-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.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
getAutoCreate : function(){
- var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag: 'td'
- };
-
- if(this.tag){
- cfg.tag = this.tag;
- }
- if (this.html) {
- cfg.html=this.html
- }
- if (this.cls) {
- cfg.cls=this.cls
- }
- if (this.abbr) {
- cfg.abbr=this.abbr
- }
- if (this.align) {
- cfg.align=this.align
- }
- if (this.axis) {
- cfg.axis=this.axis
- }
- if (this.bgcolor) {
- cfg.bgcolor=this.bgcolor
- }
- if (this.charoff) {
- cfg.charoff=this.charoff
- }
- if (this.colspan) {
- cfg.colspan=this.colspan
- }
- if (this.headers) {
- cfg.headers=this.headers
- }
- if (this.height) {
- cfg.height=this.height
- }
- if (this.nowrap) {
- cfg.nowrap=this.nowrap
- }
- if (this.rowspan) {
- cfg.rowspan=this.rowspan
- }
- if (this.scope) {
- cfg.scope=this.scope
- }
- if (this.valign) {
- cfg.valign=this.valign
- }
- if (this.width) {
- cfg.width=this.width
- }
+ var cfg = {
+ tag: 'div',
+ cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
+ cn: [
+ {
+ tag: 'a',
+ cls: 'ui-slider-handle ui-state-default ui-corner-all'
+ }
+ ]
+ };
-
return cfg;
}
});
-
-
/*
- * - LGPL
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
*
- * table row
- *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
*/
-
-/**
- * @class Roo.bootstrap.TableRow
- * @extends Roo.bootstrap.Component
- * Bootstrap TableRow class
- * @cfg {String} cls row class
- * @cfg {String} align Aligns the content in a table row
- * @cfg {String} bgcolor Specifies a background color for a table row
- * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
- * @cfg {String} valign Vertical aligns the content in a table row
- *
+ /**
+ * @extends Roo.dd.DDProxy
+ * @class Roo.grid.SplitDragZone
+ * Support for Column Header resizing
* @constructor
- * Create a new TableRow
- * @param {Object} config The config object
+ * @param {Object} config
*/
-
-Roo.bootstrap.TableRow = function(config){
- Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
+// private
+// This is a support class used internally by the Grid components
+Roo.grid.SplitDragZone = function(grid, hd, hd2){
+ this.grid = grid;
+ this.view = grid.getView();
+ this.proxy = this.view.resizeProxy;
+ Roo.grid.SplitDragZone.superclass.constructor.call(
+ this,
+ hd, // ID
+ "gridSplitters" + this.grid.getGridEl().id, // SGROUP
+ { // CONFIG
+ dragElId : Roo.id(this.proxy.dom),
+ resizeFrame:false
+ }
+ );
+
+ this.setHandleElId(Roo.id(hd));
+ if (hd2 !== false) {
+ this.setOuterHandleElId(Roo.id(hd2));
+ }
+
+ this.scroll = false;
};
+Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
+ fly: Roo.Element.fly,
-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;
+ b4StartDrag : function(x, y){
+ this.view.headersDisabled = true;
+ var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
+ this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
+ );
+ this.proxy.setHeight(h);
+
+ // for old system colWidth really stored the actual width?
+ // in bootstrap we tried using xs/ms/etc.. to do % sizing?
+ // which in reality did not work.. - it worked only for fixed sizes
+ // for resizable we need to use actual sizes.
+ var w = this.cm.getColumnWidth(this.cellIndex);
+ if (!this.view.mainWrap) {
+ // bootstrap.
+ w = this.view.getHeaderIndex(this.cellIndex).getWidth();
}
- if(this.charoff){
- cfg.charoff = this.charoff;
+
+
+
+ // this was w-this.grid.minColumnWidth;
+ // doesnt really make sense? - w = thie curren width or the rendered one?
+ var minw = Math.max(w-this.grid.minColumnWidth, 0);
+ this.resetConstraints();
+ this.setXConstraint(minw, 1000);
+ this.setYConstraint(0, 0);
+ this.minX = x - minw;
+ this.maxX = x + 1000;
+ this.startPos = x;
+ if (!this.view.mainWrap) { // this is Bootstrap code..
+ this.getDragEl().style.display='block';
}
- if(this.valign){
- cfg.valign = this.valign;
+
+ Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
+ },
+
+
+ handleMouseDown : function(e){
+ ev = Roo.EventObject.setEvent(e);
+ var t = this.fly(ev.getTarget());
+ if(t.hasClass("x-grid-split")){
+ this.cellIndex = this.view.getCellIndex(t.dom);
+ this.split = t.dom;
+ this.cm = this.grid.colModel;
+ if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
+ Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
+ }
}
-
- return cfg;
- }
-
-});
+ },
-
+ endDrag : function(e){
+ this.view.headersDisabled = false;
+ var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
+ var diff = endX - this.startPos;
+ //
+ var w = this.cm.getColumnWidth(this.cellIndex);
+ if (!this.view.mainWrap) {
+ w = 0;
+ }
+ this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
+ },
- /*
- * - LGPL
+ autoOffset : function(){
+ this.setDelta(0,0);
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
*
- * table body
- *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
*/
/**
- * @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
- *
+ * @class Roo.grid.AbstractSelectionModel
+ * @extends Roo.util.Observable
+ * @abstract
+ * 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
- * Create a new TableBody
- * @param {Object} config The config object
*/
-
-Roo.bootstrap.TableBody = function(config){
- Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
+Roo.grid.AbstractSelectionModel = function(){
+ this.locked = false;
+ Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
};
-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);
-// }
-//
-//
-});
+Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
+ /** @ignore Called by the grid automatically. Do not call directly. */
+ init : function(grid){
+ this.grid = grid;
+ this.initEvents();
+ },
-
+ /**
+ * Locks the selections.
+ */
+ lock : function(){
+ this.locked = true;
+ },
- /*
+ /**
+ * Unlocks the selections.
+ */
+ unlock : function(){
+ this.locked = false;
+ },
+
+ /**
+ * Returns true if the selections are locked.
+ * @return {Boolean}
+ */
+ isLocked : function(){
+ return this.locked;
+ }
+});/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
* 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
+/**
+ * @extends Roo.grid.AbstractSelectionModel
+ * @class Roo.grid.RowSelectionModel
+ * The default SelectionModel used by {@link Roo.grid.Grid}.
+ * It supports multiple selections and keyboard selection/navigation.
* @constructor
- * @param {Roo.form.BasicForm} el The form element or its id
- * @param {Object} config Configuration options
+ * @param {Object} config
*/
+Roo.grid.RowSelectionModel = function(config){
+ Roo.apply(this, config);
+ this.selections = new Roo.util.MixedCollection(false, function(o){
+ return o.id;
+ });
-
-
-// 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';
+ this.last = false;
+ this.lastActive = false;
-Roo.form.Action.prototype = {
- type : 'default',
- failureType : undefined,
- response : undefined,
- result : undefined,
+ this.addEvents({
+ /**
+ * @event selectionchange
+ * Fires when the selection changes
+ * @param {SelectionModel} this
+ */
+ "selectionchange" : true,
+ /**
+ * @event afterselectionchange
+ * Fires after the selection changes (eg. by key press or clicking)
+ * @param {SelectionModel} this
+ */
+ "afterselectionchange" : true,
+ /**
+ * @event beforerowselect
+ * Fires when a row is selected being selected, return false to cancel.
+ * @param {SelectionModel} this
+ * @param {Number} rowIndex The selected index
+ * @param {Boolean} keepExisting False if other selections will be cleared
+ */
+ "beforerowselect" : true,
+ /**
+ * @event rowselect
+ * Fires when a row is selected.
+ * @param {SelectionModel} this
+ * @param {Number} rowIndex The selected index
+ * @param {Roo.data.Record} r The record
+ */
+ "rowselect" : true,
+ /**
+ * @event rowdeselect
+ * Fires when a row is deselected.
+ * @param {SelectionModel} this
+ * @param {Number} rowIndex The selected index
+ */
+ "rowdeselect" : true
+ });
+ Roo.grid.RowSelectionModel.superclass.constructor.call(this);
+ this.locked = false;
+};
- // interface method
- run : function(options){
+Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
+ /**
+ * @cfg {Boolean} singleSelect
+ * True to allow selection of only one row at a time (defaults to false)
+ */
+ singleSelect : false,
- },
+ // private
+ initEvents : function(){
- // interface method
- success : function(response){
+ if(!this.grid.enableDragDrop && !this.grid.enableDrag){
+ this.grid.on("mousedown", this.handleMouseDown, this);
+ }else{ // allow click to work like normal
+ this.grid.on("rowclick", this.handleDragableRowClick, this);
+ }
+ // bootstrap does not have a view..
+ var view = this.grid.view ? this.grid.view : this.grid;
+ this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
+ "up" : function(e){
+ if(!e.shiftKey){
+ this.selectPrevious(e.shiftKey);
+ }else if(this.last !== false && this.lastActive !== false){
+ var last = this.last;
+ this.selectRange(this.last, this.lastActive-1);
+ view.focusRow(this.lastActive);
+ if(last !== false){
+ this.last = last;
+ }
+ }else{
+ this.selectFirstRow();
+ }
+ this.fireEvent("afterselectionchange", this);
+ },
+ "down" : function(e){
+ if(!e.shiftKey){
+ this.selectNext(e.shiftKey);
+ }else if(this.last !== false && this.lastActive !== false){
+ var last = this.last;
+ this.selectRange(this.last, this.lastActive+1);
+ view.focusRow(this.lastActive);
+ if(last !== false){
+ this.last = last;
+ }
+ }else{
+ this.selectFirstRow();
+ }
+ this.fireEvent("afterselectionchange", this);
+ },
+ scope: this
+ });
+
+ view.on("refresh", this.onRefresh, this);
+ view.on("rowupdated", this.onRowUpdated, this);
+ view.on("rowremoved", this.onRemove, this);
},
- // interface method
- handleResponse : function(response){
-
+ // private
+ onRefresh : function(){
+ var ds = this.grid.ds, i, v = this.grid.view;
+ var s = this.selections;
+ s.each(function(r){
+ if((i = ds.indexOfId(r.id)) != -1){
+ v.onRowSelect(i);
+ s.add(ds.getAt(i)); // updating the selection relate data
+ }else{
+ s.remove(r);
+ }
+ });
},
- // default connection failure
- failure : function(response){
-
- this.response = response;
- this.failureType = Roo.form.Action.CONNECT_FAILURE;
- this.form.afterAction(this, false);
+ // private
+ onRemove : function(v, index, r){
+ this.selections.remove(r);
},
- processResponse : function(response){
- this.response = response;
- if(!response.responseText){
- return true;
+ // private
+ onRowUpdated : function(v, index, r){
+ if(this.isSelected(r)){
+ v.onRowSelect(index);
}
- 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;
- }
+ /**
+ * Select records.
+ * @param {Array} records The records to select
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
+ */
+ selectRecords : function(records, keepExisting){
+ if(!keepExisting){
+ this.clearSelections();
+ }
+ var ds = this.grid.ds;
+ for(var i = 0, len = records.length; i < len; i++){
+ this.selectRow(ds.indexOf(records[i]), true);
}
- return url;
},
- getMethod : function(){
- return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
+ /**
+ * Gets the number of selected rows.
+ * @return {Number}
+ */
+ getCount : function(){
+ return this.selections.length;
},
- 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;
+ /**
+ * Selects the first row in the grid.
+ */
+ selectFirstRow : function(){
+ this.selectRow(0);
},
- createCallback : function(){
- return {
- success: this.success,
- failure: this.failure,
- scope: this,
- timeout: (this.form.timeout*1000),
- upload: this.form.fileUpload ? this.success : undefined
- };
- }
-};
+ /**
+ * Select the last row.
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
+ */
+ selectLastRow : function(keepExisting){
+ this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
+ },
-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;
+ /**
+ * Selects the row immediately following the last selected row.
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
+ */
+ selectNext : function(keepExisting){
+ if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
+ this.selectRow(this.last+1, keepExisting);
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.focusRow(this.last);
}
-
- 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);
+ /**
+ * Selects the row that precedes the last selected row.
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
+ */
+ selectPrevious : function(keepExisting){
+ if(this.last){
+ this.selectRow(this.last-1, keepExisting);
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.focusRow(this.last);
}
},
- 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);
+ /**
+ * Returns the selected records
+ * @return {Array} Array of selected records
+ */
+ getSelections : function(){
+ return [].concat(this.selections.items);
},
-
- 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()
- }));
+ /**
+ * Returns the first selected record.
+ * @return {Record}
+ */
+ getSelected : function(){
+ return this.selections.itemAt(0);
},
- 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);
+
+ /**
+ * Clears all selections.
+ */
+ clearSelections : function(fast){
+ if(this.locked) {
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
- };
+ if(fast !== true){
+ var ds = this.grid.ds;
+ var s = this.selections;
+ s.each(function(r){
+ this.deselectRow(ds.indexOfId(r.id));
+ }, this);
+ s.clear();
+ }else{
+ this.selections.clear();
}
- 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
- * @cfg {Boolean} loadMask load mask when submit (default true)
-
- *
- * @constructor
- * Create a new Form
- * @param {Object} config The config object
- */
-
-
-Roo.bootstrap.Form = function(config){
-
- Roo.bootstrap.Form.superclass.constructor.call(this, config);
-
- Roo.bootstrap.Form.popover.apply();
-
- 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
- });
-};
+ this.last = false;
+ },
-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.
+ * Selects all rows.
*/
+ selectAll : function(){
+ if(this.locked) {
+ return;
+ }
+ this.selections.clear();
+ for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
+ this.selectRow(i, true);
+ }
+ },
/**
- * @cfg {Object} baseParams
- * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
+ * Returns True if there is a selection.
+ * @return {Boolean}
*/
+ hasSelection : function(){
+ return this.selections.length > 0;
+ },
/**
- * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
- */
- timeout: 30,
- /**
- * @cfg {Sting} align (left|right) for navbar forms
+ * Returns True if the specified row is selected.
+ * @param {Number/Record} record The record or index of the record to check
+ * @return {Boolean}
*/
- align : 'left',
-
- // private
- activeAction : null,
+ isSelected : function(index){
+ var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
+ return (r && this.selections.key(r.id) ? true : 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
+ * Returns True if the specified record id is selected.
+ * @param {String} id The id of record to check
+ * @return {Boolean}
*/
- waitMsgTarget : false,
+ isIdSelected : function(id){
+ return (this.selections.key(id) ? true : false);
+ },
- loadMask : true,
+ // private
+ handleMouseDown : function(e, t)
+ {
+ var view = this.grid.view ? this.grid.view : this.grid;
+ var rowIndex;
+ if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
+ return;
+ };
+ if(e.shiftKey && this.last !== false){
+ var last = this.last;
+ this.selectRange(last, rowIndex, e.ctrlKey);
+ this.last = last; // reset the last
+ view.focusRow(rowIndex);
+ }else{
+ var isSelected = this.isSelected(rowIndex);
+ if(e.button !== 0 && isSelected){
+ view.focusRow(rowIndex);
+ }else if(e.ctrlKey && isSelected){
+ this.deselectRow(rowIndex);
+ }else if(!isSelected){
+ this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
+ view.focusRow(rowIndex);
+ }
+ }
+ this.fireEvent("afterselectionchange", this);
+ },
+ // private
+ handleDragableRowClick : function(grid, rowIndex, e)
+ {
+ if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
+ this.selectRow(rowIndex, false);
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.focusRow(rowIndex);
+ this.fireEvent("afterselectionchange", this);
+ }
+ },
/**
- * @cfg {Boolean} errorMask (true|false) default false
+ * Selects multiple rows.
+ * @param {Array} rows Array of the indexes of the row to select
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
*/
- errorMask : false,
-
+ selectRows : function(rows, keepExisting){
+ if(!keepExisting){
+ this.clearSelections();
+ }
+ for(var i = 0, len = rows.length; i < len; i++){
+ this.selectRow(rows[i], true);
+ }
+ },
+
/**
- * @cfg {Number} maskOffset Default 100
+ * Selects a range of rows. All rows in between startRow and endRow are also selected.
+ * @param {Number} startRow The index of the first row in the range
+ * @param {Number} endRow The index of the last row in the range
+ * @param {Boolean} keepExisting (optional) True to retain existing selections
*/
- maskOffset : 100,
-
+ selectRange : function(startRow, endRow, keepExisting){
+ if(this.locked) {
+ return;
+ }
+ if(!keepExisting){
+ this.clearSelections();
+ }
+ if(startRow <= endRow){
+ for(var i = startRow; i <= endRow; i++){
+ this.selectRow(i, true);
+ }
+ }else{
+ for(var i = startRow; i >= endRow; i--){
+ this.selectRow(i, true);
+ }
+ }
+ },
+
/**
- * @cfg {Boolean} maskBody
+ * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
+ * @param {Number} startRow The index of the first row in the range
+ * @param {Number} endRow The index of the last row in the range
*/
- maskBody : false,
-
- 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 form-inline navbar-' + this.align;
-
+ deselectRange : function(startRow, endRow, preventViewNotify){
+ if(this.locked) {
+ return;
}
-
- if (this.labelAlign == 'left' ) {
- cfg.cls += ' form-horizontal';
+ for(var i = startRow; i <= endRow; i++){
+ this.deselectRow(i, preventViewNotify);
}
-
-
- return cfg;
},
- initEvents : function()
- {
- this.el.on('submit', this.onSubmit, this);
- // this was added as random key presses on the form where triggering form submit.
- this.el.on('keypress', function(e) {
- if (e.getCharCode() != 13) {
- return true;
- }
- // we might need to allow it for textareas.. and some other items.
- // check e.getTarget().
- if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
- return true;
+ /**
+ * 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.ds.getCount())) {
+ return;
+ }
+ if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
+ if(!keepExisting || this.singleSelect){
+ this.clearSelections();
+ }
+ var r = this.grid.ds.getAt(index);
+ this.selections.add(r);
+ this.last = this.lastActive = index;
+ if(!preventViewNotify){
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.onRowSelect(index);
}
+ this.fireEvent("rowselect", this, index, r);
+ this.fireEvent("selectionchange", this);
+ }
+ },
- Roo.log("keypress blocked");
+ /**
+ * 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.ds.getAt(index);
+ this.selections.remove(r);
+ if(!preventViewNotify){
+ var view = this.grid.view ? this.grid.view : this.grid;
+ view.onRowDeselect(index);
+ }
+ this.fireEvent("rowdeselect", this, index);
+ this.fireEvent("selectionchange", this);
+ },
- e.preventDefault();
- return false;
- });
-
+ // private
+ restoreLast : function(){
+ if(this._last){
+ this.last = this._last;
+ }
},
+
// private
- onSubmit : function(e){
- e.stopEvent();
+ acceptsNav : function(row, col, cm){
+ return !cm.isHidden(col) && cm.isCellEditable(col, row);
},
- /**
- * Returns true if client-side validation on the form is successful.
- * @return Boolean
- */
- isValid : function(){
- var items = this.getItems();
- var valid = true;
- var target = false;
-
- items.each(function(f){
-
- if(f.validate()){
- return;
+ // 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);
}
-
- Roo.log('invalid field: ' + f.name);
-
- valid = false;
-
- if(!target && f.el.isVisible(true)){
- target = f;
+ }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);
}
-
- });
-
- if(this.errorMask && !valid){
- Roo.bootstrap.Form.popover.mask(this, target);
+ }else if(k == e.ESC){
+ ed.cancelEdit();
}
-
- return valid;
+ 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.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
+ * @param {Object} config An Array of column config objects. See this class's
+ * config objects for details.
+*/
+Roo.grid.ColumnModel = function(config){
+ /**
+ * The config passed into the constructor
+ */
+ this.config = []; //config;
+ this.lookup = {};
+
+ // if no id, create one
+ // if the column does not have a dataIndex mapping,
+ // map it to the order it is in the config
+ for(var i = 0, len = config.length; i < len; i++){
+ this.addColumn(config[i]);
+
+ }
+
+ /**
+ * 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 [required] The header text to display in the Grid view.
+ */
+ /**
+ * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
+ */
+ /**
+ * @cfg {String} smHeader Header at Bootsrap Small width
+ */
+ /**
+ * @cfg {String} mdHeader Header at Bootsrap Medium width
+ */
+ /**
+ * @cfg {String} lgHeader Header at Bootsrap Large width
+ */
+ /**
+ * @cfg {String} xlHeader Header at Bootsrap extra Large width
+ */
+ /**
+ * @cfg {String} dataIndex 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 The initial width in pixels of the column. Using this
+ * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
+ */
+ /**
+ * @cfg {Boolean} sortable 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 True to lock the column in place while scrolling the Grid. Defaults to false.
+ */
+ /**
+ * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
+ */
+ /**
+ * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
+ */
+ /**
+ * @cfg {Boolean} hidden True to hide the column. Defaults to false.
+ */
+ /**
+ * @cfg {Function} renderer A function used to generate HTML markup for a cell
+ * given the cell's data value. See {@link #setRenderer}. If not specified, the
+ * default renderer returns the escaped data value. If an object is returned (bootstrap only)
+ * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
+ */
+ /**
+ * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
+ */
+ /**
+ * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
+ */
+ /**
+ * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
+ */
+ /**
+ * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
+ */
+ /**
+ * @cfg {String} tooltip mouse over tooltip text
+ */
+ /**
+ * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
+ */
+ /**
+ * Returns the id of the column at the specified index.
+ * @param {Number} index The column index
+ * @return {String} the id
+ */
+ getColumnId : function(index){
+ return this.config[index].id;
+ },
+
+ /**
+ * Returns the column for a specified id.
+ * @param {String} id The column id
+ * @return {Object} the column
+ */
+ getColumnById : function(id){
+ return this.lookup[id];
},
+
/**
- * Returns true if any fields in this form have changed since their original load.
- * @return Boolean
+ * Returns the column Object for a specified dataIndex.
+ * @param {String} dataIndex The column dataIndex
+ * @return {Object|Boolean} the column or false if not found
*/
- isDirty : function(){
- var dirty = false;
- var items = this.getItems();
- items.each(function(f){
- if(f.isDirty()){
- dirty = true;
- return false;
- }
- return true;
- });
- return dirty;
+ getColumnByDataIndex: function(dataIndex){
+ var index = this.findColumnIndex(dataIndex);
+ return index > -1 ? this.config[index] : false;
},
- /**
- * 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
+
+ /**
+ * Returns the index for a specified column id.
+ * @param {String} id The column id
+ * @return {Number} the index, or -1 if not found
*/
- 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);
+ getIndexById : function(id){
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(this.config[i].id == id){
+ return i;
+ }
}
- return this;
+ return -1;
},
-
- // private
- beforeAction : function(action){
- var o = action.options;
-
- if(this.loadMask){
-
- if(this.maskBody){
- Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
- } else {
- this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
+
+ /**
+ * 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;
}
}
- // 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 -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);
},
- // private
- afterAction : function(action, success){
- this.activeAction = null;
- var o = action.options;
+ isLocked : function(colIndex){
+ return this.config[colIndex].locked === true;
+ },
- if(this.loadMask){
-
- if(this.maskBody){
- Roo.get(document.body).unmask();
- } else {
- this.el.unmask();
- }
+ setLocked : function(colIndex, value, suppressEvent){
+ if(this.isLocked(colIndex) == value){
+ return;
}
-
- //if(this.waitMsgTarget === true){
-// this.el.unmask();
- //}else if(this.waitMsgTarget){
- // this.waitMsgTarget.unmask();
- //}else{
- // Roo.MessageBox.updateProgress(1);
- // Roo.MessageBox.hide();
- // }
- //
- if(success){
- if(o.reset){
- this.reset();
- }
- Roo.callback(o.success, o.scope, [this, action]);
- this.fireEvent('actioncomplete', this, action);
-
- }else{
-
- // failure condition..
- // we have a scenario where updates need confirming.
- // eg. if a locking scenario exists..
- // we look for { errors : { needs_confirm : true }} in the response.
- if (
- (typeof(action.result) != 'undefined') &&
- (typeof(action.result.errors) != 'undefined') &&
- (typeof(action.result.errors.needs_confirm) != 'undefined')
- ){
- var _t = this;
- Roo.log("not supported yet");
- /*
-
- Roo.MessageBox.confirm(
- "Change requires confirmation",
- action.result.errorMsg,
- function(r) {
- if (r != 'yes') {
- return;
- }
- _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
- }
-
- );
- */
-
+ this.config[colIndex].locked = value;
+ if(!suppressEvent){
+ this.fireEvent("columnlockchange", this, colIndex, value);
+ }
+ },
- return;
+ 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);
}
+ }
+ return totalWidth;
+ },
- Roo.callback(o.failure, o.scope, [this, action]);
- // show an error message if no failed handler is set..
- if (!this.hasListener('actionfailed')) {
- Roo.log("need to add dialog support");
- /*
- Roo.MessageBox.alert("Error",
- (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
- action.result.errorMsg :
- "Saving Failed, please check your entries or try again"
- );
- */
+ getLockedCount : function(){
+ for(var i = 0, len = this.config.length; i < len; i++){
+ if(!this.isLocked(i)){
+ return i;
}
-
- this.fireEvent('actionfailed', this, action);
}
-
+
+ return this.config.length;
},
+
/**
- * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
- * @param {String} id The value to search for
- * @return Field
+ * Returns the number of columns.
+ * @return {Number}
*/
- findField : function(id){
- var items = this.getItems();
- var field = items.get(id);
- if(!field){
- items.each(function(f){
- if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
- field = f;
- return false;
+ 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 true;
- });
+ }
+ return c;
}
- return field || null;
+ return this.config.length;
},
- /**
- * Mark fields in this form invalid in bulk.
- * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
- * @return {BasicForm} this
+
+ /**
+ * 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
*/
- markInvalid : function(errors){
- if(errors instanceof Array){
- for(var i = 0, len = errors.length; i < len; i++){
- var fieldError = errors[i];
- var f = this.findField(fieldError.id);
- if(f){
- f.markInvalid(fieldError.msg);
- }
- }
- }else{
- var field, id;
- for(id in errors){
- if(typeof errors[id] != 'function' && (field = this.findField(id))){
- field.markInvalid(errors[id]);
- }
+ 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;
}
}
- //Roo.each(this.childForms || [], function (f) {
- // f.markInvalid(errors);
- //});
-
- return this;
+ return r;
},
/**
- * Set values for fields in this form in bulk.
- * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
- * @return {BasicForm} this
+ * Returns true if the specified column is sortable.
+ * @param {Number} col The column index
+ * @return {Boolean}
*/
- setValues : function(values){
- if(values instanceof Array){ // array of objects
- for(var i = 0, len = values.length; i < len; i++){
- var v = values[i];
- var f = this.findField(v.id);
- if(f){
- f.setValue(v.value);
- if(this.trackResetOnLoad){
- f.originalValue = f.getValue();
- }
- }
- }
- }else{ // object hash
- var field, id;
- for(id in values){
- if(typeof values[id] != 'function' && (field = this.findField(id))){
+ isSortable : function(col){
+ if(typeof this.config[col].sortable == "undefined"){
+ return this.defaultSortable;
+ }
+ return this.config[col].sortable;
+ },
- if (field.setFromData &&
- field.valueField &&
- field.displayField &&
- // combos' with local stores can
- // be queried via setValue()
- // to set their value..
- (field.store && !field.store.isLocal)
- ) {
- // it's a combo
- var sd = { };
- sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
- sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
- field.setFromData(sd);
+ /**
+ * Returns the rendering (formatting) function defined for the column.
+ * @param {Number} col The column index.
+ * @return {Function} The function used to render the cell. See {@link #setRenderer}.
+ */
+ getRenderer : function(col){
+ if(!this.config[col].renderer){
+ return Roo.grid.ColumnModel.defaultRenderer;
+ }
+ return this.config[col].renderer;
+ },
- } else if(field.setFromData && (field.store && !field.store.isLocal)) {
-
- field.setFromData(values);
-
- } else {
- field.setValue(values[id]);
- }
+ /**
+ * 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
+ * @param (optional) {String} gridSize bootstrap width size.
+ * @return {Number}
+ */
+ getColumnWidth : function(col, gridSize)
+ {
+ var cfg = this.config[col];
+
+ if (typeof(gridSize) == 'undefined') {
+ return cfg.width * 1 || this.defaultWidth;
+ }
+ if (gridSize === false) { // if we set it..
+ return cfg.width || false;
+ }
+ var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
+
+ for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
+ if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
+ continue;
+ }
+ return cfg[ sizes[i] ];
+ }
+ return 1;
+
+ },
- if(this.trackResetOnLoad){
- field.originalValue = field.getValue();
- }
+ /**
+ * 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);
+ }
+ },
+
+ /**
+ * 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);
}
}
}
+ return this.totalWidth;
+ },
- //Roo.each(this.childForms || [], function (f) {
- // f.setValues(values);
- //});
+ /**
+ * Returns the header for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
+ */
+ getColumnHeader : function(col){
+ return this.config[col].header;
+ },
- return this;
+ /**
+ * 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 fields in this form as an object with key/value pairs. If multiple fields exist with the same name
- * they are returned as an array.
- * @param {Boolean} asString
- * @return {Object}
+ * Returns the tooltip for the specified column.
+ * @param {Number} col The column index
+ * @return {String}
*/
- getValues : function(asString){
- //if (this.childForms) {
- // copy values from the child forms
- // Roo.each(this.childForms, function (f) {
- // this.setValues(f.getValues());
- // }, this);
- //}
+ 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;
+ },
- var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
- if(asString === true){
- return fs;
- }
- return Roo.urlDecode(fs);
+
+
+ /**
+ * Returns true if the cell is editable.
+ * @param {Number} colIndex The column index
+ * @param {Number} rowIndex The row index - this is nto actually used..?
+ * @return {Boolean}
+ */
+ isCellEditable : function(colIndex, rowIndex){
+ return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
},
/**
- * Returns the fields in this form as an object with key/value pairs.
- * This differs from getValues as it calls getValue on each child item, rather than using dom data.
+ * 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}
*/
- getFieldValues : function(with_hidden)
- {
- var items = this.getItems();
- var ret = {};
- items.each(function(f){
-
- if (!f.getName()) {
- return;
- }
-
- var v = f.getValue();
-
- if (f.inputType =='radio') {
- if (typeof(ret[f.getName()]) == 'undefined') {
- ret[f.getName()] = ''; // empty..
- }
+ getCellEditor : function(colIndex, rowIndex){
+ return this.config[colIndex].editor;
+ },
- if (!f.el.dom.checked) {
- return;
+ /**
+ * 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;
+ },
- }
- v = f.el.dom.value;
- }
-
- if(f.xtype == 'MoneyField'){
- ret[f.currencyName] = f.getCurrency();
- }
+ /**
+ * Returns true if the column is hidden.
+ * @param {Number} colIndex The column index
+ * @return {Boolean}
+ */
+ isHidden : function(colIndex){
+ return this.config[colIndex].hidden;
+ },
- // not sure if this supported any more..
- if ((typeof(v) == 'object') && f.getRawValue) {
- v = f.getRawValue() ; // dates..
- }
- // combo boxes where name != hiddenName...
- if (f.name !== false && f.name != '' && f.name != f.getName()) {
- ret[f.name] = f.getRawValue();
- }
- ret[f.getName()] = v;
- });
- return ret;
+ /**
+ * Returns true if the column width cannot be changed
+ */
+ isFixed : function(colIndex){
+ return this.config[colIndex].fixed;
},
/**
- * Clears all invalid messages in this form.
- * @return {BasicForm} this
+ * Returns true if the column can be resized
+ * @return {Boolean}
*/
- clearInvalid : function(){
- var items = this.getItems();
-
- items.each(function(f){
- f.clearInvalid();
- });
-
- return this;
+ 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);
},
/**
- * Resets this form.
- * @return {BasicForm} this
+ * Sets the editor for a column.
+ * @param {Number} col The column index
+ * @param {Object} editor The editor object
*/
- reset : function(){
- var items = this.getItems();
- items.each(function(f){
- f.reset();
- });
+ setEditor : function(col, editor){
+ this.config[col].editor = editor;
+ },
+ /**
+ * Add a column (experimental...) - defaults to adding to the end..
+ * @param {Object} config
+ */
+ addColumn : function(c)
+ {
+
+ var i = this.config.length;
+ this.config[i] = c;
+
+ if(typeof c.dataIndex == "undefined"){
+ c.dataIndex = i;
+ }
+ if(typeof c.renderer == "string"){
+ c.renderer = Roo.util.Format[c.renderer];
+ }
+ if(typeof c.id == "undefined"){
+ c.id = Roo.id();
+ }
+ if(c.editor && c.editor.xtype){
+ c.editor = Roo.factory(c.editor, Roo.grid);
+ }
+ if(c.editor && c.editor.isFormField){
+ c.editor = new Roo.grid.GridEditor(c.editor);
+ }
+ this.lookup[c.id] = c;
+ }
+
+});
- Roo.each(this.childForms || [], function (f) {
- f.reset();
- });
+Roo.grid.ColumnModel.defaultRenderer = function(value)
+{
+ if(typeof value == "object") {
+ return value;
+ }
+ if(typeof value == "string" && value.length < 1){
+ return " ";
+ }
+
+ return String.format("{0}", value);
+};
+
+// Alias for backwards compatibility
+Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.LoadMask
+ * A simple utility class for generically masking elements while loading data. If the element being masked has
+ * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
+ * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
+ * element's UpdateManager load indicator and will be destroyed after the initial load.
+ * @constructor
+ * Create a new LoadMask
+ * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
+ * @param {Object} config The config object
+ */
+Roo.LoadMask = function(el, config){
+ this.el = Roo.get(el);
+ Roo.apply(this, config);
+ if(this.store){
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('loadexception', this.onLoadException, this);
+ this.removeMask = false;
+ }else{
+ var um = this.el.getUpdateManager();
+ um.showLoadIndicator = false; // disable the default indicator
+ um.on('beforeupdate', this.onBeforeLoad, this);
+ um.on('update', this.onLoad, this);
+ um.on('failure', this.onLoad, this);
+ this.removeMask = true;
+ }
+};
+
+Roo.LoadMask.prototype = {
+ /**
+ * @cfg {Boolean} removeMask
+ * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
+ * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
+ */
+ removeMask : 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,
- return this;
+ /**
+ * Disables the mask to prevent it from being displayed
+ */
+ disable : function(){
+ this.disabled = true;
},
-
- getItems : function()
- {
- var r=new Roo.util.MixedCollection(false, function(o){
- return o.id || (o.id = Roo.id());
- });
- var iter = function(el) {
- if (el.inputEl) {
- r.add(el);
- }
- if (!el.items) {
- return;
- }
- Roo.each(el.items,function(e) {
- iter(e);
- });
- };
- iter(this);
- return r;
+ /**
+ * Enables the mask so that it can be displayed
+ */
+ enable : function(){
+ this.disabled = false;
},
- hideFields : function(items)
+ onLoadException : function()
{
- Roo.each(items, function(i){
-
- var f = this.findField(i);
-
- if(!f){
- return;
- }
-
- f.hide();
+ Roo.log(arguments);
+
+ if (typeof(arguments[3]) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",arguments[3]);
+ }
+ /*
+ try {
+ if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+ }
+ } catch(e) {
- }, this);
- },
+ }
+ */
- showFields : function(items)
+ (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+ },
+ // private
+ onLoad : function()
{
- Roo.each(items, function(i){
-
- var f = this.findField(i);
-
- if(!f){
- return;
- }
-
- f.show();
-
- }, this);
+ (function() { this.el.unmask(this.removeMask); }).defer(50, this);
+ },
+
+ // private
+ onBeforeLoad : function(){
+ if(!this.disabled){
+ (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
+ }
+ },
+
+ // 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);
+ }
+ }
+};/**
+ * @class Roo.bootstrap.Table
+ * @licence LGBL
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.TableBody
+ * Bootstrap Table class. This class represents the primary interface of a component based grid control.
+ * Similar to Roo.grid.Grid
+ * <pre><code>
+ var table = Roo.factory({
+ xtype : 'Table',
+ xns : Roo.bootstrap,
+ autoSizeColumns: true,
+
+
+ store : {
+ xtype : 'Store',
+ xns : Roo.data,
+ remoteSort : true,
+ sortInfo : { direction : 'ASC', field: 'name' },
+ proxy : {
+ xtype : 'HttpProxy',
+ xns : Roo.data,
+ method : 'GET',
+ url : 'https://example.com/some.data.url.json'
+ },
+ reader : {
+ xtype : 'JsonReader',
+ xns : Roo.data,
+ fields : [ 'id', 'name', whatever' ],
+ id : 'id',
+ root : 'data'
+ }
+ },
+ cm : [
+ {
+ xtype : 'ColumnModel',
+ xns : Roo.grid,
+ align : 'center',
+ cursor : 'pointer',
+ dataIndex : 'is_in_group',
+ header : "Name",
+ sortable : true,
+ renderer : function(v, x , r) {
+
+ return String.format("{0}", v)
+ }
+ width : 3
+ } // more columns..
+ ],
+ selModel : {
+ xtype : 'RowSelectionModel',
+ xns : Roo.bootstrap.Table
+ // you can add listeners to catch selection change here....
}
+
-});
+ });
+ // set any options
+ grid.render(Roo.get("some-div"));
+</code></pre>
+
+Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
+
+
+
+ *
+ * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
+ * @cfg {Roo.data.Store} store The data store to use
+ * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
+ *
+ * @cfg {String} cls table class
+ *
+ *
+ * @cfg {string} empty_results Text to display for no results
+ * @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 default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
+ * also adds table-responsive (see bootstrap docs for details)
+ * @cfg {Boolean} loadMask (true|false) default false
+ * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
+ * @cfg {Boolean} headerShow (true|false) generate thead, default true
+ * @cfg {Boolean} rowSelection (true|false) default false
+ * @cfg {Boolean} cellSelection (true|false) default false
+ * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
+ * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
+ * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
+ * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
+ * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
+ *
+ *
+ * @cfg {Number} minColumnWidth default 50 pixels minimum column width
+ *
+ * @constructor
+ * Create a new Table
+ * @param {Object} config The config object
+ */
-Roo.apply(Roo.bootstrap.Form, {
+Roo.bootstrap.Table = function(config)
+{
+ Roo.bootstrap.Table.superclass.constructor.call(this, config);
+
+ // BC...
+ this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
+ this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
+ this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
+ this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
- popover : {
-
- padding : 5,
-
- isApplied : false,
-
- isMasked : false,
-
- form : false,
-
- target : false,
-
- toolTip : false,
-
- intervalID : false,
-
- maskEl : false,
-
- apply : function()
- {
- if(this.isApplied){
- return;
- }
-
- this.maskEl = {
- top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
- left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
- bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
- right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
- };
-
- this.maskEl.top.enableDisplayMode("block");
- this.maskEl.left.enableDisplayMode("block");
- this.maskEl.bottom.enableDisplayMode("block");
- this.maskEl.right.enableDisplayMode("block");
-
- this.toolTip = new Roo.bootstrap.Tooltip({
- cls : 'roo-form-error-popover',
- alignment : {
- 'left' : ['r-l', [-2,0], 'right'],
- 'right' : ['l-r', [2,0], 'left'],
- 'bottom' : ['tl-bl', [0,2], 'top'],
- 'top' : [ 'bl-tl', [0,-2], 'bottom']
- }
- });
-
- this.toolTip.render(Roo.get(document.body));
-
- this.toolTip.el.enableDisplayMode("block");
-
- Roo.get(document.body).on('click', function(){
- this.unmask();
- }, this);
-
- Roo.get(document.body).on('touchstart', function(){
- this.unmask();
- }, this);
-
- this.isApplied = true
- },
-
- mask : function(form, target)
- {
- this.form = form;
-
- this.target = target;
-
- if(!this.form.errorMask || !target.el){
- return;
- }
-
- var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
-
- Roo.log(scrollable);
-
- var ot = this.target.el.calcOffsetsTo(scrollable);
-
- var scrollTo = ot[1] - this.form.maskOffset;
-
- scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
-
- scrollable.scrollTo('top', scrollTo);
-
- var box = this.target.el.getBox();
- Roo.log(box);
- var zIndex = Roo.bootstrap.Modal.zIndex++;
-
-
- this.maskEl.top.setStyle('position', 'absolute');
- this.maskEl.top.setStyle('z-index', zIndex);
- this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
- this.maskEl.top.setLeft(0);
- this.maskEl.top.setTop(0);
- this.maskEl.top.show();
-
- this.maskEl.left.setStyle('position', 'absolute');
- this.maskEl.left.setStyle('z-index', zIndex);
- this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
- this.maskEl.left.setLeft(0);
- this.maskEl.left.setTop(box.y - this.padding);
- this.maskEl.left.show();
-
- this.maskEl.bottom.setStyle('position', 'absolute');
- this.maskEl.bottom.setStyle('z-index', zIndex);
- this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
- this.maskEl.bottom.setLeft(0);
- this.maskEl.bottom.setTop(box.bottom + this.padding);
- this.maskEl.bottom.show();
-
- this.maskEl.right.setStyle('position', 'absolute');
- this.maskEl.right.setStyle('z-index', zIndex);
- this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
- this.maskEl.right.setLeft(box.right + this.padding);
- this.maskEl.right.setTop(box.y - this.padding);
- this.maskEl.right.show();
-
- this.toolTip.bindEl = this.target.el;
-
- this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
-
- var tip = this.target.blankText;
-
- if(this.target.getValue() !== '' ) {
-
- if (this.target.invalidText.length) {
- tip = this.target.invalidText;
- } else if (this.target.regexText.length){
- tip = this.target.regexText;
- }
- }
-
- this.toolTip.show(tip);
-
- this.intervalID = window.setInterval(function() {
- Roo.bootstrap.Form.popover.unmask();
- }, 10000);
-
- window.onwheel = function(){ return false;};
-
- (function(){ this.isMasked = true; }).defer(500, this);
-
- },
-
- unmask : function()
- {
- if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
- return;
- }
-
- this.maskEl.top.setStyle('position', 'absolute');
- this.maskEl.top.setSize(0, 0).setXY([0, 0]);
- this.maskEl.top.hide();
-
- this.maskEl.left.setStyle('position', 'absolute');
- this.maskEl.left.setSize(0, 0).setXY([0, 0]);
- this.maskEl.left.hide();
-
- this.maskEl.bottom.setStyle('position', 'absolute');
- this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
- this.maskEl.bottom.hide();
-
- this.maskEl.right.setStyle('position', 'absolute');
- this.maskEl.right.setSize(0, 0).setXY([0, 0]);
- this.maskEl.right.hide();
-
- this.toolTip.hide();
-
- this.toolTip.el.hide();
-
- window.onwheel = function(){ return true;};
-
- if(this.intervalID){
- window.clearInterval(this.intervalID);
- this.intervalID = false;
- }
-
- this.isMasked = false;
-
- }
-
+ this.view = this; // compat with grid.
+
+ this.sm = this.sm || {xtype: 'RowSelectionModel'};
+ if (this.sm) {
+ this.sm.grid = this;
+ this.selModel = Roo.factory(this.sm, Roo.grid);
+ this.sm = this.selModel;
+ this.sm.xmodule = this.xmodule || false;
}
-});
-
-/*
- * 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.form.VTypes
- * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
- * @singleton
- */
-Roo.form.VTypes = function(){
- // closure these in so they are only created once.
- var alpha = /^[a-zA-Z_]+$/;
- var alphanum = /^[a-zA-Z0-9_]+$/;
- var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
- var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
-
- // All these messages and functions are configurable
- return {
- /**
- * The function used to validate email addresses
- * @param {String} value The email address
- */
- 'email' : function(v){
- return email.test(v);
- },
- /**
- * The error text to display when the email validation function returns false
- * @type String
- */
- 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
- /**
- * The keystroke filter mask to be applied on email input
- * @type RegExp
- */
- 'emailMask' : /[a-z0-9_\.\-@]/i,
-
- /**
- * The function used to validate URLs
- * @param {String} value The URL
- */
- 'url' : function(v){
- return url.test(v);
- },
+ 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({
/**
- * The error text to display when the url validation function returns false
- * @type String
+ * @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
*/
- 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
-
+ "cellclick" : true,
/**
- * The function used to validate alpha values
- * @param {String} value The value
+ * @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
*/
- 'alpha' : function(v){
- return alpha.test(v);
- },
+ "celldblclick" : true,
/**
- * The error text to display when the alpha validation function returns false
- * @type String
+ * @event rowclick
+ * Fires when a row is clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Roo.EventObject} e
*/
- 'alphaText' : 'This field should only contain letters and _',
+ "rowclick" : true,
/**
- * The keystroke filter mask to be applied on alpha input
- * @type RegExp
+ * @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
*/
- 'alphaMask' : /[a-z_]/i,
-
+ "rowdblclick" : true,
/**
- * The function used to validate alphanumeric values
- * @param {String} value The value
+ * @event mouseover
+ * Fires when a mouseover occur
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ * @param {Roo.EventObject} e
*/
- 'alphanum' : function(v){
- return alphanum.test(v);
- },
+ "mouseover" : true,
/**
- * The error text to display when the alphanumeric validation function returns false
- * @type String
+ * @event mouseout
+ * Fires when a mouseout occur
+ * @param {Roo.bootstrap.Table} this
+ * @param {Roo.Element} el
+ * @param {Number} rowIndex
+ * @param {Number} columnIndex
+ * @param {Roo.EventObject} e
*/
- 'alphanumText' : 'This field should only contain letters, numbers and _',
+ "mouseout" : true,
/**
- * The keystroke filter mask to be applied on alphanumeric input
- * @type RegExp
+ * @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.
*/
- 'alphanumMask' : /[a-z0-9_]/i
- };
-}();/*
- * - LGPL
- *
- * Input
- *
- */
-
-/**
- * @class Roo.bootstrap.Input
- * @extends Roo.bootstrap.Component
- * Bootstrap Input class
- * @cfg {Boolean} disabled is it disabled
- * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
- * @cfg {String} name name of the input
- * @cfg {string} fieldLabel - the label associated
- * @cfg {string} placeholder - placeholder to put in text.
- * @cfg {string} before - input group add on before
- * @cfg {string} after - input group add on after
- * @cfg {string} size - (lg|sm) or leave empty..
- * @cfg {Number} xs colspan out of 12 for mobile-sized screens
- * @cfg {Number} sm colspan out of 12 for tablet-sized screens
- * @cfg {Number} md colspan out of 12 for computer-sized screens
- * @cfg {Number} lg colspan out of 12 for large computer-sized screens
- * @cfg {string} value default value of the input
- * @cfg {Number} labelWidth set the width of label
- * @cfg {Number} labellg set the width of label (1-12)
- * @cfg {Number} labelmd set the width of label (1-12)
- * @cfg {Number} labelsm set the width of label (1-12)
- * @cfg {Number} labelxs set the width of label (1-12)
- * @cfg {String} labelAlign (top|left)
- * @cfg {Boolean} readOnly Specifies that the field should be read-only
- * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
- * @cfg {String} indicatorpos (left|right) default left
- * @cfg {String} capture (user|camera) use for file input only. (default empty)
- * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
-
- * @cfg {String} align (left|center|right) Default left
- * @cfg {Boolean} forceFeedback (true|false) Default false
- *
- * @constructor
- * Create a new Input
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Input = function(config){
-
- Roo.bootstrap.Input.superclass.constructor.call(this, config);
-
- this.addEvents({
- /**
- * @event focus
- * Fires when this field receives input focus.
- * @param {Roo.form.Field} this
+ 'rowclass' : true,
+ /**
+ * @event rowsrendered
+ * Fires when all the rows have been rendered
+ * @param {Roo.bootstrap.Table} this
*/
- focus : true,
+ 'rowsrendered' : true,
/**
- * @event blur
- * Fires when this field loses input focus.
- * @param {Roo.form.Field} this
+ * @event contextmenu
+ * The raw contextmenu event for the entire grid.
+ * @param {Roo.EventObject} e
*/
- blur : true,
+ "contextmenu" : true,
/**
- * @event specialkey
- * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
- * {@link Roo.EventObject#getKey} to determine which key was pressed.
- * @param {Roo.form.Field} this
- * @param {Roo.EventObject} e The event object
+ * @event rowcontextmenu
+ * Fires when a row is right clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Number} rowIndex
+ * @param {Roo.EventObject} e
*/
- specialkey : true,
+ "rowcontextmenu" : true,
/**
- * @event change
- * Fires just before the field blurs if the field value has changed.
- * @param {Roo.form.Field} this
- * @param {Mixed} newValue The new value
- * @param {Mixed} oldValue The original value
+ * @event cellcontextmenu
+ * Fires when a cell is right clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Number} rowIndex
+ * @param {Number} cellIndex
+ * @param {Roo.EventObject} e
*/
- change : true,
- /**
- * @event invalid
- * Fires after the field has been marked as invalid.
- * @param {Roo.form.Field} this
- * @param {String} msg The validation message
+ "cellcontextmenu" : true,
+ /**
+ * @event headercontextmenu
+ * Fires when a header is right clicked
+ * @param {Roo.bootstrap.Table} this
+ * @param {Number} columnIndex
+ * @param {Roo.EventObject} e
*/
- invalid : true,
+ "headercontextmenu" : true,
/**
- * @event valid
- * Fires after the field has been validated with no errors.
- * @param {Roo.form.Field} this
- */
- valid : true,
- /**
- * @event keyup
- * Fires after the key up
- * @param {Roo.form.Field} this
- * @param {Roo.EventObject} e The event Object
+ * @event mousedown
+ * The raw mousedown event for the entire grid.
+ * @param {Roo.EventObject} e
*/
- keyup : true
+ "mousedown" : true
+
});
};
-Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
- /**
- * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
- automatic validation (defaults to "keyup").
- */
- validationEvent : "keyup",
- /**
- * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
- */
- validateOnBlur : true,
- /**
- * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
- */
- validationDelay : 250,
- /**
- * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
- */
- focusClass : "x-form-focus", // not needed???
+Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
-
- /**
- * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
- */
- invalidClass : "has-warning",
+ cls: false,
- /**
- * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
- */
- validClass : "has-success",
+ empty_results : '',
+ striped : false,
+ scrollBody : false,
+ bordered: false,
+ hover: false,
+ condensed : false,
+ responsive : false,
+ sm : false,
+ cm : false,
+ store : false,
+ loadMask : false,
+ footerShow : true,
+ headerShow : true,
+ enableColumnResize: true,
+
+ rowSelection : false,
+ cellSelection : false,
+ layout : false,
+
+ minColumnWidth : 50,
- /**
- * @cfg {Boolean} hasFeedback (true|false) default true
- */
- hasFeedback : true,
+ // Roo.Element - the tbody
+ bodyEl: false, // <tbody> Roo.Element - thead element
+ headEl: false, // <thead> Roo.Element - thead element
+ resizeProxy : false, // proxy element for dragging?
+
+
- /**
- * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
- */
- invalidFeedbackClass : "glyphicon-warning-sign",
+ container: false, // used by gridpanel...
- /**
- * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
- */
- validFeedbackClass : "glyphicon-ok",
+ lazyLoad : false,
- /**
- * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
- */
- selectOnFocus : false,
+ CSS : Roo.util.CSS,
- /**
- * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
- */
- maskRe : null,
- /**
- * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
- */
- vtype : null,
-
- /**
- * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
- */
- disableKeyFilter : false,
-
- /**
- * @cfg {Boolean} disabled True to disable the field (defaults to false).
- */
- disabled : false,
- /**
- * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
- */
- allowBlank : true,
- /**
- * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
- */
- blankText : "Please complete this mandatory field",
-
- /**
- * @cfg {Number} minLength Minimum input field length required (defaults to 0)
- */
- minLength : 0,
- /**
- * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
- */
- maxLength : Number.MAX_VALUE,
- /**
- * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
- */
- minLengthText : "The minimum length for this field is {0}",
- /**
- * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
- */
- maxLengthText : "The maximum length for this field is {0}",
-
-
- /**
- * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
- * If available, this function will be called only after the basic validators all return true, and will be passed the
- * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
- */
- validator : null,
- /**
- * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
- * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
- * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
- */
- regex : null,
- /**
- * @cfg {String} regexText -- Depricated - use Invalid Text
- */
- regexText : "",
-
- /**
- * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
- */
- invalidText : "",
-
-
-
- autocomplete: false,
-
-
- fieldLabel : '',
- inputType : 'text',
-
- name : false,
- placeholder: false,
- before : false,
- after : false,
- size : false,
- hasFocus : false,
- preventMark: false,
- isFormField : true,
- value : '',
- labelWidth : 2,
- labelAlign : false,
- readOnly : false,
- align : false,
- formatedValue : false,
- forceFeedback : false,
-
- indicatorpos : 'left',
-
- labellg : 0,
- labelmd : 0,
- labelsm : 0,
- labelxs : 0,
-
- capture : '',
- accept : '',
+ auto_hide_footer : false,
- parentLabelAlign : function()
- {
- var parent = this;
- while (parent.parent()) {
- parent = parent.parent();
- if (typeof(parent.labelAlign) !='undefined') {
- return parent.labelAlign;
- }
- }
- return 'left';
-
- },
+ view: false, // actually points to this..
getAutoCreate : function()
{
- var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
-
- var id = Roo.id();
-
- var cfg = {};
-
- if(this.inputType != 'hidden'){
- cfg.cls = 'form-group' //input-group
- }
+ var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
- var input = {
- tag: 'input',
- id : id,
- type : this.inputType,
- value : this.value,
- cls : 'form-control',
- placeholder : this.placeholder || '',
- autocomplete : this.autocomplete || 'new-password'
+ cfg = {
+ tag: 'table',
+ cls : 'table',
+ cn : []
};
-
- if(this.capture.length){
- input.capture = this.capture;
- }
-
- if(this.accept.length){
- input.accept = this.accept + "/*";
- }
-
- if(this.align){
- input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
+ // this get's auto added by panel.Grid
+ if (this.scrollBody) {
+ cfg.cls += ' table-body-fixed';
+ }
+ if (this.striped) {
+ cfg.cls += ' table-striped';
}
- if(this.maxLength && this.maxLength != Number.MAX_VALUE){
- input.maxLength = this.maxLength;
+ if (this.hover) {
+ cfg.cls += ' table-hover';
}
-
- if (this.disabled) {
- input.disabled=true;
+ if (this.bordered) {
+ cfg.cls += ' table-bordered';
}
-
- if (this.readOnly) {
- input.readonly=true;
+ if (this.condensed) {
+ cfg.cls += ' table-condensed';
}
- if (this.name) {
- input.name = this.name;
+ if (this.responsive) {
+ cfg.cls += ' table-responsive';
}
- if (this.size) {
- input.cls += ' input-' + this.size;
+ if (this.cls) {
+ cfg.cls+= ' ' +this.cls;
}
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
- }
- });
- var inputblock = input;
- var feedback = {
- tag: 'span',
- cls: 'glyphicon form-control-feedback'
- };
-
- if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
-
- inputblock = {
- cls : 'has-feedback',
- cn : [
- input,
- feedback
- ]
- };
+ if (this.layout) {
+ cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
}
- if (this.before || this.after) {
-
- inputblock = {
- cls : 'input-group',
- cn : []
- };
-
- if (this.before && typeof(this.before) == 'string') {
-
- inputblock.cn.push({
- tag :'span',
- cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
- html : this.before
- });
- }
- if (this.before && typeof(this.before) == 'object') {
- this.before = Roo.factory(this.before);
-
- inputblock.cn.push({
- tag :'span',
- cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
- (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
- });
+ if(this.store || this.cm){
+ if(this.headerShow){
+ cfg.cn.push(this.renderHeader());
}
- inputblock.cn.push(input);
+ cfg.cn.push(this.renderBody());
- if (this.after && typeof(this.after) == 'string') {
- inputblock.cn.push({
- tag :'span',
- cls : 'roo-input-after input-group-append input-group-text input-group-addon',
- html : this.after
- });
- }
- if (this.after && typeof(this.after) == 'object') {
- this.after = Roo.factory(this.after);
-
- inputblock.cn.push({
- tag :'span',
- cls : 'roo-input-after input-group-append input-group-text input-group-' +
- (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
- });
+ if(this.footerShow){
+ cfg.cn.push(this.renderFooter());
}
+ // where does this come from?
+ //cfg.cls+= ' TableGrid';
+ }
+
+ return { cn : [ cfg ] };
+ },
+
+ initEvents : function()
+ {
+ if(!this.store || !this.cm){
+ return;
+ }
+ if (this.selModel) {
+ this.selModel.initEvents();
+ }
+
+
+ //Roo.log('initEvents with ds!!!!');
+
+ this.bodyEl = this.el.select('tbody', true).first();
+ this.headEl = this.el.select('thead', true).first();
+ this.mainFoot = this.el.select('tfoot', true).first();
+
+
+
+
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.on('click', this.sort, this);
+ }, this);
+
+
+ // why is this done????? = it breaks dialogs??
+ //this.parent().el.setStyle('position', 'relative');
+
+
+ if (this.footer) {
+ this.footer.parentId = this.id;
+ this.footer.onRender(this.el.select('tfoot tr td').first(), null);
- if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
- inputblock.cls += ' has-feedback';
- inputblock.cn.push(feedback);
+ if(this.lazyLoad){
+ this.el.select('tfoot tr td').first().addClass('hide');
}
- };
- var indicator = {
- tag : 'i',
- cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- };
- if (Roo.bootstrap.version == 4) {
- indicator = {
- tag : 'i',
- style : 'display-none'
- };
+ }
+
+ if(this.loadMask) {
+ this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
}
- if (align ==='left' && this.fieldLabel.length) {
-
- cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
-
- cfg.cn = [
- indicator,
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-form-label',
- html : this.fieldLabel
+
+ this.store.on('load', this.onLoad, this);
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('update', this.onUpdate, this);
+ this.store.on('add', this.onAdd, this);
+ this.store.on("clear", this.clear, this);
+
+ this.el.on("contextmenu", this.onContextMenu, this);
+
+
+ this.cm.on("headerchange", this.onHeaderChange, this);
+ this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
- },
- {
- cls : "",
- cn: [
- inputblock
- ]
- }
- ];
-
- var labelCfg = cfg.cn[1];
- var contentCfg = cfg.cn[2];
-
- if(this.indicatorpos == 'right'){
- cfg.cn = [
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-form-label',
- cn : [
- {
- tag : 'span',
- html : this.fieldLabel
- },
- indicator
- ]
- },
- {
- cls : "",
- cn: [
- inputblock
- ]
- }
+ //?? does bodyEl get replaced on render?
+ this.bodyEl.on("click", this.onClick, this);
+ this.bodyEl.on("dblclick", this.onDblClick, this);
+ this.bodyEl.on('scroll', this.onBodyScroll, this);
- ];
-
- labelCfg = cfg.cn[0];
- contentCfg = cfg.cn[1];
-
- }
-
- if(this.labelWidth > 12){
- labelCfg.style = "width: " + this.labelWidth + 'px';
- }
-
- if(this.labelWidth < 13 && this.labelmd == 0){
- this.labelmd = this.labelWidth;
+ // guessing mainbody will work - this relays usually caught by selmodel at present.
+ this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
+
+
+ this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
+
+
+ if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
+ new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
+ }
+
+ this.initCSS();
+ },
+ // Compatibility with grid - we implement all the view features at present.
+ getView : function()
+ {
+ return this;
+ },
+
+ initCSS : function()
+ {
+
+
+ var cm = this.cm, styles = [];
+ this.CSS.removeStyleSheet(this.id + '-cssrules');
+ var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
+ // we can honour xs/sm/md/xl as widths...
+ // we first have to decide what widht we are currently at...
+ var sz = Roo.getGridSize();
+
+ var total = 0;
+ var last = -1;
+ var cols = []; // visable cols.
+ var total_abs = 0;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
+ var w = cm.getColumnWidth(i, false);
+ if(cm.isHidden(i)){
+ cols.push( { rel : false, abs : 0 });
+ continue;
}
-
- if(this.labellg > 0){
- labelCfg.cls += ' col-lg-' + this.labellg;
- contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+ if (w !== false) {
+ cols.push( { rel : false, abs : w });
+ total_abs += w;
+ last = i; // not really..
+ continue;
}
-
- if(this.labelmd > 0){
- labelCfg.cls += ' col-md-' + this.labelmd;
- contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ var w = cm.getColumnWidth(i, sz);
+ if (w > 0) {
+ last = i
}
+ total += w;
+ cols.push( { rel : w, abs : false });
+ }
+
+ var avail = this.bodyEl.dom.clientWidth - total_abs;
+
+ var unitWidth = Math.floor(avail / total);
+ var rem = avail - (unitWidth * total);
+
+ var hidden, width, pos = 0 , splithide , left;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
- if(this.labelsm > 0){
- labelCfg.cls += ' col-sm-' + this.labelsm;
- contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ hidden = 'display:none;';
+ left = '';
+ width = 'width:0px;';
+ splithide = '';
+ if(!cm.isHidden(i)){
+ hidden = '';
+
+
+ // we can honour xs/sm/md/xl ?
+ var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
+ if (w===0) {
+ hidden = 'display:none;';
+ }
+ // width should return a small number...
+ if (i == last) {
+ w+=rem; // add the remaining with..
+ }
+ pos += w;
+ left = "left:" + (pos -4) + "px;";
+ width = "width:" + w+ "px;";
+
}
-
- if(this.labelxs > 0){
- labelCfg.cls += ' col-xs-' + this.labelxs;
- contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ if (this.responsive) {
+ width = '';
+ left = '';
+ hidden = cm.isHidden(i) ? 'display:none;' : '';
+ splithide = 'display: none;';
}
-
- } else if ( this.fieldLabel.length) {
-
- cfg.cn = [
- {
- tag : 'i',
- cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- },
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
-
- inputblock
-
- ];
-
- if(this.indicatorpos == 'right'){
+ styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
+ if (this.headEl) {
+ if (i == last) {
+ splithide = 'display:none;';
+ }
- cfg.cn = [
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
- {
- tag : 'i',
- cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- },
-
- inputblock
-
- ];
-
+ styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
+ '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
+ // this is the popover version..
+ '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
+ );
}
-
- } else {
- cfg.cn = [
-
- inputblock
-
- ];
-
-
- };
-
- if (this.parentType === 'Navbar' && this.parent().bar) {
- cfg.cls += ' navbar-form';
}
-
- if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
- // on BS4 we do this only if not form
- cfg.cls += ' navbar-form';
- cfg.tag = 'li';
- }
-
- return cfg;
+ //Roo.log(styles.join(''));
+ this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
},
- /**
- * return the real input element.
- */
- inputEl: function ()
- {
- return this.el.select('input.form-control',true).first();
- },
- tooltipEl : function()
+
+
+ onContextMenu : function(e, t)
{
- return this.inputEl();
+ this.processEvent("contextmenu", e);
},
- indicatorEl : function()
+ processEvent : function(name, e)
{
- if (Roo.bootstrap.version == 4) {
- return false; // not enabled in v4 yet.
+ if (name != 'touchstart' ) {
+ this.fireEvent(name, e);
}
- var indicator = this.el.select('i.roo-required-indicator',true).first();
-
- if(!indicator){
- return false;
- }
+ var t = e.getTarget();
- return indicator;
+ var cell = Roo.get(t);
- },
-
- setDisabled : function(v)
- {
- var i = this.inputEl().dom;
- if (!v) {
- i.removeAttribute('disabled');
+ if(!cell){
return;
-
}
- i.setAttribute('disabled','true');
- },
- initEvents : function()
- {
-
- this.inputEl().on("keydown" , this.fireKey, this);
- this.inputEl().on("focus", this.onFocus, this);
- this.inputEl().on("blur", this.onBlur, this);
-
- this.inputEl().relayEvent('keyup', this);
-
- this.indicator = this.indicatorEl();
- if(this.indicator){
- this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
- }
-
- // reference to original value for reset
- this.originalValue = this.getValue();
- //Roo.form.TextField.superclass.initEvents.call(this);
- if(this.validationEvent == 'keyup'){
- this.validationTask = new Roo.util.DelayedTask(this.validate, this);
- this.inputEl().on('keyup', this.filterValidation, this);
- }
- else if(this.validationEvent !== false){
- this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
+ if(cell.findParent('tfoot', false, true)){
+ return;
}
- if(this.selectOnFocus){
- this.on("focus", this.preFocus, this);
+ if(cell.findParent('thead', false, true)){
- }
- if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
- this.inputEl().on("keypress", this.filterKeys, this);
- } else {
- this.inputEl().relayEvent('keypress', this);
- }
- /* if(this.grow){
- this.el.on("keyup", this.onKeyUp, this, {buffer:50});
- this.el.on("click", this.autoSize, this);
- }
- */
- if(this.inputEl().is('input[type=password]') && Roo.isSafari){
- this.inputEl().on('keydown', this.SafariOnKeyDown, this);
+ if(e.getTarget().nodeName.toLowerCase() != 'th'){
+ cell = Roo.get(t).findParent('th', false, true);
+ if (!cell) {
+ Roo.log("failed to find th in thead?");
+ Roo.log(e.getTarget());
+ return;
+ }
+ }
+
+ var cellIndex = cell.dom.cellIndex;
+
+ var ename = name == 'touchstart' ? 'click' : name;
+ this.fireEvent("header" + ename, this, cellIndex, e);
+
+ return;
}
- if (typeof(this.before) == 'object') {
- this.before.render(this.el.select('.roo-input-before',true).first());
- }
- if (typeof(this.after) == 'object') {
- this.after.render(this.el.select('.roo-input-after',true).first());
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = Roo.get(t).findParent('td', false, true);
+ if (!cell) {
+ Roo.log("failed to find th in tbody?");
+ Roo.log(e.getTarget());
+ return;
+ }
}
- this.inputEl().on('change', this.onChange, this);
+ var row = cell.findParent('tr', false, true);
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = row.dom.rowIndex - 1;
- },
- filterValidation : function(e){
- if(!e.isNavKeyPress()){
- this.validationTask.delay(this.validationDelay);
- }
- },
- /**
- * Validates the field value
- * @return {Boolean} True if the value is valid, else false
- */
- validate : function(){
- //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
- if(this.disabled || this.validateValue(this.getRawValue())){
- this.markValid();
- return true;
+ if(row !== false){
+
+ this.fireEvent("row" + name, this, rowIndex, e);
+
+ if(cell !== false){
+
+ this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
+ }
}
- this.markInvalid();
- return false;
},
-
- /**
- * Validates a value according to the field's validation rules and marks the field as invalid
- * if the validation fails
- * @param {Mixed} value The value to validate
- * @return {Boolean} True if the value is valid, else false
- */
- validateValue : function(value)
+ onMouseover : function(e, el)
{
- if(this.getVisibilityEl().hasClass('hidden')){
- return true;
- }
+ var cell = Roo.get(el);
- if(value.length < 1) { // if it's blank
- if(this.allowBlank){
- return true;
- }
- return false;
+ if(!cell){
+ return;
}
- if(value.length < this.minLength){
- return false;
- }
- if(value.length > this.maxLength){
- return false;
- }
- if(this.vtype){
- var vt = Roo.form.VTypes;
- if(!vt[this.vtype](value, this)){
- return false;
- }
- }
- if(typeof this.validator == "function"){
- var msg = this.validator(value);
- if(msg !== true){
- return false;
- }
- if (typeof(msg) == 'string') {
- this.invalidText = msg;
- }
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = cell.findParent('td', false, true);
}
- if(this.regex && !this.regex.test(value)){
- return false;
- }
+ var row = cell.findParent('tr', false, true);
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = row.dom.rowIndex - 1; // start from 0
+
+ this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
- return true;
- },
-
- // private
- fireKey : function(e){
- //Roo.log('field ' + e.getKey());
- if(e.isNavKeyPress()){
- this.fireEvent("specialkey", this, e);
- }
- },
- focus : function (selectText){
- if(this.rendered){
- this.inputEl().focus();
- if(selectText === true){
- this.inputEl().dom.select();
- }
- }
- return this;
- } ,
-
- onFocus : function(){
- if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
- // this.el.addClass(this.focusClass);
- }
- if(!this.hasFocus){
- this.hasFocus = true;
- this.startValue = this.getValue();
- this.fireEvent("focus", this);
- }
- },
-
- beforeBlur : Roo.emptyFn,
-
-
- // private
- onBlur : function(){
- this.beforeBlur();
- if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
- //this.el.removeClass(this.focusClass);
- }
- this.hasFocus = false;
- if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
- this.validate();
- }
- var v = this.getValue();
- if(String(v) !== String(this.startValue)){
- this.fireEvent('change', this, v, this.startValue);
- }
- this.fireEvent("blur", this);
},
- onChange : function(e)
+ onMouseout : function(e, el)
{
- var v = this.getValue();
- if(String(v) !== String(this.startValue)){
- this.fireEvent('change', this, v, this.startValue);
+ var cell = Roo.get(el);
+
+ if(!cell){
+ return;
}
- },
-
- /**
- * Resets the current field value to the originally loaded value and clears any validation messages
- */
- reset : function(){
- this.setValue(this.originalValue);
- this.validate();
- },
- /**
- * Returns the name of the field
- * @return {Mixed} name The name field
- */
- getName: function(){
- return this.name;
- },
- /**
- * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
- * @return {Mixed} value The field value
- */
- getValue : function(){
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = cell.findParent('td', false, true);
+ }
- var v = this.inputEl().getValue();
+ var row = cell.findParent('tr', false, true);
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = row.dom.rowIndex - 1; // start from 0
- return v;
- },
- /**
- * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
- * @return {Mixed} value The field value
- */
- getRawValue : function(){
- var v = this.inputEl().getValue();
+ this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
- return v;
- },
-
- /**
- * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
- * @param {Mixed} value The value to set
- */
- setRawValue : function(v){
- return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
- },
-
- selectText : function(start, end){
- var v = this.getRawValue();
- if(v.length > 0){
- start = start === undefined ? 0 : start;
- end = end === undefined ? v.length : end;
- var d = this.inputEl().dom;
- if(d.setSelectionRange){
- d.setSelectionRange(start, end);
- }else if(d.createTextRange){
- var range = d.createTextRange();
- range.moveStart("character", start);
- range.moveEnd("character", v.length-end);
- range.select();
- }
- }
- },
-
- /**
- * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
- * @param {Mixed} value The value to set
- */
- setValue : function(v){
- this.value = v;
- if(this.rendered){
- this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
- this.validate();
- }
},
- /*
- processValue : function(value){
- if(this.stripCharsRe){
- var newValue = value.replace(this.stripCharsRe, '');
- if(newValue !== value){
- this.setRawValue(newValue);
- return newValue;
- }
- }
- return value;
- },
- */
- preFocus : function(){
+ onClick : function(e, el)
+ {
+ var cell = Roo.get(el);
- if(this.selectOnFocus){
- this.inputEl().dom.select();
- }
- },
- filterKeys : function(e){
- var k = e.getKey();
- if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
- return;
- }
- var c = e.getCharCode(), cc = String.fromCharCode(c);
- if(Roo.isIE && (e.isSpecialKey() || !cc)){
+ if(!cell || (!this.cellSelection && !this.rowSelection)){
return;
}
- if(!this.maskRe.test(cc)){
- e.stopEvent();
+
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = cell.findParent('td', false, true);
}
- },
- /**
- * Clear any invalid styles/messages for this field
- */
- clearInvalid : function(){
- if(!this.el || this.preventMark){ // not rendered
+ if(!cell || typeof(cell) == 'undefined'){
return;
}
+ var row = cell.findParent('tr', false, true);
- this.el.removeClass([this.invalidClass, 'is-invalid']);
-
- if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
-
- var feedback = this.el.select('.form-control-feedback', true).first();
-
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
- }
-
+ if(!row || typeof(row) == 'undefined'){
+ return;
}
- if(this.indicator){
- this.indicator.removeClass('visible');
- this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
- }
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = this.getRowIndex(row);
- this.fireEvent('valid', this);
+ // why??? - should these not be based on SelectionModel?
+ //if(this.cellSelection){
+ this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
+ //}
+
+ //if(this.rowSelection){
+ this.fireEvent('rowclick', this, row, rowIndex, e);
+ //}
+
},
-
- /**
- * Mark this field as valid
- */
- markValid : function()
+
+ onDblClick : function(e,el)
{
- if(!this.el || this.preventMark){ // not rendered...
+ var cell = Roo.get(el);
+
+ if(!cell || (!this.cellSelection && !this.rowSelection)){
return;
}
- this.el.removeClass([this.invalidClass, this.validClass]);
- this.inputEl().removeClass(['is-valid', 'is-invalid']);
-
- var feedback = this.el.select('.form-control-feedback', true).first();
-
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+ if(e.getTarget().nodeName.toLowerCase() != 'td'){
+ cell = cell.findParent('td', false, true);
}
- if(this.indicator){
- this.indicator.removeClass('visible');
- this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
+ if(!cell || typeof(cell) == 'undefined'){
+ return;
}
- if(this.disabled){
+ var row = cell.findParent('tr', false, true);
+
+ if(!row || typeof(row) == 'undefined'){
return;
}
- if(this.allowBlank && !this.getRawValue().length){
- return;
+ var cellIndex = cell.dom.cellIndex;
+ var rowIndex = this.getRowIndex(row);
+
+ if(this.cellSelection){
+ this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
}
- if (Roo.bootstrap.version == 3) {
- this.el.addClass(this.validClass);
- } else {
- this.inputEl().addClass('is-valid');
+
+ if(this.rowSelection){
+ this.fireEvent('rowdblclick', this, row, rowIndex, e);
}
-
- if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
-
- var feedback = this.el.select('.form-control-feedback', true).first();
-
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
- this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
- }
-
+ },
+ findRowIndex : function(el)
+ {
+ var cell = Roo.get(el);
+ if(!cell) {
+ return false;
}
+ var row = cell.findParent('tr', false, true);
- this.fireEvent('valid', this);
+ if(!row || typeof(row) == 'undefined'){
+ return false;
+ }
+ return this.getRowIndex(row);
},
-
- /**
- * Mark this field as invalid
- * @param {String} msg The validation message
- */
- markInvalid : function(msg)
+ sort : function(e,el)
{
- if(!this.el || this.preventMark){ // not rendered
+ var col = Roo.get(el);
+
+ if(!col.hasClass('sortable')){
return;
}
- this.el.removeClass([this.invalidClass, this.validClass]);
- this.inputEl().removeClass(['is-valid', 'is-invalid']);
+ var sort = col.attr('sort');
+ var dir = 'ASC';
- var feedback = this.el.select('.form-control-feedback', true).first();
-
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass(
- [this.invalidFeedbackClass, this.validFeedbackClass]);
- }
-
- if(this.disabled){
- return;
+ if(col.select('i', true).first().hasClass('fa-arrow-up')){
+ dir = 'DESC';
}
- if(this.allowBlank && !this.getRawValue().length){
- return;
- }
+ this.store.sortInfo = {field : sort, direction : dir};
- if(this.indicator){
- this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
- this.indicator.addClass('visible');
- }
- if (Roo.bootstrap.version == 3) {
- this.el.addClass(this.invalidClass);
+ if (this.footer) {
+ Roo.log("calling footer first");
+ this.footer.onClick('first');
} else {
- this.inputEl().addClass('is-invalid');
- }
+ this.store.load({ params : { start : 0 } });
+ }
+ },
+
+ renderHeader : function()
+ {
+ var header = {
+ tag: 'thead',
+ cn : []
+ };
+ var cm = this.cm;
+ this.totalWidth = 0;
- if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
- var feedback = this.el.select('.form-control-feedback', true).first();
+ var config = cm.config[i];
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
-
- if(this.getValue().length || this.forceFeedback){
- this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
- }
+ var c = {
+ tag: 'th',
+ cls : 'x-hcol-' + i,
+ style : '',
+ html: cm.getColumnHeader(i)
+ };
+
+ var tooltip = cm.getColumnTooltip(i);
+ if (tooltip) {
+ c.tooltip = tooltip;
}
- }
-
- this.fireEvent('invalid', this, msg);
- },
- // private
- SafariOnKeyDown : function(event)
- {
- // this is a workaround for a password hang bug on chrome/ webkit.
- if (this.inputEl().dom.type != 'password') {
- return;
- }
-
- var isSelectAll = false;
-
- if(this.inputEl().dom.selectionEnd > 0){
- isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
- }
- if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
- event.preventDefault();
- this.setValue('');
- return;
- }
-
- if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
- event.preventDefault();
- // this is very hacky as keydown always get's upper case.
- //
- var cc = String.fromCharCode(event.getCharCode());
- this.setValue( event.shiftKey ? cc : cc.toLowerCase());
+ var hh = '';
- }
- },
- adjustWidth : function(tag, w){
- tag = tag.toLowerCase();
- if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
- if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
- if(tag == 'input'){
- return w + 2;
- }
- if(tag == 'textarea'){
- return w-2;
- }
- }else if(Roo.isOpera){
- if(tag == 'input'){
- return w + 2;
- }
- if(tag == 'textarea'){
- return w-2;
- }
- }
- }
- return w;
- },
-
- setFieldLabel : function(v)
- {
- if(!this.rendered){
- return;
- }
-
- if(this.indicatorEl()){
- var ar = this.el.select('label > span',true);
-
- if (ar.elements.length) {
- this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
- this.fieldLabel = v;
- return;
+ if(typeof(config.sortable) != 'undefined' && config.sortable){
+ c.cls += ' sortable';
+ c.html = '<i class="fa"></i>' + c.html;
}
- var br = this.el.select('label',true);
+ // could use BS4 hidden-..-down
- if(br.elements.length) {
- this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
- this.fieldLabel = v;
- return;
+ if(typeof(config.lgHeader) != 'undefined'){
+ hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
}
- Roo.log('Cannot Found any of label > span || label in input');
- return;
- }
-
- this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
- this.fieldLabel = v;
-
-
- }
-});
-
-
-/*
- * - LGPL
- *
- * Input
- *
- */
-
-/**
- * @class Roo.bootstrap.TextArea
- * @extends Roo.bootstrap.Input
- * Bootstrap TextArea class
- * @cfg {Number} cols Specifies the visible width of a text area
- * @cfg {Number} rows Specifies the visible number of lines in a text area
- * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
- * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
- * @cfg {string} html text
- *
- * @constructor
- * Create a new TextArea
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TextArea = function(config){
- Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
-
-};
-
-Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
-
- cols : false,
- rows : 5,
- readOnly : false,
- warp : 'soft',
- resize : false,
- value: false,
- html: false,
-
- getAutoCreate : function(){
-
- var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
-
- var id = Roo.id();
-
- var cfg = {};
-
- if(this.inputType != 'hidden'){
- cfg.cls = 'form-group' //input-group
- }
-
- var input = {
- tag: 'textarea',
- id : id,
- warp : this.warp,
- rows : this.rows,
- value : this.value || '',
- html: this.html || '',
- cls : 'form-control',
- placeholder : this.placeholder || ''
-
- };
-
- if(this.maxLength && this.maxLength != Number.MAX_VALUE){
- input.maxLength = this.maxLength;
- }
-
- if(this.resize){
- input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
- }
-
- if(this.cols){
- input.cols = this.cols;
- }
-
- if (this.readOnly) {
- input.readonly = true;
- }
-
- if (this.name) {
- input.name = this.name;
- }
-
- if (this.size) {
- input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
- }
-
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
+ if(typeof(config.mdHeader) != 'undefined'){
+ hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
}
- });
-
- var inputblock = input;
-
- if(this.hasFeedback && !this.allowBlank){
- var feedback = {
- tag: 'span',
- cls: 'glyphicon form-control-feedback'
- };
-
- inputblock = {
- cls : 'has-feedback',
- cn : [
- input,
- feedback
- ]
- };
- }
-
-
- if (this.before || this.after) {
+ if(typeof(config.smHeader) != 'undefined'){
+ hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
+ }
- inputblock = {
- cls : 'input-group',
- cn : []
- };
- if (this.before) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.before
- });
+ if(typeof(config.xsHeader) != 'undefined'){
+ hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
}
- inputblock.cn.push(input);
+ if(hh.length){
+ c.html = hh;
+ }
- if(this.hasFeedback && !this.allowBlank){
- inputblock.cls += ' has-feedback';
- inputblock.cn.push(feedback);
+ if(typeof(config.tooltip) != 'undefined'){
+ c.tooltip = config.tooltip;
}
- if (this.after) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.after
- });
+ if(typeof(config.colspan) != 'undefined'){
+ c.colspan = config.colspan;
}
- }
-
- if (align ==='left' && this.fieldLabel.length) {
- cfg.cn = [
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label',
- html : this.fieldLabel
- },
- {
- cls : "",
- cn: [
- inputblock
- ]
- }
-
- ];
+ // hidden is handled by CSS now
- if(this.labelWidth > 12){
- cfg.cn[0].style = "width: " + this.labelWidth + 'px';
- }
-
- if(this.labelWidth < 13 && this.labelmd == 0){
- this.labelmd = this.labelWidth;
+ if(typeof(config.dataIndex) != 'undefined'){
+ c.sort = config.dataIndex;
}
-
- if(this.labellg > 0){
- cfg.cn[0].cls += ' col-lg-' + this.labellg;
- cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
+
+
+
+ if(typeof(config.align) != 'undefined' && config.align.length){
+ c.style += ' text-align:' + config.align + ';';
}
-
- if(this.labelmd > 0){
- cfg.cn[0].cls += ' col-md-' + this.labelmd;
- cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
+
+ /* width is done in CSS
+ *if(typeof(config.width) != 'undefined'){
+ c.style += ' width:' + config.width + 'px;';
+ this.totalWidth += config.width;
+ } else {
+ this.totalWidth += 100; // assume minimum of 100 per column?
}
-
- if(this.labelsm > 0){
- cfg.cn[0].cls += ' col-sm-' + this.labelsm;
- cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
+ */
+
+ if(typeof(config.cls) != 'undefined'){
+ c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
}
-
- if(this.labelxs > 0){
- cfg.cn[0].cls += ' col-xs-' + this.labelxs;
- cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
+ // this is the bit that doesnt reall work at all...
+
+ if (this.responsive) {
+
+
+ ['xs','sm','md','lg'].map(function(size){
+
+ if(typeof(config[size]) == 'undefined'){
+ return;
+ }
+
+ if (!config[size]) { // 0 = hidden
+ // BS 4 '0' is treated as hide that column and below.
+ c.cls += ' hidden-' + size + ' hidden' + size + '-down';
+ return;
+ }
+
+ c.cls += ' col-' + size + '-' + config[size] + (
+ size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
+ );
+
+
+ });
}
+ // at the end?
- } else if ( this.fieldLabel.length) {
- cfg.cn = [
-
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
-
- inputblock
-
- ];
-
- } else {
-
- cfg.cn = [
-
- inputblock
-
- ];
-
- }
-
- if (this.disabled) {
- input.disabled=true;
+ c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
+
+
+
+
+ header.cn.push(c)
}
- return cfg;
+ return header;
+ },
+
+ renderBody : function()
+ {
+ var body = {
+ tag: 'tbody',
+ cn : [
+ {
+ tag: 'tr',
+ cn : [
+ {
+ tag : 'td',
+ colspan : this.cm.getColumnCount()
+ }
+ ]
+ }
+ ]
+ };
+ return body;
},
- /**
- * return the real textarea element.
- */
- inputEl: function ()
+
+ renderFooter : function()
{
- return this.el.select('textarea.form-control',true).first();
+ var footer = {
+ tag: 'tfoot',
+ cn : [
+ {
+ tag: 'tr',
+ cn : [
+ {
+ tag : 'td',
+ colspan : this.cm.getColumnCount()
+ }
+ ]
+ }
+ ]
+ };
+
+ return footer;
},
- /**
- * Clear any invalid styles/messages for this field
- */
- clearInvalid : function()
+
+
+ onLoad : function()
{
+// Roo.log('ds onload');
+ this.clear();
- if(!this.el || this.preventMark){ // not rendered
- return;
- }
+ var _this = this;
+ var cm = this.cm;
+ var ds = this.store;
- var label = this.el.select('label', true).first();
- var icon = this.el.select('i.fa-star', true).first();
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
+ if (_this.store.sortInfo) {
+
+ if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
+ e.select('i', true).addClass(['fa-arrow-up']);
+ }
+
+ if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
+ e.select('i', true).addClass(['fa-arrow-down']);
+ }
+ }
+ });
- if(label && icon){
- icon.remove();
+ var tbody = this.bodyEl;
+
+ if(ds.getCount() > 0){
+ ds.data.each(function(d,rowIndex){
+ var row = this.renderRow(cm, ds, rowIndex);
+
+ tbody.createChild(row);
+
+ var _this = this;
+
+ if(row.cellObjects.length){
+ Roo.each(row.cellObjects, function(r){
+ _this.renderCellObject(r);
+ })
+ }
+
+ }, this);
+ } else if (this.empty_results.length) {
+ this.el.mask(this.empty_results, 'no-spinner');
}
- this.el.removeClass( this.validClass);
- this.inputEl().removeClass('is-invalid');
-
- if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+
+ var tfoot = this.el.select('tfoot', true).first();
+
+ if(this.footerShow && this.auto_hide_footer && this.mainFoot){
- var feedback = this.el.select('.form-control-feedback', true).first();
+ this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
- }
+ var total = this.ds.getTotalCount();
+ if(this.footer.pageSize < total){
+ this.mainFoot.show();
+ }
}
- this.fireEvent('valid', this);
+ Roo.each(this.el.select('tbody td', true).elements, function(e){
+ e.on('mouseover', _this.onMouseover, _this);
+ });
+
+ Roo.each(this.el.select('tbody td', true).elements, function(e){
+ e.on('mouseout', _this.onMouseout, _this);
+ });
+ this.fireEvent('rowsrendered', this);
+
+ this.autoSize();
+
+ this.initCSS(); /// resize cols
+
+
},
- /**
- * Mark this field as valid
- */
- markValid : function()
+
+ onUpdate : function(ds,record)
{
- if(!this.el || this.preventMark){ // not rendered
- return;
+ this.refreshRow(record);
+ this.autoSize();
+ },
+
+ onRemove : function(ds, record, index, isUpdate){
+ if(isUpdate !== true){
+ this.fireEvent("beforerowremoved", this, index, record);
}
+ var bt = this.bodyEl.dom;
- this.el.removeClass([this.invalidClass, this.validClass]);
- this.inputEl().removeClass(['is-valid', 'is-invalid']);
+ var rows = this.el.select('tbody > tr', true).elements;
- var feedback = this.el.select('.form-control-feedback', true).first();
-
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
- }
-
- if(this.disabled || this.allowBlank){
- return;
+ if(typeof(rows[index]) != 'undefined'){
+ bt.removeChild(rows[index].dom);
}
- var label = this.el.select('label', true).first();
- var icon = this.el.select('i.fa-star', true).first();
+// if(bt.rows[index]){
+// bt.removeChild(bt.rows[index]);
+// }
- if(label && icon){
- icon.remove();
+ if(isUpdate !== true){
+ //this.stripeRows(index);
+ //this.syncRowHeights(index, index);
+ //this.layout();
+ this.fireEvent("rowremoved", this, index, record);
}
- if (Roo.bootstrap.version == 3) {
- this.el.addClass(this.validClass);
- } else {
- this.inputEl().addClass('is-valid');
+ },
+
+ onAdd : function(ds, records, rowIndex)
+ {
+ //Roo.log('on Add called');
+ // - note this does not handle multiple adding very well..
+ var bt = this.bodyEl.dom;
+ for (var i =0 ; i < records.length;i++) {
+ //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
+ //Roo.log(records[i]);
+ //Roo.log(this.store.getAt(rowIndex+i));
+ this.insertRow(this.store, rowIndex + i, false);
+ return;
}
-
- if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
-
- var feedback = this.el.select('.form-control-feedback', true).first();
-
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
- this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
+ },
+
+
+ refreshRow : function(record){
+ var ds = this.store, index;
+ if(typeof record == 'number'){
+ index = record;
+ record = ds.getAt(index);
+ }else{
+ index = ds.indexOf(record);
+ if (index < 0) {
+ return; // should not happen - but seems to
}
-
}
-
- this.fireEvent('valid', this);
+ this.insertRow(ds, index, true);
+ this.autoSize();
+ this.onRemove(ds, record, index+1, true);
+ this.autoSize();
+ //this.syncRowHeights(index, index);
+ //this.layout();
+ this.fireEvent("rowupdated", this, index, record);
+ },
+ // private - called by RowSelection
+ onRowSelect : function(rowIndex){
+ var row = this.getRowDom(rowIndex);
+ row.addClass(['bg-info','info']);
+ },
+ // private - called by RowSelection
+ onRowDeselect : function(rowIndex)
+ {
+ if (rowIndex < 0) {
+ return;
+ }
+ var row = this.getRowDom(rowIndex);
+ row.removeClass(['bg-info','info']);
+ },
+ /**
+ * Focuses the specified row.
+ * @param {Number} row The row index
+ */
+ focusRow : function(row)
+ {
+ //Roo.log('GridView.focusRow');
+ var x = this.bodyEl.dom.scrollLeft;
+ this.focusCell(row, 0, false);
+ this.bodyEl.dom.scrollLeft = x;
+
+ },
+ /**
+ * Focuses the specified cell.
+ * @param {Number} row The row index
+ * @param {Number} col The column index
+ * @param {Boolean} hscroll false to disable horizontal scrolling
+ */
+ focusCell : function(row, col, hscroll)
+ {
+ //Roo.log('GridView.focusCell');
+ var el = this.ensureVisible(row, col, hscroll);
+ // not sure what focusEL achives = it's a <a> pos relative
+ //this.focusEl.alignTo(el, "tl-tl");
+ //if(Roo.isGecko){
+ // this.focusEl.focus();
+ //}else{
+ // this.focusEl.focus.defer(1, this.focusEl);
+ //}
},
/**
- * Mark this field as invalid
- * @param {String} msg The validation message
+ * Scrolls the specified cell into view
+ * @param {Number} row The row index
+ * @param {Number} col The column index
+ * @param {Boolean} hscroll false to disable horizontal scrolling
*/
- markInvalid : function(msg)
+ ensureVisible : function(row, col, hscroll)
{
- if(!this.el || this.preventMark){ // not rendered
- return;
+ //Roo.log('GridView.ensureVisible,' + row + ',' + col);
+ //return null; //disable for testing.
+ if(typeof row != "number"){
+ row = row.rowIndex;
}
-
- this.el.removeClass([this.invalidClass, this.validClass]);
- this.inputEl().removeClass(['is-valid', 'is-invalid']);
-
- var feedback = this.el.select('.form-control-feedback', true).first();
-
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+ if(row < 0 && row >= this.ds.getCount()){
+ return null;
+ }
+ col = (col !== undefined ? col : 0);
+ var cm = this.cm;
+ while(cm.isHidden(col)){
+ col++;
}
- if(this.disabled || this.allowBlank){
- return;
+ var el = this.getCellDom(row, col);
+ if(!el){
+ return null;
}
-
- var label = this.el.select('label', true).first();
- var icon = this.el.select('i.fa-star', true).first();
-
- if(!this.getValue().length && label && !icon){
- this.el.createChild({
- tag : 'i',
- cls : 'text-danger fa fa-lg fa-star',
- tooltip : 'This field is required',
- style : 'margin-right:5px;'
- }, label, true);
+ var c = this.bodyEl.dom;
+
+ var ctop = parseInt(el.offsetTop, 10);
+ var cleft = parseInt(el.offsetLeft, 10);
+ var cbot = ctop + el.offsetHeight;
+ var cright = cleft + el.offsetWidth;
+
+ //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
+ var ch = 0; //?? header is not withing the area?
+ var stop = parseInt(c.scrollTop, 10);
+ var sleft = parseInt(c.scrollLeft, 10);
+ var sbot = stop + ch;
+ var sright = sleft + c.clientWidth;
+ /*
+ Roo.log('GridView.ensureVisible:' +
+ ' ctop:' + ctop +
+ ' c.clientHeight:' + c.clientHeight +
+ ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
+ ' stop:' + stop +
+ ' cbot:' + cbot +
+ ' sbot:' + sbot +
+ ' ch:' + ch
+ );
+ */
+ if(ctop < stop){
+ c.scrollTop = ctop;
+ //Roo.log("set scrolltop to ctop DISABLE?");
+ }else if(cbot > sbot){
+ //Roo.log("set scrolltop to cbot-ch");
+ c.scrollTop = cbot-ch;
+ }
+
+ if(hscroll !== false){
+ if(cleft < sleft){
+ c.scrollLeft = cleft;
+ }else if(cright > sright){
+ c.scrollLeft = cright-c.clientWidth;
+ }
}
+
+ return el;
+ },
+
+
+ insertRow : function(dm, rowIndex, isUpdate){
- if (Roo.bootstrap.version == 3) {
- this.el.addClass(this.invalidClass);
- } else {
- this.inputEl().addClass('is-invalid');
+ if(!isUpdate){
+ this.fireEvent("beforerowsinserted", this, rowIndex);
}
+ //var s = this.getScrollState();
+ var row = this.renderRow(this.cm, this.store, rowIndex);
+ // insert before rowIndex..
+ var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
- // fixme ... this may be depricated need to test..
- if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
-
- var feedback = this.el.select('.form-control-feedback', true).first();
-
- if(feedback){
- this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
-
- if(this.getValue().length || this.forceFeedback){
- this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
- }
+ var _this = this;
- }
+ if(row.cellObjects.length){
+ Roo.each(row.cellObjects, function(r){
+ _this.renderCellObject(r);
+ })
+ }
+ if(!isUpdate){
+ this.fireEvent("rowsinserted", this, rowIndex);
+ //this.syncRowHeights(firstRow, lastRow);
+ //this.stripeRows(firstRow);
+ //this.layout();
}
- this.fireEvent('invalid', this, msg);
- }
-});
-
-
-/*
- * - LGPL
- *
- * trigger field - base class for combo..
- *
- */
-
-/**
- * @class Roo.bootstrap.TriggerField
- * @extends Roo.bootstrap.Input
- * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
- * The trigger has no default action, so you must assign a function to implement the trigger click handler by
- * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
- * for which you can provide a custom implementation. For example:
- * <pre><code>
-var trigger = new Roo.bootstrap.TriggerField();
-trigger.onTriggerClick = myTriggerFn;
-trigger.applyTo('my-field');
-</code></pre>
- *
- * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
- * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
- * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
- * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
- * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
-
- * @constructor
- * Create a new TriggerField.
- * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
- * to the base TextField)
- */
-Roo.bootstrap.TriggerField = function(config){
- this.mimicing = false;
- Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
- /**
- * @cfg {String} triggerClass A CSS class to apply to the trigger
- */
- /**
- * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
- */
- hideTrigger:false,
-
- /**
- * @cfg {Boolean} removable (true|false) special filter default false
- */
- removable : false,
-
- /** @cfg {Boolean} grow @hide */
- /** @cfg {Number} growMin @hide */
- /** @cfg {Number} growMax @hide */
-
- /**
- * @hide
- * @method
- */
- autoSize: Roo.emptyFn,
- // private
- monitorTab : true,
- // private
- deferHeight : true,
-
-
- actionMode : 'wrap',
-
- caret : false,
+ },
- getAutoCreate : function(){
-
- var align = this.labelAlign || this.parentLabelAlign();
-
- var id = Roo.id();
-
- var cfg = {
- cls: 'form-group' //input-group
- };
-
+ getRowDom : function(rowIndex)
+ {
+ var rows = this.el.select('tbody > tr', true).elements;
- var input = {
- tag: 'input',
- id : id,
- type : this.inputType,
- cls : 'form-control',
- autocomplete: 'new-password',
- placeholder : this.placeholder || ''
-
- };
- if (this.name) {
- input.name = this.name;
- }
- if (this.size) {
- input.cls += ' input-' + this.size;
- }
+ return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
- if (this.disabled) {
- input.disabled=true;
+ },
+ getCellDom : function(rowIndex, colIndex)
+ {
+ var row = this.getRowDom(rowIndex);
+ if (row === false) {
+ return false;
}
+ var cols = row.select('td', true).elements;
+ return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
- var inputblock = input;
+ },
+
+ // returns the object tree for a tr..
+
+
+ renderRow : function(cm, ds, rowIndex)
+ {
+ var d = ds.getAt(rowIndex);
- if(this.hasFeedback && !this.allowBlank){
-
- var feedback = {
- tag: 'span',
- cls: 'glyphicon form-control-feedback'
- };
+ var row = {
+ tag : 'tr',
+ cls : 'x-row-' + rowIndex,
+ cn : []
+ };
- if(this.removable && !this.editable && !this.tickable){
- inputblock = {
- cls : 'has-feedback',
- cn : [
- inputblock,
- {
- tag: 'button',
- html : 'x',
- cls : 'roo-combo-removable-btn close'
- },
- feedback
- ]
- };
- } else {
- inputblock = {
- cls : 'has-feedback',
- cn : [
- inputblock,
- feedback
- ]
- };
- }
-
- } else {
- if(this.removable && !this.editable && !this.tickable){
- inputblock = {
- cls : 'roo-removable',
- cn : [
- inputblock,
- {
- tag: 'button',
- html : 'x',
- cls : 'roo-combo-removable-btn close'
- }
- ]
- };
- }
- }
+ var cellObjects = [];
- if (this.before || this.after) {
-
- inputblock = {
- cls : 'input-group',
- cn : []
- };
- if (this.before) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon input-group-prepend input-group-text',
- html : this.before
- });
- }
-
- inputblock.cn.push(input);
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++){
+ var config = cm.config[i];
- if(this.hasFeedback && !this.allowBlank){
- inputblock.cls += ' has-feedback';
- inputblock.cn.push(feedback);
- }
+ var renderer = cm.getRenderer(i);
+ var value = '';
+ var id = false;
- if (this.after) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon input-group-append input-group-text',
- html : this.after
- });
+ if(typeof(renderer) !== 'undefined'){
+ value = renderer(d.data[cm.getDataIndex(i)], false, d);
}
+ // if object are returned, then they are expected to be Roo.bootstrap.Component instances
+ // and are rendered into the cells after the row is rendered - using the id for the element.
- };
-
-
-
- var ibwrap = inputblock;
-
- if(this.multiple){
- ibwrap = {
- tag: 'ul',
- cls: 'roo-select2-choices',
- cn:[
- {
- tag: 'li',
- cls: 'roo-select2-search-field',
- cn: [
-
- inputblock
- ]
- }
- ]
- };
-
- }
-
- var combobox = {
- cls: 'roo-select2-container input-group',
- cn: [
- {
- tag: 'input',
- type : 'hidden',
- cls: 'form-hidden-field'
- },
- ibwrap
- ]
- };
-
- if(!this.multiple && this.showToggleBtn){
-
- var caret = {
- tag: 'span',
- cls: 'caret'
- };
- if (this.caret != false) {
- caret = {
- tag: 'i',
- cls: 'fa fa-' + this.caret
- };
-
+ if(typeof(value) === 'object'){
+ id = Roo.id();
+ cellObjects.push({
+ container : id,
+ cfg : value
+ })
}
- combobox.cn.push({
- tag :'span',
- cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
- cn : [
- caret,
- {
- tag: 'span',
- cls: 'combobox-clear',
- cn : [
- {
- tag : 'i',
- cls: 'icon-remove'
- }
- ]
- }
- ]
-
- })
- }
-
- if(this.multiple){
- combobox.cls += ' roo-select2-container-multi';
- }
- var indicator = {
- tag : 'i',
- cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- };
- if (Roo.bootstrap.version == 4) {
- indicator = {
- tag : 'i',
- style : 'display:none'
+ var rowcfg = {
+ record: d,
+ rowIndex : rowIndex,
+ colIndex : i,
+ rowClass : ''
};
- }
-
-
- if (align ==='left' && this.fieldLabel.length) {
-
- cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
-
- cfg.cn = [
- indicator,
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label',
- html : this.fieldLabel
-
- },
- {
- cls : "",
- cn: [
- combobox
- ]
- }
- ];
+ this.fireEvent('rowclass', this, rowcfg);
- var labelCfg = cfg.cn[1];
- var contentCfg = cfg.cn[2];
+ var td = {
+ tag: 'td',
+ // this might end up displaying HTML?
+ // this is too messy... - better to only do it on columsn you know are going to be too long
+ //tooltip : (typeof(value) === 'object') ? '' : value,
+ cls : rowcfg.rowClass + ' x-col-' + i,
+ style: '',
+ html: (typeof(value) === 'object') ? '' : value
+ };
- if(this.indicatorpos == 'right'){
- cfg.cn = [
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label',
- cn : [
- {
- tag : 'span',
- html : this.fieldLabel
- },
- indicator
- ]
- },
- {
- cls : "",
- cn: [
- combobox
- ]
- }
-
- ];
-
- labelCfg = cfg.cn[0];
- contentCfg = cfg.cn[1];
+ if (id) {
+ td.id = id;
}
- if(this.labelWidth > 12){
- labelCfg.style = "width: " + this.labelWidth + 'px';
+ if(typeof(config.colspan) != 'undefined'){
+ td.colspan = config.colspan;
}
- if(this.labelWidth < 13 && this.labelmd == 0){
- this.labelmd = this.labelWidth;
- }
- if(this.labellg > 0){
- labelCfg.cls += ' col-lg-' + this.labellg;
- contentCfg.cls += ' col-lg-' + (12 - this.labellg);
- }
- if(this.labelmd > 0){
- labelCfg.cls += ' col-md-' + this.labelmd;
- contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ if(typeof(config.align) != 'undefined' && config.align.length){
+ td.style += ' text-align:' + config.align + ';';
}
-
- if(this.labelsm > 0){
- labelCfg.cls += ' col-sm-' + this.labelsm;
- contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ if(typeof(config.valign) != 'undefined' && config.valign.length){
+ td.style += ' vertical-align:' + config.valign + ';';
}
-
- if(this.labelxs > 0){
- labelCfg.cls += ' col-xs-' + this.labelxs;
- contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ /*
+ if(typeof(config.width) != 'undefined'){
+ td.style += ' width:' + config.width + 'px;';
}
+ */
- } else if ( this.fieldLabel.length) {
-// Roo.log(" label");
- cfg.cn = [
- indicator,
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
-
- },
-
- combobox
-
- ];
-
- if(this.indicatorpos == 'right'){
-
- cfg.cn = [
- {
- tag: 'label',
- cn : [
- {
- tag : 'span',
- html : this.fieldLabel
- },
- indicator
- ]
-
- },
- combobox
-
- ];
-
+ if(typeof(config.cursor) != 'undefined'){
+ td.style += ' cursor:' + config.cursor + ';';
}
-
- } else {
-// Roo.log(" no label && no align");
- cfg = combobox
+ if(typeof(config.cls) != 'undefined'){
+ td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
+ }
+ if (this.responsive) {
+ ['xs','sm','md','lg'].map(function(size){
+
+ if(typeof(config[size]) == 'undefined'){
+ return;
+ }
+
+
+
+ if (!config[size]) { // 0 = hidden
+ // BS 4 '0' is treated as hide that column and below.
+ td.cls += ' hidden-' + size + ' hidden' + size + '-down';
+ return;
+ }
+
+ td.cls += ' col-' + size + '-' + config[size] + (
+ size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
+ );
-
- }
-
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
+
+ });
}
- });
+ row.cn.push(td);
+
+ }
- return cfg;
+ row.cellObjects = cellObjects;
+ return row;
+
},
- // private
- onResize : function(w, h){
-// Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
-// if(typeof w == 'number'){
-// var x = w - this.trigger.getWidth();
-// this.inputEl().setWidth(this.adjustWidth('input', x));
-// this.trigger.setStyle('left', x+'px');
-// }
+ onBeforeLoad : function()
+ {
+ this.el.unmask(); // if needed.
},
-
- // private
- adjustSize : Roo.BoxComponent.prototype.adjustSize,
-
- // private
- getResizeEl : function(){
- return this.inputEl();
- },
-
- // private
- getPositionEl : function(){
- return this.inputEl();
- },
-
- // private
- alignErrorIcon : function(){
- this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
+ /**
+ * Remove all rows
+ */
+ clear : function()
+ {
+ this.el.select('tbody', true).first().dom.innerHTML = '';
},
-
- // private
- initEvents : function(){
+ /**
+ * Show or hide a row.
+ * @param {Number} rowIndex to show or hide
+ * @param {Boolean} state hide
+ */
+ setRowVisibility : function(rowIndex, state)
+ {
+ var bt = this.bodyEl.dom;
- this.createList();
+ var rows = this.el.select('tbody > tr', true).elements;
- Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
- //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
- if(!this.multiple && this.showToggleBtn){
- this.trigger = this.el.select('span.dropdown-toggle',true).first();
- if(this.hideTrigger){
- this.trigger.setDisplayed(false);
- }
- this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
+ if(typeof(rows[rowIndex]) == 'undefined'){
+ return;
}
+ rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
- if(this.multiple){
- this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
+ },
+
+
+ getSelectionModel : function(){
+ if(!this.selModel){
+ this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
}
+ return this.selModel;
+ },
+ /*
+ * Render the Roo.bootstrap object from renderder
+ */
+ renderCellObject : function(r)
+ {
+ var _this = this;
- if(this.removable && !this.editable && !this.tickable){
- var close = this.closeTriggerEl();
-
- if(close){
- close.setVisibilityMode(Roo.Element.DISPLAY).hide();
- close.on('click', this.removeBtnClick, this, close);
- }
+ r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
+
+ var t = r.cfg.render(r.container);
+
+ if(r.cfg.cn){
+ Roo.each(r.cfg.cn, function(c){
+ var child = {
+ container: t.getChildContainer(),
+ cfg: c
+ };
+ _this.renderCellObject(child);
+ })
}
+ },
+ /**
+ * get the Row Index from a dom element.
+ * @param {Roo.Element} row The row to look for
+ * @returns {Number} the row
+ */
+ getRowIndex : function(row)
+ {
+ var rowIndex = -1;
- //this.trigger.addClassOnOver('x-form-trigger-over');
- //this.trigger.addClassOnClick('x-form-trigger-click');
+ Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
+ if(el != row){
+ return;
+ }
+
+ rowIndex = index;
+ });
- //if(!this.width){
- // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
- //}
+ return rowIndex;
},
-
- closeTriggerEl : function()
+ /**
+ * get the header TH element for columnIndex
+ * @param {Number} columnIndex
+ * @returns {Roo.Element}
+ */
+ getHeaderIndex: function(colIndex)
{
- var close = this.el.select('.roo-combo-removable-btn', true).first();
- return close ? close : false;
+ var cols = this.headEl.select('th', true).elements;
+ return cols[colIndex];
},
-
- removeBtnClick : function(e, h, el)
+ /**
+ * get the Column Index from a dom element. (using regex on x-hcol-{colid})
+ * @param {domElement} cell to look for
+ * @returns {Number} the column
+ */
+ getCellIndex : function(cell)
{
- e.preventDefault();
-
- if(this.fireEvent("remove", this) !== false){
- this.reset();
- this.fireEvent("afterremove", this)
+ var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
+ if(id){
+ return parseInt(id[1], 10);
}
+ return 0;
},
-
- createList : function()
+ /**
+ * Returns the grid's underlying element = used by panel.Grid
+ * @return {Element} The element
+ */
+ getGridEl : function(){
+ return this.el;
+ },
+ /**
+ * Forces a resize - used by panel.Grid
+ * @return {Element} The element
+ */
+ autoSize : function()
{
- this.list = Roo.get(document.body).createChild({
- tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
- cls: 'typeahead typeahead-long dropdown-menu',
- style: 'display:none'
- });
+ //var ctr = Roo.get(this.container.dom.parentElement);
+ var ctr = Roo.get(this.el.dom);
- this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
+ var thd = this.getGridEl().select('thead',true).first();
+ var tbd = this.getGridEl().select('tbody', true).first();
+ var tfd = this.getGridEl().select('tfoot', true).first();
- },
-
- // private
- initTrigger : function(){
-
- },
-
- // private
- onDestroy : function(){
- if(this.trigger){
- this.trigger.removeAllListeners();
- // this.trigger.remove();
+ var cw = ctr.getWidth();
+ this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
+
+ if (tbd) {
+
+ tbd.setWidth(ctr.getWidth());
+ // if the body has a max height - and then scrolls - we should perhaps set up the height here
+ // this needs fixing for various usage - currently only hydra job advers I think..
+ //tdb.setHeight(
+ // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
+ //);
+ var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
+ cw -= barsize;
}
- //if(this.wrap){
- // this.wrap.remove();
- //}
- Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
+ cw = Math.max(cw, this.totalWidth);
+ this.getGridEl().select('tbody tr',true).setWidth(cw);
+ this.initCSS();
+
+ // resize 'expandable coloumn?
+
+ return; // we doe not have a view in this design..
+
},
-
- // private
- onFocus : function(){
- Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
- /*
- if(!this.mimicing){
- this.wrap.addClass('x-trigger-wrap-focus');
- this.mimicing = true;
- Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
- if(this.monitorTab){
- this.el.on("keydown", this.checkTab, this);
+ onBodyScroll: function()
+ {
+ //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
+ if(this.headEl){
+ this.headEl.setStyle({
+ 'position' : 'relative',
+ 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
+ });
+ }
+
+ if(this.lazyLoad){
+
+ var scrollHeight = this.bodyEl.dom.scrollHeight;
+
+ var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
+
+ var height = this.bodyEl.getHeight();
+
+ if(scrollHeight - height == scrollTop) {
+
+ var total = this.ds.getTotalCount();
+
+ if(this.footer.cursor + this.footer.pageSize < total){
+
+ this.footer.ds.load({
+ params : {
+ start : this.footer.cursor + this.footer.pageSize,
+ limit : this.footer.pageSize
+ },
+ add : true
+ });
+ }
}
+
}
- */
},
-
- // private
- checkTab : function(e){
- if(e.getKey() == e.TAB){
- this.triggerBlur();
- }
+ onColumnSplitterMoved : function(i, diff)
+ {
+ this.userResized = true;
+
+ var cm = this.colModel;
+
+ var w = this.getHeaderIndex(i).getWidth() + diff;
+
+
+ cm.setColumnWidth(i, w, true);
+ this.initCSS();
+ //var cid = cm.getColumnId(i); << not used in this version?
+ /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
+
+ this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
+ this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
+ this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
+*/
+ //this.updateSplitters();
+ //this.layout(); << ??
+ this.fireEvent("columnresize", i, w);
},
-
- // private
- onBlur : function(){
- // do nothing
+ onHeaderChange : function()
+ {
+ var header = this.renderHeader();
+ var table = this.el.select('table', true).first();
+
+ this.headEl.remove();
+ this.headEl = table.createChild(header, this.bodyEl, false);
+
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.on('click', this.sort, this);
+ }, this);
+
+ if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
+ new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
+ }
+
},
-
- // private
- mimicBlur : function(e, t){
+
+ onHiddenChange : function(colModel, colIndex, hidden)
+ {
/*
- if(!this.wrap.contains(t) && this.validateBlur()){
- this.triggerBlur();
+ this.cm.setHidden()
+ var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
+ var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
+
+ this.CSS.updateRule(thSelector, "display", "");
+ this.CSS.updateRule(tdSelector, "display", "");
+
+ if(hidden){
+ this.CSS.updateRule(thSelector, "display", "none");
+ this.CSS.updateRule(tdSelector, "display", "none");
}
*/
+ // onload calls initCSS()
+ this.onHeaderChange();
+ this.onLoad();
},
-
- // private
- triggerBlur : function(){
- this.mimicing = false;
- Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
- if(this.monitorTab){
- this.el.un("keydown", this.checkTab, this);
+
+ setColumnWidth: function(col_index, width)
+ {
+ // width = "md-2 xs-2..."
+ if(!this.colModel.config[col_index]) {
+ return;
}
- //this.wrap.removeClass('x-trigger-wrap-focus');
- Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
- },
-
- // private
- // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
- validateBlur : function(e, t){
- return true;
- },
-
- // private
- onDisable : function(){
- this.inputEl().dom.disabled = true;
- //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
- //if(this.wrap){
- // this.wrap.addClass('x-item-disabled');
- //}
- },
-
- // private
- onEnable : function(){
- this.inputEl().dom.disabled = false;
- //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
- //if(this.wrap){
- // this.el.removeClass('x-item-disabled');
- //}
- },
-
- // private
- onShow : function(){
- var ae = this.getActionEl();
- if(ae){
- ae.dom.style.display = '';
- ae.dom.style.visibility = 'visible';
+ var w = width.split(" ");
+
+ var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
+
+ var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
+
+
+ for(var j = 0; j < w.length; j++) {
+
+ if(!w[j]) {
+ continue;
+ }
+
+ var size_cls = w[j].split("-");
+
+ if(!Number.isInteger(size_cls[1] * 1)) {
+ continue;
+ }
+
+ if(!this.colModel.config[col_index][size_cls[0]]) {
+ continue;
+ }
+
+ if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
+ continue;
+ }
+
+ h_row[0].classList.replace(
+ "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
+ "col-"+size_cls[0]+"-"+size_cls[1]
+ );
+
+ for(var i = 0; i < rows.length; i++) {
+
+ var size_cls = w[j].split("-");
+
+ if(!Number.isInteger(size_cls[1] * 1)) {
+ continue;
+ }
+
+ if(!this.colModel.config[col_index][size_cls[0]]) {
+ continue;
+ }
+
+ if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
+ continue;
+ }
+
+ rows[i].classList.replace(
+ "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
+ "col-"+size_cls[0]+"-"+size_cls[1]
+ );
+ }
+
+ this.colModel.config[col_index][size_cls[0]] = size_cls[1];
}
- },
+ }
+});
- // private
-
- onHide : function(){
- var ae = this.getActionEl();
- ae.dom.style.display = 'none';
- },
+// currently only used to find the split on drag..
+Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
- /**
- * The function that should handle the trigger's click event. This method does nothing by default until overridden
- * by an implementing function.
- * @method
- * @param {EventObject} e
- */
- onTriggerClick : Roo.emptyFn
-});
- /*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
+/**
+ * @depricated
+*/
+Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
+Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
+/*
+ * - LGPL
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * table cell
+ *
*/
-
/**
- * @class Roo.data.SortTypes
- * @singleton
- * Defines the default sorting (casting?) comparison functions used when sorting data.
+ * @class Roo.bootstrap.TableCell
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
+ * @parent Roo.bootstrap.TableRow
+ * 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.data.SortTypes = {
- /**
- * Default sort that does nothing
- * @param {Mixed} s The value being converted
- * @return {Mixed} The comparison value
- */
- none : function(s){
- return s;
- },
-
- /**
- * The regular expression used to strip tags
- * @type {RegExp}
- * @property
- */
- stripTagsRE : /<\/?[^>]+>/gi,
-
- /**
- * Strips all HTML tags to sort on text only
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asText : function(s){
- return String(s).replace(this.stripTagsRE, "");
- },
+
+Roo.bootstrap.TableCell = function(config){
+ Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
- /**
- * Strips all HTML tags to sort on text only - Case insensitive
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asUCText : function(s){
- return String(s).toUpperCase().replace(this.stripTagsRE, "");
- },
+ 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,
- /**
- * Case insensitive string
- * @param {Mixed} s The value being converted
- * @return {String} The comparison value
- */
- asUCString : function(s) {
- return String(s).toUpperCase();
- },
- /**
- * Date sorting
- * @param {Mixed} s The value being converted
- * @return {Number} The comparison value
- */
- asDate : function(s) {
- if(!s){
- return 0;
+ getAutoCreate : function(){
+ var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
+
+ cfg = {
+ tag: 'td'
+ };
+
+ if(this.tag){
+ cfg.tag = this.tag;
}
- if(s instanceof Date){
- return s.getTime();
+
+ if (this.html) {
+ cfg.html=this.html
}
- return Date.parse(String(s));
- },
-
- /**
- * Float sorting
- * @param {Mixed} s The value being converted
- * @return {Float} The comparison value
- */
- asFloat : function(s) {
- var val = parseFloat(String(s).replace(/,/g, ""));
- if(isNaN(val)) {
- val = 0;
+ if (this.cls) {
+ cfg.cls=this.cls
}
- return val;
- },
-
- /**
- * Integer sorting
- * @param {Mixed} s The value being converted
- * @return {Number} The comparison value
- */
- asInt : function(s) {
- var val = parseInt(String(s).replace(/,/g, ""));
- if(isNaN(val)) {
- val = 0;
+ if (this.abbr) {
+ cfg.abbr=this.abbr
}
- return val;
+ if (this.align) {
+ cfg.align=this.align
+ }
+ if (this.axis) {
+ cfg.axis=this.axis
+ }
+ if (this.bgcolor) {
+ cfg.bgcolor=this.bgcolor
+ }
+ if (this.charoff) {
+ cfg.charoff=this.charoff
+ }
+ if (this.colspan) {
+ cfg.colspan=this.colspan
+ }
+ if (this.headers) {
+ cfg.headers=this.headers
+ }
+ if (this.height) {
+ cfg.height=this.height
+ }
+ if (this.nowrap) {
+ cfg.nowrap=this.nowrap
+ }
+ if (this.rowspan) {
+ cfg.rowspan=this.rowspan
+ }
+ if (this.scope) {
+ cfg.scope=this.scope
+ }
+ if (this.valign) {
+ cfg.valign=this.valign
+ }
+ if (this.width) {
+ cfg.width=this.width
+ }
+
+
+ return cfg;
}
-};/*
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * table row
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.TableRow
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.TableCell
+ * @parent Roo.bootstrap.TableBody
+ * 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
+ * @children Roo.bootstrap.TableRow
+ * @parent Roo.bootstrap.Table
+ * 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.
* <script type="text/javascript">
*/
-/**
-* @class Roo.data.Record
- * Instances of this class encapsulate both record <em>definition</em> information, and record
- * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
- * to access Records cached in an {@link Roo.data.Store} object.<br>
- * <p>
- * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
- * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
- * objects.<br>
- * <p>
- * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
+// as we use this in bootstrap.
+Roo.namespace('Roo.form');
+ /**
+ * @class Roo.form.Action
+ * Internal Class used to handle form actions
* @constructor
- * This constructor should not be used to create Record objects. Instead, use the constructor generated by
- * {@link #create}. The parameters are the same.
- * @param {Array} data An associative Array of data values keyed by the field name.
- * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
- * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
- * not specified an integer id is generated.
+ * @param {Roo.form.BasicForm} el The form element or its id
+ * @param {Object} config Configuration options
*/
-Roo.data.Record = function(data, id){
- this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
- this.data = data;
-};
+
+
+// define the action interface
+Roo.form.Action = function(form, options){
+ this.form = form;
+ this.options = options || {};
+};
/**
- * Generate a constructor for a specific record layout.
- * @param {Array} o An Array of field definition objects which specify field names, and optionally,
- * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
- * Each field definition object may contain the following properties: <ul>
- * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
- * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
- * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
- * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
- * is being used, then this is a string containing the javascript expression to reference the data relative to
- * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
- * to the data item relative to the record element. If the mapping expression is the same as the field name,
- * this may be omitted.</p></li>
- * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
- * <ul><li>auto (Default, implies no conversion)</li>
- * <li>string</li>
- * <li>int</li>
- * <li>float</li>
- * <li>boolean</li>
- * <li>date</li></ul></p></li>
- * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
- * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
- * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
- * by the Reader into an object that will be stored in the Record. It is passed the
- * following parameters:<ul>
- * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
- * </ul></p></li>
- * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
- * </ul>
- * <br>usage:<br><pre><code>
-var TopicRecord = Roo.data.Record.create(
- {name: 'title', mapping: 'topic_title'},
- {name: 'author', mapping: 'username'},
- {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
- {name: 'lastPost', mapping: 'post_time', type: 'date'},
- {name: 'lastPoster', mapping: 'user2'},
- {name: 'excerpt', mapping: 'post_text'}
-);
-
-var myNewRecord = new TopicRecord({
- title: 'Do my job please',
- author: 'noobie',
- totalPosts: 1,
- lastPost: new Date(),
- lastPoster: 'Animal',
- excerpt: 'No way dude!'
-});
-myStore.add(myNewRecord);
-</code></pre>
- * @method create
- * @static
+ * Client Validation Failed
+ * @const
*/
-Roo.data.Record.create = function(o){
- var f = function(){
- f.superclass.constructor.apply(this, arguments);
- };
- Roo.extend(f, Roo.data.Record);
- var p = f.prototype;
- p.fields = new Roo.util.MixedCollection(false, function(field){
- return field.name;
- });
- for(var i = 0, len = o.length; i < len; i++){
- p.fields.add(new Roo.data.Field(o[i]));
- }
- f.getField = function(name){
- return p.fields.get(name);
- };
- return f;
-};
+Roo.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.data.Record.AUTO_ID = 1000;
-Roo.data.Record.EDIT = 'edit';
-Roo.data.Record.REJECT = 'reject';
-Roo.data.Record.COMMIT = 'commit';
+Roo.form.Action.prototype = {
+ type : 'default',
+ failureType : undefined,
+ response : undefined,
+ result : undefined,
-Roo.data.Record.prototype = {
- /**
- * Readonly flag - true if this record has been modified.
- * @type Boolean
- */
- dirty : false,
- editing : false,
- error: null,
- modified: null,
+ // interface method
+ run : function(options){
- // private
- join : function(store){
- this.store = store;
},
- /**
- * Set the named field to the specified value.
- * @param {String} name The name of the field to set.
- * @param {Object} value The value to set the field to.
- */
- set : function(name, value){
- if(this.data[name] == value){
- return;
- }
- this.dirty = true;
- if(!this.modified){
- this.modified = {};
- }
- if(typeof this.modified[name] == 'undefined'){
- this.modified[name] = this.data[name];
- }
- this.data[name] = value;
- if(!this.editing && this.store){
- this.store.afterEdit(this);
- }
- },
+ // interface method
+ success : function(response){
- /**
- * Get the value of the named field.
- * @param {String} name The name of the field to get the value of.
- * @return {Object} The value of the field.
- */
- get : function(name){
- return this.data[name];
},
- // private
- beginEdit : function(){
- this.editing = true;
- this.modified = {};
- },
+ // interface method
+ handleResponse : function(response){
- // private
- cancelEdit : function(){
- this.editing = false;
- delete this.modified;
},
- // private
- endEdit : function(){
- this.editing = false;
- if(this.dirty && this.store){
- this.store.afterEdit(this);
- }
+ // default connection failure
+ failure : function(response){
+
+ this.response = response;
+ this.failureType = Roo.form.Action.CONNECT_FAILURE;
+ this.form.afterAction(this, false);
},
- /**
- * Usually called by the {@link Roo.data.Store} which owns the Record.
- * Rejects all changes made to the Record since either creation, or the last commit operation.
- * Modified fields are reverted to their original values.
- * <p>
- * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
- * of reject operations.
- */
- reject : function(){
- var m = this.modified;
- for(var n in m){
- if(typeof m[n] != "function"){
- this.data[n] = m[n];
- }
- }
- this.dirty = false;
- delete this.modified;
- this.editing = false;
- if(this.store){
- this.store.afterReject(this);
+ processResponse : function(response){
+ this.response = response;
+ if(!response.responseText){
+ return true;
}
+ this.result = this.handleResponse(response);
+ return this.result;
},
- /**
- * Usually called by the {@link Roo.data.Store} which owns the Record.
- * Commits all changes made to the Record since either creation, or the last commit operation.
- * <p>
- * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
- * of commit operations.
- */
- commit : function(){
- this.dirty = false;
- delete this.modified;
- this.editing = false;
- if(this.store){
- this.store.afterCommit(this);
+ // 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;
},
- // private
- hasError : function(){
- return this.error != null;
+ getMethod : function(){
+ return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
},
- // private
- clearError : function(){
- this.error = null;
+ 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;
},
- /**
- * Creates a copy of this record.
- * @param {String} id (optional) A new record id if you don't want to use this record's id
- * @return {Record}
- */
- copy : function(newId) {
- return new this.constructor(Roo.apply({}, this.data), newId || this.id);
+ createCallback : function(){
+ return {
+ success: this.success,
+ failure: this.failure,
+ scope: this,
+ timeout: (this.form.timeout*1000),
+ upload: this.form.fileUpload ? this.success : undefined
+ };
}
-};/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
+};
+Roo.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',
-/**
- * @class Roo.data.Store
- * @extends Roo.util.Observable
- * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
- * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
- * <p>
- * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
- * has no knowledge of the format of the data returned by the Proxy.<br>
- * <p>
- * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
- * instances from the data object. These records are cached and made available through accessor functions.
- * @constructor
- * Creates a new Store.
- * @param {Object} config A config object containing the objects needed for the Store to access data,
- * and read the data into Records.
- */
-Roo.data.Store = function(config){
- this.data = new Roo.util.MixedCollection(false);
- this.data.getKey = function(o){
- return o.id;
- };
- this.baseParams = {};
- // private
- this.paramNames = {
- "start" : "start",
- "limit" : "limit",
- "sort" : "sort",
- "dir" : "dir",
- "multisort" : "_multisort"
- };
+ 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,
+ formData : this.form.formData
+ }));
+
+ this.uploadProgress();
- if(config && config.data){
- this.inlineData = config.data;
- delete config.data;
- }
+ }else if (o.clientValidation !== false){ // client validation failed
+ this.failureType = Roo.form.Action.CLIENT_INVALID;
+ this.form.afterAction(this, false);
+ }
+ },
- Roo.apply(this, config);
+ 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);
+ },
- if(this.reader){ // reader passed
- this.reader = Roo.factory(this.reader, Roo.data);
- this.reader.xmodule = this.xmodule || false;
- if(!this.recordType){
- this.recordType = this.reader.recordType;
+ handleResponse : function(response){
+ if(this.form.errorReader){
+ var rs = this.form.errorReader.read(response);
+ var errors = [];
+ if(rs.records){
+ for(var i = 0, len = rs.records.length; i < len; i++) {
+ var r = rs.records[i];
+ errors[i] = r.data;
+ }
+ }
+ if(errors.length < 1){
+ errors = null;
+ }
+ return {
+ success : rs.success,
+ errors : errors
+ };
}
- if(this.reader.onMetaChange){
- this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
+ 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;
+
}
+});
- if(this.recordType){
- this.fields = this.recordType.prototype.fields;
+
+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);
}
- this.modified = [];
+});
+
+Roo.form.Action.ACTION_TYPES = {
+ 'load' : Roo.form.Action.Load,
+ 'submit' : Roo.form.Action.Submit
+};/*
+ * - LGPL
+ *
+ * form
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.form.Form
+ * @extends Roo.bootstrap.Component
+ * @children 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
+ * @cfg {Boolean} loadMask load mask when submit (default true)
+
+ *
+ * @constructor
+ * Create a new Form
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.form.Form = function(config){
+
+ Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
+
+ Roo.bootstrap.form.Form.popover.apply();
+
this.addEvents({
/**
- * @event datachanged
- * Fires when the data cache has changed, and a widget which is using this Store
- * as a Record cache should refresh its view.
- * @param {Store} this
- */
- datachanged : true,
- /**
- * @event metachange
- * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
- * @param {Store} this
- * @param {Object} meta The JSON metadata
- */
- metachange : true,
- /**
- * @event add
- * Fires when Records have been added to the Store
- * @param {Store} this
- * @param {Roo.data.Record[]} records The array of Records added
- * @param {Number} index The index at which the record(s) were added
- */
- add : true,
- /**
- * @event remove
- * Fires when a Record has been removed from the Store
- * @param {Store} this
- * @param {Roo.data.Record} record The Record that was removed
- * @param {Number} index The index at which the record was removed
- */
- remove : true,
- /**
- * @event update
- * Fires when a Record has been updated
- * @param {Store} this
- * @param {Roo.data.Record} record The Record that was updated
- * @param {String} operation The update operation being performed. Value may be one of:
- * <pre><code>
- Roo.data.Record.EDIT
- Roo.data.Record.REJECT
- Roo.data.Record.COMMIT
- * </code></pre>
- */
- update : true,
- /**
- * @event clear
- * Fires when the data cache has been cleared.
- * @param {Store} this
- */
- clear : true,
- /**
- * @event beforeload
- * Fires before a request is made for a new data object. If the beforeload handler returns false
- * the load action will be canceled.
- * @param {Store} this
- * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ * @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
*/
- beforeload : true,
+ clientvalidation: true,
/**
- * @event beforeloadadd
- * Fires after a new set of Records has been loaded.
- * @param {Store} this
- * @param {Roo.data.Record[]} records The Records that were loaded
- * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ * @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
*/
- beforeloadadd : true,
+ beforeaction: true,
/**
- * @event load
- * Fires after a new set of Records has been loaded, before they are added to the store.
- * @param {Store} this
- * @param {Roo.data.Record[]} records The Records that were loaded
- * @param {Object} options The loading options that were specified (see {@link #load} for details)
- * @params {Object} return from reader
+ * @event actionfailed
+ * Fires when an action fails.
+ * @param {Form} this
+ * @param {Action} action The action that failed
*/
- load : true,
+ actionfailed : true,
/**
- * @event loadexception
- * Fires if an exception occurs in the Proxy during loading.
- * Called with the signature of the Proxy's "loadexception" event.
- * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
- *
- * @param {Proxy}
- * @param {Object} return from JsonData.reader() - success, totalRecords, records
- * @param {Object} load options
- * @param {Object} jsonData from your request (normally this contains the Exception)
+ * @event actioncomplete
+ * Fires when an action is completed.
+ * @param {Form} this
+ * @param {Action} action The action that completed
*/
- loadexception : true
+ actioncomplete : true
});
-
- if(this.proxy){
- this.proxy = Roo.factory(this.proxy, Roo.data);
- this.proxy.xmodule = this.xmodule || false;
- this.relayEvents(this.proxy, ["loadexception"]);
- }
- this.sortToggle = {};
- this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
-
- Roo.data.Store.superclass.constructor.call(this);
-
- if(this.inlineData){
- this.loadData(this.inlineData);
- delete this.inlineData;
- }
};
-Roo.extend(Roo.data.Store, Roo.util.Observable, {
- /**
- * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
- * without a remote query - used by combo/forms at present.
- */
-
- /**
- * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
- */
- /**
- * @cfg {Array} data Inline data to be loaded when the store is initialized.
- */
- /**
- * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
- * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
- */
- /**
- * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
- * on any HTTP request
- */
- /**
- * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
- */
- /**
- * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
- */
- multiSort: false,
- /**
- * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
- * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
- */
- remoteSort : false,
-
- /**
- * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
- * loaded or when a record is removed. (defaults to false).
- */
- pruneModifiedRecords : false,
-
- // private
- lastOptions : null,
+Roo.extend(Roo.bootstrap.form.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',
/**
- * Add Records to the Store and fires the add event.
- * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+ * @cfg {String} url
+ * The URL to use for form actions if one isn't supplied in the action options.
*/
- add : function(records){
- records = [].concat(records);
- for(var i = 0, len = records.length; i < len; i++){
- records[i].join(this);
- }
- var index = this.data.length;
- this.data.addAll(records);
- this.fireEvent("add", this, records, index);
- },
-
/**
- * Remove a Record from the Store and fires the remove event.
- * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
+ * @cfg {Boolean} fileUpload
+ * Set to true if this form is a file upload.
*/
- remove : function(record){
- var index = this.data.indexOf(record);
- this.data.removeAt(index);
-
- if(this.pruneModifiedRecords){
- this.modified.remove(record);
- }
- this.fireEvent("remove", this, record, index);
- },
/**
- * Remove all Records from the Store and fires the clear event.
+ * @cfg {Object} baseParams
+ * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
*/
- removeAll : function(){
- this.data.clear();
- if(this.pruneModifiedRecords){
- this.modified = [];
- }
- this.fireEvent("clear", this);
- },
/**
- * Inserts Records to the Store at the given index and fires the add event.
- * @param {Number} index The start index at which to insert the passed Records.
- * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+ * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
*/
- insert : function(index, records){
- records = [].concat(records);
- for(var i = 0, len = records.length; i < len; i++){
- this.data.insert(index, records[i]);
- records[i].join(this);
- }
- this.fireEvent("add", this, records, index);
- },
-
+ timeout: 30,
/**
- * Get the index within the cache of the passed Record.
- * @param {Roo.data.Record} record The Roo.data.Record object to to find.
- * @return {Number} The index of the passed Record. Returns -1 if not found.
+ * @cfg {Sting} align (left|right) for navbar forms
*/
- indexOf : function(record){
- return this.data.indexOf(record);
- },
+ align : 'left',
+
+ // private
+ activeAction : null,
/**
- * Get the index within the cache of the Record with the passed id.
- * @param {String} id The id of the Record to find.
- * @return {Number} The index of the Record. Returns -1 if not found.
+ * 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
*/
- indexOfId : function(id){
- return this.data.indexOfKey(id);
- },
+ waitMsgTarget : false,
+ loadMask : true,
+
/**
- * Get the Record with the specified id.
- * @param {String} id The id of the Record to find.
- * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
+ * @cfg {Boolean} errorMask (true|false) default false
*/
- getById : function(id){
- return this.data.key(id);
- },
-
+ errorMask : false,
+
/**
- * Get the Record at the specified index.
- * @param {Number} index The index of the Record to find.
- * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
+ * @cfg {Number} maskOffset Default 100
*/
- getAt : function(index){
- return this.data.itemAt(index);
- },
-
+ maskOffset : 100,
+
/**
- * Returns a range of Records between specified indices.
- * @param {Number} startIndex (optional) The starting index (defaults to 0)
- * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
- * @return {Roo.data.Record[]} An array of Records
+ * @cfg {Boolean} maskBody
*/
- getRange : function(start, end){
- return this.data.getRange(start, end);
+ maskBody : false,
+
+ 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 form-inline navbar-' + this.align;
+
+ }
+
+ if (this.labelAlign == 'left' ) {
+ cfg.cls += ' form-horizontal';
+ }
+
+
+ return cfg;
},
+ initEvents : function()
+ {
+ this.el.on('submit', this.onSubmit, this);
+ // this was added as random key presses on the form where triggering form submit.
+ this.el.on('keypress', function(e) {
+ if (e.getCharCode() != 13) {
+ return true;
+ }
+ // we might need to allow it for textareas.. and some other items.
+ // check e.getTarget().
+ if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
+ return true;
+ }
+
+ Roo.log("keypress blocked");
+
+ e.preventDefault();
+ return false;
+ });
+
+ },
// private
- storeOptions : function(o){
- o = Roo.apply({}, o);
- delete o.callback;
- delete o.scope;
- this.lastOptions = o;
+ onSubmit : function(e){
+ e.stopEvent();
},
- /**
- * Loads the Record cache from the configured Proxy using the configured Reader.
- * <p>
- * If using remote paging, then the first load call must specify the <em>start</em>
- * and <em>limit</em> properties in the options.params property to establish the initial
- * position within the dataset, and the number of Records to cache on each read from the Proxy.
- * <p>
- * <strong>It is important to note that for remote data sources, loading is asynchronous,
- * and this call will return before the new data has been loaded. Perform any post-processing
- * in a callback function, or in a "load" event handler.</strong>
- * <p>
- * @param {Object} options An object containing properties which control loading options:<ul>
- * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
- * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
- * passed the following arguments:<ul>
- * <li>r : Roo.data.Record[]</li>
- * <li>options: Options object from the load call</li>
- * <li>success: Boolean success indicator</li></ul></li>
- * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
- * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
- * </ul>
+ /**
+ * Returns true if client-side validation on the form is successful.
+ * @return Boolean
*/
- load : function(options){
- options = options || {};
- if(this.fireEvent("beforeload", this, options) !== false){
- this.storeOptions(options);
- var p = Roo.apply(options.params || {}, this.baseParams);
- // if meta was not loaded from remote source.. try requesting it.
- if (!this.reader.metaFromRemote) {
- p._requestMeta = 1;
- }
- if(this.sortInfo && this.remoteSort){
- var pn = this.paramNames;
- p[pn["sort"]] = this.sortInfo.field;
- p[pn["dir"]] = this.sortInfo.direction;
- }
- if (this.multiSort) {
- var pn = this.paramNames;
- p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
+ isValid : function(){
+ var items = this.getItems();
+ var valid = true;
+ var target = false;
+
+ items.each(function(f){
+
+ if(f.validate()){
+ return;
}
- this.proxy.load(p, this.reader, this.loadRecords, this, options);
+ Roo.log('invalid field: ' + f.name);
+
+ valid = false;
+
+ if(!target && f.el.isVisible(true)){
+ target = f;
+ }
+
+ });
+
+ if(this.errorMask && !valid){
+ Roo.bootstrap.form.Form.popover.mask(this, target);
}
+
+ return valid;
},
-
+
/**
- * Reloads the Record cache from the configured Proxy using the configured Reader and
- * the options from the last load operation performed.
- * @param {Object} options (optional) An object containing properties which may override the options
- * used in the last load operation. See {@link #load} for details (defaults to null, in which case
- * the most recently used options are reused).
+ * Returns true if any fields in this form have changed since their original load.
+ * @return Boolean
*/
- reload : function(options){
- this.load(Roo.applyIf(options||{}, this.lastOptions));
+ isDirty : function(){
+ var dirty = false;
+ var items = this.getItems();
+ items.each(function(f){
+ if(f.isDirty()){
+ dirty = true;
+ return false;
+ }
+ return true;
+ });
+ return dirty;
},
-
- // private
- // Called as a callback by the Reader during a load operation.
- loadRecords : function(o, options, success){
- if(!o || success === false){
- if(success !== false){
- this.fireEvent("load", this, [], options, o);
- }
- if(options.callback){
- options.callback.call(options.scope || this, [], options, false);
- }
- return;
+ /**
+ * 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 data returned failure - throw an exception.
- if (o.success === false) {
- // show a message if no listener is registered.
- if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
- Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
- }
- // loadmask wil be hooked into this..
- this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
- return;
+ if(this.fireEvent('beforeaction', this, action) !== false){
+ this.beforeAction(action);
+ action.run.defer(100, action);
}
- var r = o.records, t = o.totalRecords || r.length;
-
- this.fireEvent("beforeloadadd", this, r, options, o);
+ return this;
+ },
+
+ // private
+ beforeAction : function(action){
+ var o = action.options;
- if(!options || options.add !== true){
- if(this.pruneModifiedRecords){
- this.modified = [];
- }
- for(var i = 0, len = r.length; i < len; i++){
- r[i].join(this);
- }
- if(this.snapshot){
- this.data = this.snapshot;
- delete this.snapshot;
+ if(this.loadMask){
+
+ if(this.maskBody){
+ Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
+ } else {
+ this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
}
- this.data.clear();
- this.data.addAll(r);
- this.totalLength = t;
- this.applySort();
- this.fireEvent("datachanged", this);
- }else{
- this.totalLength = Math.max(t, this.data.length+r.length);
- this.add(r);
}
-
- if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
-
- var e = new Roo.data.Record({});
+ // not really supported yet.. ??
- e.set(this.parent.displayField, this.parent.emptyTitle);
- e.set(this.parent.valueField, '');
+ //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...');
+ // }
- this.insert(0, e);
- }
+ },
+
+ // private
+ afterAction : function(action, success){
+ this.activeAction = null;
+ var o = action.options;
+
+ if(this.loadMask){
- this.fireEvent("load", this, r, options, o);
- if(options.callback){
- options.callback.call(options.scope || this, r, options, true);
+ if(this.maskBody){
+ Roo.get(document.body).unmask();
+ } else {
+ this.el.unmask();
+ }
}
- },
+
+ //if(this.waitMsgTarget === true){
+// this.el.unmask();
+ //}else if(this.waitMsgTarget){
+ // this.waitMsgTarget.unmask();
+ //}else{
+ // Roo.MessageBox.updateProgress(1);
+ // Roo.MessageBox.hide();
+ // }
+ //
+ if(success){
+ if(o.reset){
+ this.reset();
+ }
+ Roo.callback(o.success, o.scope, [this, action]);
+ this.fireEvent('actioncomplete', this, action);
+ }else{
- /**
- * Loads data from a passed data block. A Reader which understands the format of the data
- * must have been configured in the constructor.
- * @param {Object} data The data block from which to read the Records. The format of the data expected
- * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
- * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
- */
- loadData : function(o, append){
- var r = this.reader.readRecords(o);
- this.loadRecords(r, {add: append}, true);
- },
+ // failure condition..
+ // we have a scenario where updates need confirming.
+ // eg. if a locking scenario exists..
+ // we look for { errors : { needs_confirm : true }} in the response.
+ if (
+ (typeof(action.result) != 'undefined') &&
+ (typeof(action.result.errors) != 'undefined') &&
+ (typeof(action.result.errors.needs_confirm) != 'undefined')
+ ){
+ var _t = this;
+ Roo.log("not supported yet");
+ /*
- /**
- * Gets the number of cached records.
- * <p>
- * <em>If using paging, this may not be the total size of the dataset. If the data object
- * used by the Reader contains the dataset size, then the getTotalCount() function returns
- * the data set size</em>
- */
- getCount : function(){
- return this.data.length || 0;
- },
+ Roo.MessageBox.confirm(
+ "Change requires confirmation",
+ action.result.errorMsg,
+ function(r) {
+ if (r != 'yes') {
+ return;
+ }
+ _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
+ }
- /**
- * Gets the total number of records in the dataset as returned by the server.
- * <p>
- * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
- * the dataset size</em>
- */
- getTotalCount : function(){
- return this.totalLength || 0;
- },
+ );
+ */
- /**
- * Returns the sort state of the Store as an object with two properties:
- * <pre><code>
- field {String} The name of the field by which the Records are sorted
- direction {String} The sort order, "ASC" or "DESC"
- * </code></pre>
- */
- getSortState : function(){
- return this.sortInfo;
- },
- // private
- applySort : function(){
- if(this.sortInfo && !this.remoteSort){
- var s = this.sortInfo, f = s.field;
- var st = this.fields.get(f).sortType;
- var fn = function(r1, r2){
- var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
- return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
- };
- this.data.sort(s.direction, fn);
- if(this.snapshot && this.snapshot != this.data){
- this.snapshot.sort(s.direction, fn);
+ return;
+ }
+
+ Roo.callback(o.failure, o.scope, [this, action]);
+ // show an error message if no failed handler is set..
+ if (!this.hasListener('actionfailed')) {
+ Roo.log("need to add dialog support");
+ /*
+ Roo.MessageBox.alert("Error",
+ (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
+ action.result.errorMsg :
+ "Saving Failed, please check your entries or try again"
+ );
+ */
}
+
+ this.fireEvent('actionfailed', this, action);
}
- },
+ },
/**
- * Sets the default sort column and order to be used by the next load operation.
- * @param {String} fieldName The name of the field to sort by.
- * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
+ * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
+ * @param {String} id The value to search for
+ * @return Field
*/
- setDefaultSort : function(field, dir){
- this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
+ findField : function(id){
+ var items = this.getItems();
+ var field = items.get(id);
+ if(!field){
+ items.each(function(f){
+ if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
+ field = f;
+ return false;
+ }
+ return true;
+ });
+ }
+ return field || null;
},
-
- /**
- * Sort the Records.
- * If remote sorting is used, the sort is performed on the server, and the cache is
- * reloaded. If local sorting is used, the cache is sorted internally.
- * @param {String} fieldName The name of the field to sort by.
- * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
+ /**
+ * Mark fields in this form invalid in bulk.
+ * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
+ * @return {BasicForm} this
*/
- sort : function(fieldName, dir){
- var f = this.fields.get(fieldName);
- if(!dir){
- this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
-
- if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
- dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
- }else{
- dir = f.sortDir;
+ markInvalid : function(errors){
+ if(errors instanceof Array){
+ for(var i = 0, len = errors.length; i < len; i++){
+ var fieldError = errors[i];
+ var f = this.findField(fieldError.id);
+ if(f){
+ f.markInvalid(fieldError.msg);
+ }
}
- }
- this.sortToggle[f.name] = dir;
- this.sortInfo = {field: f.name, direction: dir};
- if(!this.remoteSort){
- this.applySort();
- this.fireEvent("datachanged", this);
}else{
- this.load(this.lastOptions);
+ var field, id;
+ for(id in errors){
+ if(typeof errors[id] != 'function' && (field = this.findField(id))){
+ field.markInvalid(errors[id]);
+ }
+ }
}
- },
+ //Roo.each(this.childForms || [], function (f) {
+ // f.markInvalid(errors);
+ //});
- /**
- * Calls the specified function for each of the Records in the cache.
- * @param {Function} fn The function to call. The Record is passed as the first parameter.
- * Returning <em>false</em> aborts and exits the iteration.
- * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
- */
- each : function(fn, scope){
- this.data.each(fn, scope);
+ return this;
},
/**
- * Gets all records modified since the last commit. Modified records are persisted across load operations
- * (e.g., during paging).
- * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
+ * Set values for fields in this form in bulk.
+ * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
+ * @return {BasicForm} this
*/
- getModifiedRecords : function(){
- return this.modified;
- },
+ setValues : function(values){
+ if(values instanceof Array){ // array of objects
+ for(var i = 0, len = values.length; i < len; i++){
+ var v = values[i];
+ var f = this.findField(v.id);
+ if(f){
+ f.setValue(v.value);
+ if(this.trackResetOnLoad){
+ f.originalValue = f.getValue();
+ }
+ }
+ }
+ }else{ // object hash
+ var field, id;
+ for(id in values){
+ if(typeof values[id] != 'function' && (field = this.findField(id))){
- // private
- createFilterFn : function(property, value, anyMatch){
- if(!value.exec){ // not a regex
- value = String(value);
- if(value.length == 0){
- return false;
+ if (field.setFromData &&
+ field.valueField &&
+ field.displayField &&
+ // combos' with local stores can
+ // be queried via setValue()
+ // to set their value..
+ (field.store && !field.store.isLocal)
+ ) {
+ // it's a combo
+ var sd = { };
+ sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
+ sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
+ field.setFromData(sd);
+
+ } else if(field.setFromData && (field.store && !field.store.isLocal)) {
+
+ field.setFromData(values);
+
+ } else {
+ field.setValue(values[id]);
+ }
+
+
+ if(this.trackResetOnLoad){
+ field.originalValue = field.getValue();
+ }
+ }
}
- value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
}
- return function(r){
- return value.test(r.data[property]);
- };
+
+ //Roo.each(this.childForms || [], function (f) {
+ // f.setValues(values);
+ //});
+
+ return this;
},
/**
- * Sums the value of <i>property</i> for each record between start and end and returns the result.
- * @param {String} property A field on your records
- * @param {Number} start The record index to start at (defaults to 0)
- * @param {Number} end The last record index to include (defaults to length - 1)
- * @return {Number} The sum
+ * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
+ * they are returned as an array.
+ * @param {Boolean} asString
+ * @return {Object}
*/
- sum : function(property, start, end){
- var rs = this.data.items, v = 0;
- start = start || 0;
- end = (end || end === 0) ? end : rs.length-1;
+ getValues : function(asString){
+ //if (this.childForms) {
+ // copy values from the child forms
+ // Roo.each(this.childForms, function (f) {
+ // this.setValues(f.getValues());
+ // }, this);
+ //}
- for(var i = start; i <= end; i++){
- v += (rs[i].data[property] || 0);
+
+
+ var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
+ if(asString === true){
+ return fs;
}
- return v;
+ return Roo.urlDecode(fs);
},
/**
- * Filter the records by a specified property.
- * @param {String} field A field on your records
- * @param {String/RegExp} value Either a string that the field
- * should start with or a RegExp to test against the field
- * @param {Boolean} anyMatch True to match any part not just the beginning
+ * Returns the fields in this form as an object with key/value pairs.
+ * This differs from getValues as it calls getValue on each child item, rather than using dom data.
+ * @return {Object}
*/
- filter : function(property, value, anyMatch){
- var fn = this.createFilterFn(property, value, anyMatch);
- return fn ? this.filterBy(fn) : this.clearFilter();
- },
+ getFieldValues : function(with_hidden)
+ {
+ var items = this.getItems();
+ var ret = {};
+ items.each(function(f){
+
+ if (!f.getName()) {
+ return;
+ }
+
+ var v = f.getValue();
+
+ if (f.inputType =='radio') {
+ if (typeof(ret[f.getName()]) == 'undefined') {
+ ret[f.getName()] = ''; // empty..
+ }
- /**
- * Filter by a function. The specified function will be called with each
- * record in this data source. If the function returns true the record is included,
- * otherwise it is filtered.
- * @param {Function} fn The function to be called, it will receive 2 args (record, id)
- * @param {Object} scope (optional) The scope of the function (defaults to this)
- */
- filterBy : function(fn, scope){
- this.snapshot = this.snapshot || this.data;
- this.data = this.queryBy(fn, scope||this);
- this.fireEvent("datachanged", this);
- },
+ if (!f.el.dom.checked) {
+ return;
- /**
- * Query the records by a specified property.
- * @param {String} field A field on your records
- * @param {String/RegExp} value Either a string that the field
- * should start with or a RegExp to test against the field
- * @param {Boolean} anyMatch True to match any part not just the beginning
- * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
- */
- query : function(property, value, anyMatch){
- var fn = this.createFilterFn(property, value, anyMatch);
- return fn ? this.queryBy(fn) : this.data.clone();
- },
+ }
+ v = f.el.dom.value;
- /**
- * Query by a function. The specified function will be called with each
- * record in this data source. If the function returns true the record is included
- * in the results.
- * @param {Function} fn The function to be called, it will receive 2 args (record, id)
- * @param {Object} scope (optional) The scope of the function (defaults to this)
- @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
- **/
- queryBy : function(fn, scope){
- var data = this.snapshot || this.data;
- return data.filterBy(fn, scope||this);
- },
+ }
+
+ if(f.xtype == 'MoneyField'){
+ ret[f.currencyName] = f.getCurrency();
+ }
- /**
- * Collects unique values for a particular dataIndex from this store.
- * @param {String} dataIndex The property to collect
- * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
- * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
- * @return {Array} An array of the unique values
- **/
- collect : function(dataIndex, allowNull, bypassFilter){
- var d = (bypassFilter === true && this.snapshot) ?
- this.snapshot.items : this.data.items;
- var v, sv, r = [], l = {};
- for(var i = 0, len = d.length; i < len; i++){
- v = d[i].data[dataIndex];
- sv = String(v);
- if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
- l[sv] = true;
- r[r.length] = v;
+ // not sure if this supported any more..
+ if ((typeof(v) == 'object') && f.getRawValue) {
+ v = f.getRawValue() ; // dates..
}
- }
- return r;
+ // combo boxes where name != hiddenName...
+ if (f.name !== false && f.name != '' && f.name != f.getName()) {
+ ret[f.name] = f.getRawValue();
+ }
+ ret[f.getName()] = v;
+ });
+
+ return ret;
},
/**
- * Revert to a view of the Record cache with no filtering applied.
- * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
+ * Clears all invalid messages in this form.
+ * @return {BasicForm} this
*/
- clearFilter : function(suppressEvent){
- if(this.snapshot && this.snapshot != this.data){
- this.data = this.snapshot;
- delete this.snapshot;
- if(suppressEvent !== true){
- this.fireEvent("datachanged", this);
- }
- }
- },
+ clearInvalid : function(){
+ var items = this.getItems();
- // private
- afterEdit : function(record){
- if(this.modified.indexOf(record) == -1){
- this.modified.push(record);
- }
- this.fireEvent("update", this, record, Roo.data.Record.EDIT);
- },
-
- // private
- afterReject : function(record){
- this.modified.remove(record);
- this.fireEvent("update", this, record, Roo.data.Record.REJECT);
- },
+ items.each(function(f){
+ f.clearInvalid();
+ });
- // private
- afterCommit : function(record){
- this.modified.remove(record);
- this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
+ return this;
},
/**
- * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
- * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
+ * Resets this form.
+ * @return {BasicForm} this
*/
- commitChanges : function(){
- var m = this.modified.slice(0);
- this.modified = [];
- for(var i = 0, len = m.length; i < len; i++){
- m[i].commit();
- }
- },
+ reset : function(){
+ var items = this.getItems();
+ items.each(function(f){
+ f.reset();
+ });
- /**
- * Cancel outstanding changes on all changed records.
- */
- rejectChanges : function(){
- var m = this.modified.slice(0);
- this.modified = [];
- for(var i = 0, len = m.length; i < len; i++){
- m[i].reject();
- }
+ Roo.each(this.childForms || [], function (f) {
+ f.reset();
+ });
+
+
+ return this;
},
+
+ getItems : function()
+ {
+ var r=new Roo.util.MixedCollection(false, function(o){
+ return o.id || (o.id = Roo.id());
+ });
+ var iter = function(el) {
+ if (el.inputEl) {
+ r.add(el);
+ }
+ if (!el.items) {
+ return;
+ }
+ Roo.each(el.items,function(e) {
+ iter(e);
+ });
+ };
- onMetaChange : function(meta, rtype, o){
- this.recordType = rtype;
- this.fields = rtype.prototype.fields;
- delete this.snapshot;
- this.sortInfo = meta.sortInfo || this.sortInfo;
- this.modified = [];
- this.fireEvent('metachange', this, this.reader.meta);
+ iter(this);
+ return r;
},
- moveIndex : function(data, type)
+ hideFields : function(items)
{
- var index = this.indexOf(data);
+ Roo.each(items, function(i){
+
+ var f = this.findField(i);
+
+ if(!f){
+ return;
+ }
+
+ f.hide();
+
+ }, this);
+ },
+
+ showFields : function(items)
+ {
+ Roo.each(items, function(i){
+
+ var f = this.findField(i);
+
+ if(!f){
+ return;
+ }
+
+ f.show();
+
+ }, this);
+ }
+
+});
+
+Roo.apply(Roo.bootstrap.form.Form, {
+
+ popover : {
- var newIndex = index + type;
+ padding : 5,
- this.remove(data);
+ isApplied : false,
- this.insert(newIndex, data);
+ isMasked : false,
+
+ form : false,
+
+ target : false,
+
+ toolTip : false,
+
+ intervalID : false,
+
+ maskEl : false,
+
+ apply : function()
+ {
+ if(this.isApplied){
+ return;
+ }
+
+ this.maskEl = {
+ top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
+ left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
+ bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
+ right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
+ };
+
+ this.maskEl.top.enableDisplayMode("block");
+ this.maskEl.left.enableDisplayMode("block");
+ this.maskEl.bottom.enableDisplayMode("block");
+ this.maskEl.right.enableDisplayMode("block");
+
+ this.toolTip = new Roo.bootstrap.Tooltip({
+ cls : 'roo-form-error-popover',
+ alignment : {
+ 'left' : ['r-l', [-2,0], 'right'],
+ 'right' : ['l-r', [2,0], 'left'],
+ 'bottom' : ['tl-bl', [0,2], 'top'],
+ 'top' : [ 'bl-tl', [0,-2], 'bottom']
+ }
+ });
+
+ this.toolTip.render(Roo.get(document.body));
+
+ this.toolTip.el.enableDisplayMode("block");
+
+ Roo.get(document.body).on('click', function(){
+ this.unmask();
+ }, this);
+
+ Roo.get(document.body).on('touchstart', function(){
+ this.unmask();
+ }, this);
+
+ this.isApplied = true
+ },
+
+ mask : function(form, target)
+ {
+ this.form = form;
+
+ this.target = target;
+
+ if(!this.form.errorMask || !target.el){
+ return;
+ }
+
+ var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
+
+ Roo.log(scrollable);
+
+ var ot = this.target.el.calcOffsetsTo(scrollable);
+
+ var scrollTo = ot[1] - this.form.maskOffset;
+
+ scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
+
+ scrollable.scrollTo('top', scrollTo);
+
+ var box = this.target.el.getBox();
+ Roo.log(box);
+ var zIndex = Roo.bootstrap.Modal.zIndex++;
+
+
+ this.maskEl.top.setStyle('position', 'absolute');
+ this.maskEl.top.setStyle('z-index', zIndex);
+ this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
+ this.maskEl.top.setLeft(0);
+ this.maskEl.top.setTop(0);
+ this.maskEl.top.show();
+
+ this.maskEl.left.setStyle('position', 'absolute');
+ this.maskEl.left.setStyle('z-index', zIndex);
+ this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
+ this.maskEl.left.setLeft(0);
+ this.maskEl.left.setTop(box.y - this.padding);
+ this.maskEl.left.show();
+
+ this.maskEl.bottom.setStyle('position', 'absolute');
+ this.maskEl.bottom.setStyle('z-index', zIndex);
+ this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
+ this.maskEl.bottom.setLeft(0);
+ this.maskEl.bottom.setTop(box.bottom + this.padding);
+ this.maskEl.bottom.show();
+
+ this.maskEl.right.setStyle('position', 'absolute');
+ this.maskEl.right.setStyle('z-index', zIndex);
+ this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
+ this.maskEl.right.setLeft(box.right + this.padding);
+ this.maskEl.right.setTop(box.y - this.padding);
+ this.maskEl.right.show();
+
+ this.toolTip.bindEl = this.target.el;
+
+ this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+
+ var tip = this.target.blankText;
+
+ if(this.target.getValue() !== '' ) {
+
+ if (this.target.invalidText.length) {
+ tip = this.target.invalidText;
+ } else if (this.target.regexText.length){
+ tip = this.target.regexText;
+ }
+ }
+
+ this.toolTip.show(tip);
+
+ this.intervalID = window.setInterval(function() {
+ Roo.bootstrap.form.Form.popover.unmask();
+ }, 10000);
+
+ window.onwheel = function(){ return false;};
+
+ (function(){ this.isMasked = true; }).defer(500, this);
+
+ },
+
+ unmask : function()
+ {
+ if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
+ return;
+ }
+
+ this.maskEl.top.setStyle('position', 'absolute');
+ this.maskEl.top.setSize(0, 0).setXY([0, 0]);
+ this.maskEl.top.hide();
+
+ this.maskEl.left.setStyle('position', 'absolute');
+ this.maskEl.left.setSize(0, 0).setXY([0, 0]);
+ this.maskEl.left.hide();
+
+ this.maskEl.bottom.setStyle('position', 'absolute');
+ this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
+ this.maskEl.bottom.hide();
+
+ this.maskEl.right.setStyle('position', 'absolute');
+ this.maskEl.right.setSize(0, 0).setXY([0, 0]);
+ this.maskEl.right.hide();
+
+ this.toolTip.hide();
+
+ this.toolTip.el.hide();
+
+ window.onwheel = function(){ return true;};
+
+ if(this.intervalID){
+ window.clearInterval(this.intervalID);
+ this.intervalID = false;
+ }
+
+ this.isMasked = false;
+
+ }
}
-});/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
+
+});
-/**
- * @class Roo.data.SimpleStore
- * @extends Roo.data.Store
- * Small helper class to make creating Stores from Array data easier.
- * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
- * @cfg {Array} fields An array of field definition objects, or field name strings.
- * @cfg {Array} data The multi-dimensional array of data
- * @constructor
- * @param {Object} config
- */
-Roo.data.SimpleStore = function(config){
- Roo.data.SimpleStore.superclass.constructor.call(this, {
- isLocal : true,
- reader: new Roo.data.ArrayReader({
- id: config.id
- },
- Roo.data.Record.create(config.fields)
- ),
- proxy : new Roo.data.MemoryProxy(config.data)
- });
- this.load();
-};
-Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
+/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
* Fork - LGPL
* <script type="text/javascript">
*/
-
-/**
/**
- * @extends Roo.data.Store
- * @class Roo.data.JsonStore
- * Small helper class to make creating Stores for JSON data easier. <br/>
-<pre><code>
-var store = new Roo.data.JsonStore({
- url: 'get-images.php',
- root: 'images',
- fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
-});
-</code></pre>
- * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
- * JsonReader and HttpProxy (unless inline data is provided).</b>
- * @cfg {Array} fields An array of field definition objects, or field name strings.
- * @constructor
- * @param {Object} config
- */
-Roo.data.JsonStore = function(c){
- Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
- proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
- reader: new Roo.data.JsonReader(c, c.fields)
- }));
-};
-Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
+ * @class Roo.form.VTypes
+ * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
+ * @static
*/
+Roo.form.VTypes = function(){
+ // closure these in so they are only created once.
+ var alpha = /^[a-zA-Z_]+$/;
+ var alphanum = /^[a-zA-Z0-9_]+$/;
+ var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
+ var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
-
-Roo.data.Field = function(config){
- if(typeof config == "string"){
- config = {name: config};
- }
- Roo.apply(this, config);
-
- if(!this.type){
- this.type = "auto";
- }
-
- var st = Roo.data.SortTypes;
- // named sortTypes are supported, here we look them up
- if(typeof this.sortType == "string"){
- this.sortType = st[this.sortType];
- }
-
- // set default sortType for strings and dates
- if(!this.sortType){
- switch(this.type){
- case "string":
- this.sortType = st.asUCString;
- break;
- case "date":
- this.sortType = st.asDate;
- break;
- default:
- this.sortType = st.none;
- }
- }
-
- // define once
- var stripRe = /[\$,%]/g;
+ // All these messages and functions are configurable
+ return {
+ /**
+ * The function used to validate email addresses
+ * @param {String} value The email address
+ */
+ 'email' : function(v){
+ return email.test(v);
+ },
+ /**
+ * The error text to display when the email validation function returns false
+ * @type String
+ */
+ 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
+ /**
+ * The keystroke filter mask to be applied on email input
+ * @type RegExp
+ */
+ 'emailMask' : /[a-z0-9_\.\-@]/i,
- // prebuilt conversion function for this field, instead of
- // switching every time we're reading a value
- if(!this.convert){
- var cv, dateFormat = this.dateFormat;
- switch(this.type){
- case "":
- case "auto":
- case undefined:
- cv = function(v){ return v; };
- break;
- case "string":
- cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
- break;
- case "int":
- cv = function(v){
- return v !== undefined && v !== null && v !== '' ?
- parseInt(String(v).replace(stripRe, ""), 10) : '';
- };
- break;
- case "float":
- cv = function(v){
- return v !== undefined && v !== null && v !== '' ?
- parseFloat(String(v).replace(stripRe, ""), 10) : '';
- };
- break;
- case "bool":
- case "boolean":
- cv = function(v){ return v === true || v === "true" || v == 1; };
- break;
- case "date":
- cv = function(v){
- if(!v){
- return '';
- }
- if(v instanceof Date){
- return v;
- }
- if(dateFormat){
- if(dateFormat == "timestamp"){
- return new Date(v*1000);
- }
- return Date.parseDate(v, dateFormat);
- }
- var parsed = Date.parse(v);
- return parsed ? new Date(parsed) : null;
- };
- break;
-
- }
- this.convert = cv;
- }
-};
+ /**
+ * The function used to validate URLs
+ * @param {String} value The URL
+ */
+ 'url' : function(v){
+ return url.test(v);
+ },
+ /**
+ * The error text to display when the url validation function returns false
+ * @type String
+ */
+ 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
+
+ /**
+ * The function used to validate alpha values
+ * @param {String} value The value
+ */
+ 'alpha' : function(v){
+ return alpha.test(v);
+ },
+ /**
+ * The error text to display when the alpha validation function returns false
+ * @type String
+ */
+ 'alphaText' : 'This field should only contain letters and _',
+ /**
+ * The keystroke filter mask to be applied on alpha input
+ * @type RegExp
+ */
+ 'alphaMask' : /[a-z_]/i,
-Roo.data.Field.prototype = {
- dateFormat: null,
- defaultValue: "",
- mapping: null,
- sortType : null,
- sortDir : "ASC"
-};/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
+ /**
+ * The function used to validate alphanumeric values
+ * @param {String} value The value
+ */
+ 'alphanum' : function(v){
+ return alphanum.test(v);
+ },
+ /**
+ * The error text to display when the alphanumeric validation function returns false
+ * @type String
+ */
+ 'alphanumText' : 'This field should only contain letters, numbers and _',
+ /**
+ * The keystroke filter mask to be applied on alphanumeric input
+ * @type RegExp
+ */
+ 'alphanumMask' : /[a-z0-9_]/i
+ };
+}();/*
+ * - LGPL
*
- * Fork - LGPL
- * <script type="text/javascript">
+ * Input
+ *
*/
-
-// Base class for reading structured data from a data source. This class is intended to be
-// extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
/**
- * @class Roo.data.DataReader
- * Base class for reading structured data from a data source. This class is intended to be
- * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
+ * @class Roo.bootstrap.form.Input
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Input class
+ * @cfg {Boolean} disabled is it disabled
+ * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
+ * @cfg {String} name name of the input
+ * @cfg {string} fieldLabel - the label associated
+ * @cfg {string} placeholder - placeholder to put in text.
+ * @cfg {string} before - input group add on before
+ * @cfg {string} after - input group add on after
+ * @cfg {string} size - (lg|sm) or leave empty..
+ * @cfg {Number} xs colspan out of 12 for mobile-sized screens
+ * @cfg {Number} sm colspan out of 12 for tablet-sized screens
+ * @cfg {Number} md colspan out of 12 for computer-sized screens
+ * @cfg {Number} lg colspan out of 12 for large computer-sized screens
+ * @cfg {string} value default value of the input
+ * @cfg {Number} labelWidth set the width of label
+ * @cfg {Number} labellg set the width of label (1-12)
+ * @cfg {Number} labelmd set the width of label (1-12)
+ * @cfg {Number} labelsm set the width of label (1-12)
+ * @cfg {Number} labelxs set the width of label (1-12)
+ * @cfg {String} labelAlign (top|left)
+ * @cfg {Boolean} readOnly Specifies that the field should be read-only
+ * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
+ * @cfg {String} indicatorpos (left|right) default left
+ * @cfg {String} capture (user|camera) use for file input only. (default empty)
+ * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
+ * @cfg {Boolean} preventMark Do not show tick or cross if error/success
+ * @cfg {Roo.bootstrap.Button} before Button to show before
+ * @cfg {Roo.bootstrap.Button} afterButton to show before
+ * @cfg {String} align (left|center|right) Default left
+ * @cfg {Boolean} forceFeedback (true|false) Default false
+ *
+ * @constructor
+ * Create a new Input
+ * @param {Object} config The config object
*/
-Roo.data.DataReader = function(meta, recordType){
-
- this.meta = meta;
+Roo.bootstrap.form.Input = function(config){
- this.recordType = recordType instanceof Array ?
- Roo.data.Record.create(recordType) : recordType;
-};
-
-Roo.data.DataReader.prototype = {
- /**
- * Create an empty record
- * @param {Object} data (optional) - overlay some values
- * @return {Roo.data.Record} record created.
- */
- newRow : function(d) {
- var da = {};
- this.recordType.prototype.fields.each(function(c) {
- switch( c.type) {
- case 'int' : da[c.name] = 0; break;
- case 'date' : da[c.name] = new Date(); break;
- case 'float' : da[c.name] = 0.0; break;
- case 'boolean' : da[c.name] = false; break;
- default : da[c.name] = ""; break;
- }
-
- });
- return new this.recordType(Roo.apply(da, d));
- }
+ Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
-};/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
-
-/**
- * @class Roo.data.DataProxy
- * @extends Roo.data.Observable
- * This class is an abstract base class for implementations which provide retrieval of
- * unformatted data objects.<br>
- * <p>
- * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
- * (of the appropriate type which knows how to parse the data object) to provide a block of
- * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
- * <p>
- * Custom implementations must implement the load method as described in
- * {@link Roo.data.HttpProxy#load}.
- */
-Roo.data.DataProxy = function(){
this.addEvents({
/**
- * @event beforeload
- * Fires before a network request is made to retrieve a data object.
- * @param {Object} This DataProxy object.
- * @param {Object} params The params parameter to the load function.
+ * @event focus
+ * Fires when this field receives input focus.
+ * @param {Roo.form.Field} this
*/
- beforeload : true,
+ focus : true,
/**
- * @event load
- * Fires before the load method's callback is called.
- * @param {Object} This DataProxy object.
- * @param {Object} o The data object.
- * @param {Object} arg The callback argument object passed to the load function.
+ * @event blur
+ * Fires when this field loses input focus.
+ * @param {Roo.form.Field} this
*/
- load : true,
+ blur : true,
/**
- * @event loadexception
- * Fires if an Exception occurs during data retrieval.
- * @param {Object} This DataProxy object.
- * @param {Object} o The data object.
- * @param {Object} arg The callback argument object passed to the load function.
- * @param {Object} e The Exception.
+ * @event specialkey
+ * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
+ * {@link Roo.EventObject#getKey} to determine which key was pressed.
+ * @param {Roo.form.Field} this
+ * @param {Roo.EventObject} e The event object
*/
- loadexception : true
+ specialkey : true,
+ /**
+ * @event change
+ * Fires just before the field blurs if the field value has changed.
+ * @param {Roo.form.Field} this
+ * @param {Mixed} newValue The new value
+ * @param {Mixed} oldValue The original value
+ */
+ change : true,
+ /**
+ * @event invalid
+ * Fires after the field has been marked as invalid.
+ * @param {Roo.form.Field} this
+ * @param {String} msg The validation message
+ */
+ invalid : true,
+ /**
+ * @event valid
+ * Fires after the field has been validated with no errors.
+ * @param {Roo.form.Field} this
+ */
+ valid : true,
+ /**
+ * @event keyup
+ * Fires after the key up
+ * @param {Roo.form.Field} this
+ * @param {Roo.EventObject} e The event Object
+ */
+ keyup : true,
+ /**
+ * @event paste
+ * Fires after the user pastes into input
+ * @param {Roo.form.Field} this
+ * @param {Roo.EventObject} e The event Object
+ */
+ paste : true
});
- Roo.data.DataProxy.superclass.constructor.call(this);
};
-Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
-
+Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
+ /**
+ * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
+ automatic validation (defaults to "keyup").
+ */
+ validationEvent : "keyup",
+ /**
+ * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
+ */
+ validateOnBlur : true,
/**
- * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
+ * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
*/
-/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
-/**
- * @class Roo.data.MemoryProxy
- * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
- * to the Reader when its load method is called.
- * @constructor
- * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
- */
-Roo.data.MemoryProxy = function(data){
- if (data.data) {
- data = data.data;
- }
- Roo.data.MemoryProxy.superclass.constructor.call(this);
- this.data = data;
-};
-
-Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
+ validationDelay : 250,
+ /**
+ * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
+ */
+ focusClass : "x-form-focus", // not needed???
+
/**
- * Load data from the requested source (in this case an in-memory
- * data object passed to the constructor), read the data object into
- * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
- * process that block using the passed callback.
- * @param {Object} params This parameter is not used by the MemoryProxy class.
- * @param {Roo.data.DataReader} reader The Reader object which converts the data
- * object into a block of Roo.data.Records.
- * @param {Function} callback The function into which to pass the block of Roo.data.records.
- * The function must be passed <ul>
- * <li>The Record block object</li>
- * <li>The "arg" argument from the load function</li>
- * <li>A boolean success indicator</li>
- * </ul>
- * @param {Object} scope The scope in which to call the callback
- * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
*/
- load : function(params, reader, callback, scope, arg){
- params = params || {};
- var result;
- try {
- result = reader.readRecords(this.data);
- }catch(e){
- this.fireEvent("loadexception", this, arg, null, e);
- callback.call(scope, null, arg, false);
- return;
- }
- callback.call(scope, result, arg, true);
- },
+ invalidClass : "has-warning",
- // private
- update : function(params, records){
-
- }
-});/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
-/**
- * @class Roo.data.HttpProxy
- * @extends Roo.data.DataProxy
- * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
- * configured to reference a certain URL.<br><br>
- * <p>
- * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
- * from which the running page was served.<br><br>
- * <p>
- * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
- * <p>
- * Be aware that to enable the browser to parse an XML document, the server must set
- * the Content-Type header in the HTTP response to "text/xml".
- * @constructor
- * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
- * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
- * will be used to make the request.
- */
-Roo.data.HttpProxy = function(conn){
- Roo.data.HttpProxy.superclass.constructor.call(this);
- // is conn a conn config or a real conn?
- this.conn = conn;
- this.useAjax = !conn || !conn.events;
-
-};
-
-Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
- // thse are take from connection...
+ /**
+ * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
+ */
+ validClass : "has-success",
/**
- * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
+ * @cfg {Boolean} hasFeedback (true|false) default true
*/
+ hasFeedback : true,
+
/**
- * @cfg {Object} extraParams (Optional) An object containing properties which are used as
- * extra parameters to each request made by this object. (defaults to undefined)
+ * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
*/
+ invalidFeedbackClass : "glyphicon-warning-sign",
+
/**
- * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
- * to each request made by this object. (defaults to undefined)
+ * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
*/
+ validFeedbackClass : "glyphicon-ok",
+
/**
- * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
+ * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
+ */
+ selectOnFocus : false,
+
+ /**
+ * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
+ */
+ maskRe : null,
+ /**
+ * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
+ */
+ vtype : null,
+
+ /**
+ * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
+ */
+ disableKeyFilter : false,
+
+ /**
+ * @cfg {Boolean} disabled True to disable the field (defaults to false).
+ */
+ disabled : false,
+ /**
+ * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
*/
+ allowBlank : true,
/**
- * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+ * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
*/
+ blankText : "Please complete this mandatory field",
+
/**
- * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
- * @type Boolean
+ * @cfg {Number} minLength Minimum input field length required (defaults to 0)
*/
-
-
+ minLength : 0,
/**
- * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
- * @type Boolean
+ * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
*/
+ maxLength : Number.MAX_VALUE,
/**
- * Return the {@link Roo.data.Connection} object being used by this Proxy.
- * @return {Connection} The Connection object. This object may be used to subscribe to events on
- * a finer-grained basis than the DataProxy events.
+ * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
*/
- getConnection : function(){
- return this.useAjax ? Roo.Ajax : this.conn;
- },
-
+ minLengthText : "The minimum length for this field is {0}",
/**
- * Load data from the configured {@link Roo.data.Connection}, read the data object into
- * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
- * process that block using the passed callback.
- * @param {Object} params An object containing properties which are to be used as HTTP parameters
- * for the request to the remote server.
- * @param {Roo.data.DataReader} reader The Reader object which converts the data
- * object into a block of Roo.data.Records.
- * @param {Function} callback The function into which to pass the block of Roo.data.Records.
- * The function must be passed <ul>
- * <li>The Record block object</li>
- * <li>The "arg" argument from the load function</li>
- * <li>A boolean success indicator</li>
- * </ul>
- * @param {Object} scope The scope in which to call the callback
- * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
*/
- load : function(params, reader, callback, scope, arg){
- if(this.fireEvent("beforeload", this, params) !== false){
- var o = {
- params : params || {},
- request: {
- callback : callback,
- scope : scope,
- arg : arg
- },
- reader: reader,
- callback : this.loadResponse,
- scope: this
- };
- if(this.useAjax){
- Roo.applyIf(o, this.conn);
- if(this.activeRequest){
- Roo.Ajax.abort(this.activeRequest);
- }
- this.activeRequest = Roo.Ajax.request(o);
- }else{
- this.conn.request(o);
+ maxLengthText : "The maximum length for this field is {0}",
+
+
+ /**
+ * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
+ * If available, this function will be called only after the basic validators all return true, and will be passed the
+ * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
+ */
+ validator : null,
+ /**
+ * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
+ * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
+ * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
+ */
+ regex : null,
+ /**
+ * @cfg {String} regexText -- Depricated - use Invalid Text
+ */
+ regexText : "",
+
+ /**
+ * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
+ */
+ invalidText : "",
+
+
+
+ autocomplete: false,
+
+
+ fieldLabel : '',
+ inputType : 'text',
+
+ name : false,
+ placeholder: false,
+ before : false,
+ after : false,
+ size : false,
+ hasFocus : false,
+ preventMark: false,
+ isFormField : true,
+ value : '',
+ labelWidth : 2,
+ labelAlign : false,
+ readOnly : false,
+ align : false,
+ formatedValue : false,
+ forceFeedback : false,
+
+ indicatorpos : 'left',
+
+ labellg : 0,
+ labelmd : 0,
+ labelsm : 0,
+ labelxs : 0,
+
+ capture : '',
+ accept : '',
+
+ parentLabelAlign : function()
+ {
+ var parent = this;
+ while (parent.parent()) {
+ parent = parent.parent();
+ if (typeof(parent.labelAlign) !='undefined') {
+ return parent.labelAlign;
}
- }else{
- callback.call(scope||this, null, arg, false);
}
+ return 'left';
+
},
-
- // private
- loadResponse : function(o, success, response){
- delete this.activeRequest;
- if(!success){
- this.fireEvent("loadexception", this, o, response);
- o.request.callback.call(o.request.scope, null, o.request.arg, false);
- return;
+
+ getAutoCreate : function()
+ {
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+
+ var id = Roo.id();
+
+ var cfg = {};
+
+ if(this.inputType != 'hidden'){
+ cfg.cls = 'form-group' //input-group
}
- var result;
- try {
- result = o.reader.read(response);
- }catch(e){
- this.fireEvent("loadexception", this, o, response, e);
- o.request.callback.call(o.request.scope, null, o.request.arg, false);
- return;
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ value : this.value,
+ cls : 'form-control',
+ placeholder : this.placeholder || '',
+ autocomplete : this.autocomplete || 'new-password'
+ };
+ if (this.inputType == 'file') {
+ input.style = 'overflow:hidden'; // why not in CSS?
}
- this.fireEvent("load", this, o, o.request.arg);
- o.request.callback.call(o.request.scope, result, o.request.arg, true);
- },
-
- // private
- update : function(dataSet){
-
- },
-
- // private
- updateResponse : function(dataSet){
-
- }
-});/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
+ if(this.capture.length){
+ input.capture = this.capture;
+ }
+
+ if(this.accept.length){
+ input.accept = this.accept + "/*";
+ }
+
+ if(this.align){
+ input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
+ }
+
+ if(this.maxLength && this.maxLength != Number.MAX_VALUE){
+ input.maxLength = this.maxLength;
+ }
+
+ if (this.disabled) {
+ input.disabled=true;
+ }
+
+ if (this.readOnly) {
+ input.readonly=true;
+ }
+
+ if (this.name) {
+ input.name = this.name;
+ }
+
+ if (this.size) {
+ input.cls += ' input-' + this.size;
+ }
+
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ var inputblock = input;
+
+ var feedback = {
+ tag: 'span',
+ cls: 'glyphicon form-control-feedback'
+ };
+
+ if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+
+ inputblock = {
+ cls : 'has-feedback',
+ cn : [
+ input,
+ feedback
+ ]
+ };
+ }
+
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
+ };
+
+ if (this.before && typeof(this.before) == 'string') {
+
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
+ html : this.before
+ });
+ }
+ if (this.before && typeof(this.before) == 'object') {
+ this.before = Roo.factory(this.before);
+
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'roo-input-before input-group-prepend input-group-' +
+ (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
+ });
+ }
+
+ inputblock.cn.push(input);
+
+ if (this.after && typeof(this.after) == 'string') {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'roo-input-after input-group-append input-group-text input-group-addon',
+ html : this.after
+ });
+ }
+ if (this.after && typeof(this.after) == 'object') {
+ this.after = Roo.factory(this.after);
+
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'roo-input-after input-group-append input-group-' +
+ (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
+ });
+ }
+
+ if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+ inputblock.cls += ' has-feedback';
+ inputblock.cn.push(feedback);
+ }
+ };
+ var indicator = {
+ tag : 'i',
+ cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required'
+ };
+ if (this.allowBlank ) {
+ indicator.style = this.allowBlank ? ' display:none' : '';
+ }
+ if (align ==='left' && this.fieldLabel.length) {
+
+ cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
+
+ cfg.cn = [
+ indicator,
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-form-label',
+ html : this.fieldLabel
-/**
- * @class Roo.data.ScriptTagProxy
- * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
- * other than the originating domain of the running page.<br><br>
- * <p>
- * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
- * of the running page, you must use this class, rather than DataProxy.</em><br><br>
- * <p>
- * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
- * source code that is used as the source inside a <script> tag.<br><br>
- * <p>
- * In order for the browser to process the returned data, the server must wrap the data object
- * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
- * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
- * depending on whether the callback name was passed:
- * <p>
- * <pre><code>
-boolean scriptTag = false;
-String cb = request.getParameter("callback");
-if (cb != null) {
- scriptTag = true;
- response.setContentType("text/javascript");
-} else {
- response.setContentType("application/x-json");
-}
-Writer out = response.getWriter();
-if (scriptTag) {
- out.write(cb + "(");
-}
-out.print(dataBlock.toJsonString());
-if (scriptTag) {
- out.write(");");
-}
-</pre></code>
- *
- * @constructor
- * @param {Object} config A configuration object.
- */
-Roo.data.ScriptTagProxy = function(config){
- Roo.data.ScriptTagProxy.superclass.constructor.call(this);
- Roo.apply(this, config);
- this.head = document.getElementsByTagName("head")[0];
-};
+ },
+ {
+ cls : "",
+ cn: [
+ inputblock
+ ]
+ }
+ ];
+
+ var labelCfg = cfg.cn[1];
+ var contentCfg = cfg.cn[2];
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-form-label',
+ cn : [
+ {
+ tag : 'span',
+ html : this.fieldLabel
+ },
+ indicator
+ ]
+ },
+ {
+ cls : "",
+ cn: [
+ inputblock
+ ]
+ }
-Roo.data.ScriptTagProxy.TRANS_ID = 1000;
+ ];
+
+ labelCfg = cfg.cn[0];
+ contentCfg = cfg.cn[1];
+
+ }
+
+ if(this.labelWidth > 12){
+ labelCfg.style = "width: " + this.labelWidth + 'px';
+ }
+
+ if(this.labelWidth < 13 && this.labelmd == 0){
+ this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
+ }
+
+ if(this.labellg > 0){
+ labelCfg.cls += ' col-lg-' + this.labellg;
+ contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+ }
+
+ if(this.labelmd > 0){
+ labelCfg.cls += ' col-md-' + this.labelmd;
+ contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ }
+
+ if(this.labelsm > 0){
+ labelCfg.cls += ' col-sm-' + this.labelsm;
+ contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ }
+
+ if(this.labelxs > 0){
+ labelCfg.cls += ' col-xs-' + this.labelxs;
+ contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ }
+
+
+ } else if ( this.fieldLabel.length) {
+
+
+
+ cfg.cn = [
+ {
+ tag : 'i',
+ cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required',
+ style : this.allowBlank ? ' display:none' : ''
+ },
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
-Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
- /**
- * @cfg {String} url The URL from which to request the data object.
- */
- /**
- * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
- */
- timeout : 30000,
- /**
- * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
- * the server the name of the callback function set up by the load call to process the returned data object.
- * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
- * javascript output which calls this named function passing the data object as its only parameter.
- */
- callbackParam : "callback",
- /**
- * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
- * name to the request.
- */
- nocache : true,
+ },
- /**
- * Load data from the configured URL, read the data object into
- * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
- * process that block using the passed callback.
- * @param {Object} params An object containing properties which are to be used as HTTP parameters
- * for the request to the remote server.
- * @param {Roo.data.DataReader} reader The Reader object which converts the data
- * object into a block of Roo.data.Records.
- * @param {Function} callback The function into which to pass the block of Roo.data.Records.
- * The function must be passed <ul>
- * <li>The Record block object</li>
- * <li>The "arg" argument from the load function</li>
- * <li>A boolean success indicator</li>
- * </ul>
- * @param {Object} scope The scope in which to call the callback
- * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
- */
- load : function(params, reader, callback, scope, arg){
- if(this.fireEvent("beforeload", this, params) !== false){
+ inputblock
- var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
+ ];
+
+ if(this.indicatorpos == 'right'){
+
+ cfg.cn = [
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
- var url = this.url;
- url += (url.indexOf("?") != -1 ? "&" : "?") + p;
- if(this.nocache){
- url += "&_dc=" + (new Date().getTime());
- }
- var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
- var trans = {
- id : transId,
- cb : "stcCallback"+transId,
- scriptId : "stcScript"+transId,
- params : params,
- arg : arg,
- url : url,
- callback : callback,
- scope : scope,
- reader : reader
- };
- var conn = this;
+ },
+ {
+ tag : 'i',
+ cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required',
+ style : this.allowBlank ? ' display:none' : ''
+ },
- window[trans.cb] = function(o){
- conn.handleResponse(o, trans);
- };
+ inputblock
- url += String.format("&{0}={1}", this.callbackParam, trans.cb);
+ ];
- if(this.autoAbort !== false){
- this.abort();
}
- trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
+ } else {
+
+ cfg.cn = [
- var script = document.createElement("script");
- script.setAttribute("src", url);
- script.setAttribute("type", "text/javascript");
- script.setAttribute("id", trans.scriptId);
- this.head.appendChild(script);
+ inputblock
- this.trans = trans;
- }else{
- callback.call(scope||this, null, arg, false);
+ ];
+
+
+ };
+
+ if (this.parentType === 'Navbar' && this.parent().bar) {
+ cfg.cls += ' navbar-form';
}
+
+ if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
+ // on BS4 we do this only if not form
+ cfg.cls += ' navbar-form';
+ cfg.tag = 'li';
+ }
+
+ return cfg;
+
},
-
- // private
- isLoading : function(){
- return this.trans ? true : false;
- },
-
/**
- * Abort the current server request.
+ * return the real input element.
*/
- abort : function(){
- if(this.isLoading()){
- this.destroyTrans(this.trans);
- }
+ inputEl: function ()
+ {
+ return this.el.select('input.form-control',true).first();
},
-
- // private
- destroyTrans : function(trans, isLoaded){
- this.head.removeChild(document.getElementById(trans.scriptId));
- clearTimeout(trans.timeoutId);
- if(isLoaded){
- window[trans.cb] = undefined;
- try{
- delete window[trans.cb];
- }catch(e){}
- }else{
- // if hasn't been loaded, wait for load to remove it to prevent script error
- window[trans.cb] = function(){
- window[trans.cb] = undefined;
- try{
- delete window[trans.cb];
- }catch(e){}
- };
+
+ tooltipEl : function()
+ {
+ return this.inputEl();
+ },
+
+ indicatorEl : function()
+ {
+ if (Roo.bootstrap.version == 4) {
+ return false; // not enabled in v4 yet.
}
+
+ var indicator = this.el.select('i.roo-required-indicator',true).first();
+
+ if(!indicator){
+ return false;
+ }
+
+ return indicator;
+
},
-
- // private
- handleResponse : function(o, trans){
- this.trans = false;
- this.destroyTrans(trans, true);
- var result;
- try {
- result = trans.reader.readRecords(o);
- }catch(e){
- this.fireEvent("loadexception", this, o, trans.arg, e);
- trans.callback.call(trans.scope||window, null, trans.arg, false);
+
+ setDisabled : function(v)
+ {
+ var i = this.inputEl().dom;
+ if (!v) {
+ i.removeAttribute('disabled');
return;
+
}
- this.fireEvent("load", this, o, trans.arg);
- trans.callback.call(trans.scope||window, result, trans.arg, true);
+ i.setAttribute('disabled','true');
},
-
- // private
- handleFailure : function(trans){
- this.trans = false;
- this.destroyTrans(trans, false);
- this.fireEvent("loadexception", this, null, trans.arg);
- trans.callback.call(trans.scope||window, null, trans.arg, false);
- }
-});/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
-
-/**
- * @class Roo.data.JsonReader
- * @extends Roo.data.DataReader
- * Data reader class to create an Array of Roo.data.Record objects from a JSON response
- * based on mappings in a provided Roo.data.Record constructor.
- *
- * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
- * in the reply previously.
- *
- * <p>
- * Example code:
- * <pre><code>
-var RecordDef = Roo.data.Record.create([
- {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
- {name: 'occupation'} // This field will use "occupation" as the mapping.
-]);
-var myReader = new Roo.data.JsonReader({
- totalProperty: "results", // The property which contains the total dataset size (optional)
- root: "rows", // The property which contains an Array of row objects
- id: "id" // The property within each row object that provides an ID for the record (optional)
-}, RecordDef);
-</code></pre>
- * <p>
- * This would consume a JSON file like this:
- * <pre><code>
-{ 'results': 2, 'rows': [
- { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
- { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
-}
-</code></pre>
- * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
- * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
- * paged from the remote server.
- * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
- * @cfg {String} root name of the property which contains the Array of row objects.
- * @cfg {String} id Name of the property within a row object that contains a record identifier value.
- * @cfg {Array} fields Array of field definition objects
- * @constructor
- * Create a new JsonReader
- * @param {Object} meta Metadata configuration options
- * @param {Object} recordType Either an Array of field definition objects,
- * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
- */
-Roo.data.JsonReader = function(meta, recordType){
-
- meta = meta || {};
- // set some defaults:
- Roo.applyIf(meta, {
- totalProperty: 'total',
- successProperty : 'success',
- root : 'data',
- id : 'id'
- });
-
- Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
-};
-Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
-
- /**
- * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
- * Used by Store query builder to append _requestMeta to params.
- *
- */
- metaFromRemote : false,
- /**
- * This method is only used by a DataProxy which has retrieved data from a remote server.
- * @param {Object} response The XHR object which contains the JSON data in its responseText.
- * @return {Object} data A data block which is used by an Roo.data.Store object as
- * a cache of Roo.data.Records.
- */
- read : function(response){
- var json = response.responseText;
-
- var o = /* eval:var:o */ eval("("+json+")");
- if(!o) {
- throw {message: "JsonReader.read: Json object not found"};
+ initEvents : function()
+ {
+
+ this.inputEl().on("keydown" , this.fireKey, this);
+ this.inputEl().on("focus", this.onFocus, this);
+ this.inputEl().on("blur", this.onBlur, this);
+
+ this.inputEl().relayEvent('keyup', this);
+ this.inputEl().relayEvent('paste', this);
+
+ this.indicator = this.indicatorEl();
+
+ if(this.indicator){
+ this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
+ }
+
+ // reference to original value for reset
+ this.originalValue = this.getValue();
+ //Roo.form.TextField.superclass.initEvents.call(this);
+ if(this.validationEvent == 'keyup'){
+ this.validationTask = new Roo.util.DelayedTask(this.validate, this);
+ this.inputEl().on('keyup', this.filterValidation, this);
+ }
+ else if(this.validationEvent !== false){
+ this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
}
- if(o.metaData){
+ if(this.selectOnFocus){
+ this.on("focus", this.preFocus, this);
- delete this.ef;
- this.metaFromRemote = true;
- this.meta = o.metaData;
- this.recordType = Roo.data.Record.create(o.metaData.fields);
- this.onMetaChange(this.meta, this.recordType, o);
}
- return this.readRecords(o);
+ if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
+ this.inputEl().on("keypress", this.filterKeys, this);
+ } else {
+ this.inputEl().relayEvent('keypress', this);
+ }
+ /* if(this.grow){
+ this.el.on("keyup", this.onKeyUp, this, {buffer:50});
+ this.el.on("click", this.autoSize, this);
+ }
+ */
+ if(this.inputEl().is('input[type=password]') && Roo.isSafari){
+ this.inputEl().on('keydown', this.SafariOnKeyDown, this);
+ }
+
+ if (typeof(this.before) == 'object') {
+ this.before.render(this.el.select('.roo-input-before',true).first());
+ }
+ if (typeof(this.after) == 'object') {
+ this.after.render(this.el.select('.roo-input-after',true).first());
+ }
+
+ this.inputEl().on('change', this.onChange, this);
+
},
-
- // private function a store will implement
- onMetaChange : function(meta, recordType, o){
-
+ filterValidation : function(e){
+ if(!e.isNavKeyPress()){
+ this.validationTask.delay(this.validationDelay);
+ }
},
-
- /**
- * @ignore
- */
- simpleAccess: function(obj, subsc) {
- return obj[subsc];
+ /**
+ * Validates the field value
+ * @return {Boolean} True if the value is valid, else false
+ */
+ validate : function(){
+ //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
+ if(this.disabled || this.validateValue(this.getRawValue())){
+ this.markValid();
+ return true;
+ }
+
+ this.markInvalid();
+ return false;
},
-
- /**
- * @ignore
- */
- getJsonAccessor: function(){
- var re = /[\[\.]/;
- return function(expr) {
- try {
- return(re.test(expr))
- ? new Function("obj", "return obj." + expr)
- : function(obj){
- return obj[expr];
- };
- } catch(e){}
- return Roo.emptyFn;
- };
- }(),
-
+
+
/**
- * Create a data block containing Roo.data.Records from an XML document.
- * @param {Object} o An object which contains an Array of row objects in the property specified
- * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
- * which contains the total size of the dataset.
- * @return {Object} data A data block which is used by an Roo.data.Store object as
- * a cache of Roo.data.Records.
+ * Validates a value according to the field's validation rules and marks the field as invalid
+ * if the validation fails
+ * @param {Mixed} value The value to validate
+ * @return {Boolean} True if the value is valid, else false
*/
- readRecords : function(o){
- /**
- * After any data loads, the raw JSON data is available for further custom processing.
- * @type Object
- */
- this.o = o;
- var s = this.meta, Record = this.recordType,
- f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
-
-// Generate extraction functions for the totalProperty, the root, the id, and for each field
- if (!this.ef) {
- if(s.totalProperty) {
- this.getTotal = this.getJsonAccessor(s.totalProperty);
- }
- if(s.successProperty) {
- this.getSuccess = this.getJsonAccessor(s.successProperty);
- }
- this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
- if (s.id) {
- var g = this.getJsonAccessor(s.id);
- this.getId = function(rec) {
- var r = g(rec);
- return (r === undefined || r === "") ? null : r;
- };
- } else {
- this.getId = function(){return null;};
- }
- this.ef = [];
- for(var jj = 0; jj < fl; jj++){
- f = fi[jj];
- var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
- this.ef[jj] = this.getJsonAccessor(map);
- }
+ validateValue : function(value)
+ {
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
}
-
- var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
- if(s.totalProperty){
- var vt = parseInt(this.getTotal(o), 10);
- if(!isNaN(vt)){
- totalRecords = vt;
+
+ if(value.length < 1) { // if it's blank
+ if(this.allowBlank){
+ return true;
}
+ return false;
}
- if(s.successProperty){
- var vs = this.getSuccess(o);
- if(vs === false || vs === 'false'){
- success = false;
+
+ if(value.length < this.minLength){
+ return false;
+ }
+ if(value.length > this.maxLength){
+ return false;
+ }
+ if(this.vtype){
+ var vt = Roo.form.VTypes;
+ if(!vt[this.vtype](value, this)){
+ return false;
}
}
- var records = [];
- for(var i = 0; i < c; i++){
- var n = root[i];
- var values = {};
- var id = this.getId(n);
- for(var j = 0; j < fl; j++){
- f = fi[j];
- var v = this.ef[j](n);
- if (!f.convert) {
- Roo.log('missing convert for ' + f.name);
- Roo.log(f);
- continue;
+ if(typeof this.validator == "function"){
+ var msg = this.validator(value);
+ if(msg !== true){
+ return false;
}
- values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
+ if (typeof(msg) == 'string') {
+ this.invalidText = msg;
}
- var record = new Record(values, id);
- record.json = n;
- records[i] = record;
}
- return {
- raw : o,
- success : success,
- records : records,
- totalRecords : totalRecords
- };
- }
-});/*
- * Based on:
- * Ext JS Library 1.1.1
- * Copyright(c) 2006-2007, Ext JS, LLC.
- *
- * Originally Released Under LGPL - original licence link has changed is not relivant.
- *
- * Fork - LGPL
- * <script type="text/javascript">
- */
-
-/**
- * @class Roo.data.ArrayReader
- * @extends Roo.data.DataReader
- * Data reader class to create an Array of Roo.data.Record objects from an Array.
- * Each element of that Array represents a row of data fields. The
- * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
- * of the field definition if it exists, or the field's ordinal position in the definition.<br>
- * <p>
- * Example code:.
- * <pre><code>
-var RecordDef = Roo.data.Record.create([
- {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
- {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
-]);
-var myReader = new Roo.data.ArrayReader({
- id: 0 // The subscript within row Array that provides an ID for the Record (optional)
-}, RecordDef);
-</code></pre>
- * <p>
- * This would consume an Array like this:
- * <pre><code>
-[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
- </code></pre>
- * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
- * @constructor
- * Create a new JsonReader
- * @param {Object} meta Metadata configuration options.
- * @param {Object} recordType Either an Array of field definition objects
- * as specified to {@link Roo.data.Record#create},
- * or an {@link Roo.data.Record} object
- * created using {@link Roo.data.Record#create}.
- */
-Roo.data.ArrayReader = function(meta, recordType){
- Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
-};
+
+ if(this.regex && !this.regex.test(value)){
+ return false;
+ }
+
+ return true;
+ },
+
+ // private
+ fireKey : function(e){
+ //Roo.log('field ' + e.getKey());
+ if(e.isNavKeyPress()){
+ this.fireEvent("specialkey", this, e);
+ }
+ },
+ focus : function (selectText){
+ if(this.rendered){
+ this.inputEl().focus();
+ if(selectText === true){
+ this.inputEl().dom.select();
+ }
+ }
+ return this;
+ } ,
+
+ onFocus : function(){
+ if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
+ // this.el.addClass(this.focusClass);
+ }
+ if(!this.hasFocus){
+ this.hasFocus = true;
+ this.startValue = this.getValue();
+ this.fireEvent("focus", this);
+ }
+ },
+
+ beforeBlur : Roo.emptyFn,
-Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
+
+ // private
+ onBlur : function(){
+ this.beforeBlur();
+ if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
+ //this.el.removeClass(this.focusClass);
+ }
+ this.hasFocus = false;
+ if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
+ this.validate();
+ }
+ var v = this.getValue();
+ if(String(v) !== String(this.startValue)){
+ this.fireEvent('change', this, v, this.startValue);
+ }
+ this.fireEvent("blur", this);
+ },
+
+ onChange : function(e)
+ {
+ var v = this.getValue();
+ if(String(v) !== String(this.startValue)){
+ this.fireEvent('change', this, v, this.startValue);
+ }
+
+ },
+
/**
- * Create a data block containing Roo.data.Records from an XML document.
- * @param {Object} o An Array of row objects which represents the dataset.
- * @return {Object} data A data block which is used by an Roo.data.Store object as
- * a cache of Roo.data.Records.
+ * Resets the current field value to the originally loaded value and clears any validation messages
*/
- readRecords : function(o){
- var sid = this.meta ? this.meta.id : null;
- var recordType = this.recordType, fields = recordType.prototype.fields;
- var records = [];
- var root = o;
- for(var i = 0; i < root.length; i++){
- var n = root[i];
- var values = {};
- var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
- for(var j = 0, jlen = fields.length; j < jlen; j++){
- var f = fields.items[j];
- var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
- var v = n[k] !== undefined ? n[k] : f.defaultValue;
- v = f.convert(v);
- values[f.name] = v;
- }
- var record = new recordType(values, id);
- record.json = n;
- records[records.length] = record;
- }
- return {
- records : records,
- totalRecords : records.length
- };
- }
-});/*
- * - LGPL
- * *
- */
-
-/**
- * @class Roo.bootstrap.ComboBox
- * @extends Roo.bootstrap.TriggerField
- * A combobox control with support for autocomplete, remote-loading, paging and many other features.
- * @cfg {Boolean} append (true|false) default false
- * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
- * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
- * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
- * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
- * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
- * @cfg {Boolean} animate default true
- * @cfg {Boolean} emptyResultText only for touch device
- * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
- * @cfg {String} emptyTitle default ''
- * @constructor
- * Create a new ComboBox.
- * @param {Object} config Configuration options
- */
-Roo.bootstrap.ComboBox = function(config){
- Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event expand
- * Fires when the dropdown list is expanded
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'expand' : true,
- /**
- * @event collapse
- * Fires when the dropdown list is collapsed
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'collapse' : true,
- /**
- * @event beforeselect
- * Fires before a list item is selected. Return false to cancel the selection.
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {Roo.data.Record} record The data record returned from the underlying store
- * @param {Number} index The index of the selected item in the dropdown list
- */
- 'beforeselect' : true,
- /**
- * @event select
- * Fires when a list item is selected
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
- * @param {Number} index The index of the selected item in the dropdown list
- */
- 'select' : true,
- /**
- * @event beforequery
- * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
- * The event object passed has these properties:
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {String} query The query
- * @param {Boolean} forceAll true to force "all" query
- * @param {Boolean} cancel true to cancel the query
- * @param {Object} e The query event object
- */
- 'beforequery': true,
- /**
- * @event add
- * Fires when the 'add' icon is pressed (add a listener to enable add button)
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'add' : true,
- /**
- * @event edit
- * Fires when the 'edit' icon is pressed (add a listener to enable add button)
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
- */
- 'edit' : true,
- /**
- * @event remove
- * Fires when the remove value from the combobox array
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'remove' : true,
- /**
- * @event afterremove
- * Fires when the remove value from the combobox array
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'afterremove' : true,
- /**
- * @event specialfilter
- * Fires when specialfilter
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'specialfilter' : true,
- /**
- * @event tick
- * Fires when tick the element
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- */
- 'tick' : true,
- /**
- * @event touchviewdisplay
- * Fires when touch view require special display (default is using displayField)
- * @param {Roo.bootstrap.ComboBox} combo This combo box
- * @param {Object} cfg set html .
- */
- 'touchviewdisplay' : true
-
- });
-
- this.item = [];
- this.tickItems = [];
-
- this.selectedIndex = -1;
- if(this.mode == 'local'){
- if(config.queryDelay === undefined){
- this.queryDelay = 10;
- }
- if(config.minChars === undefined){
- this.minChars = 0;
- }
- }
-};
-
-Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
-
- /**
- * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
- * rendering into an Roo.Editor, defaults to false)
- */
- /**
- * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
- * {tag: "input", type: "text", size: "24", autocomplete: "off"})
- */
- /**
- * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
- */
- /**
- * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
- * the dropdown list (defaults to undefined, with no header element)
- */
-
+ reset : function(){
+ this.setValue(this.originalValue);
+ this.validate();
+ },
/**
- * @cfg {String/Roo.Template} tpl The template to use to render the output
+ * Returns the name of the field
+ * @return {Mixed} name The name field
*/
-
+ getName: function(){
+ return this.name;
+ },
/**
- * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
- */
- listWidth: undefined,
- /**
- * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
- * mode = 'remote' or 'text' if mode = 'local')
- */
- displayField: undefined,
-
- /**
- * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
- * mode = 'remote' or 'value' if mode = 'local').
- * Note: use of a valueField requires the user make a selection
- * in order for a value to be mapped.
- */
- valueField: undefined,
- /**
- * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
- */
- modalTitle : '',
-
- /**
- * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
- * field's data value (defaults to the underlying DOM element's name)
- */
- hiddenName: undefined,
- /**
- * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
- */
- listClass: '',
- /**
- * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
- */
- selectedClass: 'active',
-
- /**
- * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
- */
- shadow:'sides',
- /**
- * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
- * anchor positions (defaults to 'tl-bl')
- */
- listAlign: 'tl-bl?',
- /**
- * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
- */
- maxHeight: 300,
- /**
- * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
- * query specified by the allQuery config option (defaults to 'query')
- */
- triggerAction: 'query',
- /**
- * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
- * (defaults to 4, does not apply if editable = false)
- */
- minChars : 4,
- /**
- * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
- * delay (typeAheadDelay) if it matches a known value (defaults to false)
- */
- typeAhead: false,
- /**
- * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
- * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
- */
- queryDelay: 500,
- /**
- * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
- * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
- */
- pageSize: 0,
- /**
- * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
- * when editable = true (defaults to false)
- */
- selectOnFocus:false,
- /**
- * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
- */
- queryParam: 'query',
- /**
- * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
- * when mode = 'remote' (defaults to 'Loading...')
- */
- loadingText: 'Loading...',
- /**
- * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
- */
- resizable: false,
- /**
- * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
- */
- handleHeight : 8,
- /**
- * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
- * traditional select (defaults to true)
- */
- editable: true,
- /**
- * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
- */
- allQuery: '',
- /**
- * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
- */
- mode: 'remote',
- /**
- * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
- * listWidth has a higher value)
- */
- minListWidth : 70,
- /**
- * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
- * allow the user to set arbitrary text into the field (defaults to false)
- */
- forceSelection:false,
- /**
- * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
- * if typeAhead = true (defaults to 250)
- */
- typeAheadDelay : 250,
- /**
- * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
- * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
- */
- valueNotFoundText : undefined,
- /**
- * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
- */
- blockFocus : false,
-
- /**
- * @cfg {Boolean} disableClear Disable showing of clear button.
- */
- disableClear : false,
- /**
- * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
- */
- alwaysQuery : false,
-
- /**
- * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
- */
- multiple : false,
-
- /**
- * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
- */
- invalidClass : "has-warning",
-
- /**
- * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
+ * 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
*/
- validClass : "has-success",
-
+ getValue : function(){
+
+ var v = this.inputEl().getValue();
+
+ return v;
+ },
/**
- * @cfg {Boolean} specialFilter (true|false) special filter default false
+ * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
+ * @return {Mixed} value The field value
*/
- specialFilter : false,
+ getRawValue : function(){
+ var v = this.inputEl().getValue();
+
+ return v;
+ },
/**
- * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
+ * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
+ * @param {Mixed} value The value to set
*/
- mobileTouchView : true,
+ setRawValue : function(v){
+ return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
+ },
- /**
- * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
- */
- useNativeIOS : false,
+ selectText : function(start, end){
+ var v = this.getRawValue();
+ if(v.length > 0){
+ start = start === undefined ? 0 : start;
+ end = end === undefined ? v.length : end;
+ var d = this.inputEl().dom;
+ if(d.setSelectionRange){
+ d.setSelectionRange(start, end);
+ }else if(d.createTextRange){
+ var range = d.createTextRange();
+ range.moveStart("character", start);
+ range.moveEnd("character", v.length-end);
+ range.select();
+ }
+ }
+ },
/**
- * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
+ * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
+ * @param {Mixed} value The value to set
*/
- mobile_restrict_height : false,
-
- ios_options : false,
-
- //private
- addicon : false,
- editicon: false,
-
- page: 0,
- hasQuery: false,
- append: false,
- loadNext: false,
- autoFocus : true,
- tickable : false,
- btnPosition : 'right',
- triggerList : true,
- showToggleBtn : true,
- animate : true,
- emptyResultText: 'Empty',
- triggerText : 'Select',
- emptyTitle : '',
-
- // element that contains real text value.. (when hidden is used..)
+ setValue : function(v){
+ this.value = v;
+ if(this.rendered){
+ this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
+ this.validate();
+ }
+ },
- getAutoCreate : function()
- {
- var cfg = false;
- //render
- /*
- * Render classic select for iso
- */
-
- if(Roo.isIOS && this.useNativeIOS){
- cfg = this.getAutoCreateNativeIOS();
- return cfg;
+ /*
+ processValue : function(value){
+ if(this.stripCharsRe){
+ var newValue = value.replace(this.stripCharsRe, '');
+ if(newValue !== value){
+ this.setRawValue(newValue);
+ return newValue;
+ }
}
+ return value;
+ },
+ */
+ preFocus : function(){
- /*
- * Touch Devices
- */
-
- if(Roo.isTouch && this.mobileTouchView){
- cfg = this.getAutoCreateTouchView();
- return cfg;;
+ if(this.selectOnFocus){
+ this.inputEl().dom.select();
}
+ },
+ filterKeys : function(e){
+ var k = e.getKey();
+ if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
+ return;
+ }
+ var c = e.getCharCode(), cc = String.fromCharCode(c);
+ if(Roo.isIE && (e.isSpecialKey() || !cc)){
+ return;
+ }
+ if(!this.maskRe.test(cc)){
+ e.stopEvent();
+ }
+ },
+ /**
+ * Clear any invalid styles/messages for this field
+ */
+ clearInvalid : function(){
- /*
- * Normal ComboBox
- */
- if(!this.tickable){
- cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
- return cfg;
+ if(!this.el || this.preventMark){ // not rendered
+ return;
}
- /*
- * ComboBox with tickable selections
- */
-
- var align = this.labelAlign || this.parentLabelAlign();
- cfg = {
- cls : 'form-group roo-combobox-tickable' //input-group
- };
+ this.el.removeClass([this.invalidClass, 'is-invalid']);
- var btn_text_select = '';
- var btn_text_done = '';
- var btn_text_cancel = '';
+ if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+
+ var feedback = this.el.select('.form-control-feedback', true).first();
+
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
+ }
+
+ }
- if (this.btn_text_show) {
- btn_text_select = 'Select';
- btn_text_done = 'Done';
- btn_text_cancel = 'Cancel';
+ if(this.indicator){
+ this.indicator.removeClass('visible');
+ this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
}
- var buttons = {
- tag : 'div',
- cls : 'tickable-buttons',
- cn : [
- {
- tag : 'button',
- type : 'button',
- cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
- //html : this.triggerText
- html: btn_text_select
- },
- {
- tag : 'button',
- type : 'button',
- name : 'ok',
- cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
- //html : 'Done'
- html: btn_text_done
- },
- {
- tag : 'button',
- type : 'button',
- name : 'cancel',
- cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
- //html : 'Cancel'
- html: btn_text_cancel
- }
- ]
- };
+ this.fireEvent('valid', this);
+ },
+
+ /**
+ * Mark this field as valid
+ */
+ markValid : function()
+ {
+ if(!this.el || this.preventMark){ // not rendered...
+ return;
+ }
- if(this.editable){
- buttons.cn.unshift({
- tag: 'input',
- cls: 'roo-select2-search-field-input'
- });
+ this.el.removeClass([this.invalidClass, this.validClass]);
+ this.inputEl().removeClass(['is-valid', 'is-invalid']);
+
+ var feedback = this.el.select('.form-control-feedback', true).first();
+
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
}
- var _this = this;
+ if(this.indicator){
+ this.indicator.removeClass('visible');
+ this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
+ }
- Roo.each(buttons.cn, function(c){
- if (_this.size) {
- c.cls += ' btn-' + _this.size;
- }
+ if(this.disabled){
+ return;
+ }
+
+
+ if(this.allowBlank && !this.getRawValue().length){
+ return;
+ }
+ if (Roo.bootstrap.version == 3) {
+ this.el.addClass(this.validClass);
+ } else {
+ this.inputEl().addClass('is-valid');
+ }
- if (_this.disabled) {
- c.disabled = true;
+ if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
+
+ var feedback = this.el.select('.form-control-feedback', true).first();
+
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+ this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
}
- });
+
+ }
- var box = {
- tag: 'div',
- style : 'display: contents',
- cn: [
- {
- tag: 'input',
- type : 'hidden',
- cls: 'form-hidden-field'
- },
- {
- tag: 'ul',
- cls: 'roo-select2-choices',
- cn:[
- {
- tag: 'li',
- cls: 'roo-select2-search-field',
- cn: [
- buttons
- ]
- }
- ]
- }
- ]
- };
+ this.fireEvent('valid', this);
+ },
+
+ /**
+ * Mark this field as invalid
+ * @param {String} msg The validation message
+ */
+ markInvalid : function(msg)
+ {
+ if(!this.el || this.preventMark){ // not rendered
+ return;
+ }
- var combobox = {
- cls: 'roo-select2-container input-group roo-select2-container-multi',
- cn: [
+ this.el.removeClass([this.invalidClass, this.validClass]);
+ this.inputEl().removeClass(['is-valid', 'is-invalid']);
+
+ var feedback = this.el.select('.form-control-feedback', true).first();
+
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass(
+ [this.invalidFeedbackClass, this.validFeedbackClass]);
+ }
+
+ if(this.disabled){
+ return;
+ }
+
+ if(this.allowBlank && !this.getRawValue().length){
+ return;
+ }
+
+ if(this.indicator){
+ this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
+ this.indicator.addClass('visible');
+ }
+ if (Roo.bootstrap.version == 3) {
+ this.el.addClass(this.invalidClass);
+ } else {
+ this.inputEl().addClass('is-invalid');
+ }
+
+
+
+ if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
+
+ var feedback = this.el.select('.form-control-feedback', true).first();
+
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
- box
-// {
-// tag: 'ul',
-// cls: 'typeahead typeahead-long dropdown-menu',
-// style: 'display:none; max-height:' + this.maxHeight + 'px;'
-// }
- ]
+ if(this.getValue().length || this.forceFeedback){
+ this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
+ }
+
+ }
+
+ }
+
+ this.fireEvent('invalid', this, msg);
+ },
+ // private
+ SafariOnKeyDown : function(event)
+ {
+ // this is a workaround for a password hang bug on chrome/ webkit.
+ if (this.inputEl().dom.type != 'password') {
+ return;
+ }
+
+ var isSelectAll = false;
+
+ if(this.inputEl().dom.selectionEnd > 0){
+ isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
+ }
+ if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
+ event.preventDefault();
+ this.setValue('');
+ return;
+ }
+
+ if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
+
+ event.preventDefault();
+ // this is very hacky as keydown always get's upper case.
+ //
+ var cc = String.fromCharCode(event.getCharCode());
+ this.setValue( event.shiftKey ? cc : cc.toLowerCase());
+
+ }
+ },
+ adjustWidth : function(tag, w){
+ tag = tag.toLowerCase();
+ if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
+ if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
+ if(tag == 'input'){
+ return w + 2;
+ }
+ if(tag == 'textarea'){
+ return w-2;
+ }
+ }else if(Roo.isOpera){
+ if(tag == 'input'){
+ return w + 2;
+ }
+ if(tag == 'textarea'){
+ return w-2;
+ }
+ }
+ }
+ return w;
+ },
+
+ setFieldLabel : function(v)
+ {
+ if(!this.rendered){
+ return;
+ }
+
+ if(this.indicatorEl()){
+ var ar = this.el.select('label > span',true);
+
+ if (ar.elements.length) {
+ this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+ this.fieldLabel = v;
+ return;
+ }
+
+ var br = this.el.select('label',true);
+
+ if(br.elements.length) {
+ this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+ this.fieldLabel = v;
+ return;
+ }
+
+ Roo.log('Cannot Found any of label > span || label in input');
+ return;
+ }
+
+ this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+ this.fieldLabel = v;
+
+
+ }
+});
+
+
+/*
+ * - LGPL
+ *
+ * Input
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.form.TextArea
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap TextArea class
+ * @cfg {Number} cols Specifies the visible width of a text area
+ * @cfg {Number} rows Specifies the visible number of lines in a text area
+ * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
+ * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
+ * @cfg {string} html text
+ *
+ * @constructor
+ * Create a new TextArea
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.form.TextArea = function(config){
+ Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
+
+};
+
+Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
+
+ cols : false,
+ rows : 5,
+ readOnly : false,
+ warp : 'soft',
+ resize : false,
+ value: false,
+ html: false,
+
+ getAutoCreate : function(){
+
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
+
+ var id = Roo.id();
+
+ var cfg = {};
+
+ if(this.inputType != 'hidden'){
+ cfg.cls = 'form-group' //input-group
+ }
+
+ var input = {
+ tag: 'textarea',
+ id : id,
+ warp : this.warp,
+ rows : this.rows,
+ value : this.value || '',
+ html: this.html || '',
+ cls : 'form-control',
+ placeholder : this.placeholder || ''
+
};
+ if(this.maxLength && this.maxLength != Number.MAX_VALUE){
+ input.maxLength = this.maxLength;
+ }
+
+ if(this.resize){
+ input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
+ }
+
+ if(this.cols){
+ input.cols = this.cols;
+ }
+
+ if (this.readOnly) {
+ input.readonly = true;
+ }
+
+ if (this.name) {
+ input.name = this.name;
+ }
+
+ if (this.size) {
+ input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
+ }
+
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ var inputblock = input;
+
if(this.hasFeedback && !this.allowBlank){
var feedback = {
cls: 'glyphicon form-control-feedback'
};
- combobox.cn.push(feedback);
+ inputblock = {
+ cls : 'has-feedback',
+ cn : [
+ input,
+ feedback
+ ]
+ };
}
- var indicator = {
- tag : 'i',
- cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- };
- if (Roo.bootstrap.version == 4) {
- indicator = {
- tag : 'i',
- style : 'display:none'
+
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
};
- }
- if (align ==='left' && this.fieldLabel.length) {
+ if (this.before) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.before
+ });
+ }
- cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
+ inputblock.cn.push(input);
+
+ if(this.hasFeedback && !this.allowBlank){
+ inputblock.cls += ' has-feedback';
+ inputblock.cn.push(feedback);
+ }
+
+ if (this.after) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.after
+ });
+ }
+ }
+
+ if (align ==='left' && this.fieldLabel.length) {
cfg.cn = [
- indicator,
{
tag: 'label',
'for' : id,
- cls : 'control-label col-form-label',
+ cls : 'control-label',
html : this.fieldLabel
-
},
{
- cls : "",
+ cls : "",
cn: [
- combobox
+ inputblock
]
}
];
- var labelCfg = cfg.cn[1];
- var contentCfg = cfg.cn[2];
-
-
- if(this.indicatorpos == 'right'){
-
- cfg.cn = [
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-form-label',
- cn : [
- {
- tag : 'span',
- html : this.fieldLabel
- },
- indicator
- ]
- },
- {
- cls : "",
- cn: [
- combobox
- ]
- }
-
- ];
-
-
-
- labelCfg = cfg.cn[0];
- contentCfg = cfg.cn[1];
-
- }
-
if(this.labelWidth > 12){
- labelCfg.style = "width: " + this.labelWidth + 'px';
+ cfg.cn[0].style = "width: " + this.labelWidth + 'px';
}
-
+
if(this.labelWidth < 13 && this.labelmd == 0){
this.labelmd = this.labelWidth;
}
-
+
if(this.labellg > 0){
- labelCfg.cls += ' col-lg-' + this.labellg;
- contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+ cfg.cn[0].cls += ' col-lg-' + this.labellg;
+ cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
}
-
+
if(this.labelmd > 0){
- labelCfg.cls += ' col-md-' + this.labelmd;
- contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ cfg.cn[0].cls += ' col-md-' + this.labelmd;
+ cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
}
-
+
if(this.labelsm > 0){
- labelCfg.cls += ' col-sm-' + this.labelsm;
- contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ cfg.cn[0].cls += ' col-sm-' + this.labelsm;
+ cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
}
-
+
if(this.labelxs > 0){
- labelCfg.cls += ' col-xs-' + this.labelxs;
- contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ cfg.cn[0].cls += ' col-xs-' + this.labelxs;
+ cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
}
-
-
+
} else if ( this.fieldLabel.length) {
-// Roo.log(" label");
- cfg.cn = [
- indicator,
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
- },
- combobox
- ];
-
- if(this.indicatorpos == 'right'){
- cfg.cn = [
- {
- tag: 'label',
- //cls : 'input-group-addon',
- html : this.fieldLabel
- },
- indicator,
- combobox
- ];
-
- }
+ cfg.cn = [
+
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+
+ },
+
+ inputblock
+
+ ];
} else {
-
-// Roo.log(" no label && no align");
- cfg = combobox
-
+
+ cfg.cn = [
+
+ inputblock
+
+ ];
}
-
- var settings=this;
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
- }
- });
+
+ if (this.disabled) {
+ input.disabled=true;
+ }
return cfg;
},
+ /**
+ * return the real textarea element.
+ */
+ inputEl: function ()
+ {
+ return this.el.select('textarea.form-control',true).first();
+ },
- _initEventsCalled : false,
-
- // private
- initEvents: function()
- {
- if (this._initEventsCalled) { // as we call render... prevent looping...
- return;
- }
- this._initEventsCalled = true;
+ /**
+ * Clear any invalid styles/messages for this field
+ */
+ clearInvalid : function()
+ {
- if (!this.store) {
- throw "can not find store for combo";
+ if(!this.el || this.preventMark){ // not rendered
+ return;
}
- this.indicator = this.indicatorEl();
-
- this.store = Roo.factory(this.store, Roo.data);
- this.store.parent = this;
+ var label = this.el.select('label', true).first();
+ var icon = this.el.select('i.fa-star', true).first();
- // if we are building from html. then this element is so complex, that we can not really
- // use the rendered HTML.
- // so we have to trash and replace the previous code.
- if (Roo.XComponent.build_from_html) {
- // remove this element....
- var e = this.el.dom, k=0;
- while (e ) { e = e.previousSibling; ++k;}
-
- this.el.remove();
+ if(label && icon){
+ icon.remove();
+ }
+ this.el.removeClass( this.validClass);
+ this.inputEl().removeClass('is-invalid');
+
+ if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
- this.el=false;
- this.rendered = false;
+ var feedback = this.el.select('.form-control-feedback', true).first();
+
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
+ }
- this.render(this.parent().getChildContainer(true), k);
}
- if(Roo.isIOS && this.useNativeIOS){
- this.initIOSView();
+ this.fireEvent('valid', this);
+ },
+
+ /**
+ * Mark this field as valid
+ */
+ markValid : function()
+ {
+ if(!this.el || this.preventMark){ // not rendered
return;
}
- /*
- * Touch Devices
- */
+ this.el.removeClass([this.invalidClass, this.validClass]);
+ this.inputEl().removeClass(['is-valid', 'is-invalid']);
- if(Roo.isTouch && this.mobileTouchView){
- this.initTouchView();
+ var feedback = this.el.select('.form-control-feedback', true).first();
+
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+ }
+
+ if(this.disabled || this.allowBlank){
return;
}
- if(this.tickable){
- this.initTickableEvents();
- return;
+ var label = this.el.select('label', true).first();
+ var icon = this.el.select('i.fa-star', true).first();
+
+ if(label && icon){
+ icon.remove();
+ }
+ if (Roo.bootstrap.version == 3) {
+ this.el.addClass(this.validClass);
+ } else {
+ this.inputEl().addClass('is-valid');
}
- Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
- if(this.hiddenName){
+ if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
- this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+ var feedback = this.el.select('.form-control-feedback', true).first();
+
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
+ this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
+ }
- this.hiddenField.dom.value =
- this.hiddenValue !== undefined ? this.hiddenValue :
- this.value !== undefined ? this.value : '';
-
- // prevent input submission
- this.el.dom.removeAttribute('name');
- this.hiddenField.dom.setAttribute('name', this.hiddenName);
-
-
}
- //if(Roo.isGecko){
- // this.el.dom.setAttribute('autocomplete', 'off');
- //}
-
- var cls = 'x-combo-list';
-
- //this.list = new Roo.Layer({
- // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
- //});
- var _this = this;
-
- (function(){
- var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
- _this.list.setWidth(lw);
- }).defer(100);
-
- this.list.on('mouseover', this.onViewOver, this);
- this.list.on('mousemove', this.onViewMove, this);
- this.list.on('scroll', this.onViewScroll, this);
-
- /*
- this.list.swallowEvent('mousewheel');
- this.assetHeight = 0;
-
- if(this.title){
- this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
- this.assetHeight += this.header.getHeight();
+ this.fireEvent('valid', this);
+ },
+
+ /**
+ * Mark this field as invalid
+ * @param {String} msg The validation message
+ */
+ markInvalid : function(msg)
+ {
+ if(!this.el || this.preventMark){ // not rendered
+ return;
}
-
- this.innerList = this.list.createChild({cls:cls+'-inner'});
- this.innerList.on('mouseover', this.onViewOver, this);
- this.innerList.on('mousemove', this.onViewMove, this);
- this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
- if(this.allowBlank && !this.pageSize && !this.disableClear){
- this.footer = this.list.createChild({cls:cls+'-ft'});
- this.pageTb = new Roo.Toolbar(this.footer);
-
- }
- if(this.pageSize){
- this.footer = this.list.createChild({cls:cls+'-ft'});
- this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
- {pageSize: this.pageSize});
-
- }
+ this.el.removeClass([this.invalidClass, this.validClass]);
+ this.inputEl().removeClass(['is-valid', 'is-invalid']);
- if (this.pageTb && this.allowBlank && !this.disableClear) {
- var _this = this;
- this.pageTb.add(new Roo.Toolbar.Fill(), {
- cls: 'x-btn-icon x-btn-clear',
- text: ' ',
- handler: function()
- {
- _this.collapse();
- _this.clearValue();
- _this.onSelect(false, -1);
- }
- });
- }
- if (this.footer) {
- this.assetHeight += this.footer.getHeight();
- }
- */
+ var feedback = this.el.select('.form-control-feedback', true).first();
- if(!this.tpl){
- this.tpl = Roo.bootstrap.version == 4 ?
- '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
- '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
}
- this.view = new Roo.View(this.list, this.tpl, {
- singleSelect:true, store: this.store, selectedClass: this.selectedClass
- });
- //this.view.wrapEl.setDisplayed(false);
- this.view.on('click', this.onViewClick, this);
+ if(this.disabled || this.allowBlank){
+ return;
+ }
+ var label = this.el.select('label', true).first();
+ var icon = this.el.select('i.fa-star', true).first();
- this.store.on('beforeload', this.onBeforeLoad, this);
- this.store.on('load', this.onLoad, this);
- this.store.on('loadexception', this.onLoadException, this);
- /*
- if(this.resizable){
- this.resizer = new Roo.Resizable(this.list, {
- pinned:true, handles:'se'
- });
- this.resizer.on('resize', function(r, w, h){
- this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
- this.listWidth = w;
- this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
- this.restrictHeight();
- }, this);
- this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
- }
- */
- if(!this.editable){
- this.editable = true;
- this.setEditable(false);
+ if(!this.getValue().length && label && !icon){
+ this.el.createChild({
+ tag : 'i',
+ cls : 'text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required',
+ style : 'margin-right:5px;'
+ }, label, true);
}
- /*
+ if (Roo.bootstrap.version == 3) {
+ this.el.addClass(this.invalidClass);
+ } else {
+ this.inputEl().addClass('is-invalid');
+ }
- if (typeof(this.events.add.listeners) != 'undefined') {
+ // fixme ... this may be depricated need to test..
+ if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
- this.addicon = this.wrap.createChild(
- {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
-
- this.addicon.on('click', function(e) {
- this.fireEvent('add', this);
- }, this);
- }
- if (typeof(this.events.edit.listeners) != 'undefined') {
+ var feedback = this.el.select('.form-control-feedback', true).first();
- this.editicon = this.wrap.createChild(
- {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
- if (this.addicon) {
- this.editicon.setStyle('margin-left', '40px');
- }
- this.editicon.on('click', function(e) {
+ if(feedback){
+ this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
- // we fire even if inothing is selected..
- this.fireEvent('edit', this, this.lastData );
+ if(this.getValue().length || this.forceFeedback){
+ this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
+ }
- }, this);
+ }
+
}
- */
- this.keyNav = new Roo.KeyNav(this.inputEl(), {
- "up" : function(e){
- this.inKeyMode = true;
- this.selectPrev();
- },
-
- "down" : function(e){
- if(!this.isExpanded()){
- this.onTriggerClick();
- }else{
- this.inKeyMode = true;
- this.selectNext();
- }
- },
+ this.fireEvent('invalid', this, msg);
+ }
+});
- "enter" : function(e){
-// this.onViewClick();
- //return true;
- this.collapse();
-
- if(this.fireEvent("specialkey", this, e)){
- this.onViewClick(false);
- }
-
- return true;
- },
+
+/*
+ * - LGPL
+ *
+ * trigger field - base class for combo..
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.form.TriggerField
+ * @extends Roo.bootstrap.form.Input
+ * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
+ * The trigger has no default action, so you must assign a function to implement the trigger click handler by
+ * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
+ * for which you can provide a custom implementation. For example:
+ * <pre><code>
+var trigger = new Roo.bootstrap.form.TriggerField();
+trigger.onTriggerClick = myTriggerFn;
+trigger.applyTo('my-field');
+</code></pre>
+ *
+ * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
+ * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
+ * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
+ * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
+ * @cfg {String} caret (search|calendar) BS3 only - carat fa name
- "esc" : function(e){
- this.collapse();
- },
+ * @constructor
+ * Create a new TriggerField.
+ * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
+ * to the base TextField)
+ */
+Roo.bootstrap.form.TriggerField = function(config){
+ this.mimicing = false;
+ Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
+};
- "tab" : function(e){
- this.collapse();
-
- if(this.fireEvent("specialkey", this, e)){
- this.onViewClick(false);
- }
-
- return true;
- },
+Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
+ /**
+ * @cfg {String} triggerClass A CSS class to apply to the trigger
+ */
+ /**
+ * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
+ */
+ hideTrigger:false,
- scope : this,
+ /**
+ * @cfg {Boolean} removable (true|false) special filter default false
+ */
+ removable : false,
+
+ /** @cfg {Boolean} grow @hide */
+ /** @cfg {Number} growMin @hide */
+ /** @cfg {Number} growMax @hide */
- doRelay : function(foo, bar, hname){
- if(hname == 'down' || this.scope.isExpanded()){
- return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
- }
- return true;
- },
+ /**
+ * @hide
+ * @method
+ */
+ autoSize: Roo.emptyFn,
+ // private
+ monitorTab : true,
+ // private
+ deferHeight : true,
- forceKeyDown: true
- });
-
+
+ actionMode : 'wrap',
+
+ caret : false,
+
+
+ getAutoCreate : function(){
+
+ var align = this.labelAlign || this.parentLabelAlign();
- this.queryDelay = Math.max(this.queryDelay || 10,
- this.mode == 'local' ? 10 : 250);
+ var id = Roo.id();
+ var cfg = {
+ cls: 'form-group' //input-group
+ };
- this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
- if(this.typeAhead){
- this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
- }
- if(this.editable !== false){
- this.inputEl().on("keyup", this.onKeyUp, this);
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ cls : 'form-control',
+ autocomplete: 'new-password',
+ placeholder : this.placeholder || ''
+
+ };
+ if (this.name) {
+ input.name = this.name;
}
- if(this.forceSelection){
- this.inputEl().on('blur', this.doForce, this);
+ if (this.size) {
+ input.cls += ' input-' + this.size;
}
- if(this.multiple){
- this.choices = this.el.select('ul.roo-select2-choices', true).first();
- this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+ if (this.disabled) {
+ input.disabled=true;
}
- },
-
- initTickableEvents: function()
- {
- this.createList();
- if(this.hiddenName){
+ var inputblock = input;
+
+ if(this.hasFeedback && !this.allowBlank){
- this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+ var feedback = {
+ tag: 'span',
+ cls: 'glyphicon form-control-feedback'
+ };
- this.hiddenField.dom.value =
- this.hiddenValue !== undefined ? this.hiddenValue :
- this.value !== undefined ? this.value : '';
+ if(this.removable && !this.editable ){
+ inputblock = {
+ cls : 'has-feedback',
+ cn : [
+ inputblock,
+ {
+ tag: 'button',
+ html : 'x',
+ cls : 'roo-combo-removable-btn close'
+ },
+ feedback
+ ]
+ };
+ } else {
+ inputblock = {
+ cls : 'has-feedback',
+ cn : [
+ inputblock,
+ feedback
+ ]
+ };
+ }
- // prevent input submission
- this.el.dom.removeAttribute('name');
- this.hiddenField.dom.setAttribute('name', this.hiddenName);
-
-
- }
-
-// this.list = this.el.select('ul.dropdown-menu',true).first();
-
- this.choices = this.el.select('ul.roo-select2-choices', true).first();
- this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
- if(this.triggerList){
- this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
+ } else {
+ if(this.removable && !this.editable ){
+ inputblock = {
+ cls : 'roo-removable',
+ cn : [
+ inputblock,
+ {
+ tag: 'button',
+ html : 'x',
+ cls : 'roo-combo-removable-btn close'
+ }
+ ]
+ };
+ }
}
-
- this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
- this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
- this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
- this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
-
- this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
- this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
-
- this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
- this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
- this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.okBtn.hide();
- this.cancelBtn.hide();
-
- var _this = this;
-
- (function(){
- var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
- _this.list.setWidth(lw);
- }).defer(100);
+ if (this.before || this.after) {
+
+ inputblock = {
+ cls : 'input-group',
+ cn : []
+ };
+ if (this.before) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon input-group-prepend input-group-text',
+ html : this.before
+ });
+ }
+
+ inputblock.cn.push(input);
+
+ if(this.hasFeedback && !this.allowBlank){
+ inputblock.cls += ' has-feedback';
+ inputblock.cn.push(feedback);
+ }
+
+ if (this.after) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon input-group-append input-group-text',
+ html : this.after
+ });
+ }
+
+ };
- this.list.on('mouseover', this.onViewOver, this);
- this.list.on('mousemove', this.onViewMove, this);
+
- this.list.on('scroll', this.onViewScroll, this);
+ var ibwrap = inputblock;
- if(!this.tpl){
- this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
- 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
- }
+ if(this.multiple){
+ ibwrap = {
+ tag: 'ul',
+ cls: 'roo-select2-choices',
+ cn:[
+ {
+ tag: 'li',
+ cls: 'roo-select2-search-field',
+ cn: [
- this.view = new Roo.View(this.list, this.tpl, {
- singleSelect:true,
- tickable:true,
- parent:this,
- store: this.store,
- selectedClass: this.selectedClass
- });
-
- //this.view.wrapEl.setDisplayed(false);
- this.view.on('click', this.onViewClick, this);
-
-
-
- this.store.on('beforeload', this.onBeforeLoad, this);
- this.store.on('load', this.onLoad, this);
- this.store.on('loadexception', this.onLoadException, this);
+ inputblock
+ ]
+ }
+ ]
+ };
+
+ }
- if(this.editable){
- this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
- "up" : function(e){
- this.inKeyMode = true;
- this.selectPrev();
- },
-
- "down" : function(e){
- this.inKeyMode = true;
- this.selectNext();
+ var combobox = {
+ cls: 'roo-select2-container input-group',
+ cn: [
+ {
+ tag: 'input',
+ type : 'hidden',
+ cls: 'form-hidden-field'
},
-
- "enter" : function(e){
- if(this.fireEvent("specialkey", this, e)){
- this.onViewClick(false);
- }
-
- return true;
- },
-
- "esc" : function(e){
- this.onTickableFooterButtonClick(e, false, false);
- },
-
- "tab" : function(e){
- this.fireEvent("specialkey", this, e);
-
- this.onTickableFooterButtonClick(e, false, false);
-
- return true;
- },
-
- scope : this,
-
- doRelay : function(e, fn, key){
- if(this.scope.isExpanded()){
- return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+ ibwrap
+ ]
+ };
+
+ if(!this.multiple && this.showToggleBtn){
+
+ var caret = {
+ tag: 'span',
+ cls: 'caret'
+ };
+ if (this.caret != false) {
+ caret = {
+ tag: 'i',
+ cls: 'fa fa-' + this.caret
+ };
+
+ }
+
+ combobox.cn.push({
+ tag :'span',
+ cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
+ cn : [
+ Roo.bootstrap.version == 3 ? caret : '',
+ {
+ tag: 'span',
+ cls: 'combobox-clear',
+ cn : [
+ {
+ tag : 'i',
+ cls: 'icon-remove'
+ }
+ ]
}
- return true;
- },
+ ]
- forceKeyDown: true
- });
+ })
}
- this.queryDelay = Math.max(this.queryDelay || 10,
- this.mode == 'local' ? 10 : 250);
-
-
- this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
-
- if(this.typeAhead){
- this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
+ if(this.multiple){
+ combobox.cls += ' roo-select2-container-multi';
}
-
- if(this.editable !== false){
- this.tickableInputEl().on("keyup", this.onKeyUp, this);
+ var indicator = {
+ tag : 'i',
+ cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required'
+ };
+ if (Roo.bootstrap.version == 4) {
+ indicator = {
+ tag : 'i',
+ style : 'display:none'
+ };
}
- this.indicator = this.indicatorEl();
-
- if(this.indicator){
- this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
- this.indicator.hide();
- }
- },
+ if (align ==='left' && this.fieldLabel.length) {
+
+ cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
- onDestroy : function(){
- if(this.view){
- this.view.setStore(null);
- this.view.el.removeAllListeners();
- this.view.el.remove();
- this.view.purgeListeners();
- }
- if(this.list){
- this.list.dom.innerHTML = '';
+ cfg.cn = [
+ indicator,
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label',
+ html : this.fieldLabel
+
+ },
+ {
+ cls : "",
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+
+ var labelCfg = cfg.cn[1];
+ var contentCfg = cfg.cn[2];
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label',
+ cn : [
+ {
+ tag : 'span',
+ html : this.fieldLabel
+ },
+ indicator
+ ]
+ },
+ {
+ cls : "",
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+
+ labelCfg = cfg.cn[0];
+ contentCfg = cfg.cn[1];
+ }
+
+ if(this.labelWidth > 12){
+ labelCfg.style = "width: " + this.labelWidth + 'px';
+ }
+
+ if(this.labelWidth < 13 && this.labelmd == 0){
+ this.labelmd = this.labelWidth;
+ }
+
+ if(this.labellg > 0){
+ labelCfg.cls += ' col-lg-' + this.labellg;
+ contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+ }
+
+ if(this.labelmd > 0){
+ labelCfg.cls += ' col-md-' + this.labelmd;
+ contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ }
+
+ if(this.labelsm > 0){
+ labelCfg.cls += ' col-sm-' + this.labelsm;
+ contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ }
+
+ if(this.labelxs > 0){
+ labelCfg.cls += ' col-xs-' + this.labelxs;
+ contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ }
+
+ } else if ( this.fieldLabel.length) {
+// Roo.log(" label");
+ cfg.cn = [
+ indicator,
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+
+ },
+
+ combobox
+
+ ];
+
+ if(this.indicatorpos == 'right'){
+
+ cfg.cn = [
+ {
+ tag: 'label',
+ cn : [
+ {
+ tag : 'span',
+ html : this.fieldLabel
+ },
+ indicator
+ ]
+
+ },
+ combobox
+
+ ];
+
+ }
+
+ } else {
+
+// Roo.log(" no label && no align");
+ cfg = combobox
+
+
}
- if(this.store){
- this.store.un('beforeload', this.onBeforeLoad, this);
- this.store.un('load', this.onLoad, this);
- this.store.un('loadexception', this.onLoadException, this);
- }
- Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ return cfg;
+
},
-
+
+
+
// private
- fireKey : function(e){
- if(e.isNavKeyPress() && !this.list.isVisible()){
- this.fireEvent("specialkey", this, e);
- }
+ onResize : function(w, h){
+// Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
+// if(typeof w == 'number'){
+// var x = w - this.trigger.getWidth();
+// this.inputEl().setWidth(this.adjustWidth('input', x));
+// this.trigger.setStyle('left', x+'px');
+// }
},
// private
- onResize: function(w, h){
-// Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
-//
-// if(typeof w != 'number'){
-// // we do not handle it!?!?
-// return;
-// }
-// var tw = this.trigger.getWidth();
-// // tw += this.addicon ? this.addicon.getWidth() : 0;
-// // tw += this.editicon ? this.editicon.getWidth() : 0;
-// var x = w - tw;
-// this.inputEl().setWidth( this.adjustWidth('input', x));
-//
-// //this.trigger.setStyle('left', x+'px');
-//
-// if(this.list && this.listWidth === undefined){
-// var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
-// this.list.setWidth(lw);
-// this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
-// }
-
-
-
+ adjustSize : Roo.BoxComponent.prototype.adjustSize,
+
+ // private
+ getResizeEl : function(){
+ return this.inputEl();
},
- /**
- * Allow or prevent the user from directly editing the field text. If false is passed,
- * the user will only be able to select from the items defined in the dropdown list. This method
- * is the runtime equivalent of setting the 'editable' config option at config time.
- * @param {Boolean} value True to allow the user to directly edit the field text
- */
- setEditable : function(value){
- if(value == this.editable){
- return;
- }
- this.editable = value;
- if(!value){
- this.inputEl().dom.setAttribute('readOnly', true);
- this.inputEl().on('mousedown', this.onTriggerClick, this);
- this.inputEl().addClass('x-combo-noedit');
- }else{
- this.inputEl().dom.setAttribute('readOnly', false);
- this.inputEl().un('mousedown', this.onTriggerClick, this);
- this.inputEl().removeClass('x-combo-noedit');
- }
+ // private
+ getPositionEl : function(){
+ return this.inputEl();
},
// private
-
- onBeforeLoad : function(combo,opts){
- if(!this.hasFocus){
- return;
- }
- if (!opts.add) {
- this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
- }
- this.restrictHeight();
- this.selectedIndex = -1;
+ alignErrorIcon : function(){
+ this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
},
// private
- onLoad : function(){
+ initEvents : function(){
- this.hasQuery = false;
+ this.createList();
- if(!this.hasFocus){
- return;
+ Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
+ //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
+ if(!this.multiple && this.showToggleBtn){
+ this.trigger = this.el.select('span.dropdown-toggle',true).first();
+ if(this.hideTrigger){
+ this.trigger.setDisplayed(false);
+ }
+ this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
}
- if(typeof(this.loading) !== 'undefined' && this.loading !== null){
- this.loading.hide();
+ if(this.multiple){
+ this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
}
- if(this.store.getCount() > 0){
+ if(this.removable && !this.editable && !this.tickable){
+ var close = this.closeTriggerEl();
- this.expand();
- this.restrictHeight();
- if(this.lastQuery == this.allQuery){
- if(this.editable && !this.tickable){
- this.inputEl().dom.select();
- }
-
- if(
- !this.selectByValue(this.value, true) &&
- this.autoFocus &&
- (
- !this.store.lastOptions ||
- typeof(this.store.lastOptions.add) == 'undefined' ||
- this.store.lastOptions.add != true
- )
- ){
- this.select(0, true);
- }
- }else{
- if(this.autoFocus){
- this.selectNext();
- }
- if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
- this.taTask.delay(this.typeAheadDelay);
- }
+ if(close){
+ close.setVisibilityMode(Roo.Element.DISPLAY).hide();
+ close.on('click', this.removeBtnClick, this, close);
}
- }else{
- this.onEmptyResults();
}
- //this.el.focus();
+ //this.trigger.addClassOnOver('x-form-trigger-over');
+ //this.trigger.addClassOnClick('x-form-trigger-click');
+
+ //if(!this.width){
+ // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
+ //}
},
- // private
- onLoadException : function()
+
+ closeTriggerEl : function()
{
- this.hasQuery = false;
+ var close = this.el.select('.roo-combo-removable-btn', true).first();
+ return close ? close : false;
+ },
+
+ removeBtnClick : function(e, h, el)
+ {
+ e.preventDefault();
- if(typeof(this.loading) !== 'undefined' && this.loading !== null){
- this.loading.hide();
+ if(this.fireEvent("remove", this) !== false){
+ this.reset();
+ this.fireEvent("afterremove", this)
}
+ },
+
+ createList : function()
+ {
+ this.list = Roo.get(document.body).createChild({
+ tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
+ cls: 'typeahead typeahead-long dropdown-menu shadow',
+ style: 'display:none'
+ });
- if(this.tickable && this.editable){
- return;
- }
+ this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
- this.collapse();
- // only causes errors at present
- //Roo.log(this.store.reader.jsonData);
- //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
- // fixme
- //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+ },
+
+ // private
+ initTrigger : function(){
+
+ },
+
+ // private
+ onDestroy : function(){
+ if(this.trigger){
+ this.trigger.removeAllListeners();
+ // this.trigger.remove();
+ }
+ //if(this.wrap){
+ // this.wrap.remove();
//}
-
-
+ Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
},
+
// private
- onTypeAhead : function(){
- if(this.store.getCount() > 0){
- var r = this.store.getAt(0);
- var newValue = r.data[this.displayField];
- var len = newValue.length;
- var selStart = this.getRawValue().length;
-
- if(selStart != len){
- this.setRawValue(newValue);
- this.selectText(selStart, newValue.length);
+ onFocus : function(){
+ Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
+ /*
+ if(!this.mimicing){
+ this.wrap.addClass('x-trigger-wrap-focus');
+ this.mimicing = true;
+ Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
+ if(this.monitorTab){
+ this.el.on("keydown", this.checkTab, this);
}
}
+ */
},
// private
- onSelect : function(record, index){
-
- if(this.fireEvent('beforeselect', this, record, index) !== false){
-
- this.setFromData(index > -1 ? record.data : false);
-
- this.collapse();
- this.fireEvent('select', this, record, index);
+ checkTab : function(e){
+ if(e.getKey() == e.TAB){
+ this.triggerBlur();
}
},
- /**
- * Returns the currently selected field value or empty string if no value is set.
- * @return {String} value The selected value
- */
- getValue : function()
- {
- if(Roo.isIOS && this.useNativeIOS){
- return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
- }
-
- if(this.multiple){
- return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
- }
-
- if(this.valueField){
- return typeof this.value != 'undefined' ? this.value : '';
- }else{
- return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
- }
+ // private
+ onBlur : function(){
+ // do nothing
},
-
- getRawValue : function()
- {
- if(Roo.isIOS && this.useNativeIOS){
- return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
+
+ // private
+ mimicBlur : function(e, t){
+ /*
+ if(!this.wrap.contains(t) && this.validateBlur()){
+ this.triggerBlur();
}
-
- var v = this.inputEl().getValue();
-
- return v;
+ */
},
- /**
- * Clears any text/value currently set in the field
- */
- clearValue : function(){
-
- if(this.hiddenField){
- this.hiddenField.dom.value = '';
- }
- this.value = '';
- this.setRawValue('');
- this.lastSelectionText = '';
- this.lastData = false;
-
- var close = this.closeTriggerEl();
-
- if(close){
- close.hide();
+ // private
+ triggerBlur : function(){
+ this.mimicing = false;
+ Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
+ if(this.monitorTab){
+ this.el.un("keydown", this.checkTab, this);
}
-
- this.validate();
-
+ //this.wrap.removeClass('x-trigger-wrap-focus');
+ Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
},
- /**
- * Sets the specified value into the field. If the value finds a match, the corresponding record text
- * will be displayed in the field. If the value does not match the data value of an existing item,
- * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
- * Otherwise the field will be blank (although the value will still be set).
- * @param {String} value The value to match
- */
- setValue : function(v)
- {
- if(Roo.isIOS && this.useNativeIOS){
- this.setIOSValue(v);
- return;
- }
-
- if(this.multiple){
- this.syncValue();
- return;
- }
-
- var text = v;
- if(this.valueField){
- var r = this.findRecord(this.valueField, v);
- if(r){
- text = r.data[this.displayField];
- }else if(this.valueNotFoundText !== undefined){
- text = this.valueNotFoundText;
- }
- }
- this.lastSelectionText = text;
- if(this.hiddenField){
- this.hiddenField.dom.value = v;
- }
- Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
- this.value = v;
-
- var close = this.closeTriggerEl();
+ // private
+ // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
+ validateBlur : function(e, t){
+ return true;
+ },
+
+ // private
+ onDisable : function(){
+ this.inputEl().dom.disabled = true;
+ //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
+ //if(this.wrap){
+ // this.wrap.addClass('x-item-disabled');
+ //}
+ },
+
+ // private
+ onEnable : function(){
+ this.inputEl().dom.disabled = false;
+ //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
+ //if(this.wrap){
+ // this.el.removeClass('x-item-disabled');
+ //}
+ },
+
+ // private
+ onShow : function(){
+ var ae = this.getActionEl();
- if(close){
- (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
+ if(ae){
+ ae.dom.style.display = '';
+ ae.dom.style.visibility = 'visible';
}
-
- this.validate();
},
- /**
- * @property {Object} the last set data for the element
- */
+
+ // private
- lastData : false,
+ onHide : function(){
+ var ae = this.getActionEl();
+ ae.dom.style.display = 'none';
+ },
+
/**
- * Sets the value of the field based on a object which is related to the record format for the store.
- * @param {Object} value the value to set as. or false on reset?
+ * The function that should handle the trigger's click event. This method does nothing by default until overridden
+ * by an implementing function.
+ * @method
+ * @param {EventObject} e
*/
- setFromData : function(o){
-
- if(this.multiple){
- this.addItem(o);
- return;
- }
-
- var dv = ''; // display value
- var vv = ''; // value value..
- this.lastData = o;
- if (this.displayField) {
- dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
- } else {
- // this is an error condition!!!
- Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
- }
-
- if(this.valueField){
- vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
- }
+ onTriggerClick : Roo.emptyFn
+});
+
+/*
+* Licence: LGPL
+*/
+
+/**
+ * @class Roo.bootstrap.form.CardUploader
+ * @extends Roo.bootstrap.Button
+ * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
+ * @cfg {Number} errorTimeout default 3000
+ * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
+ * @cfg {Array} html The button text.
+
+ *
+ * @constructor
+ * Create a new CardUploader
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.form.CardUploader = function(config){
+
+
+
+ Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
+
+
+ this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
+ return r.data.id
+ });
+
+ this.addEvents({
+ // raw events
+ /**
+ * @event preview
+ * When a image is clicked on - and needs to display a slideshow or similar..
+ * @param {Roo.bootstrap.Card} this
+ * @param {Object} The image information data
+ *
+ */
+ 'preview' : true,
+ /**
+ * @event download
+ * When a the download link is clicked
+ * @param {Roo.bootstrap.Card} this
+ * @param {Object} The image information data contains
+ */
+ 'download' : true
- var close = this.closeTriggerEl();
+ });
+};
+
+Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
+
+
+ errorTimeout : 3000,
+
+ images : false,
+
+ fileCollection : false,
+ allowBlank : true,
+
+ getAutoCreate : function()
+ {
- if(close){
- if(dv.length || vv * 1 > 0){
- close.show() ;
- this.blockFocus=true;
- } else {
- close.hide();
- }
- }
+ var cfg = {
+ cls :'form-group' ,
+ cn : [
+
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+
+ },
+
+ {
+ tag: 'input',
+ type : 'hidden',
+ name : this.name,
+ value : this.value,
+ cls : 'd-none form-control'
+ },
+
+ {
+ tag: 'input',
+ multiple : 'multiple',
+ type : 'file',
+ cls : 'd-none roo-card-upload-selector'
+ },
+
+ {
+ cls : 'roo-card-uploader-button-container w-100 mb-2'
+ },
+ {
+ cls : 'card-columns roo-card-uploader-container'
+ }
+
+ ]
+ };
+
+
+ return cfg;
+ },
+
+ getChildContainer : function() /// what children are added to.
+ {
+ return this.containerEl;
+ },
+
+ getButtonContainer : function() /// what children are added to.
+ {
+ return this.el.select(".roo-card-uploader-button-container").first();
+ },
+
+ initEvents : function()
+ {
- if(this.hiddenField){
- this.hiddenField.dom.value = vv;
-
- this.lastSelectionText = dv;
- Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
- this.value = vv;
- return;
- }
- // no hidden field.. - we store the value in 'value', but still display
- // display field!!!!
- this.lastSelectionText = dv;
- Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
- this.value = vv;
+ Roo.bootstrap.form.Input.prototype.initEvents.call(this);
+ var t = this;
+ this.addxtype({
+ xns: Roo.bootstrap,
+
+ xtype : 'Button',
+ container_method : 'getButtonContainer' ,
+ html : this.html, // fix changable?
+ cls : 'w-100 ',
+ listeners : {
+ 'click' : function(btn, e) {
+ t.onClick(e);
+ }
+ }
+ });
- },
- // private
- reset : function(){
- // overridden so that last data is reset..
- if(this.multiple){
- this.clearItem();
- return;
- }
- this.setValue(this.originalValue);
- //this.clearInvalid();
- this.lastData = false;
- if (this.view) {
- this.view.clearSelections();
- }
+ this.urlAPI = (window.createObjectURL && window) ||
+ (window.URL && URL.revokeObjectURL && URL) ||
+ (window.webkitURL && webkitURL);
+
+
+
+
+ this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
- this.validate();
- },
- // private
- findRecord : function(prop, value){
- var record;
- if(this.store.getCount() > 0){
- this.store.each(function(r){
- if(r.data[prop] == value){
- record = r;
- return false;
- }
- return true;
+ this.selectorEl.on('change', this.onFileSelected, this);
+ if (this.images) {
+ var t = this;
+ this.images.forEach(function(img) {
+ t.addCard(img)
});
+ this.images = false;
}
- return record;
+ this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
+
+
},
- getName: function()
+
+ onClick : function(e)
{
- // returns hidden if it's set..
- if (!this.rendered) {return ''};
- return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
-
- },
- // private
- onViewMove : function(e, t){
- this.inKeyMode = false;
+ e.preventDefault();
+
+ this.selectorEl.dom.click();
+
},
-
- // private
- onViewOver : function(e, t){
- if(this.inKeyMode){ // prevent key nav and mouse over conflicts
+
+ onFileSelected : function(e)
+ {
+ e.preventDefault();
+
+ if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
return;
}
- var item = this.view.findItemFromChild(t);
- if(item){
- var index = this.view.indexOf(item);
- this.select(index, false);
- }
+ Roo.each(this.selectorEl.dom.files, function(file){
+ this.addFile(file);
+ }, this);
+
},
-
- // private
- onViewClick : function(view, doFocus, el, e)
+
+
+
+
+
+ addFile : function(file)
{
- var index = this.view.getSelectedIndexes()[0];
+
+ if(typeof(file) === 'string'){
+ throw "Add file by name?"; // should not happen
+ return;
+ }
- var r = this.store.getAt(index);
+ if(!file || !this.urlAPI){
+ return;
+ }
- if(this.tickable){
-
- if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
- return;
+ // file;
+ // file.type;
+
+ var _this = this;
+
+
+ var url = _this.urlAPI.createObjectURL( file);
+
+ this.addCard({
+ id : Roo.bootstrap.form.CardUploader.ID--,
+ is_uploaded : false,
+ src : url,
+ srcfile : file,
+ title : file.name,
+ mimetype : file.type,
+ preview : false,
+ is_deleted : 0
+ });
+
+ },
+
+ /**
+ * addCard - add an Attachment to the uploader
+ * @param data - the data about the image to upload
+ *
+ * {
+ id : 123
+ title : "Title of file",
+ is_uploaded : false,
+ src : "http://.....",
+ srcfile : { the File upload object },
+ mimetype : file.type,
+ preview : false,
+ is_deleted : 0
+ .. any other data...
+ }
+ *
+ *
+ */
+
+ addCard : function (data)
+ {
+ // hidden input element?
+ // if the file is not an image...
+ //then we need to use something other that and header_image
+ var t = this;
+ // remove.....
+ var footer = [
+ {
+ xns : Roo.bootstrap,
+ xtype : 'CardFooter',
+ items: [
+ {
+ xns : Roo.bootstrap,
+ xtype : 'Element',
+ cls : 'd-flex',
+ items : [
+
+ {
+ xns : Roo.bootstrap,
+ xtype : 'Button',
+ html : String.format("<small>{0}</small>", data.title),
+ cls : 'col-10 text-left',
+ size: 'sm',
+ weight: 'link',
+ fa : 'download',
+ listeners : {
+ click : function() {
+
+ t.fireEvent( "download", t, data );
+ }
+ }
+ },
+
+ {
+ xns : Roo.bootstrap,
+ xtype : 'Button',
+ style: 'max-height: 28px; ',
+ size : 'sm',
+ weight: 'danger',
+ cls : 'col-2',
+ fa : 'times',
+ listeners : {
+ click : function() {
+ t.removeCard(data.id)
+ }
+ }
+ }
+ ]
+ }
+
+ ]
}
- var rm = false;
- var _this = this;
-
- Roo.each(this.tickItems, function(v,k){
-
- if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
- Roo.log(v);
- _this.tickItems.splice(k, 1);
-
- if(typeof(e) == 'undefined' && view == false){
- Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
+ ];
+
+ var cn = this.addxtype(
+ {
+
+ xns : Roo.bootstrap,
+ xtype : 'Card',
+ closeable : true,
+ header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
+ header_image : data.mimetype.match(/image/) ? data.src : data.preview,
+ header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
+ data : data,
+ html : false,
+
+ items : footer,
+ initEvents : function() {
+ Roo.bootstrap.Card.prototype.initEvents.call(this);
+ var card = this;
+ this.imgEl = this.el.select('.card-img-top').first();
+ if (this.imgEl) {
+ this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
+ this.imgEl.set({ 'pointer' : 'cursor' });
+
}
+ this.getCardFooter().addClass('p-1');
- rm = true;
- return;
+
}
- });
-
- if(rm){
- return;
- }
-
- if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
- this.tickItems.push(r.data);
- }
-
- if(typeof(e) == 'undefined' && view == false){
- Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
+
}
-
+ );
+ // dont' really need ot update items.
+ // this.items.push(cn);
+ this.fileCollection.add(cn);
+
+ if (!data.srcfile) {
+ this.updateInput();
return;
}
+
+ var _t = this;
+ var reader = new FileReader();
+ reader.addEventListener("load", function() {
+ data.srcdata = reader.result;
+ _t.updateInput();
+ });
+ reader.readAsDataURL(data.srcfile);
+
+
- if(r){
- this.onSelect(r, index);
- }
- if(doFocus !== false && !this.blockFocus){
- this.inputEl().focus();
- }
},
+ removeCard : function(id)
+ {
+
+ var card = this.fileCollection.get(id);
+ card.data.is_deleted = 1;
+ card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
+ //this.fileCollection.remove(card);
+ //this.items = this.items.filter(function(e) { return e != card });
+ // dont' really need ot update items.
+ card.el.dom.parentNode.removeChild(card.el.dom);
+ this.updateInput();
- // private
- restrictHeight : function(){
- //this.innerList.dom.style.height = '';
- //var inner = this.innerList.dom;
- //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
- //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
- //this.list.beginUpdate();
- //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
- this.list.alignTo(this.inputEl(), this.listAlign);
- this.list.alignTo(this.inputEl(), this.listAlign);
- //this.list.endUpdate();
+
},
-
- // private
- onEmptyResults : function(){
+ reset: function()
+ {
+ this.fileCollection.each(function(card) {
+ if (card.el.dom && card.el.dom.parentNode) {
+ card.el.dom.parentNode.removeChild(card.el.dom);
+ }
+ });
+ this.fileCollection.clear();
+ this.updateInput();
+ },
+
+ updateInput : function()
+ {
+ var data = [];
+ this.fileCollection.each(function(e) {
+ data.push(e.data);
+
+ });
+ this.inputEl().dom.value = JSON.stringify(data);
- if(this.tickable && this.editable){
- this.hasFocus = false;
- this.restrictHeight();
- return;
- }
- this.collapse();
- },
+
+ }
+
+
+});
+
+
+Roo.bootstrap.form.CardUploader.ID = -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.data.SortTypes
+ * @static
+ * Defines the default sorting (casting?) comparison functions used when sorting data.
+ */
+Roo.data.SortTypes = {
/**
- * Returns true if the dropdown list is expanded, else false.
+ * Default sort that does nothing
+ * @param {Mixed} s The value being converted
+ * @return {Mixed} The comparison value
*/
- isExpanded : function(){
- return this.list.isVisible();
+ none : function(s){
+ return s;
},
-
+
/**
- * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
- * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
- * @param {String} value The data value of the item to select
- * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
- * selected item if it is not currently in view (defaults to true)
- * @return {Boolean} True if the value matched an item in the list, else false
+ * The regular expression used to strip tags
+ * @type {RegExp}
+ * @property
*/
- selectByValue : function(v, scrollIntoView){
- if(v !== undefined && v !== null){
- var r = this.findRecord(this.valueField || this.displayField, v);
- if(r){
- this.select(this.store.indexOf(r), scrollIntoView);
- return true;
- }
- }
- return false;
+ stripTagsRE : /<\/?[^>]+>/gi,
+
+ /**
+ * Strips all HTML tags to sort on text only
+ * @param {Mixed} s The value being converted
+ * @return {String} The comparison value
+ */
+ asText : function(s){
+ return String(s).replace(this.stripTagsRE, "");
},
-
+
/**
- * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
- * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
- * @param {Number} index The zero-based index of the list item to select
- * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
- * selected item if it is not currently in view (defaults to true)
+ * Strips all HTML tags to sort on text only - Case insensitive
+ * @param {Mixed} s The value being converted
+ * @return {String} The comparison value
*/
- select : function(index, scrollIntoView){
- this.selectedIndex = index;
- this.view.select(index);
- if(scrollIntoView !== false){
- var el = this.view.getNode(index);
- /*
- * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
- */
- if(el){
- this.list.scrollChildIntoView(el, false);
- }
- }
+ asUCText : function(s){
+ return String(s).toUpperCase().replace(this.stripTagsRE, "");
},
-
- // private
- selectNext : function(){
- var ct = this.store.getCount();
- if(ct > 0){
- if(this.selectedIndex == -1){
- this.select(0);
- }else if(this.selectedIndex < ct-1){
- this.select(this.selectedIndex+1);
- }
- }
+
+ /**
+ * Case insensitive string
+ * @param {Mixed} s The value being converted
+ * @return {String} The comparison value
+ */
+ asUCString : function(s) {
+ return String(s).toUpperCase();
},
-
- // private
- selectPrev : function(){
- var ct = this.store.getCount();
- if(ct > 0){
- if(this.selectedIndex == -1){
- this.select(0);
- }else if(this.selectedIndex != 0){
- this.select(this.selectedIndex-1);
- }
+
+ /**
+ * Date sorting
+ * @param {Mixed} s The value being converted
+ * @return {Number} The comparison value
+ */
+ asDate : function(s) {
+ if(!s){
+ return 0;
+ }
+ if(s instanceof Date){
+ return s.getTime();
+ }
+ return Date.parse(String(s));
+ },
+
+ /**
+ * Float sorting
+ * @param {Mixed} s The value being converted
+ * @return {Float} The comparison value
+ */
+ asFloat : function(s) {
+ var val = parseFloat(String(s).replace(/,/g, ""));
+ if(isNaN(val)) {
+ val = 0;
}
+ return val;
},
+
+ /**
+ * Integer sorting
+ * @param {Mixed} s The value being converted
+ * @return {Number} The comparison value
+ */
+ asInt : function(s) {
+ var val = parseInt(String(s).replace(/,/g, ""));
+ if(isNaN(val)) {
+ val = 0;
+ }
+ return val;
+ }
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+* @class Roo.data.Record
+ * Instances of this class encapsulate both record <em>definition</em> information, and record
+ * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
+ * to access Records cached in an {@link Roo.data.Store} object.<br>
+ * <p>
+ * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
+ * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
+ * objects.<br>
+ * <p>
+ * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
+ * @constructor
+ * This constructor should not be used to create Record objects. Instead, use the constructor generated by
+ * {@link #create}. The parameters are the same.
+ * @param {Array} data An associative Array of data values keyed by the field name.
+ * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
+ * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
+ * not specified an integer id is generated.
+ */
+Roo.data.Record = function(data, id){
+ this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
+ this.data = data;
+};
+
+/**
+ * Generate a constructor for a specific record layout.
+ * @param {Array} o An Array of field definition objects which specify field names, and optionally,
+ * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
+ * Each field definition object may contain the following properties: <ul>
+ * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
+ * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
+ * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
+ * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
+ * is being used, then this is a string containing the javascript expression to reference the data relative to
+ * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
+ * to the data item relative to the record element. If the mapping expression is the same as the field name,
+ * this may be omitted.</p></li>
+ * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
+ * <ul><li>auto (Default, implies no conversion)</li>
+ * <li>string</li>
+ * <li>int</li>
+ * <li>float</li>
+ * <li>boolean</li>
+ * <li>date</li></ul></p></li>
+ * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
+ * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
+ * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
+ * by the Reader into an object that will be stored in the Record. It is passed the
+ * following parameters:<ul>
+ * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
+ * </ul></p></li>
+ * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
+ * </ul>
+ * <br>usage:<br><pre><code>
+var TopicRecord = Roo.data.Record.create(
+ {name: 'title', mapping: 'topic_title'},
+ {name: 'author', mapping: 'username'},
+ {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
+ {name: 'lastPost', mapping: 'post_time', type: 'date'},
+ {name: 'lastPoster', mapping: 'user2'},
+ {name: 'excerpt', mapping: 'post_text'}
+);
+
+var myNewRecord = new TopicRecord({
+ title: 'Do my job please',
+ author: 'noobie',
+ totalPosts: 1,
+ lastPost: new Date(),
+ lastPoster: 'Animal',
+ excerpt: 'No way dude!'
+});
+myStore.add(myNewRecord);
+</code></pre>
+ * @method create
+ * @static
+ */
+Roo.data.Record.create = function(o){
+ var f = function(){
+ f.superclass.constructor.apply(this, arguments);
+ };
+ Roo.extend(f, Roo.data.Record);
+ var p = f.prototype;
+ p.fields = new Roo.util.MixedCollection(false, function(field){
+ return field.name;
+ });
+ for(var i = 0, len = o.length; i < len; i++){
+ p.fields.add(new Roo.data.Field(o[i]));
+ }
+ f.getField = function(name){
+ return p.fields.get(name);
+ };
+ return f;
+};
+
+Roo.data.Record.AUTO_ID = 1000;
+Roo.data.Record.EDIT = 'edit';
+Roo.data.Record.REJECT = 'reject';
+Roo.data.Record.COMMIT = 'commit';
+
+Roo.data.Record.prototype = {
+ /**
+ * Readonly flag - true if this record has been modified.
+ * @type Boolean
+ */
+ dirty : false,
+ editing : false,
+ error: null,
+ modified: null,
// private
- onKeyUp : function(e){
- if(this.editable !== false && !e.isSpecialKey()){
- this.lastKey = e.getKey();
- this.dqTask.delay(this.queryDelay);
+ join : function(store){
+ this.store = store;
+ },
+
+ /**
+ * Set the named field to the specified value.
+ * @param {String} name The name of the field to set.
+ * @param {Object} value The value to set the field to.
+ */
+ set : function(name, value){
+ if(this.data[name] == value){
+ return;
+ }
+ this.dirty = true;
+ if(!this.modified){
+ this.modified = {};
+ }
+ if(typeof this.modified[name] == 'undefined'){
+ this.modified[name] = this.data[name];
}
+ this.data[name] = value;
+ if(!this.editing && this.store){
+ this.store.afterEdit(this);
+ }
+ },
+
+ /**
+ * Get the value of the named field.
+ * @param {String} name The name of the field to get the value of.
+ * @return {Object} The value of the field.
+ */
+ get : function(name){
+ return this.data[name];
},
// private
- validateBlur : function(){
- return !this.list || !this.list.isVisible();
+ beginEdit : function(){
+ this.editing = true;
+ this.modified = {};
},
// private
- initQuery : function(){
-
- var v = this.getRawValue();
-
- if(this.tickable && this.editable){
- v = this.tickableInputEl().getValue();
- }
-
- this.doQuery(v);
+ cancelEdit : function(){
+ this.editing = false;
+ delete this.modified;
},
// private
- doForce : function(){
- if(this.inputEl().dom.value.length > 0){
- this.inputEl().dom.value =
- this.lastSelectionText === undefined ? '' : this.lastSelectionText;
-
+ endEdit : function(){
+ this.editing = false;
+ if(this.dirty && this.store){
+ this.store.afterEdit(this);
}
},
/**
- * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
- * query allowing the query action to be canceled if needed.
- * @param {String} query The SQL query to execute
- * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
- * in the field than the minimum specified by the minChars config option. It also clears any filter previously
- * saved in the current store (defaults to false)
+ * Usually called by the {@link Roo.data.Store} which owns the Record.
+ * Rejects all changes made to the Record since either creation, or the last commit operation.
+ * Modified fields are reverted to their original values.
+ * <p>
+ * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
+ * of reject operations.
*/
- doQuery : function(q, forceAll){
-
- if(q === undefined || q === null){
- q = '';
- }
- var qe = {
- query: q,
- forceAll: forceAll,
- combo: this,
- cancel:false
- };
- if(this.fireEvent('beforequery', qe)===false || qe.cancel){
- return false;
- }
- q = qe.query;
-
- forceAll = qe.forceAll;
- if(forceAll === true || (q.length >= this.minChars)){
-
- this.hasQuery = true;
-
- if(this.lastQuery != q || this.alwaysQuery){
- this.lastQuery = q;
- if(this.mode == 'local'){
- this.selectedIndex = -1;
- if(forceAll){
- this.store.clearFilter();
- }else{
-
- if(this.specialFilter){
- this.fireEvent('specialfilter', this);
- this.onLoad();
- return;
- }
-
- this.store.filter(this.displayField, q);
- }
-
- this.store.fireEvent("datachanged", this.store);
-
- this.onLoad();
-
-
- }else{
-
- this.store.baseParams[this.queryParam] = q;
-
- var options = {params : this.getParams(q)};
-
- if(this.loadNext){
- options.add = true;
- options.params.start = this.page * this.pageSize;
- }
-
- this.store.load(options);
-
- /*
- * this code will make the page width larger, at the beginning, the list not align correctly,
- * we should expand the list on onLoad
- * so command out it
- */
-// this.expand();
- }
- }else{
- this.selectedIndex = -1;
- this.onLoad();
+ reject : function(){
+ var m = this.modified;
+ for(var n in m){
+ if(typeof m[n] != "function"){
+ this.data[n] = m[n];
}
}
-
- this.loadNext = false;
- },
-
- // private
- getParams : function(q){
- var p = {};
- //p[this.queryParam] = q;
-
- if(this.pageSize){
- p.start = 0;
- p.limit = this.pageSize;
+ this.dirty = false;
+ delete this.modified;
+ this.editing = false;
+ if(this.store){
+ this.store.afterReject(this);
}
- return p;
},
/**
- * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
+ * Usually called by the {@link Roo.data.Store} which owns the Record.
+ * Commits all changes made to the Record since either creation, or the last commit operation.
+ * <p>
+ * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
+ * of commit operations.
*/
- collapse : function(){
- if(!this.isExpanded()){
- return;
- }
-
- this.list.hide();
-
- this.hasFocus = false;
-
- if(this.tickable){
- this.okBtn.hide();
- this.cancelBtn.hide();
- this.trigger.show();
-
- if(this.editable){
- this.tickableInputEl().dom.value = '';
- this.tickableInputEl().blur();
- }
-
- }
-
- Roo.get(document).un('mousedown', this.collapseIf, this);
- Roo.get(document).un('mousewheel', this.collapseIf, this);
- if (!this.editable) {
- Roo.get(document).un('keydown', this.listKeyPress, this);
+ commit : function(){
+ this.dirty = false;
+ delete this.modified;
+ this.editing = false;
+ if(this.store){
+ this.store.afterCommit(this);
}
- this.fireEvent('collapse', this);
-
- this.validate();
},
// private
- collapseIf : function(e){
- var in_combo = e.within(this.el);
- var in_list = e.within(this.list);
- var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
-
- if (in_combo || in_list || is_list) {
- //e.stopPropagation();
- return;
- }
-
- if(this.tickable){
- this.onTickableFooterButtonClick(e, false, false);
- }
+ hasError : function(){
+ return this.error != null;
+ },
- this.collapse();
-
+ // private
+ clearError : function(){
+ this.error = null;
},
/**
- * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
+ * Creates a copy of this record.
+ * @param {String} id (optional) A new record id if you don't want to use this record's id
+ * @return {Record}
*/
- expand : function(){
-
- if(this.isExpanded() || !this.hasFocus){
- return;
- }
-
- var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
- this.list.setWidth(lw);
-
- Roo.log('expand');
-
- this.list.show();
-
- this.restrictHeight();
-
- if(this.tickable){
-
- this.tickItems = Roo.apply([], this.item);
-
- this.okBtn.show();
- this.cancelBtn.show();
- this.trigger.hide();
-
- if(this.editable){
- this.tickableInputEl().focus();
- }
-
- }
-
- Roo.get(document).on('mousedown', this.collapseIf, this);
- Roo.get(document).on('mousewheel', this.collapseIf, this);
- if (!this.editable) {
- Roo.get(document).on('keydown', this.listKeyPress, this);
- }
-
- this.fireEvent('expand', this);
- },
+ copy : function(newId) {
+ return new this.constructor(Roo.apply({}, this.data), newId || this.id);
+ }
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+
+/**
+ * @class Roo.data.Store
+ * @extends Roo.util.Observable
+ * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
+ * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
+ * <p>
+ * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
+ * has no knowledge of the format of the data returned by the Proxy.<br>
+ * <p>
+ * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
+ * instances from the data object. These records are cached and made available through accessor functions.
+ * @constructor
+ * Creates a new Store.
+ * @param {Object} config A config object containing the objects needed for the Store to access data,
+ * and read the data into Records.
+ */
+Roo.data.Store = function(config){
+ this.data = new Roo.util.MixedCollection(false);
+ this.data.getKey = function(o){
+ return o.id;
+ };
+ this.baseParams = {};
// private
- // Implements the default empty TriggerField.onTriggerClick function
- onTriggerClick : function(e)
- {
- Roo.log('trigger click');
-
- if(this.disabled || !this.triggerList){
- return;
- }
-
- this.page = 0;
- this.loadNext = false;
-
- if(this.isExpanded()){
- this.collapse();
- if (!this.blockFocus) {
- this.inputEl().focus();
- }
-
- }else {
- this.hasFocus = true;
- if(this.triggerAction == 'all') {
- this.doQuery(this.allQuery, true);
- } else {
- this.doQuery(this.getRawValue());
- }
- if (!this.blockFocus) {
- this.inputEl().focus();
- }
- }
- },
+ this.paramNames = {
+ "start" : "start",
+ "limit" : "limit",
+ "sort" : "sort",
+ "dir" : "dir",
+ "multisort" : "_multisort"
+ };
+
+ if(config && config.data){
+ this.inlineData = config.data;
+ delete config.data;
+ }
+
+ Roo.apply(this, config);
- onTickableTriggerClick : function(e)
- {
- if(this.disabled){
- return;
+ if(this.reader){ // reader passed
+ this.reader = Roo.factory(this.reader, Roo.data);
+ this.reader.xmodule = this.xmodule || false;
+ if(!this.recordType){
+ this.recordType = this.reader.recordType;
}
-
- this.page = 0;
- this.loadNext = false;
- this.hasFocus = true;
-
- if(this.triggerAction == 'all') {
- this.doQuery(this.allQuery, true);
- } else {
- this.doQuery(this.getRawValue());
+ if(this.reader.onMetaChange){
+ this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
}
- },
+ }
+
+ if(this.recordType){
+ this.fields = this.recordType.prototype.fields;
+ }
+ this.modified = [];
+
+ this.addEvents({
+ /**
+ * @event datachanged
+ * Fires when the data cache has changed, and a widget which is using this Store
+ * as a Record cache should refresh its view.
+ * @param {Store} this
+ */
+ datachanged : true,
+ /**
+ * @event metachange
+ * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
+ * @param {Store} this
+ * @param {Object} meta The JSON metadata
+ */
+ metachange : true,
+ /**
+ * @event add
+ * Fires when Records have been added to the Store
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The array of Records added
+ * @param {Number} index The index at which the record(s) were added
+ */
+ add : true,
+ /**
+ * @event remove
+ * Fires when a Record has been removed from the Store
+ * @param {Store} this
+ * @param {Roo.data.Record} record The Record that was removed
+ * @param {Number} index The index at which the record was removed
+ */
+ remove : true,
+ /**
+ * @event update
+ * Fires when a Record has been updated
+ * @param {Store} this
+ * @param {Roo.data.Record} record The Record that was updated
+ * @param {String} operation The update operation being performed. Value may be one of:
+ * <pre><code>
+ Roo.data.Record.EDIT
+ Roo.data.Record.REJECT
+ Roo.data.Record.COMMIT
+ * </code></pre>
+ */
+ update : true,
+ /**
+ * @event clear
+ * Fires when the data cache has been cleared.
+ * @param {Store} this
+ */
+ clear : true,
+ /**
+ * @event beforeload
+ * Fires before a request is made for a new data object. If the beforeload handler returns false
+ * the load action will be canceled.
+ * @param {Store} this
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ */
+ beforeload : true,
+ /**
+ * @event beforeloadadd
+ * Fires after a new set of Records has been loaded.
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The Records that were loaded
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ */
+ beforeloadadd : true,
+ /**
+ * @event load
+ * Fires after a new set of Records has been loaded, before they are added to the store.
+ * @param {Store} this
+ * @param {Roo.data.Record[]} records The Records that were loaded
+ * @param {Object} options The loading options that were specified (see {@link #load} for details)
+ * @params {Object} return from reader
+ */
+ load : true,
+ /**
+ * @event loadexception
+ * Fires if an exception occurs in the Proxy during loading.
+ * Called with the signature of the Proxy's "loadexception" event.
+ * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
+ *
+ * @param {Proxy}
+ * @param {Object} return from JsonData.reader() - success, totalRecords, records
+ * @param {Object} load options
+ * @param {Object} jsonData from your request (normally this contains the Exception)
+ */
+ loadexception : true
+ });
- onSearchFieldClick : function(e)
- {
- if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
- this.onTickableFooterButtonClick(e, false, false);
- return;
- }
-
- if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
- return;
+ if(this.proxy){
+ this.proxy = Roo.factory(this.proxy, Roo.data);
+ this.proxy.xmodule = this.xmodule || false;
+ this.relayEvents(this.proxy, ["loadexception"]);
+ }
+ this.sortToggle = {};
+ this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
+
+ Roo.data.Store.superclass.constructor.call(this);
+
+ if(this.inlineData){
+ this.loadData(this.inlineData);
+ delete this.inlineData;
+ }
+};
+
+Roo.extend(Roo.data.Store, Roo.util.Observable, {
+ /**
+ * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
+ * without a remote query - used by combo/forms at present.
+ */
+
+ /**
+ * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
+ */
+ /**
+ * @cfg {Array} data Inline data to be loaded when the store is initialized.
+ */
+ /**
+ * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
+ * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
+ */
+ /**
+ * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
+ * on any HTTP request
+ */
+ /**
+ * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
+ */
+ /**
+ * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
+ */
+ multiSort: false,
+ /**
+ * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
+ * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
+ */
+ remoteSort : false,
+
+ /**
+ * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
+ * loaded or when a record is removed. (defaults to false).
+ */
+ pruneModifiedRecords : false,
+
+ // private
+ lastOptions : null,
+
+ /**
+ * Add Records to the Store and fires the add event.
+ * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+ */
+ add : function(records){
+ records = [].concat(records);
+ for(var i = 0, len = records.length; i < len; i++){
+ records[i].join(this);
}
-
- this.page = 0;
- this.loadNext = false;
- this.hasFocus = true;
-
- if(this.triggerAction == 'all') {
- this.doQuery(this.allQuery, true);
- } else {
- this.doQuery(this.getRawValue());
+ var index = this.data.length;
+ this.data.addAll(records);
+ this.fireEvent("add", this, records, index);
+ },
+
+ /**
+ * Remove a Record from the Store and fires the remove event.
+ * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
+ */
+ remove : function(record){
+ var index = this.data.indexOf(record);
+ this.data.removeAt(index);
+
+ if(this.pruneModifiedRecords){
+ this.modified.remove(record);
}
+ this.fireEvent("remove", this, record, index);
},
-
- listKeyPress : function(e)
- {
- //Roo.log('listkeypress');
- // scroll to first matching element based on key pres..
- if (e.isSpecialKey()) {
- return false;
+
+ /**
+ * Remove all Records from the Store and fires the clear event.
+ */
+ removeAll : function(){
+ this.data.clear();
+ if(this.pruneModifiedRecords){
+ this.modified = [];
}
- var k = String.fromCharCode(e.getKey()).toUpperCase();
- //Roo.log(k);
- var match = false;
- var csel = this.view.getSelectedNodes();
- var cselitem = false;
- if (csel.length) {
- var ix = this.view.indexOf(csel[0]);
- cselitem = this.store.getAt(ix);
- if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
- cselitem = false;
- }
-
+ this.fireEvent("clear", this);
+ },
+
+ /**
+ * Inserts Records to the Store at the given index and fires the add event.
+ * @param {Number} index The start index at which to insert the passed Records.
+ * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
+ */
+ insert : function(index, records){
+ records = [].concat(records);
+ for(var i = 0, len = records.length; i < len; i++){
+ this.data.insert(index, records[i]);
+ records[i].join(this);
}
-
- this.store.each(function(v) {
- if (cselitem) {
- // start at existing selection.
- if (cselitem.id == v.id) {
- cselitem = false;
+ this.fireEvent("add", this, records, index);
+ },
+
+ /**
+ * Get the index within the cache of the passed Record.
+ * @param {Roo.data.Record} record The Roo.data.Record object to to find.
+ * @return {Number} The index of the passed Record. Returns -1 if not found.
+ */
+ indexOf : function(record){
+ return this.data.indexOf(record);
+ },
+
+ /**
+ * Get the index within the cache of the Record with the passed id.
+ * @param {String} id The id of the Record to find.
+ * @return {Number} The index of the Record. Returns -1 if not found.
+ */
+ indexOfId : function(id){
+ return this.data.indexOfKey(id);
+ },
+
+ /**
+ * Get the Record with the specified id.
+ * @param {String} id The id of the Record to find.
+ * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
+ */
+ getById : function(id){
+ return this.data.key(id);
+ },
+
+ /**
+ * Get the Record at the specified index.
+ * @param {Number} index The index of the Record to find.
+ * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
+ */
+ getAt : function(index){
+ return this.data.itemAt(index);
+ },
+
+ /**
+ * Returns a range of Records between specified indices.
+ * @param {Number} startIndex (optional) The starting index (defaults to 0)
+ * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
+ * @return {Roo.data.Record[]} An array of Records
+ */
+ getRange : function(start, end){
+ return this.data.getRange(start, end);
+ },
+
+ // private
+ storeOptions : function(o){
+ o = Roo.apply({}, o);
+ delete o.callback;
+ delete o.scope;
+ this.lastOptions = o;
+ },
+
+ /**
+ * Loads the Record cache from the configured Proxy using the configured Reader.
+ * <p>
+ * If using remote paging, then the first load call must specify the <em>start</em>
+ * and <em>limit</em> properties in the options.params property to establish the initial
+ * position within the dataset, and the number of Records to cache on each read from the Proxy.
+ * <p>
+ * <strong>It is important to note that for remote data sources, loading is asynchronous,
+ * and this call will return before the new data has been loaded. Perform any post-processing
+ * in a callback function, or in a "load" event handler.</strong>
+ * <p>
+ * @param {Object} options An object containing properties which control loading options:<ul>
+ * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
+ * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
+ * <pre>
+ {
+ data : data, // array of key=>value data like JsonReader
+ total : data.length,
+ success : true
+
}
- return true;
+ </pre>
+ }.</li>
+ * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
+ * passed the following arguments:<ul>
+ * <li>r : Roo.data.Record[]</li>
+ * <li>options: Options object from the load call</li>
+ * <li>success: Boolean success indicator</li></ul></li>
+ * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
+ * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
+ * </ul>
+ */
+ load : function(options){
+ options = options || {};
+ if(this.fireEvent("beforeload", this, options) !== false){
+ this.storeOptions(options);
+ var p = Roo.apply(options.params || {}, this.baseParams);
+ // if meta was not loaded from remote source.. try requesting it.
+ if (!this.reader.metaFromRemote) {
+ p._requestMeta = 1;
}
-
- if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
- match = this.store.indexOf(v);
- return false;
+ if(this.sortInfo && this.remoteSort){
+ var pn = this.paramNames;
+ p[pn["sort"]] = this.sortInfo.field;
+ p[pn["dir"]] = this.sortInfo.direction;
}
- return true;
- }, this);
-
- if (match === false) {
- return true; // no more action?
+ if (this.multiSort) {
+ var pn = this.paramNames;
+ p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
+ }
+
+ this.proxy.load(p, this.reader, this.loadRecords, this, options);
}
- // scroll to?
- this.view.select(match);
- var sn = Roo.get(this.view.getSelectedNodes()[0]);
- sn.scrollIntoView(sn.dom.parentNode, false);
},
-
- onViewScroll : function(e, t){
-
- if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
+
+ /**
+ * Reloads the Record cache from the configured Proxy using the configured Reader and
+ * the options from the last load operation performed.
+ * @param {Object} options (optional) An object containing properties which may override the options
+ * used in the last load operation. See {@link #load} for details (defaults to null, in which case
+ * the most recently used options are reused).
+ */
+ reload : function(options){
+ this.load(Roo.applyIf(options||{}, this.lastOptions));
+ },
+
+ // private
+ // Called as a callback by the Reader during a load operation.
+ loadRecords : function(o, options, success){
+
+ if(!o){
+ if(success !== false){
+ this.fireEvent("load", this, [], options, o);
+ }
+ if(options.callback){
+ options.callback.call(options.scope || this, [], options, false);
+ }
return;
}
-
- this.hasQuery = true;
-
- this.loading = this.list.select('.loading', true).first();
-
- if(this.loading === null){
- this.list.createChild({
- tag: 'div',
- cls: 'loading roo-select2-more-results roo-select2-active',
- html: 'Loading more results...'
- });
-
- this.loading = this.list.select('.loading', true).first();
-
- this.loading.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.loading.hide();
+ // if data returned failure - throw an exception.
+ if (o.success === false) {
+ // show a message if no listener is registered.
+ if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
+ Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
+ }
+ // loadmask wil be hooked into this..
+ this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
+ return;
}
+ var r = o.records, t = o.totalRecords || r.length;
- this.loading.show();
-
- var _combo = this;
-
- this.page++;
- this.loadNext = true;
-
- (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
-
- return;
- },
-
- addItem : function(o)
- {
- var dv = ''; // display value
+ this.fireEvent("beforeloadadd", this, r, options, o);
- if (this.displayField) {
- dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
- } else {
- // this is an error condition!!!
- Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
+ if(!options || options.add !== true){
+ if(this.pruneModifiedRecords){
+ this.modified = [];
+ }
+ for(var i = 0, len = r.length; i < len; i++){
+ r[i].join(this);
+ }
+ if(this.snapshot){
+ this.data = this.snapshot;
+ delete this.snapshot;
+ }
+ this.data.clear();
+ this.data.addAll(r);
+ this.totalLength = t;
+ this.applySort();
+ this.fireEvent("datachanged", this);
+ }else{
+ this.totalLength = Math.max(t, this.data.length+r.length);
+ this.add(r);
}
- if(!dv.length){
- return;
+ if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
+
+ var e = new Roo.data.Record({});
+
+ e.set(this.parent.displayField, this.parent.emptyTitle);
+ e.set(this.parent.valueField, '');
+
+ this.insert(0, e);
}
-
- var choice = this.choices.createChild({
- tag: 'li',
- cls: 'roo-select2-search-choice',
- cn: [
- {
- tag: 'div',
- html: dv
- },
- {
- tag: 'a',
- href: '#',
- cls: 'roo-select2-search-choice-close fa fa-times',
- tabindex: '-1'
- }
- ]
- }, this.searchField);
-
- var close = choice.select('a.roo-select2-search-choice-close', true).first();
-
- close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
-
- this.item.push(o);
-
- this.lastData = o;
-
- this.syncValue();
-
- this.inputEl().dom.value = '';
-
- this.validate();
+ this.fireEvent("load", this, r, options, o);
+ if(options.callback){
+ options.callback.call(options.scope || this, r, options, true);
+ }
+ },
+
+
+ /**
+ * Loads data from a passed data block. A Reader which understands the format of the data
+ * must have been configured in the constructor.
+ * @param {Object} data The data block from which to read the Records. The format of the data expected
+ * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
+ * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
+ */
+ loadData : function(o, append){
+ var r = this.reader.readRecords(o);
+ this.loadRecords(r, {add: append}, true);
},
- onRemoveItem : function(e, _self, o)
+ /**
+ * using 'cn' the nested child reader read the child array into it's child stores.
+ * @param {Object} rec The record with a 'children array
+ */
+ loadDataFromChildren : function(rec)
{
- e.preventDefault();
-
- this.lastItem = Roo.apply([], this.item);
-
- var index = this.item.indexOf(o.data) * 1;
-
- if( index < 0){
- Roo.log('not this item?!');
- return;
- }
-
- this.item.splice(index, 1);
- o.item.remove();
-
- this.syncValue();
-
- this.fireEvent('remove', this, e);
-
- this.validate();
-
+ this.loadData(this.reader.toLoadData(rec));
},
- syncValue : function()
- {
- if(!this.item.length){
- this.clearValue();
- return;
+
+ /**
+ * Gets the number of cached records.
+ * <p>
+ * <em>If using paging, this may not be the total size of the dataset. If the data object
+ * used by the Reader contains the dataset size, then the getTotalCount() function returns
+ * the data set size</em>
+ */
+ getCount : function(){
+ return this.data.length || 0;
+ },
+
+ /**
+ * Gets the total number of records in the dataset as returned by the server.
+ * <p>
+ * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
+ * the dataset size</em>
+ */
+ getTotalCount : function(){
+ return this.totalLength || 0;
+ },
+
+ /**
+ * Returns the sort state of the Store as an object with two properties:
+ * <pre><code>
+ field {String} The name of the field by which the Records are sorted
+ direction {String} The sort order, "ASC" or "DESC"
+ * </code></pre>
+ */
+ getSortState : function(){
+ return this.sortInfo;
+ },
+
+ // private
+ applySort : function(){
+ if(this.sortInfo && !this.remoteSort){
+ var s = this.sortInfo, f = s.field;
+ var st = this.fields.get(f).sortType;
+ var fn = function(r1, r2){
+ var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
+ return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
+ };
+ this.data.sort(s.direction, fn);
+ if(this.snapshot && this.snapshot != this.data){
+ this.snapshot.sort(s.direction, fn);
+ }
}
+ },
+
+ /**
+ * Sets the default sort column and order to be used by the next load operation.
+ * @param {String} fieldName The name of the field to sort by.
+ * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
+ */
+ setDefaultSort : function(field, dir){
+ this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
+ },
+
+ /**
+ * Sort the Records.
+ * If remote sorting is used, the sort is performed on the server, and the cache is
+ * reloaded. If local sorting is used, the cache is sorted internally.
+ * @param {String} fieldName The name of the field to sort by.
+ * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
+ */
+ sort : function(fieldName, dir){
+ var f = this.fields.get(fieldName);
+ if(!dir){
+ this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
- var value = [];
- var _this = this;
- Roo.each(this.item, function(i){
- if(_this.valueField){
- value.push(i[_this.valueField]);
- return;
+ if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
+ dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
+ }else{
+ dir = f.sortDir;
}
+ }
+ this.sortToggle[f.name] = dir;
+ this.sortInfo = {field: f.name, direction: dir};
+ if(!this.remoteSort){
+ this.applySort();
+ this.fireEvent("datachanged", this);
+ }else{
+ this.load(this.lastOptions);
+ }
+ },
- value.push(i);
- });
+ /**
+ * Calls the specified function for each of the Records in the cache.
+ * @param {Function} fn The function to call. The Record is passed as the first parameter.
+ * Returning <em>false</em> aborts and exits the iteration.
+ * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
+ */
+ each : function(fn, scope){
+ this.data.each(fn, scope);
+ },
- this.value = value.join(',');
+ /**
+ * Gets all records modified since the last commit. Modified records are persisted across load operations
+ * (e.g., during paging).
+ * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
+ */
+ getModifiedRecords : function(){
+ return this.modified;
+ },
- if(this.hiddenField){
- this.hiddenField.dom.value = this.value;
+ // private
+ createFilterFn : function(property, value, anyMatch){
+ if(!value.exec){ // not a regex
+ value = String(value);
+ if(value.length == 0){
+ return false;
+ }
+ value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
}
-
- this.store.fireEvent("datachanged", this.store);
-
- this.validate();
+ return function(r){
+ return value.test(r.data[property]);
+ };
},
-
- clearItem : function()
- {
- if(!this.multiple){
- return;
- }
-
- this.item = [];
-
- Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
- c.remove();
- });
-
- this.syncValue();
-
- this.validate();
-
- if(this.tickable && !Roo.isTouch){
- this.view.refresh();
+
+ /**
+ * Sums the value of <i>property</i> for each record between start and end and returns the result.
+ * @param {String} property A field on your records
+ * @param {Number} start The record index to start at (defaults to 0)
+ * @param {Number} end The last record index to include (defaults to length - 1)
+ * @return {Number} The sum
+ */
+ sum : function(property, start, end){
+ var rs = this.data.items, v = 0;
+ start = start || 0;
+ end = (end || end === 0) ? end : rs.length-1;
+
+ for(var i = start; i <= end; i++){
+ v += (rs[i].data[property] || 0);
}
+ return v;
},
-
- inputEl: function ()
- {
- if(Roo.isIOS && this.useNativeIOS){
- return this.el.select('select.roo-ios-select', true).first();
- }
-
- if(Roo.isTouch && this.mobileTouchView){
- return this.el.select('input.form-control',true).first();
+
+ /**
+ * Filter the records by a specified property.
+ * @param {String} field A field on your records
+ * @param {String/RegExp} value Either a string that the field
+ * should start with or a RegExp to test against the field
+ * @param {Boolean} anyMatch True to match any part not just the beginning
+ */
+ filter : function(property, value, anyMatch){
+ var fn = this.createFilterFn(property, value, anyMatch);
+ return fn ? this.filterBy(fn) : this.clearFilter();
+ },
+
+ /**
+ * Filter by a function. The specified function will be called with each
+ * record in this data source. If the function returns true the record is included,
+ * otherwise it is filtered.
+ * @param {Function} fn The function to be called, it will receive 2 args (record, id)
+ * @param {Object} scope (optional) The scope of the function (defaults to this)
+ */
+ filterBy : function(fn, scope){
+ this.snapshot = this.snapshot || this.data;
+ this.data = this.queryBy(fn, scope||this);
+ this.fireEvent("datachanged", this);
+ },
+
+ /**
+ * Query the records by a specified property.
+ * @param {String} field A field on your records
+ * @param {String/RegExp} value Either a string that the field
+ * should start with or a RegExp to test against the field
+ * @param {Boolean} anyMatch True to match any part not just the beginning
+ * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
+ */
+ query : function(property, value, anyMatch){
+ var fn = this.createFilterFn(property, value, anyMatch);
+ return fn ? this.queryBy(fn) : this.data.clone();
+ },
+
+ /**
+ * Query by a function. The specified function will be called with each
+ * record in this data source. If the function returns true the record is included
+ * in the results.
+ * @param {Function} fn The function to be called, it will receive 2 args (record, id)
+ * @param {Object} scope (optional) The scope of the function (defaults to this)
+ @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
+ **/
+ queryBy : function(fn, scope){
+ var data = this.snapshot || this.data;
+ return data.filterBy(fn, scope||this);
+ },
+
+ /**
+ * Collects unique values for a particular dataIndex from this store.
+ * @param {String} dataIndex The property to collect
+ * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
+ * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
+ * @return {Array} An array of the unique values
+ **/
+ collect : function(dataIndex, allowNull, bypassFilter){
+ var d = (bypassFilter === true && this.snapshot) ?
+ this.snapshot.items : this.data.items;
+ var v, sv, r = [], l = {};
+ for(var i = 0, len = d.length; i < len; i++){
+ v = d[i].data[dataIndex];
+ sv = String(v);
+ if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
+ l[sv] = true;
+ r[r.length] = v;
+ }
}
-
- if(this.tickable){
- return this.searchField;
+ return r;
+ },
+
+ /**
+ * Revert to a view of the Record cache with no filtering applied.
+ * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
+ */
+ clearFilter : function(suppressEvent){
+ if(this.snapshot && this.snapshot != this.data){
+ this.data = this.snapshot;
+ delete this.snapshot;
+ if(suppressEvent !== true){
+ this.fireEvent("datachanged", this);
+ }
}
-
- return this.el.select('input.form-control',true).first();
},
-
- onTickableFooterButtonClick : function(e, btn, el)
- {
- e.preventDefault();
-
- this.lastItem = Roo.apply([], this.item);
-
- if(btn && btn.name == 'cancel'){
- this.tickItems = Roo.apply([], this.item);
- this.collapse();
- return;
+
+ // private
+ afterEdit : function(record){
+ if(this.modified.indexOf(record) == -1){
+ this.modified.push(record);
}
-
- this.clearItem();
-
- var _this = this;
-
- Roo.each(this.tickItems, function(o){
- _this.addItem(o);
- });
-
- this.collapse();
-
+ this.fireEvent("update", this, record, Roo.data.Record.EDIT);
},
- validate : function()
- {
- if(this.getVisibilityEl().hasClass('hidden')){
- return true;
- }
-
- var v = this.getRawValue();
-
- if(this.multiple){
- v = this.getValue();
- }
-
- if(this.disabled || this.allowBlank || v.length){
- this.markValid();
- return true;
+ // private
+ afterReject : function(record){
+ this.modified.remove(record);
+ this.fireEvent("update", this, record, Roo.data.Record.REJECT);
+ },
+
+ // private
+ afterCommit : function(record){
+ this.modified.remove(record);
+ this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
+ },
+
+ /**
+ * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
+ * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
+ */
+ commitChanges : function(){
+ var m = this.modified.slice(0);
+ this.modified = [];
+ for(var i = 0, len = m.length; i < len; i++){
+ m[i].commit();
}
-
- this.markInvalid();
- return false;
},
-
- tickableInputEl : function()
- {
- if(!this.tickable || !this.editable){
- return this.inputEl();
+
+ /**
+ * Cancel outstanding changes on all changed records.
+ */
+ rejectChanges : function(){
+ var m = this.modified.slice(0);
+ this.modified = [];
+ for(var i = 0, len = m.length; i < len; i++){
+ m[i].reject();
}
-
- return this.inputEl().select('.roo-select2-search-field-input', true).first();
+ },
+
+ onMetaChange : function(meta, rtype, o){
+ this.recordType = rtype;
+ this.fields = rtype.prototype.fields;
+ delete this.snapshot;
+ this.sortInfo = meta.sortInfo || this.sortInfo;
+ this.modified = [];
+ this.fireEvent('metachange', this, this.reader.meta);
},
-
- getAutoCreateTouchView : function()
+ moveIndex : function(data, type)
{
- var id = Roo.id();
-
- var cfg = {
- cls: 'form-group' //input-group
- };
-
- var input = {
- tag: 'input',
- id : id,
- type : this.inputType,
- cls : 'form-control x-combo-noedit',
- autocomplete: 'new-password',
- placeholder : this.placeholder || '',
- readonly : true
- };
-
- if (this.name) {
- input.name = this.name;
- }
-
- if (this.size) {
- input.cls += ' input-' + this.size;
- }
+ var index = this.indexOf(data);
- if (this.disabled) {
- input.disabled = true;
- }
+ var newIndex = index + type;
- var inputblock = {
- cls : '',
- cn : [
- input
- ]
- };
+ this.remove(data);
- if(this.before){
- inputblock.cls += ' input-group';
-
- inputblock.cn.unshift({
- tag :'span',
- cls : 'input-group-addon input-group-prepend input-group-text',
- html : this.before
- });
- }
+ this.insert(newIndex, data);
- if(this.removable && !this.multiple){
- inputblock.cls += ' roo-removable';
-
- inputblock.cn.push({
- tag: 'button',
- html : 'x',
- cls : 'roo-combo-removable-btn close'
- });
- }
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
- if(this.hasFeedback && !this.allowBlank){
-
- inputblock.cls += ' has-feedback';
-
- inputblock.cn.push({
- tag: 'span',
- cls: 'glyphicon form-control-feedback'
- });
-
- }
-
- if (this.after) {
-
- inputblock.cls += (this.before) ? '' : ' input-group';
-
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon input-group-append input-group-text',
- html : this.after
- });
- }
+/**
+ * @class Roo.data.SimpleStore
+ * @extends Roo.data.Store
+ * Small helper class to make creating Stores from Array data easier.
+ * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
+ * @cfg {Array} fields An array of field definition objects, or field name strings.
+ * @cfg {Object} an existing reader (eg. copied from another store)
+ * @cfg {Array} data The multi-dimensional array of data
+ * @cfg {Roo.data.DataProxy} proxy [not-required]
+ * @cfg {Roo.data.Reader} reader [not-required]
+ * @constructor
+ * @param {Object} config
+ */
+Roo.data.SimpleStore = function(config)
+{
+ Roo.data.SimpleStore.superclass.constructor.call(this, {
+ isLocal : true,
+ reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
+ id: config.id
+ },
+ Roo.data.Record.create(config.fields)
+ ),
+ proxy : new Roo.data.MemoryProxy(config.data)
+ });
+ this.load();
+};
+Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+/**
+ * @extends Roo.data.Store
+ * @class Roo.data.JsonStore
+ * Small helper class to make creating Stores for JSON data easier. <br/>
+<pre><code>
+var store = new Roo.data.JsonStore({
+ url: 'get-images.php',
+ root: 'images',
+ fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
+});
+</code></pre>
+ * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
+ * JsonReader and HttpProxy (unless inline data is provided).</b>
+ * @cfg {Array} fields An array of field definition objects, or field name strings.
+ * @constructor
+ * @param {Object} config
+ */
+Roo.data.JsonStore = function(c){
+ Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
+ proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
+ reader: new Roo.data.JsonReader(c, c.fields)
+ }));
+};
+Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+
+Roo.data.Field = function(config){
+ if(typeof config == "string"){
+ config = {name: config};
+ }
+ Roo.apply(this, config);
+
+ if(!this.type){
+ this.type = "auto";
+ }
+
+ var st = Roo.data.SortTypes;
+ // named sortTypes are supported, here we look them up
+ if(typeof this.sortType == "string"){
+ this.sortType = st[this.sortType];
+ }
+
+ // set default sortType for strings and dates
+ if(!this.sortType){
+ switch(this.type){
+ case "string":
+ this.sortType = st.asUCString;
+ break;
+ case "date":
+ this.sortType = st.asDate;
+ break;
+ default:
+ this.sortType = st.none;
+ }
+ }
+
+ // define once
+ var stripRe = /[\$,%]/g;
+
+ // prebuilt conversion function for this field, instead of
+ // switching every time we're reading a value
+ if(!this.convert){
+ var cv, dateFormat = this.dateFormat;
+ switch(this.type){
+ case "":
+ case "auto":
+ case undefined:
+ cv = function(v){ return v; };
+ break;
+ case "string":
+ cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
+ break;
+ case "int":
+ cv = function(v){
+ return v !== undefined && v !== null && v !== '' ?
+ parseInt(String(v).replace(stripRe, ""), 10) : '';
+ };
+ break;
+ case "float":
+ cv = function(v){
+ return v !== undefined && v !== null && v !== '' ?
+ parseFloat(String(v).replace(stripRe, ""), 10) : '';
+ };
+ break;
+ case "bool":
+ case "boolean":
+ cv = function(v){ return v === true || v === "true" || v == 1; };
+ break;
+ case "date":
+ cv = function(v){
+ if(!v){
+ return '';
+ }
+ if(v instanceof Date){
+ return v;
+ }
+ if(dateFormat){
+ if(dateFormat == "timestamp"){
+ return new Date(v*1000);
+ }
+ return Date.parseDate(v, dateFormat);
+ }
+ var parsed = Date.parse(v);
+ return parsed ? new Date(parsed) : null;
+ };
+ break;
+
+ }
+ this.convert = cv;
+ }
+};
+
+Roo.data.Field.prototype = {
+ dateFormat: null,
+ defaultValue: "",
+ mapping: null,
+ sortType : null,
+ sortDir : "ASC"
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+// Base class for reading structured data from a data source. This class is intended to be
+// extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
+
+/**
+ * @class Roo.data.DataReader
+ * @abstract
+ * Base class for reading structured data from a data source. This class is intended to be
+ * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
+ */
+
+Roo.data.DataReader = function(meta, recordType){
+
+ this.meta = meta;
+
+ this.recordType = recordType instanceof Array ?
+ Roo.data.Record.create(recordType) : recordType;
+};
+
+Roo.data.DataReader.prototype = {
+
+
+ readerType : 'Data',
+ /**
+ * Create an empty record
+ * @param {Object} data (optional) - overlay some values
+ * @return {Roo.data.Record} record created.
+ */
+ newRow : function(d) {
+ var da = {};
+ this.recordType.prototype.fields.each(function(c) {
+ switch( c.type) {
+ case 'int' : da[c.name] = 0; break;
+ case 'date' : da[c.name] = new Date(); break;
+ case 'float' : da[c.name] = 0.0; break;
+ case 'boolean' : da[c.name] = false; break;
+ default : da[c.name] = ""; break;
+ }
+
+ });
+ return new this.recordType(Roo.apply(da, d));
+ }
+
+
+};/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.DataProxy
+ * @extends Roo.util.Observable
+ * @abstract
+ * This class is an abstract base class for implementations which provide retrieval of
+ * unformatted data objects.<br>
+ * <p>
+ * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
+ * (of the appropriate type which knows how to parse the data object) to provide a block of
+ * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
+ * <p>
+ * Custom implementations must implement the load method as described in
+ * {@link Roo.data.HttpProxy#load}.
+ */
+Roo.data.DataProxy = function(){
+ this.addEvents({
+ /**
+ * @event beforeload
+ * Fires before a network request is made to retrieve a data object.
+ * @param {Object} This DataProxy object.
+ * @param {Object} params The params parameter to the load function.
+ */
+ beforeload : true,
+ /**
+ * @event load
+ * Fires before the load method's callback is called.
+ * @param {Object} This DataProxy object.
+ * @param {Object} o The data object.
+ * @param {Object} arg The callback argument object passed to the load function.
+ */
+ load : true,
+ /**
+ * @event loadexception
+ * Fires if an Exception occurs during data retrieval.
+ * @param {Object} This DataProxy object.
+ * @param {Object} o The data object.
+ * @param {Object} arg The callback argument object passed to the load function.
+ * @param {Object} e The Exception.
+ */
+ loadexception : true
+ });
+ Roo.data.DataProxy.superclass.constructor.call(this);
+};
+
+Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
+
+ /**
+ * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
+ */
+/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.data.MemoryProxy
+ * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
+ * to the Reader when its load method is called.
+ * @constructor
+ * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
+ */
+Roo.data.MemoryProxy = function(data){
+ if (data.data) {
+ data = data.data;
+ }
+ Roo.data.MemoryProxy.superclass.constructor.call(this);
+ this.data = data;
+};
+
+Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
+
+ /**
+ * Load data from the requested source (in this case an in-memory
+ * data object passed to the constructor), read the data object into
+ * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params This parameter is not used by the MemoryProxy class.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ */
+ load : function(params, reader, callback, scope, arg){
+ params = params || {};
+ var result;
+ try {
+ result = reader.readRecords(params.data ? params.data :this.data);
+ }catch(e){
+ this.fireEvent("loadexception", this, arg, null, e);
+ callback.call(scope, null, arg, false);
+ return;
+ }
+ callback.call(scope, result, arg, true);
+ },
+
+ // private
+ update : function(params, records){
+
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @class Roo.data.HttpProxy
+ * @extends Roo.data.DataProxy
+ * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
+ * configured to reference a certain URL.<br><br>
+ * <p>
+ * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
+ * from which the running page was served.<br><br>
+ * <p>
+ * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
+ * <p>
+ * Be aware that to enable the browser to parse an XML document, the server must set
+ * the Content-Type header in the HTTP response to "text/xml".
+ * @constructor
+ * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
+ * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
+ * will be used to make the request.
+ */
+Roo.data.HttpProxy = function(conn){
+ Roo.data.HttpProxy.superclass.constructor.call(this);
+ // is conn a conn config or a real conn?
+ this.conn = conn;
+ this.useAjax = !conn || !conn.events;
+
+};
+
+Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
+ // thse are take from connection...
+
+ /**
+ * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
+ */
+ /**
+ * @cfg {Object} extraParams (Optional) An object containing properties which are used as
+ * extra parameters to each request made by this object. (defaults to undefined)
+ */
+ /**
+ * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
+ * to each request made by this object. (defaults to undefined)
+ */
+ /**
+ * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
+ */
+ /**
+ * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
+ */
+ /**
+ * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
+ * @type Boolean
+ */
+
+
+ /**
+ * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
+ * @type Boolean
+ */
+ /**
+ * Return the {@link Roo.data.Connection} object being used by this Proxy.
+ * @return {Connection} The Connection object. This object may be used to subscribe to events on
+ * a finer-grained basis than the DataProxy events.
+ */
+ getConnection : function(){
+ return this.useAjax ? Roo.Ajax : this.conn;
+ },
+
+ /**
+ * Load data from the configured {@link Roo.data.Connection}, read the data object into
+ * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params An object containing properties which are to be used as HTTP parameters
+ * for the request to the remote server.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.Records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ */
+ load : function(params, reader, callback, scope, arg){
+ if(this.fireEvent("beforeload", this, params) !== false){
+ var o = {
+ params : params || {},
+ request: {
+ callback : callback,
+ scope : scope,
+ arg : arg
+ },
+ reader: reader,
+ callback : this.loadResponse,
+ scope: this
+ };
+ if(this.useAjax){
+ Roo.applyIf(o, this.conn);
+ if(this.activeRequest){
+ Roo.Ajax.abort(this.activeRequest);
+ }
+ this.activeRequest = Roo.Ajax.request(o);
+ }else{
+ this.conn.request(o);
+ }
+ }else{
+ callback.call(scope||this, null, arg, false);
+ }
+ },
+
+ // private
+ loadResponse : function(o, success, response){
+ delete this.activeRequest;
+ if(!success){
+ this.fireEvent("loadexception", this, o, response);
+ o.request.callback.call(o.request.scope, null, o.request.arg, false);
+ return;
+ }
+ var result;
+ try {
+ result = o.reader.read(response);
+ }catch(e){
+ o.success = false;
+ o.raw = { errorMsg : response.responseText };
+ this.fireEvent("loadexception", this, o, response, e);
+ o.request.callback.call(o.request.scope, o, o.request.arg, false);
+ return;
+ }
+
+ this.fireEvent("load", this, o, o.request.arg);
+ o.request.callback.call(o.request.scope, result, o.request.arg, true);
+ },
+
+ // private
+ update : function(dataSet){
+
+ },
+
+ // private
+ updateResponse : function(dataSet){
+
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.ScriptTagProxy
+ * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
+ * other than the originating domain of the running page.<br><br>
+ * <p>
+ * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
+ * of the running page, you must use this class, rather than DataProxy.</em><br><br>
+ * <p>
+ * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
+ * source code that is used as the source inside a <script> tag.<br><br>
+ * <p>
+ * In order for the browser to process the returned data, the server must wrap the data object
+ * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
+ * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
+ * depending on whether the callback name was passed:
+ * <p>
+ * <pre><code>
+boolean scriptTag = false;
+String cb = request.getParameter("callback");
+if (cb != null) {
+ scriptTag = true;
+ response.setContentType("text/javascript");
+} else {
+ response.setContentType("application/x-json");
+}
+Writer out = response.getWriter();
+if (scriptTag) {
+ out.write(cb + "(");
+}
+out.print(dataBlock.toJsonString());
+if (scriptTag) {
+ out.write(");");
+}
+</pre></code>
+ *
+ * @constructor
+ * @param {Object} config A configuration object.
+ */
+Roo.data.ScriptTagProxy = function(config){
+ Roo.data.ScriptTagProxy.superclass.constructor.call(this);
+ Roo.apply(this, config);
+ this.head = document.getElementsByTagName("head")[0];
+};
+
+Roo.data.ScriptTagProxy.TRANS_ID = 1000;
+
+Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
+ /**
+ * @cfg {String} url The URL from which to request the data object.
+ */
+ /**
+ * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
+ */
+ timeout : 30000,
+ /**
+ * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
+ * the server the name of the callback function set up by the load call to process the returned data object.
+ * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
+ * javascript output which calls this named function passing the data object as its only parameter.
+ */
+ callbackParam : "callback",
+ /**
+ * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
+ * name to the request.
+ */
+ nocache : true,
+
+ /**
+ * Load data from the configured URL, read the data object into
+ * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
+ * process that block using the passed callback.
+ * @param {Object} params An object containing properties which are to be used as HTTP parameters
+ * for the request to the remote server.
+ * @param {Roo.data.DataReader} reader The Reader object which converts the data
+ * object into a block of Roo.data.Records.
+ * @param {Function} callback The function into which to pass the block of Roo.data.Records.
+ * The function must be passed <ul>
+ * <li>The Record block object</li>
+ * <li>The "arg" argument from the load function</li>
+ * <li>A boolean success indicator</li>
+ * </ul>
+ * @param {Object} scope The scope in which to call the callback
+ * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
+ */
+ load : function(params, reader, callback, scope, arg){
+ if(this.fireEvent("beforeload", this, params) !== false){
+
+ var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
+
+ var url = this.url;
+ url += (url.indexOf("?") != -1 ? "&" : "?") + p;
+ if(this.nocache){
+ url += "&_dc=" + (new Date().getTime());
+ }
+ var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
+ var trans = {
+ id : transId,
+ cb : "stcCallback"+transId,
+ scriptId : "stcScript"+transId,
+ params : params,
+ arg : arg,
+ url : url,
+ callback : callback,
+ scope : scope,
+ reader : reader
+ };
+ var conn = this;
+
+ window[trans.cb] = function(o){
+ conn.handleResponse(o, trans);
+ };
+
+ url += String.format("&{0}={1}", this.callbackParam, trans.cb);
+
+ if(this.autoAbort !== false){
+ this.abort();
+ }
+
+ trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
+
+ var script = document.createElement("script");
+ script.setAttribute("src", url);
+ script.setAttribute("type", "text/javascript");
+ script.setAttribute("id", trans.scriptId);
+ this.head.appendChild(script);
+
+ this.trans = trans;
+ }else{
+ callback.call(scope||this, null, arg, false);
+ }
+ },
+
+ // private
+ isLoading : function(){
+ return this.trans ? true : false;
+ },
+
+ /**
+ * Abort the current server request.
+ */
+ abort : function(){
+ if(this.isLoading()){
+ this.destroyTrans(this.trans);
+ }
+ },
+
+ // private
+ destroyTrans : function(trans, isLoaded){
+ this.head.removeChild(document.getElementById(trans.scriptId));
+ clearTimeout(trans.timeoutId);
+ if(isLoaded){
+ window[trans.cb] = undefined;
+ try{
+ delete window[trans.cb];
+ }catch(e){}
+ }else{
+ // if hasn't been loaded, wait for load to remove it to prevent script error
+ window[trans.cb] = function(){
+ window[trans.cb] = undefined;
+ try{
+ delete window[trans.cb];
+ }catch(e){}
+ };
+ }
+ },
+
+ // private
+ handleResponse : function(o, trans){
+ this.trans = false;
+ this.destroyTrans(trans, true);
+ var result;
+ try {
+ result = trans.reader.readRecords(o);
+ }catch(e){
+ this.fireEvent("loadexception", this, o, trans.arg, e);
+ trans.callback.call(trans.scope||window, null, trans.arg, false);
+ return;
+ }
+ this.fireEvent("load", this, o, trans.arg);
+ trans.callback.call(trans.scope||window, result, trans.arg, true);
+ },
+
+ // private
+ handleFailure : function(trans){
+ this.trans = false;
+ this.destroyTrans(trans, false);
+ this.fireEvent("loadexception", this, null, trans.arg);
+ trans.callback.call(trans.scope||window, null, trans.arg, false);
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.JsonReader
+ * @extends Roo.data.DataReader
+ * Data reader class to create an Array of Roo.data.Record objects from a JSON response
+ * based on mappings in a provided Roo.data.Record constructor.
+ *
+ * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
+ * in the reply previously.
+ *
+ * <p>
+ * Example code:
+ * <pre><code>
+var RecordDef = Roo.data.Record.create([
+ {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
+ {name: 'occupation'} // This field will use "occupation" as the mapping.
+]);
+var myReader = new Roo.data.JsonReader({
+ totalProperty: "results", // The property which contains the total dataset size (optional)
+ root: "rows", // The property which contains an Array of row objects
+ id: "id" // The property within each row object that provides an ID for the record (optional)
+}, RecordDef);
+</code></pre>
+ * <p>
+ * This would consume a JSON file like this:
+ * <pre><code>
+{ 'results': 2, 'rows': [
+ { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
+ { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
+}
+</code></pre>
+ * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
+ * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
+ * paged from the remote server.
+ * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
+ * @cfg {String} root name of the property which contains the Array of row objects.
+ * @cfg {String} id Name of the property within a row object that contains a record identifier value.
+ * @cfg {Array} fields Array of field definition objects
+ * @constructor
+ * Create a new JsonReader
+ * @param {Object} meta Metadata configuration options
+ * @param {Object} recordType Either an Array of field definition objects,
+ * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
+ */
+Roo.data.JsonReader = function(meta, recordType){
+
+ meta = meta || {};
+ // set some defaults:
+ Roo.applyIf(meta, {
+ totalProperty: 'total',
+ successProperty : 'success',
+ root : 'data',
+ id : 'id'
+ });
+
+ Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+};
+Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
+
+ readerType : 'Json',
+
+ /**
+ * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
+ * Used by Store query builder to append _requestMeta to params.
+ *
+ */
+ metaFromRemote : false,
+ /**
+ * This method is only used by a DataProxy which has retrieved data from a remote server.
+ * @param {Object} response The XHR object which contains the JSON data in its responseText.
+ * @return {Object} data A data block which is used by an Roo.data.Store object as
+ * a cache of Roo.data.Records.
+ */
+ read : function(response){
+ var json = response.responseText;
+
+ var o = /* eval:var:o */ eval("("+json+")");
+ if(!o) {
+ throw {message: "JsonReader.read: Json object not found"};
+ }
+
+ if(o.metaData){
+
+ delete this.ef;
+ this.metaFromRemote = true;
+ this.meta = o.metaData;
+ this.recordType = Roo.data.Record.create(o.metaData.fields);
+ this.onMetaChange(this.meta, this.recordType, o);
+ }
+ return this.readRecords(o);
+ },
+
+ // private function a store will implement
+ onMetaChange : function(meta, recordType, o){
+
+ },
+
+ /**
+ * @ignore
+ */
+ simpleAccess: function(obj, subsc) {
+ return obj[subsc];
+ },
+
+ /**
+ * @ignore
+ */
+ getJsonAccessor: function(){
+ var re = /[\[\.]/;
+ return function(expr) {
+ try {
+ return(re.test(expr))
+ ? new Function("obj", "return obj." + expr)
+ : function(obj){
+ return obj[expr];
+ };
+ } catch(e){}
+ return Roo.emptyFn;
+ };
+ }(),
+
+ /**
+ * Create a data block containing Roo.data.Records from an XML document.
+ * @param {Object} o An object which contains an Array of row objects in the property specified
+ * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
+ * which contains the total size of the dataset.
+ * @return {Object} data A data block which is used by an Roo.data.Store object as
+ * a cache of Roo.data.Records.
+ */
+ readRecords : function(o){
+ /**
+ * After any data loads, the raw JSON data is available for further custom processing.
+ * @type Object
+ */
+ this.o = o;
+ var s = this.meta, Record = this.recordType,
+ f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
+
+// Generate extraction functions for the totalProperty, the root, the id, and for each field
+ if (!this.ef) {
+ if(s.totalProperty) {
+ this.getTotal = this.getJsonAccessor(s.totalProperty);
+ }
+ if(s.successProperty) {
+ this.getSuccess = this.getJsonAccessor(s.successProperty);
+ }
+ this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
+ if (s.id) {
+ var g = this.getJsonAccessor(s.id);
+ this.getId = function(rec) {
+ var r = g(rec);
+ return (r === undefined || r === "") ? null : r;
+ };
+ } else {
+ this.getId = function(){return null;};
+ }
+ this.ef = [];
+ for(var jj = 0; jj < fl; jj++){
+ f = fi[jj];
+ var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
+ this.ef[jj] = this.getJsonAccessor(map);
+ }
+ }
+
+ var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
+ if(s.totalProperty){
+ var vt = parseInt(this.getTotal(o), 10);
+ if(!isNaN(vt)){
+ totalRecords = vt;
+ }
+ }
+ if(s.successProperty){
+ var vs = this.getSuccess(o);
+ if(vs === false || vs === 'false'){
+ success = false;
+ }
+ }
+ var records = [];
+ for(var i = 0; i < c; i++){
+ var n = root[i];
+ var values = {};
+ var id = this.getId(n);
+ for(var j = 0; j < fl; j++){
+ f = fi[j];
+ var v = this.ef[j](n);
+ if (!f.convert) {
+ Roo.log('missing convert for ' + f.name);
+ Roo.log(f);
+ continue;
+ }
+ values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
+ }
+ if (!Record) {
+ return {
+ raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
+ success : false,
+ records : [],
+ totalRecords : 0
+ };
+ }
+ var record = new Record(values, id);
+ record.json = n;
+ records[i] = record;
+ }
+ return {
+ raw : o,
+ success : success,
+ records : records,
+ totalRecords : totalRecords
+ };
+ },
+ // used when loading children.. @see loadDataFromChildren
+ toLoadData: function(rec)
+ {
+ // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
+ var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
+ return { data : data, total : data.length };
+
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.data.ArrayReader
+ * @extends Roo.data.DataReader
+ * Data reader class to create an Array of Roo.data.Record objects from an Array.
+ * Each element of that Array represents a row of data fields. The
+ * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
+ * of the field definition if it exists, or the field's ordinal position in the definition.<br>
+ * <p>
+ * Example code:.
+ * <pre><code>
+var RecordDef = Roo.data.Record.create([
+ {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
+ {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
+]);
+var myReader = new Roo.data.ArrayReader({
+ id: 0 // The subscript within row Array that provides an ID for the Record (optional)
+}, RecordDef);
+</code></pre>
+ * <p>
+ * This would consume an Array like this:
+ * <pre><code>
+[ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
+ </code></pre>
+
+ * @constructor
+ * Create a new JsonReader
+ * @param {Object} meta Metadata configuration options.
+ * @param {Object|Array} recordType Either an Array of field definition objects
+ *
+ * @cfg {Array} fields Array of field definition objects
+ * @cfg {String} id Name of the property within a row object that contains a record identifier value.
+ * as specified to {@link Roo.data.Record#create},
+ * or an {@link Roo.data.Record} object
+ *
+ *
+ * created using {@link Roo.data.Record#create}.
+ */
+Roo.data.ArrayReader = function(meta, recordType)
+{
+ Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
+};
+
+Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
+
+ /**
+ * Create a data block containing Roo.data.Records from an XML document.
+ * @param {Object} o An Array of row objects which represents the dataset.
+ * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
+ * a cache of Roo.data.Records.
+ */
+ readRecords : function(o)
+ {
+ var sid = this.meta ? this.meta.id : null;
+ var recordType = this.recordType, fields = recordType.prototype.fields;
+ var records = [];
+ var root = o;
+ for(var i = 0; i < root.length; i++){
+ var n = root[i];
+ var values = {};
+ var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
+ for(var j = 0, jlen = fields.length; j < jlen; j++){
+ var f = fields.items[j];
+ var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
+ var v = n[k] !== undefined ? n[k] : f.defaultValue;
+ v = f.convert(v);
+ values[f.name] = v;
+ }
+ var record = new recordType(values, id);
+ record.json = n;
+ records[records.length] = record;
+ }
+ return {
+ records : records,
+ totalRecords : records.length
+ };
+ },
+ // used when loading children.. @see loadDataFromChildren
+ toLoadData: function(rec)
+ {
+ // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
+ return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
+
+ }
+
+
+});/*
+ * - LGPL
+ * *
+ */
+
+/**
+ * @class Roo.bootstrap.form.ComboBox
+ * @extends Roo.bootstrap.form.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
+ * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
+ * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
+ * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
+ * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
+ * @cfg {Boolean} animate default true
+ * @cfg {Boolean} emptyResultText only for touch device
+ * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
+ * @cfg {String} emptyTitle default ''
+ * @cfg {Number} width fixed with? experimental
+ * @constructor
+ * Create a new ComboBox.
+ * @param {Object} config Configuration options
+ */
+Roo.bootstrap.form.ComboBox = function(config){
+ Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event expand
+ * Fires when the dropdown list is expanded
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'expand' : true,
+ /**
+ * @event collapse
+ * Fires when the dropdown list is collapsed
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'collapse' : true,
+ /**
+ * @event beforeselect
+ * Fires before a list item is selected. Return false to cancel the selection.
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {Roo.data.Record} record The data record returned from the underlying store
+ * @param {Number} index The index of the selected item in the dropdown list
+ */
+ 'beforeselect' : true,
+ /**
+ * @event select
+ * Fires when a list item is selected
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
+ * @param {Number} index The index of the selected item in the dropdown list
+ */
+ 'select' : true,
+ /**
+ * @event beforequery
+ * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
+ * The event object passed has these properties:
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {String} query The query
+ * @param {Boolean} forceAll true to force "all" query
+ * @param {Boolean} cancel true to cancel the query
+ * @param {Object} e The query event object
+ */
+ 'beforequery': true,
+ /**
+ * @event add
+ * Fires when the 'add' icon is pressed (add a listener to enable add button)
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'add' : true,
+ /**
+ * @event edit
+ * Fires when the 'edit' icon is pressed (add a listener to enable add button)
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
+ */
+ 'edit' : true,
+ /**
+ * @event remove
+ * Fires when the remove value from the combobox array
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'remove' : true,
+ /**
+ * @event afterremove
+ * Fires when the remove value from the combobox array
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'afterremove' : true,
+ /**
+ * @event specialfilter
+ * Fires when specialfilter
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'specialfilter' : true,
+ /**
+ * @event tick
+ * Fires when tick the element
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ */
+ 'tick' : true,
+ /**
+ * @event touchviewdisplay
+ * Fires when touch view require special display (default is using displayField)
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
+ * @param {Object} cfg set html .
+ */
+ 'touchviewdisplay' : true
+
+ });
+
+ this.item = [];
+ this.tickItems = [];
+
+ this.selectedIndex = -1;
+ if(this.mode == 'local'){
+ if(config.queryDelay === undefined){
+ this.queryDelay = 10;
+ }
+ if(config.minChars === undefined){
+ this.minChars = 0;
+ }
+ }
+};
+
+Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
+
+ /**
+ * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
+ * rendering into an Roo.Editor, defaults to false)
+ */
+ /**
+ * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
+ * {tag: "input", type: "text", size: "24", autocomplete: "off"})
+ */
+ /**
+ * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
+ */
+ /**
+ * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
+ * the dropdown list (defaults to undefined, with no header element)
+ */
+
+ /**
+ * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
+ */
+
+ /**
+ * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
+ */
+ listWidth: undefined,
+ /**
+ * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
+ * mode = 'remote' or 'text' if mode = 'local')
+ */
+ displayField: undefined,
+
+ /**
+ * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
+ * mode = 'remote' or 'value' if mode = 'local').
+ * Note: use of a valueField requires the user make a selection
+ * in order for a value to be mapped.
+ */
+ valueField: undefined,
+ /**
+ * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
+ */
+ modalTitle : '',
+
+ /**
+ * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
+ * field's data value (defaults to the underlying DOM element's name)
+ */
+ hiddenName: undefined,
+ /**
+ * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
+ */
+ listClass: '',
+ /**
+ * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
+ */
+ selectedClass: 'active',
+
+ /**
+ * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
+ */
+ shadow:'sides',
+ /**
+ * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
+ * anchor positions (defaults to 'tl-bl')
+ */
+ listAlign: 'tl-bl?',
+ /**
+ * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
+ */
+ maxHeight: 300,
+ /**
+ * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
+ * query specified by the allQuery config option (defaults to 'query')
+ */
+ triggerAction: 'query',
+ /**
+ * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
+ * (defaults to 4, does not apply if editable = false)
+ */
+ minChars : 4,
+ /**
+ * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
+ * delay (typeAheadDelay) if it matches a known value (defaults to false)
+ */
+ typeAhead: false,
+ /**
+ * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
+ * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
+ */
+ queryDelay: 500,
+ /**
+ * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
+ * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
+ */
+ pageSize: 0,
+ /**
+ * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
+ * when editable = true (defaults to false)
+ */
+ selectOnFocus:false,
+ /**
+ * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
+ */
+ queryParam: 'query',
+ /**
+ * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
+ * when mode = 'remote' (defaults to 'Loading...')
+ */
+ loadingText: 'Loading...',
+ /**
+ * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
+ */
+ resizable: false,
+ /**
+ * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
+ */
+ handleHeight : 8,
+ /**
+ * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
+ * traditional select (defaults to true)
+ */
+ editable: true,
+ /**
+ * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
+ */
+ allQuery: '',
+ /**
+ * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
+ */
+ mode: 'remote',
+ /**
+ * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
+ * listWidth has a higher value)
+ */
+ minListWidth : 70,
+ /**
+ * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
+ * allow the user to set arbitrary text into the field (defaults to false)
+ */
+ forceSelection:false,
+ /**
+ * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
+ * if typeAhead = true (defaults to 250)
+ */
+ typeAheadDelay : 250,
+ /**
+ * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
+ * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
+ */
+ valueNotFoundText : undefined,
+ /**
+ * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
+ */
+ blockFocus : false,
+
+ /**
+ * @cfg {Boolean} disableClear Disable showing of clear button.
+ */
+ disableClear : false,
+ /**
+ * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
+ */
+ alwaysQuery : false,
+
+ /**
+ * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
+ */
+ multiple : false,
+
+ /**
+ * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
+ */
+ invalidClass : "has-warning",
+
+ /**
+ * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
+ */
+ validClass : "has-success",
+
+ /**
+ * @cfg {Boolean} specialFilter (true|false) special filter default false
+ */
+ specialFilter : false,
+
+ /**
+ * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
+ */
+ mobileTouchView : true,
+
+ /**
+ * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
+ */
+ useNativeIOS : false,
+
+ /**
+ * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
+ */
+ mobile_restrict_height : false,
+
+ ios_options : false,
+
+ //private
+ addicon : false,
+ editicon: false,
+
+ page: 0,
+ hasQuery: false,
+ append: false,
+ loadNext: false,
+ autoFocus : true,
+ tickable : false,
+ btnPosition : 'right',
+ triggerList : true,
+ showToggleBtn : true,
+ animate : true,
+ emptyResultText: 'Empty',
+ triggerText : 'Select',
+ emptyTitle : '',
+ width : false,
+
+ // element that contains real text value.. (when hidden is used..)
+
+ getAutoCreate : function()
+ {
+ var cfg = false;
+ //render
+ /*
+ * Render classic select for iso
+ */
+
+ if(Roo.isIOS && this.useNativeIOS){
+ cfg = this.getAutoCreateNativeIOS();
+ return cfg;
+ }
+
+ /*
+ * Touch Devices
+ */
+
+ if(Roo.isTouch && this.mobileTouchView){
+ cfg = this.getAutoCreateTouchView();
+ return cfg;;
+ }
+
+ /*
+ * Normal ComboBox
+ */
+ if(!this.tickable){
+ cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
+ return cfg;
+ }
+
+ /*
+ * ComboBox with tickable selections
+ */
+
+ var align = this.labelAlign || this.parentLabelAlign();
+
+ cfg = {
+ cls : 'form-group roo-combobox-tickable' //input-group
+ };
+
+ var btn_text_select = '';
+ var btn_text_done = '';
+ var btn_text_cancel = '';
+
+ if (this.btn_text_show) {
+ btn_text_select = 'Select';
+ btn_text_done = 'Done';
+ btn_text_cancel = 'Cancel';
+ }
+
+ var buttons = {
+ tag : 'div',
+ cls : 'tickable-buttons',
+ cn : [
+ {
+ tag : 'button',
+ type : 'button',
+ cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
+ //html : this.triggerText
+ html: btn_text_select
+ },
+ {
+ tag : 'button',
+ type : 'button',
+ name : 'ok',
+ cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
+ //html : 'Done'
+ html: btn_text_done
+ },
+ {
+ tag : 'button',
+ type : 'button',
+ name : 'cancel',
+ cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
+ //html : 'Cancel'
+ html: btn_text_cancel
+ }
+ ]
+ };
+
+ if(this.editable){
+ buttons.cn.unshift({
+ tag: 'input',
+ cls: 'roo-select2-search-field-input'
+ });
+ }
+
+ var _this = this;
+
+ Roo.each(buttons.cn, function(c){
+ if (_this.size) {
+ c.cls += ' btn-' + _this.size;
+ }
+
+ if (_this.disabled) {
+ c.disabled = true;
+ }
+ });
+
+ var box = {
+ tag: 'div',
+ style : 'display: contents',
+ cn: [
+ {
+ tag: 'input',
+ type : 'hidden',
+ cls: 'form-hidden-field'
+ },
+ {
+ tag: 'ul',
+ cls: 'roo-select2-choices',
+ cn:[
+ {
+ tag: 'li',
+ cls: 'roo-select2-search-field',
+ cn: [
+ buttons
+ ]
+ }
+ ]
+ }
+ ]
+ };
+
+ var combobox = {
+ cls: 'roo-select2-container input-group roo-select2-container-multi',
+ cn: [
+
+ box
+// {
+// tag: 'ul',
+// cls: 'typeahead typeahead-long dropdown-menu',
+// style: 'display:none; max-height:' + this.maxHeight + 'px;'
+// }
+ ]
+ };
+
+ if(this.hasFeedback && !this.allowBlank){
+
+ var feedback = {
+ tag: 'span',
+ cls: 'glyphicon form-control-feedback'
+ };
+
+ combobox.cn.push(feedback);
+ }
+
+
+
+ var indicator = {
+ tag : 'i',
+ cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required'
+ };
+ if (Roo.bootstrap.version == 4) {
+ indicator = {
+ tag : 'i',
+ style : 'display:none'
+ };
+ }
+ if (align ==='left' && this.fieldLabel.length) {
+
+ cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
+
+ cfg.cn = [
+ indicator,
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-form-label',
+ html : this.fieldLabel
+
+ },
+ {
+ cls : "",
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+
+ var labelCfg = cfg.cn[1];
+ var contentCfg = cfg.cn[2];
+
+
+ if(this.indicatorpos == 'right'){
+
+ cfg.cn = [
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-form-label',
+ cn : [
+ {
+ tag : 'span',
+ html : this.fieldLabel
+ },
+ indicator
+ ]
+ },
+ {
+ cls : "",
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+
+
+
+ labelCfg = cfg.cn[0];
+ contentCfg = cfg.cn[1];
+
+ }
+
+ if(this.labelWidth > 12){
+ labelCfg.style = "width: " + this.labelWidth + 'px';
+ }
+ if(this.width * 1 > 0){
+ contentCfg.style = "width: " + this.width + 'px';
+ }
+ if(this.labelWidth < 13 && this.labelmd == 0){
+ this.labelmd = this.labelWidth;
+ }
+
+ if(this.labellg > 0){
+ labelCfg.cls += ' col-lg-' + this.labellg;
+ contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+ }
+
+ if(this.labelmd > 0){
+ labelCfg.cls += ' col-md-' + this.labelmd;
+ contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ }
+
+ if(this.labelsm > 0){
+ labelCfg.cls += ' col-sm-' + this.labelsm;
+ contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ }
+
+ if(this.labelxs > 0){
+ labelCfg.cls += ' col-xs-' + this.labelxs;
+ contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ }
+
+
+ } else if ( this.fieldLabel.length) {
+// Roo.log(" label");
+ cfg.cn = [
+ indicator,
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+ },
+ combobox
+ ];
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ //cls : 'input-group-addon',
+ html : this.fieldLabel
+ },
+ indicator,
+ combobox
+ ];
+
+ }
+
+ } else {
+
+// Roo.log(" no label && no align");
+ cfg = combobox
+
+
+ }
+
+ var settings=this;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ return cfg;
+
+ },
+
+ _initEventsCalled : false,
+
+ // private
+ initEvents: function()
+ {
+ if (this._initEventsCalled) { // as we call render... prevent looping...
+ return;
+ }
+ this._initEventsCalled = true;
+
+ if (!this.store) {
+ throw "can not find store for combo";
+ }
+
+ this.indicator = this.indicatorEl();
+
+ this.store = Roo.factory(this.store, Roo.data);
+ this.store.parent = this;
+
+ // if we are building from html. then this element is so complex, that we can not really
+ // use the rendered HTML.
+ // so we have to trash and replace the previous code.
+ if (Roo.XComponent.build_from_html) {
+ // remove this element....
+ var e = this.el.dom, k=0;
+ while (e ) { e = e.previousSibling; ++k;}
+
+ this.el.remove();
+
+ this.el=false;
+ this.rendered = false;
+
+ this.render(this.parent().getChildContainer(true), k);
+ }
+
+ if(Roo.isIOS && this.useNativeIOS){
+ this.initIOSView();
+ return;
+ }
+
+ /*
+ * Touch Devices
+ */
+
+ if(Roo.isTouch && this.mobileTouchView){
+ this.initTouchView();
+ return;
+ }
+
+ if(this.tickable){
+ this.initTickableEvents();
+ return;
+ }
+
+ Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
+
+ if(this.hiddenName){
+
+ this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+
+ this.hiddenField.dom.value =
+ this.hiddenValue !== undefined ? this.hiddenValue :
+ this.value !== undefined ? this.value : '';
+
+ // prevent input submission
+ this.el.dom.removeAttribute('name');
+ this.hiddenField.dom.setAttribute('name', this.hiddenName);
+
+
+ }
+ //if(Roo.isGecko){
+ // this.el.dom.setAttribute('autocomplete', 'off');
+ //}
+
+ var cls = 'x-combo-list';
+
+ //this.list = new Roo.Layer({
+ // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
+ //});
+
+ var _this = this;
+
+ (function(){
+ var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
+ _this.list.setWidth(lw);
+ }).defer(100);
+
+ this.list.on('mouseover', this.onViewOver, this);
+ this.list.on('mousemove', this.onViewMove, this);
+ this.list.on('scroll', this.onViewScroll, this);
+
+ /*
+ this.list.swallowEvent('mousewheel');
+ this.assetHeight = 0;
+
+ if(this.title){
+ this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
+ this.assetHeight += this.header.getHeight();
+ }
+
+ this.innerList = this.list.createChild({cls:cls+'-inner'});
+ this.innerList.on('mouseover', this.onViewOver, this);
+ this.innerList.on('mousemove', this.onViewMove, this);
+ this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+
+ if(this.allowBlank && !this.pageSize && !this.disableClear){
+ this.footer = this.list.createChild({cls:cls+'-ft'});
+ this.pageTb = new Roo.Toolbar(this.footer);
+
+ }
+ if(this.pageSize){
+ this.footer = this.list.createChild({cls:cls+'-ft'});
+ this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
+ {pageSize: this.pageSize});
+
+ }
+
+ if (this.pageTb && this.allowBlank && !this.disableClear) {
+ var _this = this;
+ this.pageTb.add(new Roo.Toolbar.Fill(), {
+ cls: 'x-btn-icon x-btn-clear',
+ text: ' ',
+ handler: function()
+ {
+ _this.collapse();
+ _this.clearValue();
+ _this.onSelect(false, -1);
+ }
+ });
+ }
+ if (this.footer) {
+ this.assetHeight += this.footer.getHeight();
+ }
+ */
+
+ if(!this.tpl){
+ this.tpl = Roo.bootstrap.version == 4 ?
+ '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
+ '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
+ }
+
+ this.view = new Roo.View(this.list, this.tpl, {
+ singleSelect:true, store: this.store, selectedClass: this.selectedClass
+ });
+ //this.view.wrapEl.setDisplayed(false);
+ this.view.on('click', this.onViewClick, this);
+
+
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('loadexception', this.onLoadException, this);
+ /*
+ if(this.resizable){
+ this.resizer = new Roo.Resizable(this.list, {
+ pinned:true, handles:'se'
+ });
+ this.resizer.on('resize', function(r, w, h){
+ this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
+ this.listWidth = w;
+ this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
+ this.restrictHeight();
+ }, this);
+ this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
+ }
+ */
+ if(!this.editable){
+ this.editable = true;
+ this.setEditable(false);
+ }
+
+ /*
+
+ if (typeof(this.events.add.listeners) != 'undefined') {
+
+ this.addicon = this.wrap.createChild(
+ {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
+
+ this.addicon.on('click', function(e) {
+ this.fireEvent('add', this);
+ }, this);
+ }
+ if (typeof(this.events.edit.listeners) != 'undefined') {
+
+ this.editicon = this.wrap.createChild(
+ {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
+ if (this.addicon) {
+ this.editicon.setStyle('margin-left', '40px');
+ }
+ this.editicon.on('click', function(e) {
+
+ // we fire even if inothing is selected..
+ this.fireEvent('edit', this, this.lastData );
+
+ }, this);
+ }
+ */
+
+ this.keyNav = new Roo.KeyNav(this.inputEl(), {
+ "up" : function(e){
+ this.inKeyMode = true;
+ this.selectPrev();
+ },
+
+ "down" : function(e){
+ if(!this.isExpanded()){
+ this.onTriggerClick();
+ }else{
+ this.inKeyMode = true;
+ this.selectNext();
+ }
+ },
+
+ "enter" : function(e){
+// this.onViewClick();
+ //return true;
+ this.collapse();
+
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
+ },
+
+ "esc" : function(e){
+ this.collapse();
+ },
+
+ "tab" : function(e){
+ this.collapse();
+
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
+ },
+
+ scope : this,
+
+ doRelay : function(foo, bar, hname){
+ if(hname == 'down' || this.scope.isExpanded()){
+ return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+ }
+ return true;
+ },
+
+ forceKeyDown: true
+ });
+
+
+ this.queryDelay = Math.max(this.queryDelay || 10,
+ this.mode == 'local' ? 10 : 250);
+
+
+ this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
+
+ if(this.typeAhead){
+ this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
+ }
+ if(this.editable !== false){
+ this.inputEl().on("keyup", this.onKeyUp, this);
+ }
+ if(this.forceSelection){
+ this.inputEl().on('blur', this.doForce, this);
+ }
+
+ if(this.multiple){
+ this.choices = this.el.select('ul.roo-select2-choices', true).first();
+ this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+ }
+ },
+
+ initTickableEvents: function()
+ {
+ this.createList();
+
+ if(this.hiddenName){
+
+ this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+
+ this.hiddenField.dom.value =
+ this.hiddenValue !== undefined ? this.hiddenValue :
+ this.value !== undefined ? this.value : '';
+
+ // prevent input submission
+ this.el.dom.removeAttribute('name');
+ this.hiddenField.dom.setAttribute('name', this.hiddenName);
+
+
+ }
+
+// this.list = this.el.select('ul.dropdown-menu',true).first();
+
+ this.choices = this.el.select('ul.roo-select2-choices', true).first();
+ this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+ if(this.triggerList){
+ this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
+ }
+
+ this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
+ this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
+
+ this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
+ this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
+
+ this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
+ this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
+
+ this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
+ this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
+ this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.okBtn.hide();
+ this.cancelBtn.hide();
+
+ var _this = this;
+
+ (function(){
+ var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
+ _this.list.setWidth(lw);
+ }).defer(100);
+
+ this.list.on('mouseover', this.onViewOver, this);
+ this.list.on('mousemove', this.onViewMove, this);
+
+ this.list.on('scroll', this.onViewScroll, this);
+
+ if(!this.tpl){
+ this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
+ 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
+ }
+
+ this.view = new Roo.View(this.list, this.tpl, {
+ singleSelect:true,
+ tickable:true,
+ parent:this,
+ store: this.store,
+ selectedClass: this.selectedClass
+ });
+
+ //this.view.wrapEl.setDisplayed(false);
+ this.view.on('click', this.onViewClick, this);
+
+
+
+ this.store.on('beforeload', this.onBeforeLoad, this);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('loadexception', this.onLoadException, this);
+
+ if(this.editable){
+ this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
+ "up" : function(e){
+ this.inKeyMode = true;
+ this.selectPrev();
+ },
+
+ "down" : function(e){
+ this.inKeyMode = true;
+ this.selectNext();
+ },
+
+ "enter" : function(e){
+ if(this.fireEvent("specialkey", this, e)){
+ this.onViewClick(false);
+ }
+
+ return true;
+ },
+
+ "esc" : function(e){
+ this.onTickableFooterButtonClick(e, false, false);
+ },
+
+ "tab" : function(e){
+ this.fireEvent("specialkey", this, e);
+
+ this.onTickableFooterButtonClick(e, false, false);
+
+ return true;
+ },
+
+ scope : this,
+
+ doRelay : function(e, fn, key){
+ if(this.scope.isExpanded()){
+ return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
+ }
+ return true;
+ },
+
+ forceKeyDown: true
+ });
+ }
+
+ this.queryDelay = Math.max(this.queryDelay || 10,
+ this.mode == 'local' ? 10 : 250);
+
+
+ this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
+
+ if(this.typeAhead){
+ this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
+ }
+
+ if(this.editable !== false){
+ this.tickableInputEl().on("keyup", this.onKeyUp, this);
+ }
+
+ this.indicator = this.indicatorEl();
+
+ if(this.indicator){
+ this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
+ this.indicator.hide();
+ }
+
+ },
+
+ onDestroy : function(){
+ if(this.view){
+ this.view.setStore(null);
+ this.view.el.removeAllListeners();
+ this.view.el.remove();
+ this.view.purgeListeners();
+ }
+ if(this.list){
+ this.list.dom.innerHTML = '';
+ }
+
+ if(this.store){
+ this.store.un('beforeload', this.onBeforeLoad, this);
+ this.store.un('load', this.onLoad, this);
+ this.store.un('loadexception', this.onLoadException, this);
+ }
+ Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
+ },
+
+ // private
+ fireKey : function(e){
+ if(e.isNavKeyPress() && !this.list.isVisible()){
+ this.fireEvent("specialkey", this, e);
+ }
+ },
+
+ // private
+ onResize: function(w, h)
+ {
+
+
+// Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
+//
+// if(typeof w != 'number'){
+// // we do not handle it!?!?
+// return;
+// }
+// var tw = this.trigger.getWidth();
+// // tw += this.addicon ? this.addicon.getWidth() : 0;
+// // tw += this.editicon ? this.editicon.getWidth() : 0;
+// var x = w - tw;
+// this.inputEl().setWidth( this.adjustWidth('input', x));
+//
+// //this.trigger.setStyle('left', x+'px');
+//
+// if(this.list && this.listWidth === undefined){
+// var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
+// this.list.setWidth(lw);
+// this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
+// }
+
+
+
+ },
+
+ /**
+ * Allow or prevent the user from directly editing the field text. If false is passed,
+ * the user will only be able to select from the items defined in the dropdown list. This method
+ * is the runtime equivalent of setting the 'editable' config option at config time.
+ * @param {Boolean} value True to allow the user to directly edit the field text
+ */
+ setEditable : function(value){
+ if(value == this.editable){
+ return;
+ }
+ this.editable = value;
+ if(!value){
+ this.inputEl().dom.setAttribute('readOnly', true);
+ this.inputEl().on('mousedown', this.onTriggerClick, this);
+ this.inputEl().addClass('x-combo-noedit');
+ }else{
+ this.inputEl().dom.removeAttribute('readOnly');
+ this.inputEl().un('mousedown', this.onTriggerClick, this);
+ this.inputEl().removeClass('x-combo-noedit');
+ }
+ },
+
+ // private
+
+ onBeforeLoad : function(combo,opts){
+ if(!this.hasFocus){
+ return;
+ }
+ if (!opts.add) {
+ this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
+ }
+ this.restrictHeight();
+ this.selectedIndex = -1;
+ },
+
+ // private
+ onLoad : function(){
+
+ this.hasQuery = false;
+
+ if(!this.hasFocus){
+ return;
+ }
+
+ if(typeof(this.loading) !== 'undefined' && this.loading !== null){
+ this.loading.hide();
+ }
+
+ if(this.store.getCount() > 0){
+
+ this.expand();
+ this.restrictHeight();
+ if(this.lastQuery == this.allQuery){
+ if(this.editable && !this.tickable){
+ this.inputEl().dom.select();
+ }
+
+ if(
+ !this.selectByValue(this.value, true) &&
+ this.autoFocus &&
+ (
+ !this.store.lastOptions ||
+ typeof(this.store.lastOptions.add) == 'undefined' ||
+ this.store.lastOptions.add != true
+ )
+ ){
+ this.select(0, true);
+ }
+ }else{
+ if(this.autoFocus){
+ this.selectNext();
+ }
+ if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
+ this.taTask.delay(this.typeAheadDelay);
+ }
+ }
+ }else{
+ this.onEmptyResults();
+ }
+
+ //this.el.focus();
+ },
+ // private
+ onLoadException : function()
+ {
+ this.hasQuery = false;
+
+ if(typeof(this.loading) !== 'undefined' && this.loading !== null){
+ this.loading.hide();
+ }
+
+ if(this.tickable && this.editable){
+ return;
+ }
+
+ this.collapse();
+ // only causes errors at present
+ //Roo.log(this.store.reader.jsonData);
+ //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
+ // fixme
+ //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
+ //}
+
+
+ },
+ // private
+ onTypeAhead : function(){
+ if(this.store.getCount() > 0){
+ var r = this.store.getAt(0);
+ var newValue = r.data[this.displayField];
+ var len = newValue.length;
+ var selStart = this.getRawValue().length;
+
+ if(selStart != len){
+ this.setRawValue(newValue);
+ this.selectText(selStart, newValue.length);
+ }
+ }
+ },
+
+ // private
+ onSelect : function(record, index){
+
+ if(this.fireEvent('beforeselect', this, record, index) !== false){
+
+ this.setFromData(index > -1 ? record.data : false);
+
+ this.collapse();
+ this.fireEvent('select', this, record, index);
+ }
+ },
+
+ /**
+ * Returns the currently selected field value or empty string if no value is set.
+ * @return {String} value The selected value
+ */
+ getValue : function()
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
+ }
+
+ if(this.multiple){
+ return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
+ }
+
+ if(this.valueField){
+ return typeof this.value != 'undefined' ? this.value : '';
+ }else{
+ return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
+ }
+ },
+
+ getRawValue : function()
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
+ }
+
+ var v = this.inputEl().getValue();
+
+ return v;
+ },
+
+ /**
+ * Clears any text/value currently set in the field
+ */
+ clearValue : function(){
+
+ if(this.hiddenField){
+ this.hiddenField.dom.value = '';
+ }
+ this.value = '';
+ this.setRawValue('');
+ this.lastSelectionText = '';
+ this.lastData = false;
+
+ var close = this.closeTriggerEl();
+
+ if(close){
+ close.hide();
+ }
+
+ this.validate();
+
+ },
+
+ /**
+ * Sets the specified value into the field. If the value finds a match, the corresponding record text
+ * will be displayed in the field. If the value does not match the data value of an existing item,
+ * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
+ * Otherwise the field will be blank (although the value will still be set).
+ * @param {String} value The value to match
+ */
+ setValue : function(v)
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ this.setIOSValue(v);
+ return;
+ }
+
+ if(this.multiple){
+ this.syncValue();
+ return;
+ }
+
+ var text = v;
+ if(this.valueField){
+ var r = this.findRecord(this.valueField, v);
+ if(r){
+ text = r.data[this.displayField];
+ }else if(this.valueNotFoundText !== undefined){
+ text = this.valueNotFoundText;
+ }
+ }
+ this.lastSelectionText = text;
+ if(this.hiddenField){
+ this.hiddenField.dom.value = v;
+ }
+ Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
+ this.value = v;
+
+ var close = this.closeTriggerEl();
+
+ if(close){
+ (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
+ }
+
+ this.validate();
+ },
+ /**
+ * @property {Object} the last set data for the element
+ */
+
+ lastData : false,
+ /**
+ * Sets the value of the field based on a object which is related to the record format for the store.
+ * @param {Object} value the value to set as. or false on reset?
+ */
+ setFromData : function(o){
+
+ if(this.multiple){
+ this.addItem(o);
+ return;
+ }
+
+ var dv = ''; // display value
+ var vv = ''; // value value..
+ this.lastData = o;
+ if (this.displayField) {
+ dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+ } else {
+ // this is an error condition!!!
+ Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
+ }
+
+ if(this.valueField){
+ vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
+ }
+
+ var close = this.closeTriggerEl();
+
+ if(close){
+ if(dv.length || vv * 1 > 0){
+ close.show() ;
+ this.blockFocus=true;
+ } else {
+ close.hide();
+ }
+ }
+
+ if(this.hiddenField){
+ this.hiddenField.dom.value = vv;
+
+ this.lastSelectionText = dv;
+ Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
+ this.value = vv;
+ return;
+ }
+ // no hidden field.. - we store the value in 'value', but still display
+ // display field!!!!
+ this.lastSelectionText = dv;
+ Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
+ this.value = vv;
+
+
+
+ },
+ // private
+ reset : function(){
+ // overridden so that last data is reset..
+
+ if(this.multiple){
+ this.clearItem();
+ return;
+ }
+
+ this.setValue(this.originalValue);
+ //this.clearInvalid();
+ this.lastData = false;
+ if (this.view) {
+ this.view.clearSelections();
+ }
+
+ this.validate();
+ },
+ // private
+ findRecord : function(prop, value){
+ var record;
+ if(this.store.getCount() > 0){
+ this.store.each(function(r){
+ if(r.data[prop] == value){
+ record = r;
+ return false;
+ }
+ return true;
+ });
+ }
+ return record;
+ },
+
+ getName: function()
+ {
+ // returns hidden if it's set..
+ if (!this.rendered) {return ''};
+ return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
+
+ },
+ // private
+ onViewMove : function(e, t){
+ this.inKeyMode = false;
+ },
+
+ // private
+ onViewOver : function(e, t){
+ if(this.inKeyMode){ // prevent key nav and mouse over conflicts
+ return;
+ }
+ var item = this.view.findItemFromChild(t);
+
+ if(item){
+ var index = this.view.indexOf(item);
+ this.select(index, false);
+ }
+ },
+
+ // private
+ onViewClick : function(view, doFocus, el, e)
+ {
+ var index = this.view.getSelectedIndexes()[0];
+
+ var r = this.store.getAt(index);
+
+ if(this.tickable){
+
+ if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
+ return;
+ }
+
+ var rm = false;
+ var _this = this;
+
+ Roo.each(this.tickItems, function(v,k){
+
+ if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
+ Roo.log(v);
+ _this.tickItems.splice(k, 1);
+
+ if(typeof(e) == 'undefined' && view == false){
+ Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
+ }
+
+ rm = true;
+ return;
+ }
+ });
+
+ if(rm){
+ return;
+ }
+
+ if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
+ this.tickItems.push(r.data);
+ }
+
+ if(typeof(e) == 'undefined' && view == false){
+ Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
+ }
+
+ return;
+ }
+
+ if(r){
+ this.onSelect(r, index);
+ }
+ if(doFocus !== false && !this.blockFocus){
+ this.inputEl().focus();
+ }
+ },
+
+ // private
+ restrictHeight : function(){
+ //this.innerList.dom.style.height = '';
+ //var inner = this.innerList.dom;
+ //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
+ //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
+ //this.list.beginUpdate();
+ //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
+ this.list.alignTo(this.inputEl(), this.listAlign);
+ this.list.alignTo(this.inputEl(), this.listAlign);
+ //this.list.endUpdate();
+ },
+
+ // private
+ onEmptyResults : function(){
+
+ if(this.tickable && this.editable){
+ this.hasFocus = false;
+ this.restrictHeight();
+ return;
+ }
+
+ this.collapse();
+ },
+
+ /**
+ * Returns true if the dropdown list is expanded, else false.
+ */
+ isExpanded : function(){
+ return this.list.isVisible();
+ },
+
+ /**
+ * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
+ * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+ * @param {String} value The data value of the item to select
+ * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+ * selected item if it is not currently in view (defaults to true)
+ * @return {Boolean} True if the value matched an item in the list, else false
+ */
+ selectByValue : function(v, scrollIntoView){
+ if(v !== undefined && v !== null){
+ var r = this.findRecord(this.valueField || this.displayField, v);
+ if(r){
+ this.select(this.store.indexOf(r), scrollIntoView);
+ return true;
+ }
+ }
+ return false;
+ },
+
+ /**
+ * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
+ * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
+ * @param {Number} index The zero-based index of the list item to select
+ * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
+ * selected item if it is not currently in view (defaults to true)
+ */
+ select : function(index, scrollIntoView){
+ this.selectedIndex = index;
+ this.view.select(index);
+ if(scrollIntoView !== false){
+ var el = this.view.getNode(index);
+ /*
+ * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
+ */
+ if(el){
+ this.list.scrollChildIntoView(el, false);
+ }
+ }
+ },
+
+ // private
+ selectNext : function(){
+ var ct = this.store.getCount();
+ if(ct > 0){
+ if(this.selectedIndex == -1){
+ this.select(0);
+ }else if(this.selectedIndex < ct-1){
+ this.select(this.selectedIndex+1);
+ }
+ }
+ },
+
+ // private
+ selectPrev : function(){
+ var ct = this.store.getCount();
+ if(ct > 0){
+ if(this.selectedIndex == -1){
+ this.select(0);
+ }else if(this.selectedIndex != 0){
+ this.select(this.selectedIndex-1);
+ }
+ }
+ },
+
+ // private
+ onKeyUp : function(e){
+ if(this.editable !== false && !e.isSpecialKey()){
+ this.lastKey = e.getKey();
+ this.dqTask.delay(this.queryDelay);
+ }
+ },
+
+ // private
+ validateBlur : function(){
+ return !this.list || !this.list.isVisible();
+ },
+
+ // private
+ initQuery : function(){
+
+ var v = this.getRawValue();
+
+ if(this.tickable && this.editable){
+ v = this.tickableInputEl().getValue();
+ }
+
+ this.doQuery(v);
+ },
+
+ // private
+ doForce : function(){
+ if(this.inputEl().dom.value.length > 0){
+ this.inputEl().dom.value =
+ this.lastSelectionText === undefined ? '' : this.lastSelectionText;
+
+ }
+ },
+
+ /**
+ * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
+ * query allowing the query action to be canceled if needed.
+ * @param {String} query The SQL query to execute
+ * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
+ * in the field than the minimum specified by the minChars config option. It also clears any filter previously
+ * saved in the current store (defaults to false)
+ */
+ doQuery : function(q, forceAll){
+
+ if(q === undefined || q === null){
+ q = '';
+ }
+ var qe = {
+ query: q,
+ forceAll: forceAll,
+ combo: this,
+ cancel:false
+ };
+ if(this.fireEvent('beforequery', qe)===false || qe.cancel){
+ return false;
+ }
+ q = qe.query;
+
+ forceAll = qe.forceAll;
+ if(forceAll === true || (q.length >= this.minChars)){
+
+ this.hasQuery = true;
+
+ if(this.lastQuery != q || this.alwaysQuery){
+ this.lastQuery = q;
+ if(this.mode == 'local'){
+ this.selectedIndex = -1;
+ if(forceAll){
+ this.store.clearFilter();
+ }else{
+
+ if(this.specialFilter){
+ this.fireEvent('specialfilter', this);
+ this.onLoad();
+ return;
+ }
+
+ this.store.filter(this.displayField, q);
+ }
+
+ this.store.fireEvent("datachanged", this.store);
+
+ this.onLoad();
+
+
+ }else{
+
+ this.store.baseParams[this.queryParam] = q;
+
+ var options = {params : this.getParams(q)};
+
+ if(this.loadNext){
+ options.add = true;
+ options.params.start = this.page * this.pageSize;
+ }
+
+ this.store.load(options);
+
+ /*
+ * this code will make the page width larger, at the beginning, the list not align correctly,
+ * we should expand the list on onLoad
+ * so command out it
+ */
+// this.expand();
+ }
+ }else{
+ this.selectedIndex = -1;
+ this.onLoad();
+ }
+ }
+
+ this.loadNext = false;
+ },
+
+ // private
+ getParams : function(q){
+ var p = {};
+ //p[this.queryParam] = q;
+
+ if(this.pageSize){
+ p.start = 0;
+ p.limit = this.pageSize;
+ }
+ return p;
+ },
+
+ /**
+ * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
+ */
+ collapse : function(){
+ if(!this.isExpanded()){
+ return;
+ }
+
+ this.list.hide();
+
+ this.hasFocus = false;
+
+ if(this.tickable){
+ this.okBtn.hide();
+ this.cancelBtn.hide();
+ this.trigger.show();
+
+ if(this.editable){
+ this.tickableInputEl().dom.value = '';
+ this.tickableInputEl().blur();
+ }
+
+ }
+
+ Roo.get(document).un('mousedown', this.collapseIf, this);
+ Roo.get(document).un('mousewheel', this.collapseIf, this);
+ if (!this.editable) {
+ Roo.get(document).un('keydown', this.listKeyPress, this);
+ }
+ this.fireEvent('collapse', this);
+
+ this.validate();
+ },
+
+ // private
+ collapseIf : function(e){
+ var in_combo = e.within(this.el);
+ var in_list = e.within(this.list);
+ var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
+
+ if (in_combo || in_list || is_list) {
+ //e.stopPropagation();
+ return;
+ }
+
+ if(this.tickable){
+ this.onTickableFooterButtonClick(e, false, false);
+ }
+
+ this.collapse();
+
+ },
+
+ /**
+ * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
+ */
+ expand : function(){
+
+ if(this.isExpanded() || !this.hasFocus){
+ return;
+ }
+
+ var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
+ this.list.setWidth(lw);
+
+ Roo.log('expand');
+
+ this.list.show();
+
+ this.restrictHeight();
+
+ if(this.tickable){
+
+ this.tickItems = Roo.apply([], this.item);
+
+ this.okBtn.show();
+ this.cancelBtn.show();
+ this.trigger.hide();
+
+ if(this.editable){
+ this.tickableInputEl().focus();
+ }
+
+ }
+
+ Roo.get(document).on('mousedown', this.collapseIf, this);
+ Roo.get(document).on('mousewheel', this.collapseIf, this);
+ if (!this.editable) {
+ Roo.get(document).on('keydown', this.listKeyPress, this);
+ }
+
+ this.fireEvent('expand', this);
+ },
+
+ // private
+ // Implements the default empty TriggerField.onTriggerClick function
+ onTriggerClick : function(e)
+ {
+ Roo.log('trigger click');
+
+ if(this.disabled || !this.triggerList){
+ return;
+ }
+
+ this.page = 0;
+ this.loadNext = false;
+
+ if(this.isExpanded()){
+ this.collapse();
+ if (!this.blockFocus) {
+ this.inputEl().focus();
+ }
+
+ }else {
+ this.hasFocus = true;
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
+ }
+ if (!this.blockFocus) {
+ this.inputEl().focus();
+ }
+ }
+ },
+
+ onTickableTriggerClick : function(e)
+ {
+ if(this.disabled){
+ return;
+ }
+
+ this.page = 0;
+ this.loadNext = false;
+ this.hasFocus = true;
+
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
+ }
+ },
+
+ onSearchFieldClick : function(e)
+ {
+ if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
+ this.onTickableFooterButtonClick(e, false, false);
+ return;
+ }
+
+ if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
+ return;
+ }
+
+ this.page = 0;
+ this.loadNext = false;
+ this.hasFocus = true;
+
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
+ }
+ },
+
+ listKeyPress : function(e)
+ {
+ //Roo.log('listkeypress');
+ // scroll to first matching element based on key pres..
+ if (e.isSpecialKey()) {
+ return false;
+ }
+ var k = String.fromCharCode(e.getKey()).toUpperCase();
+ //Roo.log(k);
+ var match = false;
+ var csel = this.view.getSelectedNodes();
+ var cselitem = false;
+ if (csel.length) {
+ var ix = this.view.indexOf(csel[0]);
+ cselitem = this.store.getAt(ix);
+ if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
+ cselitem = false;
+ }
+
+ }
+
+ this.store.each(function(v) {
+ if (cselitem) {
+ // start at existing selection.
+ if (cselitem.id == v.id) {
+ cselitem = false;
+ }
+ return true;
+ }
+
+ if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
+ match = this.store.indexOf(v);
+ return false;
+ }
+ return true;
+ }, this);
+
+ if (match === false) {
+ return true; // no more action?
+ }
+ // scroll to?
+ this.view.select(match);
+ var sn = Roo.get(this.view.getSelectedNodes()[0]);
+ sn.scrollIntoView(sn.dom.parentNode, false);
+ },
+
+ onViewScroll : function(e, t){
+
+ if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
+ return;
+ }
+
+ this.hasQuery = true;
+
+ this.loading = this.list.select('.loading', true).first();
+
+ if(this.loading === null){
+ this.list.createChild({
+ tag: 'div',
+ cls: 'loading roo-select2-more-results roo-select2-active',
+ html: 'Loading more results...'
+ });
+
+ this.loading = this.list.select('.loading', true).first();
+
+ this.loading.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.loading.hide();
+ }
+
+ this.loading.show();
+
+ var _combo = this;
+
+ this.page++;
+ this.loadNext = true;
+
+ (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
+
+ return;
+ },
+
+ addItem : function(o)
+ {
+ var dv = ''; // display value
+
+ if (this.displayField) {
+ dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
+ } else {
+ // this is an error condition!!!
+ Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
+ }
+
+ if(!dv.length){
+ return;
+ }
+
+ var choice = this.choices.createChild({
+ tag: 'li',
+ cls: 'roo-select2-search-choice',
+ cn: [
+ {
+ tag: 'div',
+ html: dv
+ },
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'roo-select2-search-choice-close fa fa-times',
+ tabindex: '-1'
+ }
+ ]
+
+ }, this.searchField);
+
+ var close = choice.select('a.roo-select2-search-choice-close', true).first();
+
+ close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
+
+ this.item.push(o);
+
+ this.lastData = o;
+
+ this.syncValue();
+
+ this.inputEl().dom.value = '';
+
+ this.validate();
+ },
+
+ onRemoveItem : function(e, _self, o)
+ {
+ e.preventDefault();
+
+ this.lastItem = Roo.apply([], this.item);
+
+ var index = this.item.indexOf(o.data) * 1;
+
+ if( index < 0){
+ Roo.log('not this item?!');
+ return;
+ }
+
+ this.item.splice(index, 1);
+ o.item.remove();
+
+ this.syncValue();
+
+ this.fireEvent('remove', this, e);
+
+ this.validate();
+
+ },
+
+ syncValue : function()
+ {
+ if(!this.item.length){
+ this.clearValue();
+ return;
+ }
+
+ var value = [];
+ var _this = this;
+ Roo.each(this.item, function(i){
+ if(_this.valueField){
+ value.push(i[_this.valueField]);
+ return;
+ }
+
+ value.push(i);
+ });
+
+ this.value = value.join(',');
+
+ if(this.hiddenField){
+ this.hiddenField.dom.value = this.value;
+ }
+
+ this.store.fireEvent("datachanged", this.store);
+
+ this.validate();
+ },
+
+ clearItem : function()
+ {
+ if(!this.multiple){
+ return;
+ }
+
+ this.item = [];
+
+ Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
+ c.remove();
+ });
+
+ this.syncValue();
+
+ this.validate();
+
+ if(this.tickable && !Roo.isTouch){
+ this.view.refresh();
+ }
+ },
+
+ inputEl: function ()
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ return this.el.select('select.roo-ios-select', true).first();
+ }
+
+ if(Roo.isTouch && this.mobileTouchView){
+ return this.el.select('input.form-control',true).first();
+ }
+
+ if(this.tickable){
+ return this.searchField;
+ }
+
+ return this.el.select('input.form-control',true).first();
+ },
+
+ onTickableFooterButtonClick : function(e, btn, el)
+ {
+ e.preventDefault();
+
+ this.lastItem = Roo.apply([], this.item);
+
+ if(btn && btn.name == 'cancel'){
+ this.tickItems = Roo.apply([], this.item);
+ this.collapse();
+ return;
+ }
+
+ this.clearItem();
+
+ var _this = this;
+
+ Roo.each(this.tickItems, function(o){
+ _this.addItem(o);
+ });
+
+ this.collapse();
+
+ },
+
+ validate : function()
+ {
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
+ }
+
+ var v = this.getRawValue();
+
+ if(this.multiple){
+ v = this.getValue();
+ }
+
+ if(this.disabled || this.allowBlank || v.length){
+ this.markValid();
+ return true;
+ }
+
+ this.markInvalid();
+ return false;
+ },
+
+ tickableInputEl : function()
+ {
+ if(!this.tickable || !this.editable){
+ return this.inputEl();
+ }
+
+ return this.inputEl().select('.roo-select2-search-field-input', true).first();
+ },
+
+
+ getAutoCreateTouchView : function()
+ {
+ var id = Roo.id();
+
+ var cfg = {
+ cls: 'form-group' //input-group
+ };
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ cls : 'form-control x-combo-noedit',
+ autocomplete: 'new-password',
+ placeholder : this.placeholder || '',
+ readonly : true
+ };
+
+ if (this.name) {
+ input.name = this.name;
+ }
+
+ if (this.size) {
+ input.cls += ' input-' + this.size;
+ }
+
+ if (this.disabled) {
+ input.disabled = true;
+ }
+
+ var inputblock = {
+ cls : 'roo-combobox-wrap',
+ cn : [
+ input
+ ]
+ };
+
+ if(this.before){
+ inputblock.cls += ' input-group';
+
+ inputblock.cn.unshift({
+ tag :'span',
+ cls : 'input-group-addon input-group-prepend input-group-text',
+ html : this.before
+ });
+ }
+
+ if(this.removable && !this.multiple){
+ inputblock.cls += ' roo-removable';
+
+ inputblock.cn.push({
+ tag: 'button',
+ html : 'x',
+ cls : 'roo-combo-removable-btn close'
+ });
+ }
+
+ if(this.hasFeedback && !this.allowBlank){
+
+ inputblock.cls += ' has-feedback';
+
+ inputblock.cn.push({
+ tag: 'span',
+ cls: 'glyphicon form-control-feedback'
+ });
+
+ }
+
+ if (this.after) {
+
+ inputblock.cls += (this.before) ? '' : ' input-group';
+
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon input-group-append input-group-text',
+ html : this.after
+ });
+ }
+
+
+ var ibwrap = inputblock;
+
+ if(this.multiple){
+ ibwrap = {
+ tag: 'ul',
+ cls: 'roo-select2-choices',
+ cn:[
+ {
+ tag: 'li',
+ cls: 'roo-select2-search-field',
+ cn: [
+
+ inputblock
+ ]
+ }
+ ]
+ };
+
+
+ }
+
+ var combobox = {
+ cls: 'roo-select2-container input-group roo-touchview-combobox ',
+ cn: [
+ {
+ tag: 'input',
+ type : 'hidden',
+ cls: 'form-hidden-field'
+ },
+ ibwrap
+ ]
+ };
+
+ if(!this.multiple && this.showToggleBtn){
+
+ var caret = {
+ cls: 'caret'
+ };
+
+ if (this.caret != false) {
+ caret = {
+ tag: 'i',
+ cls: 'fa fa-' + this.caret
+ };
+
+ }
+
+ combobox.cn.push({
+ tag :'span',
+ cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
+ cn : [
+ Roo.bootstrap.version == 3 ? caret : '',
+ {
+ tag: 'span',
+ cls: 'combobox-clear',
+ cn : [
+ {
+ tag : 'i',
+ cls: 'icon-remove'
+ }
+ ]
+ }
+ ]
+
+ })
+ }
+
+ if(this.multiple){
+ combobox.cls += ' roo-select2-container-multi';
+ }
+
+ var required = this.allowBlank ? {
+ tag : 'i',
+ style: 'display: none'
+ } : {
+ tag : 'i',
+ cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
+ tooltip : 'This field is required'
+ };
+
+ var align = this.labelAlign || this.parentLabelAlign();
+
+ if (align ==='left' && this.fieldLabel.length) {
+
+ cfg.cn = [
+ required,
+ {
+ tag: 'label',
+ cls : 'control-label col-form-label',
+ html : this.fieldLabel
+
+ },
+ {
+ cls : 'roo-combobox-wrap ',
+ cn: [
+ combobox
+ ]
+ }
+ ];
+
+ var labelCfg = cfg.cn[1];
+ var contentCfg = cfg.cn[2];
+
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label col-form-label',
+ cn : [
+ {
+ tag : 'span',
+ html : this.fieldLabel
+ },
+ required
+ ]
+ },
+ {
+ cls : "roo-combobox-wrap ",
+ cn: [
+ combobox
+ ]
+ }
+
+ ];
+
+ labelCfg = cfg.cn[0];
+ contentCfg = cfg.cn[1];
+ }
+
+
+
+ if(this.labelWidth > 12){
+ labelCfg.style = "width: " + this.labelWidth + 'px';
+ }
+
+ if(this.labelWidth < 13 && this.labelmd == 0){
+ this.labelmd = this.labelWidth;
+ }
+
+ if(this.labellg > 0){
+ labelCfg.cls += ' col-lg-' + this.labellg;
+ contentCfg.cls += ' col-lg-' + (12 - this.labellg);
+ }
+
+ if(this.labelmd > 0){
+ labelCfg.cls += ' col-md-' + this.labelmd;
+ contentCfg.cls += ' col-md-' + (12 - this.labelmd);
+ }
+
+ if(this.labelsm > 0){
+ labelCfg.cls += ' col-sm-' + this.labelsm;
+ contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
+ }
+
+ if(this.labelxs > 0){
+ labelCfg.cls += ' col-xs-' + this.labelxs;
+ contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ }
+
+
+ } else if ( this.fieldLabel.length) {
+ cfg.cn = [
+ required,
+ {
+ tag: 'label',
+ cls : 'control-label',
+ html : this.fieldLabel
+
+ },
+ {
+ cls : '',
+ cn: [
+ combobox
+ ]
+ }
+ ];
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ cls : 'control-label',
+ html : this.fieldLabel,
+ cn : [
+ required
+ ]
+ },
+ {
+ cls : '',
+ cn: [
+ combobox
+ ]
+ }
+ ];
+ }
+ } else {
+ cfg.cn = combobox;
+ }
+
+
+ var settings = this;
+
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ return cfg;
+ },
+
+ initTouchView : function()
+ {
+ this.renderTouchView();
+
+ this.touchViewEl.on('scroll', function(){
+ this.el.dom.scrollTop = 0;
+ }, this);
+
+ this.originalValue = this.getValue();
+
+ this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
+
+ this.inputEl().on("click", this.showTouchView, this);
+ if (this.triggerEl) {
+ this.triggerEl.on("click", this.showTouchView, this);
+ }
+
+
+ this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
+ this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
+
+ this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
+
+ this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
+ this.store.on('load', this.onTouchViewLoad, this);
+ this.store.on('loadexception', this.onTouchViewLoadException, this);
+
+ if(this.hiddenName){
+
+ this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+
+ this.hiddenField.dom.value =
+ this.hiddenValue !== undefined ? this.hiddenValue :
+ this.value !== undefined ? this.value : '';
+
+ this.el.dom.removeAttribute('name');
+ this.hiddenField.dom.setAttribute('name', this.hiddenName);
+ }
+
+ if(this.multiple){
+ this.choices = this.el.select('ul.roo-select2-choices', true).first();
+ this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+ }
+
+ if(this.removable && !this.multiple){
+ var close = this.closeTriggerEl();
+ if(close){
+ close.setVisibilityMode(Roo.Element.DISPLAY).hide();
+ close.on('click', this.removeBtnClick, this, close);
+ }
+ }
+ /*
+ * fix the bug in Safari iOS8
+ */
+ this.inputEl().on("focus", function(e){
+ document.activeElement.blur();
+ }, this);
+
+ this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
+
+ return;
+
+
+ },
+
+ renderTouchView : function()
+ {
+ this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
+ this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
+ this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
+ this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+ this.touchViewBodyEl.setStyle('overflow', 'auto');
+
+ this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
+ this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
+ this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ },
+
+ showTouchView : function()
+ {
+ if(this.disabled){
+ return;
+ }
+
+ this.touchViewHeaderEl.hide();
+
+ if(this.modalTitle.length){
+ this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
+ this.touchViewHeaderEl.show();
+ }
+
+ this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+ this.touchViewEl.show();
+
+ this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
+
+ //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
+ // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
+
+ var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
+
+ if(this.modalTitle.length){
+ bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+ }
+
+ this.touchViewBodyEl.setHeight(bodyHeight);
+
+ if(this.animate){
+ var _this = this;
+ (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
+ }else{
+ this.touchViewEl.addClass(['in','show']);
+ }
+
+ if(this._touchViewMask){
+ Roo.get(document.body).addClass("x-body-masked");
+ this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
+ this._touchViewMask.setStyle('z-index', 10000);
+ this._touchViewMask.addClass('show');
+ }
+
+ this.doTouchViewQuery();
+
+ },
+
+ hideTouchView : function()
+ {
+ this.touchViewEl.removeClass(['in','show']);
+
+ if(this.animate){
+ var _this = this;
+ (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
+ }else{
+ this.touchViewEl.setStyle('display', 'none');
+ }
+
+ if(this._touchViewMask){
+ this._touchViewMask.removeClass('show');
+ Roo.get(document.body).removeClass("x-body-masked");
+ }
+ },
+
+ setTouchViewValue : function()
+ {
+ if(this.multiple){
+ this.clearItem();
+
+ var _this = this;
+
+ Roo.each(this.tickItems, function(o){
+ this.addItem(o);
+ }, this);
+ }
+
+ this.hideTouchView();
+ },
+
+ doTouchViewQuery : function()
+ {
+ var qe = {
+ query: '',
+ forceAll: true,
+ combo: this,
+ cancel:false
+ };
+
+ if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
+ return false;
+ }
+
+ if(!this.alwaysQuery || this.mode == 'local'){
+ this.onTouchViewLoad();
+ return;
+ }
+
+ this.store.load();
+ },
+
+ onTouchViewBeforeLoad : function(combo,opts)
+ {
+ return;
+ },
+
+ // private
+ onTouchViewLoad : function()
+ {
+ if(this.store.getCount() < 1){
+ this.onTouchViewEmptyResults();
+ return;
+ }
+
+ this.clearTouchView();
+
+ var rawValue = this.getRawValue();
+
+ var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
+
+ this.tickItems = [];
+
+ this.store.data.each(function(d, rowIndex){
+ var row = this.touchViewListGroup.createChild(template);
+
+ if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
+ row.addClass(d.data.cls);
+ }
+
+ if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
+ var cfg = {
+ data : d.data,
+ html : d.data[this.displayField]
+ };
+
+ if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
+ row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
+ }
+ }
+ row.removeClass('selected');
+ if(!this.multiple && this.valueField &&
+ typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
+ {
+ // radio buttons..
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+ row.addClass('selected');
+ }
+
+ if(this.multiple && this.valueField &&
+ typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
+ {
+
+ // checkboxes...
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+ this.tickItems.push(d.data);
+ }
+
+ row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
+
+ }, this);
+
+ var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
+
+ var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
+
+ if(this.modalTitle.length){
+ bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+ }
+
+ var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
+
+ if(this.mobile_restrict_height && listHeight < bodyHeight){
+ this.touchViewBodyEl.setHeight(listHeight);
+ }
+
+ var _this = this;
+
+ if(firstChecked && listHeight > bodyHeight){
+ (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
+ }
+
+ },
+
+ onTouchViewLoadException : function()
+ {
+ this.hideTouchView();
+ },
+
+ onTouchViewEmptyResults : function()
+ {
+ this.clearTouchView();
+
+ this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
+
+ this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
+
+ },
+
+ clearTouchView : function()
+ {
+ this.touchViewListGroup.dom.innerHTML = '';
+ },
+
+ onTouchViewClick : function(e, el, o)
+ {
+ e.preventDefault();
+
+ var row = o.row;
+ var rowIndex = o.rowIndex;
+
+ var r = this.store.getAt(rowIndex);
+
+ if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
+
+ if(!this.multiple){
+ Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
+ c.dom.removeAttribute('checked');
+ }, this);
+
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+
+ this.setFromData(r.data);
+
+ var close = this.closeTriggerEl();
+
+ if(close){
+ close.show();
+ }
+
+ this.hideTouchView();
+
+ this.fireEvent('select', this, r, rowIndex);
+
+ return;
+ }
+
+ if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
+ row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
+ this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
+ return;
+ }
+
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+ this.addItem(r.data);
+ this.tickItems.push(r.data);
+ }
+ },
+
+ getAutoCreateNativeIOS : function()
+ {
+ var cfg = {
+ cls: 'form-group' //input-group,
+ };
+
+ var combobox = {
+ tag: 'select',
+ cls : 'roo-ios-select'
+ };
+
+ if (this.name) {
+ combobox.name = this.name;
+ }
+
+ if (this.disabled) {
+ combobox.disabled = true;
+ }
+
+ var settings = this;
+
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ cfg.cn = combobox;
+
+ return cfg;
+
+ },
+
+ initIOSView : function()
+ {
+ this.store.on('load', this.onIOSViewLoad, this);
+
+ return;
+ },
+
+ onIOSViewLoad : function()
+ {
+ if(this.store.getCount() < 1){
+ return;
+ }
+
+ this.clearIOSView();
+
+ if(this.allowBlank) {
+
+ var default_text = '-- SELECT --';
+
+ if(this.placeholder.length){
+ default_text = this.placeholder;
+ }
+
+ if(this.emptyTitle.length){
+ default_text += ' - ' + this.emptyTitle + ' -';
+ }
+
+ var opt = this.inputEl().createChild({
+ tag: 'option',
+ value : 0,
+ html : default_text
+ });
+
+ var o = {};
+ o[this.valueField] = 0;
+ o[this.displayField] = default_text;
+
+ this.ios_options.push({
+ data : o,
+ el : opt
+ });
+
+ }
+
+ this.store.data.each(function(d, rowIndex){
+
+ var html = '';
+
+ if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
+ html = d.data[this.displayField];
+ }
+
+ var value = '';
+
+ if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
+ value = d.data[this.valueField];
+ }
+
+ var option = {
+ tag: 'option',
+ value : value,
+ html : html
+ };
+
+ if(this.value == d.data[this.valueField]){
+ option['selected'] = true;
+ }
+
+ var opt = this.inputEl().createChild(option);
+
+ this.ios_options.push({
+ data : d.data,
+ el : opt
+ });
+
+ }, this);
+
+ this.inputEl().on('change', function(){
+ this.fireEvent('select', this);
+ }, this);
+
+ },
+
+ clearIOSView: function()
+ {
+ this.inputEl().dom.innerHTML = '';
+
+ this.ios_options = [];
+ },
+
+ setIOSValue: function(v)
+ {
+ this.value = v;
+
+ if(!this.ios_options){
+ return;
+ }
+
+ Roo.each(this.ios_options, function(opts){
+
+ opts.el.dom.removeAttribute('selected');
+
+ if(opts.data[this.valueField] != v){
+ return;
+ }
+
+ opts.el.dom.setAttribute('selected', true);
+
+ }, this);
+ }
+
+ /**
+ * @cfg {Boolean} grow
+ * @hide
+ */
+ /**
+ * @cfg {Number} growMin
+ * @hide
+ */
+ /**
+ * @cfg {Number} growMax
+ * @hide
+ */
+ /**
+ * @hide
+ * @method autoSize
+ */
+});
+
+Roo.apply(Roo.bootstrap.form.ComboBox, {
+
+ header : {
+ tag: 'div',
+ cls: 'modal-header',
+ cn: [
+ {
+ tag: 'h4',
+ cls: 'modal-title'
+ }
+ ]
+ },
+
+ body : {
+ tag: 'div',
+ cls: 'modal-body',
+ cn: [
+ {
+ tag: 'ul',
+ cls: 'list-group'
+ }
+ ]
+ },
+
+ listItemRadio : {
+ tag: 'li',
+ cls: 'list-group-item',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'roo-combobox-list-group-item-value'
+ },
+ {
+ tag: 'div',
+ cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
+ cn: [
+ {
+ tag: 'input',
+ type: 'radio'
+ },
+ {
+ tag: 'label'
+ }
+ ]
+ }
+ ]
+ },
+
+ listItemCheckbox : {
+ tag: 'li',
+ cls: 'list-group-item',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'roo-combobox-list-group-item-value'
+ },
+ {
+ tag: 'div',
+ cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
+ cn: [
+ {
+ tag: 'input',
+ type: 'checkbox'
+ },
+ {
+ tag: 'label'
+ }
+ ]
+ }
+ ]
+ },
+
+ emptyResult : {
+ tag: 'div',
+ cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
+ },
+
+ footer : {
+ tag: 'div',
+ cls: 'modal-footer',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'row',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'col-xs-6 text-left',
+ cn: {
+ tag: 'button',
+ cls: 'btn btn-danger roo-touch-view-cancel',
+ html: 'Cancel'
+ }
+ },
+ {
+ tag: 'div',
+ cls: 'col-xs-6 text-right',
+ cn: {
+ tag: 'button',
+ cls: 'btn btn-success roo-touch-view-ok',
+ html: 'OK'
+ }
+ }
+ ]
+ }
+ ]
+
+ }
+});
+
+Roo.apply(Roo.bootstrap.form.ComboBox, {
+
+ touchViewTemplate : {
+ tag: 'div',
+ cls: 'modal fade roo-combobox-touch-view',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'modal-dialog',
+ style : 'position:fixed', // we have to fix position....
+ cn: [
+ {
+ tag: 'div',
+ cls: 'modal-content',
+ cn: [
+ Roo.bootstrap.form.ComboBox.header,
+ Roo.bootstrap.form.ComboBox.body,
+ Roo.bootstrap.form.ComboBox.footer
+ ]
+ }
+ ]
+ }
+ ]
+ }
+});/*
+ * 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.View
+ * @extends Roo.util.Observable
+ * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
+ * This class also supports single and multi selection modes. <br>
+ * Create a data model bound view:
+ <pre><code>
+ var store = new Roo.data.Store(...);
+
+ var view = new Roo.View({
+ el : "my-element",
+ tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
+
+ singleSelect: true,
+ selectedClass: "ydataview-selected",
+ store: store
+ });
+
+ // listen for node click?
+ view.on("click", function(vw, index, node, e){
+ alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
+ });
+
+ // load XML data
+ dataModel.load("foobar.xml");
+ </code></pre>
+ For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
+ * <br><br>
+ * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
+ * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
+ *
+ * Note: old style constructor is still suported (container, template, config)
+ *
+ * @constructor
+ * Create a new View
+ * @param {Object} config The config object
+ *
+ */
+Roo.View = function(config, depreciated_tpl, depreciated_config){
+
+ this.parent = false;
+
+ if (typeof(depreciated_tpl) == 'undefined') {
+ // new way.. - universal constructor.
+ Roo.apply(this, config);
+ this.el = Roo.get(this.el);
+ } else {
+ // old format..
+ this.el = Roo.get(config);
+ this.tpl = depreciated_tpl;
+ Roo.apply(this, depreciated_config);
+ }
+ this.wrapEl = this.el.wrap().wrap();
+ ///this.el = this.wrapEla.appendChild(document.createElement("div"));
+
+
+ if(typeof(this.tpl) == "string"){
+ this.tpl = new Roo.Template(this.tpl);
+ } else {
+ // support xtype ctors..
+ this.tpl = new Roo.factory(this.tpl, Roo);
+ }
+
+
+ this.tpl.compile();
+
+ /** @private */
+ this.addEvents({
+ /**
+ * @event beforeclick
+ * Fires before a click is processed. Returns false to cancel the default action.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "beforeclick" : true,
+ /**
+ * @event click
+ * Fires when a template node is clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "click" : true,
+ /**
+ * @event dblclick
+ * Fires when a template node is double clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "dblclick" : true,
+ /**
+ * @event contextmenu
+ * Fires when a template node is right clicked.
+ * @param {Roo.View} this
+ * @param {Number} index The index of the target node
+ * @param {HTMLElement} node The target node
+ * @param {Roo.EventObject} e The raw event object
+ */
+ "contextmenu" : true,
+ /**
+ * @event selectionchange
+ * Fires when the selected nodes change.
+ * @param {Roo.View} this
+ * @param {Array} selections Array of the selected nodes
+ */
+ "selectionchange" : true,
+
+ /**
+ * @event beforeselect
+ * Fires before a selection is made. If any handlers return false, the selection is cancelled.
+ * @param {Roo.View} this
+ * @param {HTMLElement} node The node to be selected
+ * @param {Array} selections Array of currently selected nodes
+ */
+ "beforeselect" : true,
+ /**
+ * @event preparedata
+ * Fires on every row to render, to allow you to change the data.
+ * @param {Roo.View} this
+ * @param {Object} data to be rendered (change this)
+ */
+ "preparedata" : true
+
+
+ });
+
+
+
+ this.el.on({
+ "click": this.onClick,
+ "dblclick": this.onDblClick,
+ "contextmenu": this.onContextMenu,
+ scope:this
+ });
+
+ this.selections = [];
+ this.nodes = [];
+ this.cmp = new Roo.CompositeElementLite([]);
+ if(this.store){
+ this.store = Roo.factory(this.store, Roo.data);
+ this.setStore(this.store, true);
+ }
+
+ if ( this.footer && this.footer.xtype) {
+
+ var fctr = this.wrapEl.appendChild(document.createElement("div"));
- var ibwrap = inputblock;
-
- if(this.multiple){
- ibwrap = {
- tag: 'ul',
- cls: 'roo-select2-choices',
- cn:[
- {
- tag: 'li',
- cls: 'roo-select2-search-field',
- cn: [
+ this.footer.dataSource = this.store;
+ this.footer.container = fctr;
+ this.footer = Roo.factory(this.footer, Roo);
+ fctr.insertFirst(this.el);
+
+ // this is a bit insane - as the paging toolbar seems to detach the el..
+// dom.parentNode.parentNode.parentNode
+ // they get detached?
+ }
+
+
+ Roo.View.superclass.constructor.call(this);
+
+
+};
- inputblock
- ]
- }
- ]
- };
+Roo.extend(Roo.View, Roo.util.Observable, {
+
+ /**
+ * @cfg {Roo.data.Store} store Data store to load data from.
+ */
+ store : false,
+
+ /**
+ * @cfg {String|Roo.Element} el The container element.
+ */
+ el : '',
+
+ /**
+ * @cfg {String|Roo.Template} tpl The template used by this View
+ */
+ tpl : false,
+ /**
+ * @cfg {String} dataName the named area of the template to use as the data area
+ * Works with domtemplates roo-name="name"
+ */
+ dataName: false,
+ /**
+ * @cfg {String} selectedClass The css class to add to selected nodes
+ */
+ selectedClass : "x-view-selected",
+ /**
+ * @cfg {String} emptyText The empty text to show when nothing is loaded.
+ */
+ emptyText : "",
+
+ /**
+ * @cfg {String} text to display on mask (default Loading)
+ */
+ mask : false,
+ /**
+ * @cfg {Boolean} multiSelect Allow multiple selection
+ */
+ multiSelect : false,
+ /**
+ * @cfg {Boolean} singleSelect Allow single selection
+ */
+ singleSelect: false,
+
+ /**
+ * @cfg {Boolean} toggleSelect - selecting
+ */
+ toggleSelect : false,
+
+ /**
+ * @cfg {Boolean} tickable - selecting
+ */
+ tickable : false,
+
+ /**
+ * Returns the element this view is bound to.
+ * @return {Roo.Element}
+ */
+ getEl : function(){
+ return this.wrapEl;
+ },
+
+
+
+ /**
+ * Refreshes the view. - called by datachanged on the store. - do not call directly.
+ */
+ refresh : function(){
+ //Roo.log('refresh');
+ var t = this.tpl;
+ // if we are using something like 'domtemplate', then
+ // the what gets used is:
+ // t.applySubtemplate(NAME, data, wrapping data..)
+ // the outer template then get' applied with
+ // the store 'extra data'
+ // and the body get's added to the
+ // roo-name="data" node?
+ // <span class='roo-tpl-{name}'></span> ?????
+
+
+
+ this.clearSelections();
+ this.el.update("");
+ var html = [];
+ var records = this.store.getRange();
+ if(records.length < 1) {
+ // is this valid?? = should it render a template??
+
+ this.el.update(this.emptyText);
+ return;
+ }
+ var el = this.el;
+ if (this.dataName) {
+ this.el.update(t.apply(this.store.meta)); //????
+ el = this.el.child('.roo-tpl-' + this.dataName);
}
- var combobox = {
- cls: 'roo-select2-container input-group roo-touchview-combobox ',
- cn: [
- {
- tag: 'input',
- type : 'hidden',
- cls: 'form-hidden-field'
- },
- ibwrap
- ]
- };
-
- if(!this.multiple && this.showToggleBtn){
+ for(var i = 0, len = records.length; i < len; i++){
+ var data = this.prepareData(records[i].data, i, records[i]);
+ this.fireEvent("preparedata", this, data, i, records[i]);
- var caret = {
- tag: 'span',
- cls: 'caret'
- };
+ var d = Roo.apply({}, data);
- if (this.caret != false) {
- caret = {
- tag: 'i',
- cls: 'fa fa-' + this.caret
- };
+ if(this.tickable){
+ Roo.apply(d, {'roo-id' : Roo.id()});
- }
+ var _this = this;
- combobox.cn.push({
- tag :'span',
- cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
- cn : [
- caret,
- {
- tag: 'span',
- cls: 'combobox-clear',
- cn : [
- {
- tag : 'i',
- cls: 'icon-remove'
- }
- ]
+ Roo.each(this.parent.item, function(item){
+ if(item[_this.parent.valueField] != data[_this.parent.valueField]){
+ return;
}
- ]
-
- })
+ Roo.apply(d, {'roo-data-checked' : 'checked'});
+ });
+ }
+
+ html[html.length] = Roo.util.Format.trim(
+ this.dataName ?
+ t.applySubtemplate(this.dataName, d, this.store.meta) :
+ t.apply(d)
+ );
}
- if(this.multiple){
- combobox.cls += ' roo-select2-container-multi';
- }
- var align = this.labelAlign || this.parentLabelAlign();
- if (align ==='left' && this.fieldLabel.length) {
-
- cfg.cn = [
- {
- tag : 'i',
- cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- },
- {
- tag: 'label',
- cls : 'control-label col-form-label',
- html : this.fieldLabel
+ el.update(html.join(""));
+ this.nodes = el.dom.childNodes;
+ this.updateIndexes(0);
+ },
+
- },
- {
- cls : '',
- cn: [
- combobox
- ]
- }
- ];
-
- var labelCfg = cfg.cn[1];
- var contentCfg = cfg.cn[2];
-
+ /**
+ * Function to override to reformat the data that is sent to
+ * the template for each node.
+ * DEPRICATED - use the preparedata event handler.
+ * @param {Array/Object} data The raw data (array of colData for a data model bound view or
+ * a JSON object for an UpdateManager bound view).
+ */
+ prepareData : function(data, index, record)
+ {
+ this.fireEvent("preparedata", this, data, index, record);
+ return data;
+ },
- if(this.indicatorpos == 'right'){
- cfg.cn = [
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label col-form-label',
- cn : [
- {
- tag : 'span',
- html : this.fieldLabel
- },
- {
- tag : 'i',
- cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- }
- ]
- },
- {
- cls : "",
- cn: [
- combobox
- ]
- }
+ onUpdate : function(ds, record){
+ // Roo.log('on update');
+ this.clearSelections();
+ var index = this.store.indexOf(record);
+ var n = this.nodes[index];
+ this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
+ n.parentNode.removeChild(n);
+ this.updateIndexes(index, index);
+ },
- ];
+
+
+// --------- FIXME
+ onAdd : function(ds, records, index)
+ {
+ //Roo.log(['on Add', ds, records, index] );
+ this.clearSelections();
+ if(this.nodes.length == 0){
+ this.refresh();
+ return;
+ }
+ var n = this.nodes[index];
+ for(var i = 0, len = records.length; i < len; i++){
+ var d = this.prepareData(records[i].data, i, records[i]);
+ if(n){
+ this.tpl.insertBefore(n, d);
+ }else{
- labelCfg = cfg.cn[0];
- contentCfg = cfg.cn[1];
- }
-
-
-
- if(this.labelWidth > 12){
- labelCfg.style = "width: " + this.labelWidth + 'px';
- }
-
- if(this.labelWidth < 13 && this.labelmd == 0){
- this.labelmd = this.labelWidth;
- }
-
- if(this.labellg > 0){
- labelCfg.cls += ' col-lg-' + this.labellg;
- contentCfg.cls += ' col-lg-' + (12 - this.labellg);
- }
-
- if(this.labelmd > 0){
- labelCfg.cls += ' col-md-' + this.labelmd;
- contentCfg.cls += ' col-md-' + (12 - this.labelmd);
- }
-
- if(this.labelsm > 0){
- labelCfg.cls += ' col-sm-' + this.labelsm;
- contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
- }
-
- if(this.labelxs > 0){
- labelCfg.cls += ' col-xs-' + this.labelxs;
- contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
+ this.tpl.append(this.el, d);
}
-
-
- } else if ( this.fieldLabel.length) {
- cfg.cn = [
- {
- tag : 'i',
- cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- },
- {
- tag: 'label',
- cls : 'control-label',
- html : this.fieldLabel
+ }
+ this.updateIndexes(index);
+ },
- },
- {
- cls : '',
- cn: [
- combobox
- ]
- }
- ];
-
- if(this.indicatorpos == 'right'){
- cfg.cn = [
- {
- tag: 'label',
- cls : 'control-label',
- html : this.fieldLabel,
- cn : [
- {
- tag : 'i',
- cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- }
- ]
- },
- {
- cls : '',
- cn: [
- combobox
- ]
- }
- ];
+ onRemove : function(ds, record, index){
+ // Roo.log('onRemove');
+ this.clearSelections();
+ var el = this.dataName ?
+ this.el.child('.roo-tpl-' + this.dataName) :
+ this.el;
+
+ el.dom.removeChild(this.nodes[index]);
+ this.updateIndexes(index);
+ },
+
+ /**
+ * Refresh an individual node.
+ * @param {Number} index
+ */
+ refreshNode : function(index){
+ this.onUpdate(this.store, this.store.getAt(index));
+ },
+
+ updateIndexes : function(startIndex, endIndex){
+ var ns = this.nodes;
+ startIndex = startIndex || 0;
+ endIndex = endIndex || ns.length - 1;
+ for(var i = startIndex; i <= endIndex; i++){
+ ns[i].nodeIndex = i;
+ }
+ },
+
+ /**
+ * Changes the data store this view uses and refresh the view.
+ * @param {Store} store
+ */
+ setStore : function(store, initial){
+ if(!initial && this.store){
+ this.store.un("datachanged", this.refresh);
+ this.store.un("add", this.onAdd);
+ this.store.un("remove", this.onRemove);
+ this.store.un("update", this.onUpdate);
+ this.store.un("clear", this.refresh);
+ this.store.un("beforeload", this.onBeforeLoad);
+ this.store.un("load", this.onLoad);
+ this.store.un("loadexception", this.onLoad);
+ }
+ if(store){
+
+ store.on("datachanged", this.refresh, this);
+ store.on("add", this.onAdd, this);
+ store.on("remove", this.onRemove, this);
+ store.on("update", this.onUpdate, this);
+ store.on("clear", this.refresh, this);
+ store.on("beforeload", this.onBeforeLoad, this);
+ store.on("load", this.onLoad, this);
+ store.on("loadexception", this.onLoad, this);
+ }
+
+ if(store){
+ this.refresh();
+ }
+ },
+ /**
+ * onbeforeLoad - masks the loading area.
+ *
+ */
+ onBeforeLoad : function(store,opts)
+ {
+ //Roo.log('onBeforeLoad');
+ if (!opts.add) {
+ this.el.update("");
+ }
+ this.el.mask(this.mask ? this.mask : "Loading" );
+ },
+ onLoad : function ()
+ {
+ this.el.unmask();
+ },
+
+
+ /**
+ * Returns the template node the passed child belongs to or null if it doesn't belong to one.
+ * @param {HTMLElement} node
+ * @return {HTMLElement} The template node
+ */
+ findItemFromChild : function(node){
+ var el = this.dataName ?
+ this.el.child('.roo-tpl-' + this.dataName,true) :
+ this.el.dom;
+
+ if(!node || node.parentNode == el){
+ return node;
+ }
+ var p = node.parentNode;
+ while(p && p != el){
+ if(p.parentNode == el){
+ return p;
}
- } else {
- cfg.cn = combobox;
+ p = p.parentNode;
}
-
-
- var settings = this;
-
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
+ return null;
+ },
+
+ /** @ignore */
+ onClick : function(e){
+ var item = this.findItemFromChild(e.getTarget());
+ if(item){
+ var index = this.indexOf(item);
+ if(this.onItemClick(item, index, e) !== false){
+ this.fireEvent("click", this, index, item, e);
}
- });
-
- return cfg;
+ }else{
+ this.clearSelections();
+ }
},
-
- initTouchView : function()
+
+ /** @ignore */
+ onContextMenu : function(e){
+ var item = this.findItemFromChild(e.getTarget());
+ if(item){
+ this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
+ }
+ },
+
+ /** @ignore */
+ onDblClick : function(e){
+ var item = this.findItemFromChild(e.getTarget());
+ if(item){
+ this.fireEvent("dblclick", this, this.indexOf(item), item, e);
+ }
+ },
+
+ onItemClick : function(item, index, e)
{
- this.renderTouchView();
-
- this.touchViewEl.on('scroll', function(){
- this.el.dom.scrollTop = 0;
- }, this);
-
- this.originalValue = this.getValue();
-
- this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
-
- this.inputEl().on("click", this.showTouchView, this);
- if (this.triggerEl) {
- this.triggerEl.on("click", this.showTouchView, this);
+ if(this.fireEvent("beforeclick", this, index, item, e) === false){
+ return false;
}
-
-
- this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
- this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
-
- this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
-
- this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
- this.store.on('load', this.onTouchViewLoad, this);
- this.store.on('loadexception', this.onTouchViewLoadException, this);
-
- if(this.hiddenName){
+ if (this.toggleSelect) {
+ var m = this.isSelected(item) ? 'unselect' : 'select';
+ //Roo.log(m);
+ var _t = this;
+ _t[m](item, true, false);
+ return true;
+ }
+ if(this.multiSelect || this.singleSelect){
+ if(this.multiSelect && e.shiftKey && this.lastSelection){
+ this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
+ }else{
+ this.select(item, this.multiSelect && e.ctrlKey);
+ this.lastSelection = item;
+ }
- this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+ if(!this.tickable){
+ e.preventDefault();
+ }
- this.hiddenField.dom.value =
- this.hiddenValue !== undefined ? this.hiddenValue :
- this.value !== undefined ? this.value : '';
-
- this.el.dom.removeAttribute('name');
- this.hiddenField.dom.setAttribute('name', this.hiddenName);
}
-
- if(this.multiple){
- this.choices = this.el.select('ul.roo-select2-choices', true).first();
- this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
+ return true;
+ },
+
+ /**
+ * Get the number of selected nodes.
+ * @return {Number}
+ */
+ getSelectionCount : function(){
+ return this.selections.length;
+ },
+
+ /**
+ * Get the currently selected nodes.
+ * @return {Array} An array of HTMLElements
+ */
+ getSelectedNodes : function(){
+ return this.selections;
+ },
+
+ /**
+ * Get the indexes of the selected nodes.
+ * @return {Array}
+ */
+ getSelectedIndexes : function(){
+ var indexes = [], s = this.selections;
+ for(var i = 0, len = s.length; i < len; i++){
+ indexes.push(s[i].nodeIndex);
}
-
- if(this.removable && !this.multiple){
- var close = this.closeTriggerEl();
- if(close){
- close.setVisibilityMode(Roo.Element.DISPLAY).hide();
- close.on('click', this.removeBtnClick, this, close);
+ return indexes;
+ },
+
+ /**
+ * Clear all selections
+ * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
+ */
+ clearSelections : function(suppressEvent){
+ if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
+ this.cmp.elements = this.selections;
+ this.cmp.removeClass(this.selectedClass);
+ this.selections = [];
+ if(!suppressEvent){
+ this.fireEvent("selectionchange", this, this.selections);
}
}
- /*
- * fix the bug in Safari iOS8
- */
- this.inputEl().on("focus", function(e){
- document.activeElement.blur();
- }, this);
-
- this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
-
- return;
-
-
},
-
- renderTouchView : function()
- {
- this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
- this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-
- this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
- this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-
- this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
- this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- this.touchViewBodyEl.setStyle('overflow', 'auto');
+
+ /**
+ * Returns true if the passed node is selected
+ * @param {HTMLElement/Number} node The node or node index
+ * @return {Boolean}
+ */
+ isSelected : function(node){
+ var s = this.selections;
+ if(s.length < 1){
+ return false;
+ }
+ node = this.getNode(node);
+ return s.indexOf(node) !== -1;
+ },
+
+ /**
+ * Selects nodes.
+ * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
+ * @param {Boolean} keepExisting (optional) true to keep existing selections
+ * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+ */
+ select : function(nodeInfo, keepExisting, suppressEvent){
+ if(nodeInfo instanceof Array){
+ if(!keepExisting){
+ this.clearSelections(true);
+ }
+ for(var i = 0, len = nodeInfo.length; i < len; i++){
+ this.select(nodeInfo[i], true, true);
+ }
+ return;
+ }
+ var node = this.getNode(nodeInfo);
+ if(!node || this.isSelected(node)){
+ return; // already selected.
+ }
+ if(!keepExisting){
+ this.clearSelections(true);
+ }
- this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
- this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+ if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
+ Roo.fly(node).addClass(this.selectedClass);
+ this.selections.push(node);
+ if(!suppressEvent){
+ this.fireEvent("selectionchange", this, this.selections);
+ }
+ }
- this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
- this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
},
-
- showTouchView : function()
+ /**
+ * Unselects nodes.
+ * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
+ * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
+ * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
+ */
+ unselect : function(nodeInfo, keepExisting, suppressEvent)
{
- if(this.disabled){
+ if(nodeInfo instanceof Array){
+ Roo.each(this.selections, function(s) {
+ this.unselect(s, nodeInfo);
+ }, this);
return;
}
-
- this.touchViewHeaderEl.hide();
-
- if(this.modalTitle.length){
- this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
- this.touchViewHeaderEl.show();
+ var node = this.getNode(nodeInfo);
+ if(!node || !this.isSelected(node)){
+ //Roo.log("not selected");
+ return; // not selected.
}
+ // fireevent???
+ var ns = [];
+ Roo.each(this.selections, function(s) {
+ if (s == node ) {
+ Roo.fly(node).removeClass(this.selectedClass);
- this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
- this.touchViewEl.show();
-
- this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
+ return;
+ }
+ ns.push(s);
+ },this);
- //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
- // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
-
- var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
+ this.selections= ns;
+ this.fireEvent("selectionchange", this, this.selections);
+ },
- if(this.modalTitle.length){
- bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+ /**
+ * Gets a template node.
+ * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
+ * @return {HTMLElement} The node or null if it wasn't found
+ */
+ getNode : function(nodeInfo){
+ if(typeof nodeInfo == "string"){
+ return document.getElementById(nodeInfo);
+ }else if(typeof nodeInfo == "number"){
+ return this.nodes[nodeInfo];
}
-
- this.touchViewBodyEl.setHeight(bodyHeight);
+ return nodeInfo;
+ },
- if(this.animate){
- var _this = this;
- (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
- }else{
- this.touchViewEl.addClass('in');
- }
-
- if(this._touchViewMask){
- Roo.get(document.body).addClass("x-body-masked");
- this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
- this._touchViewMask.setStyle('z-index', 10000);
- this._touchViewMask.addClass('show');
+ /**
+ * Gets a range template nodes.
+ * @param {Number} startIndex
+ * @param {Number} endIndex
+ * @return {Array} An array of nodes
+ */
+ getNodes : function(start, end){
+ var ns = this.nodes;
+ start = start || 0;
+ end = typeof end == "undefined" ? ns.length - 1 : end;
+ var nodes = [];
+ if(start <= end){
+ for(var i = start; i <= end; i++){
+ nodes.push(ns[i]);
+ }
+ } else{
+ for(var i = start; i >= end; i--){
+ nodes.push(ns[i]);
+ }
}
-
- this.doTouchViewQuery();
-
+ return nodes;
},
-
- hideTouchView : function()
- {
- this.touchViewEl.removeClass('in');
- if(this.animate){
- var _this = this;
- (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
- }else{
- this.touchViewEl.setStyle('display', 'none');
+ /**
+ * Finds the index of the passed node
+ * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
+ * @return {Number} The index of the node or -1
+ */
+ indexOf : function(node){
+ node = this.getNode(node);
+ if(typeof node.nodeIndex == "number"){
+ return node.nodeIndex;
}
-
- if(this._touchViewMask){
- this._touchViewMask.removeClass('show');
- Roo.get(document.body).removeClass("x-body-masked");
+ var ns = this.nodes;
+ for(var i = 0, len = ns.length; i < len; i++){
+ if(ns[i] == node){
+ return i;
+ }
}
- },
-
- setTouchViewValue : function()
- {
- if(this.multiple){
- this.clearItem();
-
- var _this = this;
+ return -1;
+ }
+});
+/*
+ * - LGPL
+ *
+ * based on jquery fullcalendar
+ *
+ */
- Roo.each(this.tickItems, function(o){
- this.addItem(o);
- }, this);
- }
+Roo.bootstrap = Roo.bootstrap || {};
+/**
+ * @class Roo.bootstrap.Calendar
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Calendar class
+ * @cfg {Boolean} loadMask (true|false) default false
+ * @cfg {Object} header generate the user specific header of the calendar, default false
+
+ * @constructor
+ * Create a new Container
+ * @param {Object} config The config object
+ */
+
+
+
+Roo.bootstrap.Calendar = function(config){
+ Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event select
+ * Fires when a date is selected
+ * @param {DatePicker} this
+ * @param {Date} date The selected date
+ */
+ 'select': true,
+ /**
+ * @event monthchange
+ * Fires when the displayed month changes
+ * @param {DatePicker} this
+ * @param {Date} date The selected month
+ */
+ 'monthchange': true,
+ /**
+ * @event evententer
+ * Fires when mouse over an event
+ * @param {Calendar} this
+ * @param {event} Event
+ */
+ 'evententer': true,
+ /**
+ * @event eventleave
+ * Fires when the mouse leaves an
+ * @param {Calendar} this
+ * @param {event}
+ */
+ 'eventleave': true,
+ /**
+ * @event eventclick
+ * Fires when the mouse click an
+ * @param {Calendar} this
+ * @param {event}
+ */
+ 'eventclick': true
- this.hideTouchView();
- },
+ });
+
+};
+
+Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
- doTouchViewQuery : function()
- {
- var qe = {
- query: '',
- forceAll: true,
- combo: this,
- cancel:false
- };
+ /**
+ * @cfg {Roo.data.Store} store
+ * The data source for the calendar
+ */
+ store : false,
+ /**
+ * @cfg {Number} startDay
+ * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
+ */
+ startDay : 0,
+
+ loadMask : false,
+
+ header : false,
+
+ getAutoCreate : function(){
- if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
- return false;
- }
- if(!this.alwaysQuery || this.mode == 'local'){
- this.onTouchViewLoad();
- return;
- }
+ var fc_button = function(name, corner, style, content ) {
+ return Roo.apply({},{
+ tag : 'span',
+ cls : 'fc-button fc-button-'+name+' fc-state-default ' +
+ (corner.length ?
+ 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
+ ''
+ ),
+ html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
+ unselectable: 'on'
+ });
+ };
- this.store.load();
- },
-
- onTouchViewBeforeLoad : function(combo,opts)
- {
- return;
- },
+ var header = {};
+
+ if(!this.header){
+ header = {
+ tag : 'table',
+ cls : 'fc-header',
+ style : 'width:100%',
+ cn : [
+ {
+ tag: 'tr',
+ cn : [
+ {
+ tag : 'td',
+ cls : 'fc-header-left',
+ cn : [
+ fc_button('prev', 'left', 'arrow', '‹' ),
+ fc_button('next', 'right', 'arrow', '›' ),
+ { tag: 'span', cls: 'fc-header-space' },
+ fc_button('today', 'left right', '', 'today' ) // neds state disabled..
+
+
+ ]
+ },
+
+ {
+ tag : 'td',
+ cls : 'fc-header-center',
+ cn : [
+ {
+ tag: 'span',
+ cls: 'fc-header-title',
+ cn : {
+ tag: 'H2',
+ html : 'month / year'
+ }
+ }
- // private
- onTouchViewLoad : function()
- {
- if(this.store.getCount() < 1){
- this.onTouchViewEmptyResults();
- return;
+ ]
+ },
+ {
+ tag : 'td',
+ cls : 'fc-header-right',
+ cn : [
+ /* fc_button('month', 'left', '', 'month' ),
+ fc_button('week', '', '', 'week' ),
+ fc_button('day', 'right', '', 'day' )
+ */
+
+ ]
+ }
+
+ ]
+ }
+ ]
+ };
}
- this.clearTouchView();
-
- var rawValue = this.getRawValue();
-
- var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
-
- this.tickItems = [];
+ header = this.header;
- this.store.data.each(function(d, rowIndex){
- var row = this.touchViewListGroup.createChild(template);
+
+ var cal_heads = function() {
+ var ret = [];
+ // fixme - handle this.
- if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
- row.addClass(d.data.cls);
+ for (var i =0; i < Date.dayNames.length; i++) {
+ var d = Date.dayNames[i];
+ ret.push({
+ tag: 'th',
+ cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
+ html : d.substring(0,3)
+ });
+
+ }
+ ret[0].cls += ' fc-first';
+ ret[6].cls += ' fc-last';
+ return ret;
+ };
+ var cal_cell = function(n) {
+ return {
+ tag: 'td',
+ cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
+ cn : [
+ {
+ cn : [
+ {
+ cls: 'fc-day-number',
+ html: 'D'
+ },
+ {
+ cls: 'fc-day-content',
+
+ cn : [
+ {
+ style: 'position: relative;' // height: 17px;
+ }
+ ]
+ }
+
+
+ ]
+ }
+ ]
+
}
+ };
+ var cal_rows = function() {
- if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
- var cfg = {
- data : d.data,
- html : d.data[this.displayField]
+ var ret = [];
+ for (var r = 0; r < 6; r++) {
+ var row= {
+ tag : 'tr',
+ cls : 'fc-week',
+ cn : []
};
- if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
- row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
+ for (var i =0; i < Date.dayNames.length; i++) {
+ var d = Date.dayNames[i];
+ row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
+
}
- }
- row.removeClass('selected');
- if(!this.multiple && this.valueField &&
- typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
- {
- // radio buttons..
- row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
- row.addClass('selected');
- }
-
- if(this.multiple && this.valueField &&
- typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
- {
+ row.cn[0].cls+=' fc-first';
+ row.cn[0].cn[0].style = 'min-height:90px';
+ row.cn[6].cls+=' fc-last';
+ ret.push(row);
- // checkboxes...
- row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
- this.tickItems.push(d.data);
}
+ ret[0].cls += ' fc-first';
+ ret[4].cls += ' fc-prev-last';
+ ret[5].cls += ' fc-last';
+ return ret;
- row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
+ };
+
+ var cal_table = {
+ tag: 'table',
+ cls: 'fc-border-separate',
+ style : 'width:100%',
+ cellspacing : 0,
+ cn : [
+ {
+ tag: 'thead',
+ cn : [
+ {
+ tag: 'tr',
+ cls : 'fc-first fc-last',
+ cn : cal_heads()
+ }
+ ]
+ },
+ {
+ tag: 'tbody',
+ cn : cal_rows()
+ }
+
+ ]
+ };
+
+ var cfg = {
+ cls : 'fc fc-ltr',
+ cn : [
+ header,
+ {
+ cls : 'fc-content',
+ style : "position: relative;",
+ cn : [
+ {
+ cls : 'fc-view fc-view-month fc-grid',
+ style : 'position: relative',
+ unselectable : 'on',
+ cn : [
+ {
+ cls : 'fc-event-container',
+ style : 'position:absolute;z-index:8;top:0;left:0;'
+ },
+ cal_table
+ ]
+ }
+ ]
+
+ }
+ ]
- }, this);
+ };
- var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
+
- var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
-
- if(this.modalTitle.length){
- bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
+ return cfg;
+ },
+
+
+ initEvents : function()
+ {
+ if(!this.store){
+ throw "can not find store for calendar";
}
-
- var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
- if(this.mobile_restrict_height && listHeight < bodyHeight){
- this.touchViewBodyEl.setHeight(listHeight);
+ var mark = {
+ tag: "div",
+ cls:"x-dlg-mask",
+ style: "text-align:center",
+ cn: [
+ {
+ tag: "div",
+ style: "background-color:white;width:50%;margin:250 auto",
+ cn: [
+ {
+ tag: "img",
+ src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
+ },
+ {
+ tag: "span",
+ html: "Loading"
+ }
+
+ ]
+ }
+ ]
+ };
+ this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
+
+ var size = this.el.select('.fc-content', true).first().getSize();
+ this.maskEl.setSize(size.width, size.height);
+ this.maskEl.enableDisplayMode("block");
+ if(!this.loadMask){
+ this.maskEl.hide();
}
- var _this = this;
+ this.store = Roo.factory(this.store, Roo.data);
+ this.store.on('load', this.onLoad, this);
+ this.store.on('beforeload', this.onBeforeLoad, this);
- if(firstChecked && listHeight > bodyHeight){
- (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
- }
+ this.resize();
- },
-
- onTouchViewLoadException : function()
- {
- this.hideTouchView();
- },
-
- onTouchViewEmptyResults : function()
- {
- this.clearTouchView();
+ this.cells = this.el.select('.fc-day',true);
+ //Roo.log(this.cells);
+ this.textNodes = this.el.query('.fc-day-number');
+ this.cells.addClassOnOver('fc-state-hover');
- this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
+ this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
+ this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
+ this.el.select('.fc-button-today',true).on('click', this.showToday, this);
+ this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
- this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
+ this.on('monthchange', this.onMonthChange, this);
+ this.update(new Date().clearTime());
},
- clearTouchView : function()
- {
- this.touchViewListGroup.dom.innerHTML = '';
+ resize : function() {
+ var sz = this.el.getSize();
+
+ this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
+ this.el.select('.fc-day-content div',true).setHeight(34);
},
- onTouchViewClick : function(e, el, o)
- {
- e.preventDefault();
-
- var row = o.row;
- var rowIndex = o.rowIndex;
-
- var r = this.store.getAt(rowIndex);
-
- if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
-
- if(!this.multiple){
- Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
- c.dom.removeAttribute('checked');
- }, this);
-
- row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
-
- this.setFromData(r.data);
-
- var close = this.closeTriggerEl();
-
- if(close){
- close.show();
- }
-
- this.hideTouchView();
-
- this.fireEvent('select', this, r, rowIndex);
-
- return;
- }
+
+ // private
+ showPrevMonth : function(e){
+ this.update(this.activeDate.add("mo", -1));
+ },
+ showToday : function(e){
+ this.update(new Date().clearTime());
+ },
+ // private
+ showNextMonth : function(e){
+ this.update(this.activeDate.add("mo", 1));
+ },
- if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
- row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
- this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
- return;
- }
+ // private
+ showPrevYear : function(){
+ this.update(this.activeDate.add("y", -1));
+ },
- row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
- this.addItem(r.data);
- this.tickItems.push(r.data);
- }
+ // private
+ showNextYear : function(){
+ this.update(this.activeDate.add("y", 1));
},
+
- getAutoCreateNativeIOS : function()
+ // private
+ update : function(date)
{
- var cfg = {
- cls: 'form-group' //input-group,
- };
+ var vd = this.activeDate;
+ this.activeDate = date;
+// if(vd && this.el){
+// var t = date.getTime();
+// if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
+// Roo.log('using add remove');
+//
+// this.fireEvent('monthchange', this, date);
+//
+// this.cells.removeClass("fc-state-highlight");
+// this.cells.each(function(c){
+// if(c.dateValue == t){
+// c.addClass("fc-state-highlight");
+// setTimeout(function(){
+// try{c.dom.firstChild.focus();}catch(e){}
+// }, 50);
+// return false;
+// }
+// return true;
+// });
+// return;
+// }
+// }
- var combobox = {
- tag: 'select',
- cls : 'roo-ios-select'
- };
+ var days = date.getDaysInMonth();
- if (this.name) {
- combobox.name = this.name;
- }
+ var firstOfMonth = date.getFirstDateOfMonth();
+ var startingPos = firstOfMonth.getDay()-this.startDay;
- if (this.disabled) {
- combobox.disabled = true;
+ if(startingPos < this.startDay){
+ startingPos += 7;
}
- var settings = this;
-
- ['xs','sm','md','lg'].map(function(size){
- if (settings[size]) {
- cfg.cls += ' col-' + size + '-' + settings[size];
- }
- });
-
- cfg.cn = combobox;
+ var pm = date.add(Date.MONTH, -1);
+ var prevStart = pm.getDaysInMonth()-startingPos;
+//
+ this.cells = this.el.select('.fc-day',true);
+ this.textNodes = this.el.query('.fc-day-number');
+ this.cells.addClassOnOver('fc-state-hover');
- return cfg;
+ var cells = this.cells.elements;
+ var textEls = this.textNodes;
- },
-
- initIOSView : function()
- {
- this.store.on('load', this.onIOSViewLoad, this);
+ Roo.each(cells, function(cell){
+ cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
+ });
- return;
- },
-
- onIOSViewLoad : function()
- {
- if(this.store.getCount() < 1){
- return;
- }
+ days += startingPos;
+
+ // convert everything to numbers so it's fast
+ var day = 86400000;
+ var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
+ //Roo.log(d);
+ //Roo.log(pm);
+ //Roo.log(prevStart);
- this.clearIOSView();
+ var today = new Date().clearTime().getTime();
+ var sel = date.clearTime().getTime();
+ var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
+ var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
+ var ddMatch = this.disabledDatesRE;
+ var ddText = this.disabledDatesText;
+ var ddays = this.disabledDays ? this.disabledDays.join("") : false;
+ var ddaysText = this.disabledDaysText;
+ var format = this.format;
- if(this.allowBlank) {
+ var setCellClass = function(cal, cell){
+ cell.row = 0;
+ cell.events = [];
+ cell.more = [];
+ //Roo.log('set Cell Class');
+ cell.title = "";
+ var t = d.getTime();
- var default_text = '-- SELECT --';
+ //Roo.log(d);
- if(this.placeholder.length){
- default_text = this.placeholder;
+ cell.dateValue = t;
+ if(t == today){
+ cell.className += " fc-today";
+ cell.className += " fc-state-highlight";
+ cell.title = cal.todayText;
}
-
- if(this.emptyTitle.length){
- default_text += ' - ' + this.emptyTitle + ' -';
+ if(t == sel){
+ // disable highlight in other month..
+ //cell.className += " fc-state-highlight";
+
}
-
- var opt = this.inputEl().createChild({
- tag: 'option',
- value : 0,
- html : default_text
- });
-
- var o = {};
- o[this.valueField] = 0;
- o[this.displayField] = default_text;
-
- this.ios_options.push({
- data : o,
- el : opt
- });
-
- }
-
- this.store.data.each(function(d, rowIndex){
-
- var html = '';
-
- if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
- html = d.data[this.displayField];
+ // disabling
+ if(t < min) {
+ cell.className = " fc-state-disabled";
+ cell.title = cal.minText;
+ return;
}
-
- var value = '';
-
- if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
- value = d.data[this.valueField];
+ if(t > max) {
+ cell.className = " fc-state-disabled";
+ cell.title = cal.maxText;
+ return;
+ }
+ if(ddays){
+ if(ddays.indexOf(d.getDay()) != -1){
+ cell.title = ddaysText;
+ cell.className = " fc-state-disabled";
+ }
+ }
+ if(ddMatch && format){
+ var fvalue = d.dateFormat(format);
+ if(ddMatch.test(fvalue)){
+ cell.title = ddText.replace("%0", fvalue);
+ cell.className = " fc-state-disabled";
+ }
}
- var option = {
- tag: 'option',
- value : value,
- html : html
- };
-
- if(this.value == d.data[this.valueField]){
- option['selected'] = true;
+ if (!cell.initialClassName) {
+ cell.initialClassName = cell.dom.className;
}
- var opt = this.inputEl().createChild(option);
+ cell.dom.className = cell.initialClassName + ' ' + cell.className;
+ };
+
+ var i = 0;
+
+ for(; i < startingPos; i++) {
+ textEls[i].innerHTML = (++prevStart);
+ d.setDate(d.getDate()+1);
+
+ cells[i].className = "fc-past fc-other-month";
+ setCellClass(this, cells[i]);
+ }
+
+ var intDay = 0;
+
+ for(; i < days; i++){
+ intDay = i - startingPos + 1;
+ textEls[i].innerHTML = (intDay);
+ d.setDate(d.getDate()+1);
- this.ios_options.push({
- data : d.data,
- el : opt
- });
+ cells[i].className = ''; // "x-date-active";
+ setCellClass(this, cells[i]);
+ }
+ var extraDays = 0;
+
+ for(; i < 42; i++) {
+ textEls[i].innerHTML = (++extraDays);
+ d.setDate(d.getDate()+1);
- }, this);
+ cells[i].className = "fc-future fc-other-month";
+ setCellClass(this, cells[i]);
+ }
- this.inputEl().on('change', function(){
- this.fireEvent('select', this);
- }, this);
+ this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
- },
-
- clearIOSView: function()
- {
- this.inputEl().dom.innerHTML = '';
+ var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
- this.ios_options = [];
- },
-
- setIOSValue: function(v)
- {
- this.value = v;
+ this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
+ this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
- if(!this.ios_options){
- return;
+ if(totalRows != 6){
+ this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
+ this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
}
- Roo.each(this.ios_options, function(opts){
-
- opts.el.dom.removeAttribute('selected');
-
- if(opts.data[this.valueField] != v){
- return;
- }
-
- opts.el.dom.setAttribute('selected', true);
-
- }, this);
- }
-
- /**
- * @cfg {Boolean} grow
- * @hide
- */
- /**
- * @cfg {Number} growMin
- * @hide
- */
- /**
- * @cfg {Number} growMax
- * @hide
- */
- /**
- * @hide
- * @method autoSize
- */
-});
-
-Roo.apply(Roo.bootstrap.ComboBox, {
-
- header : {
- tag: 'div',
- cls: 'modal-header',
- cn: [
- {
- tag: 'h4',
- cls: 'modal-title'
- }
- ]
- },
-
- body : {
- tag: 'div',
- cls: 'modal-body',
- cn: [
- {
- tag: 'ul',
- cls: 'list-group'
- }
- ]
- },
-
- listItemRadio : {
- tag: 'li',
- cls: 'list-group-item',
- cn: [
- {
- tag: 'span',
- cls: 'roo-combobox-list-group-item-value'
- },
- {
- tag: 'div',
- cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
- cn: [
- {
- tag: 'input',
- type: 'radio'
- },
- {
- tag: 'label'
- }
- ]
+ this.fireEvent('monthchange', this, date);
+
+
+ /*
+ if(!this.internalRender){
+ var main = this.el.dom.firstChild;
+ var w = main.offsetWidth;
+ this.el.setWidth(w + this.el.getBorderWidth("lr"));
+ Roo.fly(main).setWidth(w);
+ this.internalRender = true;
+ // opera does not respect the auto grow header center column
+ // then, after it gets a width opera refuses to recalculate
+ // without a second pass
+ if(Roo.isOpera && !this.secondPass){
+ main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
+ this.secondPass = true;
+ this.update.defer(10, this, [date]);
}
- ]
+ }
+ */
+
},
- listItemCheckbox : {
- tag: 'li',
- cls: 'list-group-item',
- cn: [
- {
- tag: 'span',
- cls: 'roo-combobox-list-group-item-value'
- },
- {
- tag: 'div',
- cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
- cn: [
- {
- tag: 'input',
- type: 'checkbox'
- },
- {
- tag: 'label'
- }
- ]
+ findCell : function(dt) {
+ dt = dt.clearTime().getTime();
+ var ret = false;
+ this.cells.each(function(c){
+ //Roo.log("check " +c.dateValue + '?=' + dt);
+ if(c.dateValue == dt){
+ ret = c;
+ return false;
}
- ]
- },
-
- emptyResult : {
- tag: 'div',
- cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
+ return true;
+ });
+
+ return ret;
},
- footer : {
- tag: 'div',
- cls: 'modal-footer',
- cn: [
- {
- tag: 'div',
- cls: 'row',
- cn: [
- {
- tag: 'div',
- cls: 'col-xs-6 text-left',
- cn: {
- tag: 'button',
- cls: 'btn btn-danger roo-touch-view-cancel',
- html: 'Cancel'
- }
- },
- {
- tag: 'div',
- cls: 'col-xs-6 text-right',
- cn: {
- tag: 'button',
- cls: 'btn btn-success roo-touch-view-ok',
- html: 'OK'
- }
- }
- ]
+ findCells : function(ev) {
+ var s = ev.start.clone().clearTime().getTime();
+ // Roo.log(s);
+ var e= ev.end.clone().clearTime().getTime();
+ // Roo.log(e);
+ var ret = [];
+ this.cells.each(function(c){
+ ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
+
+ if(c.dateValue > e){
+ return ;
}
- ]
-
- }
-});
-
-Roo.apply(Roo.bootstrap.ComboBox, {
-
- touchViewTemplate : {
- tag: 'div',
- cls: 'modal fade roo-combobox-touch-view',
- cn: [
- {
- tag: 'div',
- cls: 'modal-dialog',
- style : 'position:fixed', // we have to fix position....
- cn: [
- {
- tag: 'div',
- cls: 'modal-content',
- cn: [
- Roo.bootstrap.ComboBox.header,
- Roo.bootstrap.ComboBox.body,
- Roo.bootstrap.ComboBox.footer
- ]
- }
- ]
+ if(c.dateValue < s){
+ return ;
}
- ]
- }
-});/*
- * 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.View
- * @extends Roo.util.Observable
- * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
- * This class also supports single and multi selection modes. <br>
- * Create a data model bound view:
- <pre><code>
- var store = new Roo.data.Store(...);
-
- var view = new Roo.View({
- el : "my-element",
- tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
-
- singleSelect: true,
- selectedClass: "ydataview-selected",
- store: store
- });
-
- // listen for node click?
- view.on("click", function(vw, index, node, e){
- alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
- });
-
- // load XML data
- dataModel.load("foobar.xml");
- </code></pre>
- For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
- * <br><br>
- * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
- * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
- *
- * Note: old style constructor is still suported (container, template, config)
- *
- * @constructor
- * Create a new View
- * @param {Object} config The config object
- *
- */
-Roo.View = function(config, depreciated_tpl, depreciated_config){
-
- this.parent = false;
-
- if (typeof(depreciated_tpl) == 'undefined') {
- // new way.. - universal constructor.
- Roo.apply(this, config);
- this.el = Roo.get(this.el);
- } else {
- // old format..
- this.el = Roo.get(config);
- this.tpl = depreciated_tpl;
- Roo.apply(this, depreciated_config);
- }
- this.wrapEl = this.el.wrap().wrap();
- ///this.el = this.wrapEla.appendChild(document.createElement("div"));
-
-
- if(typeof(this.tpl) == "string"){
- this.tpl = new Roo.Template(this.tpl);
- } else {
- // support xtype ctors..
- this.tpl = new Roo.factory(this.tpl, Roo);
- }
-
-
- this.tpl.compile();
-
- /** @private */
- this.addEvents({
- /**
- * @event beforeclick
- * Fires before a click is processed. Returns false to cancel the default action.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "beforeclick" : true,
- /**
- * @event click
- * Fires when a template node is clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "click" : true,
- /**
- * @event dblclick
- * Fires when a template node is double clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "dblclick" : true,
- /**
- * @event contextmenu
- * Fires when a template node is right clicked.
- * @param {Roo.View} this
- * @param {Number} index The index of the target node
- * @param {HTMLElement} node The target node
- * @param {Roo.EventObject} e The raw event object
- */
- "contextmenu" : true,
- /**
- * @event selectionchange
- * Fires when the selected nodes change.
- * @param {Roo.View} this
- * @param {Array} selections Array of the selected nodes
- */
- "selectionchange" : true,
-
- /**
- * @event beforeselect
- * Fires before a selection is made. If any handlers return false, the selection is cancelled.
- * @param {Roo.View} this
- * @param {HTMLElement} node The node to be selected
- * @param {Array} selections Array of currently selected nodes
- */
- "beforeselect" : true,
- /**
- * @event preparedata
- * Fires on every row to render, to allow you to change the data.
- * @param {Roo.View} this
- * @param {Object} data to be rendered (change this)
- */
- "preparedata" : true
-
-
+ ret.push(c);
});
-
-
-
- this.el.on({
- "click": this.onClick,
- "dblclick": this.onDblClick,
- "contextmenu": this.onContextMenu,
- scope:this
- });
-
- this.selections = [];
- this.nodes = [];
- this.cmp = new Roo.CompositeElementLite([]);
- if(this.store){
- this.store = Roo.factory(this.store, Roo.data);
- this.setStore(this.store, true);
- }
-
- if ( this.footer && this.footer.xtype) {
-
- var fctr = this.wrapEl.appendChild(document.createElement("div"));
-
- this.footer.dataSource = this.store;
- this.footer.container = fctr;
- this.footer = Roo.factory(this.footer, Roo);
- fctr.insertFirst(this.el);
- // this is a bit insane - as the paging toolbar seems to detach the el..
-// dom.parentNode.parentNode.parentNode
- // they get detached?
- }
-
-
- Roo.View.superclass.constructor.call(this);
-
-
-};
-
-Roo.extend(Roo.View, Roo.util.Observable, {
-
- /**
- * @cfg {Roo.data.Store} store Data store to load data from.
- */
- store : false,
-
- /**
- * @cfg {String|Roo.Element} el The container element.
- */
- el : '',
-
- /**
- * @cfg {String|Roo.Template} tpl The template used by this View
- */
- tpl : false,
- /**
- * @cfg {String} dataName the named area of the template to use as the data area
- * Works with domtemplates roo-name="name"
- */
- dataName: false,
- /**
- * @cfg {String} selectedClass The css class to add to selected nodes
- */
- selectedClass : "x-view-selected",
- /**
- * @cfg {String} emptyText The empty text to show when nothing is loaded.
- */
- emptyText : "",
-
- /**
- * @cfg {String} text to display on mask (default Loading)
- */
- mask : false,
- /**
- * @cfg {Boolean} multiSelect Allow multiple selection
- */
- multiSelect : false,
- /**
- * @cfg {Boolean} singleSelect Allow single selection
- */
- singleSelect: false,
-
- /**
- * @cfg {Boolean} toggleSelect - selecting
- */
- toggleSelect : false,
-
- /**
- * @cfg {Boolean} tickable - selecting
- */
- tickable : false,
-
- /**
- * Returns the element this view is bound to.
- * @return {Roo.Element}
- */
- getEl : function(){
- return this.wrapEl;
+ return ret;
},
+// findBestRow: function(cells)
+// {
+// var ret = 0;
+//
+// for (var i =0 ; i < cells.length;i++) {
+// ret = Math.max(cells[i].rows || 0,ret);
+// }
+// return ret;
+//
+// },
-
- /**
- * Refreshes the view. - called by datachanged on the store. - do not call directly.
- */
- refresh : function(){
- //Roo.log('refresh');
- var t = this.tpl;
-
- // if we are using something like 'domtemplate', then
- // the what gets used is:
- // t.applySubtemplate(NAME, data, wrapping data..)
- // the outer template then get' applied with
- // the store 'extra data'
- // and the body get's added to the
- // roo-name="data" node?
- // <span class='roo-tpl-{name}'></span> ?????
-
+
+ addItem : function(ev)
+ {
+ // look for vertical location slot in
+ var cells = this.findCells(ev);
+// ev.row = this.findBestRow(cells);
- this.clearSelections();
- this.el.update("");
- var html = [];
- var records = this.store.getRange();
- if(records.length < 1) {
-
- // is this valid?? = should it render a template??
-
- this.el.update(this.emptyText);
- return;
- }
- var el = this.el;
- if (this.dataName) {
- this.el.update(t.apply(this.store.meta)); //????
- el = this.el.child('.roo-tpl-' + this.dataName);
- }
+ // work out the location.
- for(var i = 0, len = records.length; i < len; i++){
- var data = this.prepareData(records[i].data, i, records[i]);
- this.fireEvent("preparedata", this, data, i, records[i]);
+ var crow = false;
+ var rows = [];
+ for(var i =0; i < cells.length; i++) {
- var d = Roo.apply({}, data);
+ cells[i].row = cells[0].row;
- if(this.tickable){
- Roo.apply(d, {'roo-id' : Roo.id()});
-
- var _this = this;
+ if(i == 0){
+ cells[i].row = cells[i].row + 1;
+ }
- Roo.each(this.parent.item, function(item){
- if(item[_this.parent.valueField] != data[_this.parent.valueField]){
- return;
- }
- Roo.apply(d, {'roo-data-checked' : 'checked'});
- });
+ if (!crow) {
+ crow = {
+ start : cells[i],
+ end : cells[i]
+ };
+ continue;
+ }
+ if (crow.start.getY() == cells[i].getY()) {
+ // on same row.
+ crow.end = cells[i];
+ continue;
}
+ // different row.
+ rows.push(crow);
+ crow = {
+ start: cells[i],
+ end : cells[i]
+ };
- html[html.length] = Roo.util.Format.trim(
- this.dataName ?
- t.applySubtemplate(this.dataName, d, this.store.meta) :
- t.apply(d)
- );
}
+ rows.push(crow);
+ ev.els = [];
+ ev.rows = rows;
+ ev.cells = cells;
+ cells[0].events.push(ev);
- el.update(html.join(""));
- this.nodes = el.dom.childNodes;
- this.updateIndexes(0);
- },
-
-
- /**
- * Function to override to reformat the data that is sent to
- * the template for each node.
- * DEPRICATED - use the preparedata event handler.
- * @param {Array/Object} data The raw data (array of colData for a data model bound view or
- * a JSON object for an UpdateManager bound view).
- */
- prepareData : function(data, index, record)
- {
- this.fireEvent("preparedata", this, data, index, record);
- return data;
- },
-
- onUpdate : function(ds, record){
- // Roo.log('on update');
- this.clearSelections();
- var index = this.store.indexOf(record);
- var n = this.nodes[index];
- this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
- n.parentNode.removeChild(n);
- this.updateIndexes(index, index);
+ this.calevents.push(ev);
},
-
-
-// --------- FIXME
- onAdd : function(ds, records, index)
- {
- //Roo.log(['on Add', ds, records, index] );
- this.clearSelections();
- if(this.nodes.length == 0){
- this.refresh();
+ clearEvents: function() {
+
+ if(!this.calevents){
return;
}
- var n = this.nodes[index];
- for(var i = 0, len = records.length; i < len; i++){
- var d = this.prepareData(records[i].data, i, records[i]);
- if(n){
- this.tpl.insertBefore(n, d);
- }else{
-
- this.tpl.append(this.el, d);
- }
- }
- this.updateIndexes(index);
- },
-
- onRemove : function(ds, record, index){
- // Roo.log('onRemove');
- this.clearSelections();
- var el = this.dataName ?
- this.el.child('.roo-tpl-' + this.dataName) :
- this.el;
- el.dom.removeChild(this.nodes[index]);
- this.updateIndexes(index);
- },
-
- /**
- * Refresh an individual node.
- * @param {Number} index
- */
- refreshNode : function(index){
- this.onUpdate(this.store, this.store.getAt(index));
- },
-
- updateIndexes : function(startIndex, endIndex){
- var ns = this.nodes;
- startIndex = startIndex || 0;
- endIndex = endIndex || ns.length - 1;
- for(var i = startIndex; i <= endIndex; i++){
- ns[i].nodeIndex = i;
- }
- },
-
- /**
- * Changes the data store this view uses and refresh the view.
- * @param {Store} store
- */
- setStore : function(store, initial){
- if(!initial && this.store){
- this.store.un("datachanged", this.refresh);
- this.store.un("add", this.onAdd);
- this.store.un("remove", this.onRemove);
- this.store.un("update", this.onUpdate);
- this.store.un("clear", this.refresh);
- this.store.un("beforeload", this.onBeforeLoad);
- this.store.un("load", this.onLoad);
- this.store.un("loadexception", this.onLoad);
- }
- if(store){
-
- store.on("datachanged", this.refresh, this);
- store.on("add", this.onAdd, this);
- store.on("remove", this.onRemove, this);
- store.on("update", this.onUpdate, this);
- store.on("clear", this.refresh, this);
- store.on("beforeload", this.onBeforeLoad, this);
- store.on("load", this.onLoad, this);
- store.on("loadexception", this.onLoad, this);
- }
+ Roo.each(this.cells.elements, function(c){
+ c.row = 0;
+ c.events = [];
+ c.more = [];
+ });
+
+ Roo.each(this.calevents, function(e) {
+ Roo.each(e.els, function(el) {
+ el.un('mouseenter' ,this.onEventEnter, this);
+ el.un('mouseleave' ,this.onEventLeave, this);
+ el.remove();
+ },this);
+ },this);
+
+ Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
+ e.remove();
+ });
- if(store){
- this.refresh();
- }
- },
- /**
- * onbeforeLoad - masks the loading area.
- *
- */
- onBeforeLoad : function(store,opts)
- {
- //Roo.log('onBeforeLoad');
- if (!opts.add) {
- this.el.update("");
- }
- this.el.mask(this.mask ? this.mask : "Loading" );
- },
- onLoad : function ()
- {
- this.el.unmask();
},
-
- /**
- * Returns the template node the passed child belongs to or null if it doesn't belong to one.
- * @param {HTMLElement} node
- * @return {HTMLElement} The template node
- */
- findItemFromChild : function(node){
- var el = this.dataName ?
- this.el.child('.roo-tpl-' + this.dataName,true) :
- this.el.dom;
+ renderEvents: function()
+ {
+ var _this = this;
- if(!node || node.parentNode == el){
- return node;
- }
- var p = node.parentNode;
- while(p && p != el){
- if(p.parentNode == el){
- return p;
+ this.cells.each(function(c) {
+
+ if(c.row < 5){
+ return;
}
- p = p.parentNode;
- }
- return null;
- },
-
- /** @ignore */
- onClick : function(e){
- var item = this.findItemFromChild(e.getTarget());
- if(item){
- var index = this.indexOf(item);
- if(this.onItemClick(item, index, e) !== false){
- this.fireEvent("click", this, index, item, e);
+
+ var ev = c.events;
+
+ var r = 4;
+ if(c.row != c.events.length){
+ r = 4 - (4 - (c.row - c.events.length));
}
- }else{
- this.clearSelections();
- }
- },
+
+ 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) {
+
+ c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
+
+
+ for (var e = 0; e < c.events.length; e++){
+ var ev = c.events[e];
+ var rows = ev.rows;
+
+ for(var i = 0; i < rows.length; i++) {
+
+ // how many rows should it span..
- /** @ignore */
- onContextMenu : function(e){
- var item = this.findItemFromChild(e.getTarget());
- if(item){
- this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
- }
- },
+ var cfg = {
+ cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
+ style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
- /** @ignore */
- onDblClick : function(e){
- var item = this.findItemFromChild(e.getTarget());
- if(item){
- this.fireEvent("dblclick", this, this.indexOf(item), item, e);
- }
- },
+ unselectable : "on",
+ cn : [
+ {
+ cls: 'fc-event-inner',
+ cn : [
+ // {
+ // tag:'span',
+ // cls: 'fc-event-time',
+ // html : cells.length > 1 ? '' : ev.time
+ // },
+ {
+ tag:'span',
+ cls: 'fc-event-title',
+ html : String.format('{0}', ev.title)
+ }
- onItemClick : function(item, index, e)
- {
- if(this.fireEvent("beforeclick", this, index, item, e) === false){
- return false;
- }
- if (this.toggleSelect) {
- var m = this.isSelected(item) ? 'unselect' : 'select';
- //Roo.log(m);
- var _t = this;
- _t[m](item, true, false);
- return true;
- }
- if(this.multiSelect || this.singleSelect){
- if(this.multiSelect && e.shiftKey && this.lastSelection){
- this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
- }else{
- this.select(item, this.multiSelect && e.ctrlKey);
- this.lastSelection = item;
+
+ ]
+ },
+ {
+ cls: 'ui-resizable-handle ui-resizable-e',
+ html : '  '
+ }
+
+ ]
+ };
+
+ if (i == 0) {
+ cfg.cls += ' fc-event-start';
+ }
+ if ((i+1) == rows.length) {
+ cfg.cls += ' fc-event-end';
+ }
+
+ var ctr = _this.el.select('.fc-event-container',true).first();
+ var cg = ctr.createChild(cfg);
+
+ var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
+ var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
+
+ 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);
+ cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
+ cg.on('click', _this.onEventClick, _this, ev);
+
+ ev.els.push(cg);
+
+ }
+
}
- if(!this.tickable){
- e.preventDefault();
- }
- }
- return true;
- },
+ if(c.more.length){
+ var cfg = {
+ cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
+ style : 'position: absolute',
+ unselectable : "on",
+ cn : [
+ {
+ cls: 'fc-event-inner',
+ cn : [
+ {
+ tag:'span',
+ cls: 'fc-event-title',
+ html : 'More'
+ }
- /**
- * Get the number of selected nodes.
- * @return {Number}
- */
- getSelectionCount : function(){
- return this.selections.length;
- },
- /**
- * Get the currently selected nodes.
- * @return {Array} An array of HTMLElements
- */
- getSelectedNodes : function(){
- return this.selections;
- },
+ ]
+ },
+ {
+ cls: 'ui-resizable-handle ui-resizable-e',
+ html : '  '
+ }
- /**
- * Get the indexes of the selected nodes.
- * @return {Array}
- */
- getSelectedIndexes : function(){
- var indexes = [], s = this.selections;
- for(var i = 0, len = s.length; i < len; i++){
- indexes.push(s[i].nodeIndex);
- }
- return indexes;
- },
+ ]
+ };
- /**
- * Clear all selections
- * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
- */
- clearSelections : function(suppressEvent){
- if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
- this.cmp.elements = this.selections;
- this.cmp.removeClass(this.selectedClass);
- this.selections = [];
- if(!suppressEvent){
- this.fireEvent("selectionchange", this, this.selections);
- }
- }
- },
+ var ctr = _this.el.select('.fc-event-container',true).first();
+ var cg = ctr.createChild(cfg);
- /**
- * Returns true if the passed node is selected
- * @param {HTMLElement/Number} node The node or node index
- * @return {Boolean}
- */
- isSelected : function(node){
- var s = this.selections;
- if(s.length < 1){
- return false;
- }
- node = this.getNode(node);
- return s.indexOf(node) !== -1;
- },
+ var sbox = c.select('.fc-day-content',true).first().getBox();
+ var ebox = c.select('.fc-day-content',true).first().getBox();
+ //Roo.log(cg);
+ cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
+ cg.setWidth(ebox.right - sbox.x -2);
- /**
- * Selects nodes.
- * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
- * @param {Boolean} keepExisting (optional) true to keep existing selections
- * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
- */
- select : function(nodeInfo, keepExisting, suppressEvent){
- if(nodeInfo instanceof Array){
- if(!keepExisting){
- this.clearSelections(true);
- }
- for(var i = 0, len = nodeInfo.length; i < len; i++){
- this.select(nodeInfo[i], true, true);
+ cg.on('click', _this.onMoreEventClick, _this, c.more);
+
}
- return;
- }
- var node = this.getNode(nodeInfo);
- if(!node || this.isSelected(node)){
- return; // already selected.
- }
- if(!keepExisting){
- this.clearSelections(true);
- }
+
+ });
+
+
+
+ },
+
+ onEventEnter: function (e, el,event,d) {
+ this.fireEvent('evententer', this, el, event);
+ },
+
+ onEventLeave: function (e, el,event,d) {
+ this.fireEvent('eventleave', this, el, event);
+ },
+
+ onEventClick: function (e, el,event,d) {
+ this.fireEvent('eventclick', this, el, event);
+ },
+
+ onMonthChange: function () {
+ this.store.load();
+ },
+
+ onMoreEventClick: function(e, el, more)
+ {
+ var _this = this;
+
+ this.calpopover.placement = 'right';
+ this.calpopover.setTitle('More');
+
+ this.calpopover.setContent('');
+
+ var ctr = this.calpopover.el.select('.popover-content', true).first();
+
+ Roo.each(more, function(m){
+ var cfg = {
+ cls : 'fc-event-hori fc-event-draggable',
+ html : m.title
+ };
+ var cg = ctr.createChild(cfg);
+
+ cg.on('click', _this.onEventClick, _this, m);
+ });
- if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
- Roo.fly(node).addClass(this.selectedClass);
- this.selections.push(node);
- if(!suppressEvent){
- this.fireEvent("selectionchange", this, this.selections);
- }
- }
+ this.calpopover.show(el);
},
- /**
- * Unselects nodes.
- * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
- * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
- * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
- */
- unselect : function(nodeInfo, keepExisting, suppressEvent)
- {
- if(nodeInfo instanceof Array){
- Roo.each(this.selections, function(s) {
- this.unselect(s, nodeInfo);
- }, this);
- return;
- }
- var node = this.getNode(nodeInfo);
- if(!node || !this.isSelected(node)){
- //Roo.log("not selected");
- return; // not selected.
- }
- // fireevent???
- var ns = [];
- Roo.each(this.selections, function(s) {
- if (s == node ) {
- Roo.fly(node).removeClass(this.selectedClass);
-
- return;
- }
- ns.push(s);
- },this);
+
+ onLoad: function ()
+ {
+ this.calevents = [];
+ var cal = this;
- this.selections= ns;
- this.fireEvent("selectionchange", this, this.selections);
- },
-
- /**
- * Gets a template node.
- * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
- * @return {HTMLElement} The node or null if it wasn't found
- */
- getNode : function(nodeInfo){
- if(typeof nodeInfo == "string"){
- return document.getElementById(nodeInfo);
- }else if(typeof nodeInfo == "number"){
- return this.nodes[nodeInfo];
+ if(this.store.getCount() > 0){
+ this.store.data.each(function(d){
+ cal.addItem({
+ id : d.data.id,
+ start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
+ end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
+ time : d.data.start_time,
+ title : d.data.title,
+ description : d.data.description,
+ venue : d.data.venue
+ });
+ });
}
- return nodeInfo;
- },
-
- /**
- * Gets a range template nodes.
- * @param {Number} startIndex
- * @param {Number} endIndex
- * @return {Array} An array of nodes
- */
- getNodes : function(start, end){
- var ns = this.nodes;
- start = start || 0;
- end = typeof end == "undefined" ? ns.length - 1 : end;
- var nodes = [];
- if(start <= end){
- for(var i = start; i <= end; i++){
- nodes.push(ns[i]);
- }
- } else{
- for(var i = start; i >= end; i--){
- nodes.push(ns[i]);
- }
+
+ this.renderEvents();
+
+ if(this.calevents.length && this.loadMask){
+ this.maskEl.hide();
}
- return nodes;
},
-
- /**
- * Finds the index of the passed node
- * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
- * @return {Number} The index of the node or -1
- */
- indexOf : function(node){
- node = this.getNode(node);
- if(typeof node.nodeIndex == "number"){
- return node.nodeIndex;
- }
- var ns = this.nodes;
- for(var i = 0, len = ns.length; i < len; i++){
- if(ns[i] == node){
- return i;
- }
+
+ onBeforeLoad: function()
+ {
+ this.clearEvents();
+ if(this.loadMask){
+ this.maskEl.show();
}
- return -1;
}
});
-/*
+
+
+ /*
* - LGPL
*
- * based on jquery fullcalendar
+ * element
*
*/
-Roo.bootstrap = Roo.bootstrap || {};
/**
- * @class Roo.bootstrap.Calendar
+ * @class Roo.bootstrap.Popover
* @extends Roo.bootstrap.Component
- * Bootstrap Calendar class
- * @cfg {Boolean} loadMask (true|false) default false
- * @cfg {Object} header generate the user specific header of the calendar, default false
-
+ * @parent none builder
+ * @children Roo.bootstrap.Component
+ * Bootstrap Popover class
+ * @cfg {String} html contents of the popover (or false to use children..)
+ * @cfg {String} title of popover (or false to hide)
+ * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
+ * @cfg {String} trigger click || hover (or false to trigger manually)
+ * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
+ * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
+ * - if false and it has a 'parent' then it will be automatically added to that element
+ * - if string - Roo.get will be called
+ * @cfg {Number} delay - delay before showing
+
* @constructor
- * Create a new Container
+ * Create a new Popover
* @param {Object} config The config object
*/
-
-
-Roo.bootstrap.Calendar = function(config){
- Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event select
- * Fires when a date is selected
- * @param {DatePicker} this
- * @param {Date} date The selected date
- */
- 'select': true,
- /**
- * @event monthchange
- * Fires when the displayed month changes
- * @param {DatePicker} this
- * @param {Date} date The selected month
- */
- 'monthchange': true,
- /**
- * @event evententer
- * Fires when mouse over an event
- * @param {Calendar} this
- * @param {event} Event
- */
- 'evententer': true,
- /**
- * @event eventleave
- * Fires when the mouse leaves an
- * @param {Calendar} this
- * @param {event}
- */
- 'eventleave': true,
+Roo.bootstrap.Popover = function(config){
+ Roo.bootstrap.Popover.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ // raw events
+ /**
+ * @event show
+ * After the popover show
+ *
+ * @param {Roo.bootstrap.Popover} this
+ */
+ "show" : true,
/**
- * @event eventclick
- * Fires when the mouse click an
- * @param {Calendar} this
- * @param {event}
- */
- 'eventclick': true
-
+ * @event hide
+ * After the popover hide
+ *
+ * @param {Roo.bootstrap.Popover} this
+ */
+ "hide" : true
});
-
};
-Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
- /**
- * @cfg {Number} startDay
- * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
- */
- startDay : 0,
+ title: false,
+ html: false,
- loadMask : false,
+ placement : 'right',
+ trigger : 'hover', // hover
+ modal : false,
+ delay : 0,
- header : false,
-
- getAutoCreate : function(){
-
+ over: false,
+
+ can_build_overlaid : false,
+
+ maskEl : false, // the mask element
+ headerEl : false,
+ contentEl : false,
+ alignEl : false, // when show is called with an element - this get's stored.
+
+ getChildContainer : function()
+ {
+ return this.contentEl;
- var fc_button = function(name, corner, style, content ) {
- return Roo.apply({},{
- tag : 'span',
- cls : 'fc-button fc-button-'+name+' fc-state-default ' +
- (corner.length ?
- 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
- ''
- ),
- html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
- unselectable: 'on'
- });
+ },
+ getPopoverHeader : function()
+ {
+ this.title = true; // flag not to hide it..
+ this.headerEl.addClass('p-0');
+ return this.headerEl
+ },
+
+
+ getAutoCreate : function(){
+
+ var cfg = {
+ cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
+ style: 'display:block',
+ cn : [
+ {
+ cls : 'arrow'
+ },
+ {
+ cls : 'popover-inner ',
+ cn : [
+ {
+ tag: 'h3',
+ cls: 'popover-title popover-header',
+ html : this.title === false ? '' : this.title
+ },
+ {
+ cls : 'popover-content popover-body ' + (this.cls || ''),
+ html : this.html || ''
+ }
+ ]
+
+ }
+ ]
};
- var header = {};
+ return cfg;
+ },
+ /**
+ * @param {string} the title
+ */
+ setTitle: function(str)
+ {
+ this.title = str;
+ if (this.el) {
+ this.headerEl.dom.innerHTML = str;
+ }
- if(!this.header){
- header = {
- tag : 'table',
- cls : 'fc-header',
- style : 'width:100%',
- cn : [
- {
- tag: 'tr',
- cn : [
- {
- tag : 'td',
- cls : 'fc-header-left',
- cn : [
- fc_button('prev', 'left', 'arrow', '‹' ),
- fc_button('next', 'right', 'arrow', '›' ),
- { tag: 'span', cls: 'fc-header-space' },
- fc_button('today', 'left right', '', 'today' ) // neds state disabled..
-
-
- ]
- },
-
- {
- tag : 'td',
- cls : 'fc-header-center',
- cn : [
- {
- tag: 'span',
- cls: 'fc-header-title',
- cn : {
- tag: 'H2',
- html : 'month / year'
- }
- }
-
- ]
- },
- {
- tag : 'td',
- cls : 'fc-header-right',
- cn : [
- /* fc_button('month', 'left', '', 'month' ),
- fc_button('week', '', '', 'week' ),
- fc_button('day', 'right', '', 'day' )
- */
-
- ]
- }
-
- ]
- }
- ]
- };
+ },
+ /**
+ * @param {string} the body content
+ */
+ setContent: function(str)
+ {
+ this.html = str;
+ if (this.contentEl) {
+ this.contentEl.dom.innerHTML = str;
}
- header = this.header;
+ },
+ // as it get's added to the bottom of the page.
+ onRender : function(ct, position)
+ {
+ Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
-
- var cal_heads = function() {
- var ret = [];
- // fixme - handle this.
+
+
+ if(!this.el){
+ var cfg = Roo.apply({}, this.getAutoCreate());
+ cfg.id = Roo.id();
- for (var i =0; i < Date.dayNames.length; i++) {
- var d = Date.dayNames[i];
- ret.push({
- tag: 'th',
- cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
- html : d.substring(0,3)
- });
-
+ if (this.cls) {
+ cfg.cls += ' ' + this.cls;
}
- ret[0].cls += ' fc-first';
- ret[6].cls += ' fc-last';
- return ret;
- };
- var cal_cell = function(n) {
- return {
- tag: 'td',
- cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
- cn : [
- {
- cn : [
- {
- cls: 'fc-day-number',
- html: 'D'
- },
- {
- cls: 'fc-day-content',
-
- cn : [
- {
- style: 'position: relative;' // height: 17px;
- }
- ]
- }
-
-
- ]
- }
- ]
-
+ if (this.style) {
+ cfg.style = this.style;
}
- };
- var cal_rows = function() {
-
- var ret = [];
- for (var r = 0; r < 6; r++) {
- var row= {
- tag : 'tr',
- cls : 'fc-week',
- cn : []
- };
-
- for (var i =0; i < Date.dayNames.length; i++) {
- var d = Date.dayNames[i];
- row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
+ //Roo.log("adding to ");
+ this.el = Roo.get(document.body).createChild(cfg, position);
+// Roo.log(this.el);
+ }
+
+ this.contentEl = this.el.select('.popover-content',true).first();
+ this.headerEl = this.el.select('.popover-title',true).first();
+
+ var nitems = [];
+ if(typeof(this.items) != 'undefined'){
+ var items = this.items;
+ delete this.items;
- }
- row.cn[0].cls+=' fc-first';
- row.cn[0].cn[0].style = 'min-height:90px';
- row.cn[6].cls+=' fc-last';
- ret.push(row);
-
+ for(var i =0;i < items.length;i++) {
+ nitems.push(this.addxtype(Roo.apply({}, items[i])));
}
- ret[0].cls += ' fc-first';
- ret[4].cls += ' fc-prev-last';
- ret[5].cls += ' fc-last';
- return ret;
-
- };
+ }
+
+ this.items = nitems;
- var cal_table = {
- tag: 'table',
- cls: 'fc-border-separate',
- style : 'width:100%',
- cellspacing : 0,
- cn : [
- {
- tag: 'thead',
- cn : [
- {
- tag: 'tr',
- cls : 'fc-first fc-last',
- cn : cal_heads()
- }
- ]
- },
- {
- tag: 'tbody',
- cn : cal_rows()
- }
-
- ]
- };
+ this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
+ Roo.EventManager.onWindowResize(this.resizeMask, this, true);
+
+
+
+ this.initEvents();
+ },
+
+ resizeMask : function()
+ {
+ this.maskEl.setSize(
+ Roo.lib.Dom.getViewWidth(true),
+ Roo.lib.Dom.getViewHeight(true)
+ );
+ },
+
+ initEvents : function()
+ {
+
+ if (!this.modal) {
+ Roo.bootstrap.Popover.register(this);
+ }
- var cfg = {
- cls : 'fc fc-ltr',
- cn : [
- header,
- {
- cls : 'fc-content',
- style : "position: relative;",
- cn : [
- {
- cls : 'fc-view fc-view-month fc-grid',
- style : 'position: relative',
- unselectable : 'on',
- cn : [
- {
- cls : 'fc-event-container',
- style : 'position:absolute;z-index:8;top:0;left:0;'
- },
- cal_table
- ]
- }
- ]
+ this.arrowEl = this.el.select('.arrow',true).first();
+ this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
+ this.el.enableDisplayMode('block');
+ this.el.hide();
+
+
+ if (this.over === false && !this.parent()) {
+ return;
+ }
+ if (this.triggers === false) {
+ return;
+ }
+
+ // support parent
+ var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
+ var triggers = this.trigger ? this.trigger.split(' ') : [];
+ Roo.each(triggers, function(trigger) {
+
+ if (trigger == 'click') {
+ on_el.on('click', this.toggle, this);
+ } else if (trigger != 'manual') {
+ var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
+ var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
+
+ on_el.on(eventIn ,this.enter, this);
+ on_el.on(eventOut, this.leave, this);
+ }
+ }, this);
+ },
- }
- ]
-
- };
+
+ // private
+ timeout : null,
+ hoverState : null,
+
+ toggle : function () {
+ this.hoverState == 'in' ? this.leave() : this.enter();
+ },
+
+ enter : function () {
+
+ clearTimeout(this.timeout);
+
+ this.hoverState = 'in';
+
+ if (!this.delay || !this.delay.show) {
+ this.show();
+ return;
+ }
+ var _t = this;
+ this.timeout = setTimeout(function () {
+ if (_t.hoverState == 'in') {
+ _t.show();
+ }
+ }, this.delay.show)
+ },
+
+ leave : function() {
+ clearTimeout(this.timeout);
+
+ this.hoverState = 'out';
+
+ if (!this.delay || !this.delay.hide) {
+ this.hide();
+ return;
+ }
+ var _t = this;
+ this.timeout = setTimeout(function () {
+ if (_t.hoverState == 'out') {
+ _t.hide();
+ }
+ }, this.delay.hide)
+ },
+
+ /**
+ * update the position of the dialog
+ * normally this is needed if the popover get's bigger - due to a Table reload etc..
+ *
+ *
+ */
+
+ doAlign : function()
+ {
+ if (this.alignEl) {
+ this.updatePosition(this.placement, true);
+
+ } else {
+ // this is usually just done by the builder = to show the popoup in the middle of the scren.
+ var es = this.el.getSize();
+ var x = Roo.lib.Dom.getViewWidth()/2;
+ var y = Roo.lib.Dom.getViewHeight()/2;
+ this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
+
+ }
+
+
- return cfg;
+
},
-
- initEvents : function()
+ /**
+ * Show the popover
+ * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
+ * @param {string} (left|right|top|bottom) position
+ */
+ show : function (on_el, placement)
{
- if(!this.store){
- throw "can not find store for calendar";
+ this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
+ on_el = on_el || false; // default to false
+
+ if (!on_el) {
+ if (this.parent() && (this.over == 'parent' || (this.over === false))) {
+ on_el = this.parent().el;
+ } else if (this.over) {
+ on_el = Roo.get(this.over);
+ }
+
}
- var mark = {
- tag: "div",
- cls:"x-dlg-mask",
- style: "text-align:center",
- cn: [
- {
- tag: "div",
- style: "background-color:white;width:50%;margin:250 auto",
- cn: [
- {
- tag: "img",
- src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
- },
- {
- tag: "span",
- html: "Loading"
- }
-
- ]
- }
- ]
- };
- this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
+ this.alignEl = Roo.get( on_el );
+
+ if (!this.el) {
+ this.render(document.body);
+ }
- var size = this.el.select('.fc-content', true).first().getSize();
- this.maskEl.setSize(size.width, size.height);
- this.maskEl.enableDisplayMode("block");
- if(!this.loadMask){
- this.maskEl.hide();
+
+
+
+ if (this.title === false) {
+ this.headerEl.hide();
}
- this.store = Roo.factory(this.store, Roo.data);
- this.store.on('load', this.onLoad, this);
- this.store.on('beforeload', this.onBeforeLoad, this);
+
+ this.el.show();
+ this.el.dom.style.display = 'block';
+
+ this.doAlign();
- this.resize();
+ //var arrow = this.el.select('.arrow',true).first();
+ //arrow.set(align[2],
- this.cells = this.el.select('.fc-day',true);
- //Roo.log(this.cells);
- this.textNodes = this.el.query('.fc-day-number');
- this.cells.addClassOnOver('fc-state-hover');
+ this.el.addClass('in');
- this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
- this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
- this.el.select('.fc-button-today',true).on('click', this.showToday, this);
- this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
+
- this.on('monthchange', this.onMonthChange, this);
+ this.hoverState = 'in';
+
+ if (this.modal) {
+ this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
+ this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
+ this.maskEl.dom.style.display = 'block';
+ this.maskEl.addClass('show');
+ }
+ this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
+
+ this.fireEvent('show', this);
- this.update(new Date().clearTime());
},
-
- resize : function() {
- var sz = this.el.getSize();
+ /**
+ * fire this manually after loading a grid in the table for example
+ * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
+ * @param {Boolean} try and move it if we cant get right position.
+ */
+ updatePosition : function(placement, try_move)
+ {
+ // allow for calling with no parameters
+ placement = placement ? placement : this.placement;
+ try_move = typeof(try_move) == 'undefined' ? true : try_move;
- this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
- this.el.select('.fc-day-content div',true).setHeight(34);
+ this.el.removeClass([
+ 'fade','top','bottom', 'left', 'right','in',
+ 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
+ ]);
+ this.el.addClass(placement + ' bs-popover-' + placement);
+
+ if (!this.alignEl ) {
+ return false;
+ }
+
+ switch (placement) {
+ case 'right':
+ var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
+ var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
+ if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
+ //normal display... or moved up/down.
+ this.el.setXY(offset);
+ var xy = this.alignEl.getAnchorXY('tr', false);
+ xy[0]+=2;xy[1]+=5;
+ this.arrowEl.setXY(xy);
+ return true;
+ }
+ // continue through...
+ return this.updatePosition('left', false);
+
+
+ case 'left':
+ var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
+ var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
+ if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
+ //normal display... or moved up/down.
+ this.el.setXY(offset);
+ var xy = this.alignEl.getAnchorXY('tl', false);
+ xy[0]-=10;xy[1]+=5; // << fix me
+ this.arrowEl.setXY(xy);
+ return true;
+ }
+ // call self...
+ return this.updatePosition('right', false);
+
+ case 'top':
+ var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
+ var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
+ if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
+ //normal display... or moved up/down.
+ this.el.setXY(offset);
+ var xy = this.alignEl.getAnchorXY('t', false);
+ xy[1]-=10; // << fix me
+ this.arrowEl.setXY(xy);
+ return true;
+ }
+ // fall through
+ return this.updatePosition('bottom', false);
+
+ case 'bottom':
+ var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
+ var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
+ if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
+ //normal display... or moved up/down.
+ this.el.setXY(offset);
+ var xy = this.alignEl.getAnchorXY('b', false);
+ xy[1]+=2; // << fix me
+ this.arrowEl.setXY(xy);
+ return true;
+ }
+ // fall through
+ return this.updatePosition('top', false);
+
+
+ }
+
+
+ return false;
},
+ hide : function()
+ {
+ this.el.setXY([0,0]);
+ this.el.removeClass('in');
+ this.el.hide();
+ this.hoverState = null;
+ this.maskEl.hide(); // always..
+ this.fireEvent('hide', this);
+ }
- // private
- showPrevMonth : function(e){
- this.update(this.activeDate.add("mo", -1));
+});
+
+
+Roo.apply(Roo.bootstrap.Popover, {
+
+ alignment : {
+ 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
+ 'right' : ['l-br', [10,0], 'right bs-popover-right'],
+ 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
+ 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
},
- showToday : function(e){
- this.update(new Date().clearTime());
+
+ zIndex : 20001,
+
+ clickHander : false,
+
+
+
+ onMouseDown : function(e)
+ {
+ if (this.popups.length && !e.getTarget(".roo-popover")) {
+ /// what is nothing is showing..
+ this.hideAll();
+ }
+
},
- // private
- showNextMonth : function(e){
- this.update(this.activeDate.add("mo", 1));
+
+
+ popups : [],
+
+ register : function(popup)
+ {
+ if (!Roo.bootstrap.Popover.clickHandler) {
+ Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
+ }
+ // hide other popups.
+ popup.on('show', Roo.bootstrap.Popover.onShow, popup);
+ popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
+ this.hideAll(); //<< why?
+ //this.popups.push(popup);
},
-
- // private
- showPrevYear : function(){
- this.update(this.activeDate.add("y", -1));
+ hideAll : function()
+ {
+ this.popups.forEach(function(p) {
+ p.hide();
+ });
},
-
- // private
- showNextYear : function(){
- this.update(this.activeDate.add("y", 1));
+ onShow : function() {
+ Roo.bootstrap.Popover.popups.push(this);
},
+ onHide : function() {
+ Roo.bootstrap.Popover.popups.remove(this);
+ }
+
+});
+/**
+ * @class Roo.bootstrap.PopoverNav
+ * @extends Roo.bootstrap.nav.Simplebar
+ * @parent Roo.bootstrap.Popover
+ * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
+ * @licence LGPL
+ * Bootstrap Popover header navigation class
+ * FIXME? should this go under nav?
+ *
+ *
+ * @constructor
+ * Create a new Popover Header Navigation
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.PopoverNav = function(config){
+ Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
+};
+Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
- // private
- update : function(date)
+
+ container_method : 'getPopoverHeader'
+
+
+
+
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * Progress
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.Progress
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.ProgressBar
+ * Bootstrap Progress class
+ * @cfg {Boolean} striped striped of the progress bar
+ * @cfg {Boolean} active animated of the progress bar
+ *
+ *
+ * @constructor
+ * Create a new Progress
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Progress = function(config){
+ Roo.bootstrap.Progress.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
+
+ striped : false,
+ active: false,
+
+ getAutoCreate : function(){
+ var cfg = {
+ tag: 'div',
+ cls: 'progress'
+ };
+
+
+ if(this.striped){
+ cfg.cls += ' progress-striped';
+ }
+
+ if(this.active){
+ cfg.cls += ' active';
+ }
+
+
+ return cfg;
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * ProgressBar
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.ProgressBar
+ * @extends Roo.bootstrap.Component
+ * Bootstrap ProgressBar class
+ * @cfg {Number} aria_valuenow aria-value now
+ * @cfg {Number} aria_valuemin aria-value min
+ * @cfg {Number} aria_valuemax aria-value max
+ * @cfg {String} label label for the progress bar
+ * @cfg {String} panel (success | info | warning | danger )
+ * @cfg {String} role role of the progress bar
+ * @cfg {String} sr_only text
+ *
+ *
+ * @constructor
+ * Create a new ProgressBar
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.ProgressBar = function(config){
+ Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
+
+ aria_valuenow : 0,
+ aria_valuemin : 0,
+ aria_valuemax : 100,
+ label : false,
+ panel : false,
+ role : false,
+ sr_only: false,
+
+ getAutoCreate : function()
{
- var vd = this.activeDate;
- this.activeDate = date;
-// if(vd && this.el){
-// var t = date.getTime();
-// if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
-// Roo.log('using add remove');
-//
-// this.fireEvent('monthchange', this, date);
-//
-// this.cells.removeClass("fc-state-highlight");
-// this.cells.each(function(c){
-// if(c.dateValue == t){
-// c.addClass("fc-state-highlight");
-// setTimeout(function(){
-// try{c.dom.firstChild.focus();}catch(e){}
-// }, 50);
-// return false;
-// }
-// return true;
-// });
-// return;
-// }
-// }
- var days = date.getDaysInMonth();
+ var cfg = {
+ tag: 'div',
+ cls: 'progress-bar',
+ style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
+ };
- var firstOfMonth = date.getFirstDateOfMonth();
- var startingPos = firstOfMonth.getDay()-this.startDay;
+ if(this.sr_only){
+ cfg.cn = {
+ tag: 'span',
+ cls: 'sr-only',
+ html: this.sr_only
+ }
+ }
- if(startingPos < this.startDay){
- startingPos += 7;
+ if(this.role){
+ cfg.role = this.role;
}
- var pm = date.add(Date.MONTH, -1);
- var prevStart = pm.getDaysInMonth()-startingPos;
-//
- this.cells = this.el.select('.fc-day',true);
- this.textNodes = this.el.query('.fc-day-number');
- this.cells.addClassOnOver('fc-state-hover');
+ if(this.aria_valuenow){
+ cfg['aria-valuenow'] = this.aria_valuenow;
+ }
- var cells = this.cells.elements;
- var textEls = this.textNodes;
+ if(this.aria_valuemin){
+ cfg['aria-valuemin'] = this.aria_valuemin;
+ }
- Roo.each(cells, function(cell){
- cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
- });
+ if(this.aria_valuemax){
+ cfg['aria-valuemax'] = this.aria_valuemax;
+ }
- days += startingPos;
-
- // convert everything to numbers so it's fast
- var day = 86400000;
- var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
- //Roo.log(d);
- //Roo.log(pm);
- //Roo.log(prevStart);
+ if(this.label && !this.sr_only){
+ cfg.html = this.label;
+ }
- var today = new Date().clearTime().getTime();
- var sel = date.clearTime().getTime();
- var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
- var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
- var ddMatch = this.disabledDatesRE;
- var ddText = this.disabledDatesText;
- var ddays = this.disabledDays ? this.disabledDays.join("") : false;
- var ddaysText = this.disabledDaysText;
- var format = this.format;
+ if(this.panel){
+ cfg.cls += ' progress-bar-' + this.panel;
+ }
- var setCellClass = function(cal, cell){
- cell.row = 0;
- cell.events = [];
- cell.more = [];
- //Roo.log('set Cell Class');
- cell.title = "";
- var t = d.getTime();
-
- //Roo.log(d);
-
- cell.dateValue = t;
- if(t == today){
- cell.className += " fc-today";
- cell.className += " fc-state-highlight";
- cell.title = cal.todayText;
- }
- if(t == sel){
- // disable highlight in other month..
- //cell.className += " fc-state-highlight";
-
- }
- // disabling
- if(t < min) {
- cell.className = " fc-state-disabled";
- cell.title = cal.minText;
- return;
- }
- if(t > max) {
- cell.className = " fc-state-disabled";
- cell.title = cal.maxText;
- return;
- }
- if(ddays){
- if(ddays.indexOf(d.getDay()) != -1){
- cell.title = ddaysText;
- cell.className = " fc-state-disabled";
- }
- }
- if(ddMatch && format){
- var fvalue = d.dateFormat(format);
- if(ddMatch.test(fvalue)){
- cell.title = ddText.replace("%0", fvalue);
- cell.className = " fc-state-disabled";
+ return cfg;
+ },
+
+ update : function(aria_valuenow)
+ {
+ this.aria_valuenow = aria_valuenow;
+
+ this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
+ }
+
+});
+
+
+
+ /**
+ * @class Roo.bootstrap.TabGroup
+ * @extends Roo.bootstrap.Column
+ * @children Roo.bootstrap.TabPanel
+ * Bootstrap Column class
+ * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
+ * @cfg {Boolean} carousel true to make the group behave like a carousel
+ * @cfg {Boolean} bullets show bullets for the panels
+ * @cfg {Boolean} autoslide (true|false) auto slide .. default false
+ * @cfg {Number} timer auto slide timer .. default 0 millisecond
+ * @cfg {Boolean} showarrow (true|false) show arrow default true
+ *
+ * @constructor
+ * Create a new TabGroup
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.TabGroup = function(config){
+ Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
+ if (!this.navId) {
+ this.navId = Roo.id();
+ }
+ this.tabs = [];
+ Roo.bootstrap.TabGroup.register(this);
+
+};
+
+Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
+
+ carousel : false,
+ transition : false,
+ bullets : 0,
+ timer : 0,
+ autoslide : false,
+ slideFn : false,
+ slideOnTouch : false,
+ showarrow : true,
+
+ getAutoCreate : function()
+ {
+ var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
+
+ cfg.cls += ' tab-content';
+
+ if (this.carousel) {
+ cfg.cls += ' carousel slide';
+
+ cfg.cn = [{
+ cls : 'carousel-inner',
+ cn : []
+ }];
+
+ if(this.bullets && !Roo.isTouch){
+
+ var bullets = {
+ cls : 'carousel-bullets',
+ cn : []
+ };
+
+ if(this.bullets_cls){
+ bullets.cls = bullets.cls + ' ' + this.bullets_cls;
}
+
+ bullets.cn.push({
+ cls : 'clear'
+ });
+
+ cfg.cn[0].cn.push(bullets);
}
- if (!cell.initialClassName) {
- cell.initialClassName = cell.dom.className;
+ if(this.showarrow){
+ cfg.cn[0].cn.push({
+ tag : 'div',
+ class : 'carousel-arrow',
+ cn : [
+ {
+ tag : 'div',
+ class : 'carousel-prev',
+ cn : [
+ {
+ tag : 'i',
+ class : 'fa fa-chevron-left'
+ }
+ ]
+ },
+ {
+ tag : 'div',
+ class : 'carousel-next',
+ cn : [
+ {
+ tag : 'i',
+ class : 'fa fa-chevron-right'
+ }
+ ]
+ }
+ ]
+ });
}
- cell.dom.className = cell.initialClassName + ' ' + cell.className;
- };
-
- var i = 0;
-
- for(; i < startingPos; i++) {
- textEls[i].innerHTML = (++prevStart);
- d.setDate(d.getDate()+1);
-
- cells[i].className = "fc-past fc-other-month";
- setCellClass(this, cells[i]);
}
- var intDay = 0;
-
- for(; i < days; i++){
- intDay = i - startingPos + 1;
- textEls[i].innerHTML = (intDay);
- d.setDate(d.getDate()+1);
-
- cells[i].className = ''; // "x-date-active";
- setCellClass(this, cells[i]);
- }
- var extraDays = 0;
+ return cfg;
+ },
+
+ initEvents: function()
+ {
+// if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
+// this.el.on("touchstart", this.onTouchStart, this);
+// }
- for(; i < 42; i++) {
- textEls[i].innerHTML = (++extraDays);
- d.setDate(d.getDate()+1);
+ if(this.autoslide){
+ var _this = this;
- cells[i].className = "fc-future fc-other-month";
- setCellClass(this, cells[i]);
+ this.slideFn = window.setInterval(function() {
+ _this.showPanelNext();
+ }, this.timer);
}
- this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
-
- var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
-
- this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
- this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
-
- if(totalRows != 6){
- this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
- this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
+ if(this.showarrow){
+ this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
+ this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
}
- this.fireEvent('monthchange', this, date);
-
-
- /*
- if(!this.internalRender){
- var main = this.el.dom.firstChild;
- var w = main.offsetWidth;
- this.el.setWidth(w + this.el.getBorderWidth("lr"));
- Roo.fly(main).setWidth(w);
- this.internalRender = true;
- // opera does not respect the auto grow header center column
- // then, after it gets a width opera refuses to recalculate
- // without a second pass
- if(Roo.isOpera && !this.secondPass){
- main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
- this.secondPass = true;
- this.update.defer(10, this, [date]);
- }
- }
- */
},
- findCell : function(dt) {
- dt = dt.clearTime().getTime();
- var ret = false;
- this.cells.each(function(c){
- //Roo.log("check " +c.dateValue + '?=' + dt);
- if(c.dateValue == dt){
- ret = c;
+// onTouchStart : function(e, el, o)
+// {
+// if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
+// return;
+// }
+//
+// this.showPanelNext();
+// },
+
+
+ getChildContainer : function()
+ {
+ return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
+ },
+
+ /**
+ * register a Navigation item
+ * @param {Roo.bootstrap.nav.Item} the navitem to add
+ */
+ register : function(item)
+ {
+ this.tabs.push( item);
+ item.navId = this.navId; // not really needed..
+ this.addBullet();
+
+ },
+
+ getActivePanel : function()
+ {
+ var r = false;
+ Roo.each(this.tabs, function(t) {
+ if (t.active) {
+ r = t;
return false;
}
- return true;
+ return null;
});
+ return r;
- return ret;
},
-
- findCells : function(ev) {
- var s = ev.start.clone().clearTime().getTime();
- // Roo.log(s);
- var e= ev.end.clone().clearTime().getTime();
- // Roo.log(e);
- var ret = [];
- this.cells.each(function(c){
- ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
-
- if(c.dateValue > e){
- return ;
+ getPanelByName : function(n)
+ {
+ var r = false;
+ Roo.each(this.tabs, function(t) {
+ if (t.tabId == n) {
+ r = t;
+ return false;
}
- if(c.dateValue < s){
- return ;
+ return null;
+ });
+ return r;
+ },
+ indexOfPanel : function(p)
+ {
+ var r = false;
+ Roo.each(this.tabs, function(t,i) {
+ if (t.tabId == p.tabId) {
+ r = i;
+ return false;
}
- ret.push(c);
+ return null;
});
-
- return ret;
+ return r;
},
-
-// findBestRow: function(cells)
-// {
-// var ret = 0;
-//
-// for (var i =0 ; i < cells.length;i++) {
-// ret = Math.max(cells[i].rows || 0,ret);
-// }
-// return ret;
-//
-// },
-
-
- addItem : function(ev)
+ /**
+ * show a specific panel
+ * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
+ * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
+ */
+ showPanel : function (pan)
{
- // look for vertical location slot in
- var cells = this.findCells(ev);
+ if(this.transition || typeof(pan) == 'undefined'){
+ Roo.log("waiting for the transitionend");
+ return false;
+ }
-// ev.row = this.findBestRow(cells);
+ if (typeof(pan) == 'number') {
+ pan = this.tabs[pan];
+ }
- // work out the location.
+ if (typeof(pan) == 'string') {
+ pan = this.getPanelByName(pan);
+ }
- var crow = false;
- var rows = [];
- for(var i =0; i < cells.length; i++) {
+ var cur = this.getActivePanel();
+
+ if(!pan || !cur){
+ Roo.log('pan or acitve pan is undefined');
+ return false;
+ }
+
+ if (pan.tabId == this.getActivePanel().tabId) {
+ return true;
+ }
+
+ if (false === cur.fireEvent('beforedeactivate')) {
+ return false;
+ }
+
+ if(this.bullets > 0 && !Roo.isTouch){
+ this.setActiveBullet(this.indexOfPanel(pan));
+ }
+
+ if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
- cells[i].row = cells[0].row;
+ //class="carousel-item carousel-item-next carousel-item-left"
- if(i == 0){
- cells[i].row = cells[i].row + 1;
- }
+ this.transition = true;
+ var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
+ var lr = dir == 'next' ? 'left' : 'right';
+ pan.el.addClass(dir); // or prev
+ pan.el.addClass('carousel-item-' + dir); // or prev
+ pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
+ cur.el.addClass(lr); // or right
+ pan.el.addClass(lr);
+ cur.el.addClass('carousel-item-' +lr); // or right
+ pan.el.addClass('carousel-item-' +lr);
- if (!crow) {
- crow = {
- start : cells[i],
- end : cells[i]
- };
- continue;
- }
- if (crow.start.getY() == cells[i].getY()) {
- // on same row.
- crow.end = cells[i];
- continue;
- }
- // different row.
- rows.push(crow);
- crow = {
- start: cells[i],
- end : cells[i]
- };
+ var _this = this;
+ cur.el.on('transitionend', function() {
+ Roo.log("trans end?");
+
+ pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
+ pan.setActive(true);
+
+ cur.el.removeClass([lr, 'carousel-item-' + lr]);
+ cur.setActive(false);
+
+ _this.transition = false;
+
+ }, this, { single: true } );
+
+ return true;
}
- rows.push(crow);
- ev.els = [];
- ev.rows = rows;
- ev.cells = cells;
+ cur.setActive(false);
+ pan.setActive(true);
- cells[0].events.push(ev);
+ return true;
- this.calevents.push(ev);
},
-
- clearEvents: function() {
+ showPanelNext : function()
+ {
+ var i = this.indexOfPanel(this.getActivePanel());
- if(!this.calevents){
+ if (i >= this.tabs.length - 1 && !this.autoslide) {
return;
}
- Roo.each(this.cells.elements, function(c){
- c.row = 0;
- c.events = [];
- c.more = [];
- });
+ if (i >= this.tabs.length - 1 && this.autoslide) {
+ i = -1;
+ }
- Roo.each(this.calevents, function(e) {
- Roo.each(e.els, function(el) {
- el.un('mouseenter' ,this.onEventEnter, this);
- el.un('mouseleave' ,this.onEventLeave, this);
- el.remove();
- },this);
- },this);
+ this.showPanel(this.tabs[i+1]);
+ },
+
+ showPanelPrev : function()
+ {
+ var i = this.indexOfPanel(this.getActivePanel());
- Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
- e.remove();
- });
+ if (i < 1 && !this.autoslide) {
+ return;
+ }
+
+ if (i < 1 && this.autoslide) {
+ i = this.tabs.length;
+ }
+ this.showPanel(this.tabs[i-1]);
},
- renderEvents: function()
- {
+
+ addBullet: function()
+ {
+ if(!this.bullets || Roo.isTouch){
+ return;
+ }
+ var ctr = this.el.select('.carousel-bullets',true).first();
+ var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
+ var bullet = ctr.createChild({
+ cls : 'bullet bullet-' + i
+ },ctr.dom.lastChild);
+
+
var _this = this;
- this.cells.each(function(c) {
-
- if(c.row < 5){
- return;
- }
-
- var ev = c.events;
-
- var r = 4;
- if(c.row != c.events.length){
- r = 4 - (4 - (c.row - c.events.length));
- }
-
- c.events = ev.slice(0, r);
- c.more = ev.slice(r);
-
- if(c.more.length && c.more.length == 1){
- c.events.push(c.more.pop());
- }
-
- c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
-
- });
-
- this.cells.each(function(c) {
-
- c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
-
-
- for (var e = 0; e < c.events.length; e++){
- var ev = c.events[e];
- var rows = ev.rows;
-
- for(var i = 0; i < rows.length; i++) {
-
- // how many rows should it span..
-
- var cfg = {
- cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
- style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
-
- unselectable : "on",
- cn : [
- {
- cls: 'fc-event-inner',
- cn : [
- // {
- // tag:'span',
- // cls: 'fc-event-time',
- // html : cells.length > 1 ? '' : ev.time
- // },
- {
- tag:'span',
- cls: 'fc-event-title',
- html : String.format('{0}', ev.title)
- }
-
-
- ]
- },
- {
- cls: 'ui-resizable-handle ui-resizable-e',
- html : '  '
- }
-
- ]
- };
-
- if (i == 0) {
- cfg.cls += ' fc-event-start';
- }
- if ((i+1) == rows.length) {
- cfg.cls += ' fc-event-end';
- }
-
- var ctr = _this.el.select('.fc-event-container',true).first();
- var cg = ctr.createChild(cfg);
+ bullet.on('click', (function(e, el, o, ii, t){
- var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
- var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
+ e.preventDefault();
- 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);
+ this.showPanel(ii);
- cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
- cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
- cg.on('click', _this.onEventClick, _this, ev);
+ if(this.autoslide && this.slideFn){
+ clearInterval(this.slideFn);
+ this.slideFn = window.setInterval(function() {
+ _this.showPanelNext();
+ }, this.timer);
+ }
- ev.els.push(cg);
-
- }
+ }).createDelegate(this, [i, bullet], true));
- }
-
-
- if(c.more.length){
- var cfg = {
- cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
- style : 'position: absolute',
- unselectable : "on",
- cn : [
- {
- cls: 'fc-event-inner',
- cn : [
- {
- tag:'span',
- cls: 'fc-event-title',
- html : 'More'
- }
+
+ },
+
+ setActiveBullet : function(i)
+ {
+ if(Roo.isTouch){
+ return;
+ }
+
+ Roo.each(this.el.select('.bullet', true).elements, function(el){
+ el.removeClass('selected');
+ });
+
+ var bullet = this.el.select('.bullet-' + i, true).first();
+
+ if(!bullet){
+ return;
+ }
+
+ bullet.addClass('selected');
+ }
+
+
+
+});
+
- ]
- },
- {
- cls: 'ui-resizable-handle ui-resizable-e',
- html : '  '
- }
+
+
+Roo.apply(Roo.bootstrap.TabGroup, {
+
+ groups: {},
+ /**
+ * register a Navigation Group
+ * @param {Roo.bootstrap.nav.Group} the navgroup to add
+ */
+ register : function(navgrp)
+ {
+ this.groups[navgrp.navId] = navgrp;
+
+ },
+ /**
+ * fetch a Navigation Group based on the navigation ID
+ * if one does not exist , it will get created.
+ * @param {string} the navgroup to add
+ * @returns {Roo.bootstrap.nav.Group} the navgroup
+ */
+ get: function(navId) {
+ if (typeof(this.groups[navId]) == 'undefined') {
+ this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
+ }
+ return this.groups[navId] ;
+ }
+
+
+
+});
- ]
- };
+ /*
+ * - LGPL
+ *
+ * TabPanel
+ *
+ */
- var ctr = _this.el.select('.fc-event-container',true).first();
- var cg = ctr.createChild(cfg);
+/**
+ * @class Roo.bootstrap.TabPanel
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
+ * Bootstrap TabPanel class
+ * @cfg {Boolean} active panel active
+ * @cfg {String} html panel content
+ * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
+ * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
+ * @cfg {String} href click to link..
+ * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
+ *
+ *
+ * @constructor
+ * Create a new TabPanel
+ * @param {Object} config The config object
+ */
- var sbox = c.select('.fc-day-content',true).first().getBox();
- var ebox = c.select('.fc-day-content',true).first().getBox();
- //Roo.log(cg);
- cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
- cg.setWidth(ebox.right - sbox.x -2);
+Roo.bootstrap.TabPanel = function(config){
+ Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event changed
+ * Fires when the active status changes
+ * @param {Roo.bootstrap.TabPanel} this
+ * @param {Boolean} state the new state
+
+ */
+ 'changed': true,
+ /**
+ * @event beforedeactivate
+ * Fires before a tab is de-activated - can be used to do validation on a form.
+ * @param {Roo.bootstrap.TabPanel} this
+ * @return {Boolean} false if there is an error
+
+ */
+ 'beforedeactivate': true
+ });
+
+ this.tabId = this.tabId || Roo.id();
+
+};
- cg.on('click', _this.onMoreEventClick, _this, c.more);
-
- }
-
- });
+Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
+
+ active: false,
+ html: false,
+ tabId: false,
+ navId : false,
+ href : '',
+ touchSlide : false,
+ getAutoCreate : function(){
+
+ var cfg = {
+ tag: 'div',
+ // item is needed for carousel - not sure if it has any effect otherwise
+ cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
+ html: this.html || ''
+ };
+ if(this.active){
+ cfg.cls += ' active';
+ }
+ if(this.tabId){
+ cfg.tabId = this.tabId;
+ }
+
+
+
+ return cfg;
},
- onEventEnter: function (e, el,event,d) {
- this.fireEvent('evententer', this, el, event);
- },
-
- onEventLeave: function (e, el,event,d) {
- this.fireEvent('eventleave', this, el, event);
- },
-
- onEventClick: function (e, el,event,d) {
- this.fireEvent('eventclick', this, el, event);
- },
-
- onMonthChange: function () {
- this.store.load();
- },
-
- onMoreEventClick: function(e, el, more)
+ initEvents: function()
{
- var _this = this;
-
- this.calpopover.placement = 'right';
- this.calpopover.setTitle('More');
-
- this.calpopover.setContent('');
+ var p = this.parent();
- var ctr = this.calpopover.el.select('.popover-content', true).first();
+ this.navId = this.navId || p.navId;
- Roo.each(more, function(m){
- var cfg = {
- cls : 'fc-event-hori fc-event-draggable',
- html : m.title
- };
- var cg = ctr.createChild(cfg);
+ if (typeof(this.navId) != 'undefined') {
+ // not really needed.. but just in case.. parent should be a NavGroup.
+ var tg = Roo.bootstrap.TabGroup.get(this.navId);
- cg.on('click', _this.onEventClick, _this, m);
- });
+ tg.register(this);
+
+ var i = tg.tabs.length - 1;
+
+ if(this.active && tg.bullets > 0 && i < tg.bullets){
+ tg.setActiveBullet(i);
+ }
+ }
- this.calpopover.show(el);
+ this.el.on('click', this.onClick, this);
+ if(Roo.isTouch && this.touchSlide){
+ this.el.on("touchstart", this.onTouchStart, this);
+ this.el.on("touchmove", this.onTouchMove, this);
+ this.el.on("touchend", this.onTouchEnd, this);
+ }
},
- onLoad: function ()
- {
- this.calevents = [];
- var cal = this;
+ onRender : function(ct, position)
+ {
+ Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
+ },
+
+ setActive : function(state)
+ {
+ Roo.log("panel - set active " + this.tabId + "=" + state);
- if(this.store.getCount() > 0){
- this.store.data.each(function(d){
- cal.addItem({
- id : d.data.id,
- start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
- end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
- time : d.data.start_time,
- title : d.data.title,
- description : d.data.description,
- venue : d.data.venue
- });
- });
+ this.active = state;
+ if (!state) {
+ this.el.removeClass('active');
+
+ } else if (!this.el.hasClass('active')) {
+ this.el.addClass('active');
}
- this.renderEvents();
+ this.fireEvent('changed', this, state);
+ },
+
+ onClick : function(e)
+ {
+ e.preventDefault();
- if(this.calevents.length && this.loadMask){
- this.maskEl.hide();
+ if(!this.href.length){
+ return;
}
+
+ window.location.href = this.href;
},
- onBeforeLoad: function()
+ startX : 0,
+ startY : 0,
+ endX : 0,
+ endY : 0,
+ swiping : false,
+
+ onTouchStart : function(e)
{
- this.clearEvents();
- if(this.loadMask){
- this.maskEl.show();
+ this.swiping = false;
+
+ this.startX = e.browserEvent.touches[0].clientX;
+ this.startY = e.browserEvent.touches[0].clientY;
+ },
+
+ onTouchMove : function(e)
+ {
+ this.swiping = true;
+
+ this.endX = e.browserEvent.touches[0].clientX;
+ this.endY = e.browserEvent.touches[0].clientY;
+ },
+
+ onTouchEnd : function(e)
+ {
+ if(!this.swiping){
+ this.onClick(e);
+ return;
+ }
+
+ var tabGroup = this.parent();
+
+ if(this.endX > this.startX){ // swiping right
+ tabGroup.showPanelPrev();
+ return;
+ }
+
+ if(this.startX > this.endX){ // swiping left
+ tabGroup.showPanelNext();
+ return;
}
}
+
+
});
+
+
/*
* - LGPL
*
- * element
+ * DateField
*
*/
/**
- * @class Roo.bootstrap.Popover
- * @extends Roo.bootstrap.Component
- * Bootstrap Popover class
- * @cfg {String} html contents of the popover (or false to use children..)
- * @cfg {String} title of popover (or false to hide)
- * @cfg {String} placement how it is placed
- * @cfg {String} trigger click || hover (or false to trigger manually)
- * @cfg {String} over what (parent or false to trigger manually.)
- * @cfg {Number} delay - delay before showing
-
+ * @class Roo.bootstrap.form.DateField
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap DateField class
+ * @cfg {Number} weekStart default 0
+ * @cfg {String} viewMode default empty, (months|years)
+ * @cfg {String} minViewMode default empty, (months|years)
+ * @cfg {Number} startDate default -Infinity
+ * @cfg {Number} endDate default Infinity
+ * @cfg {Boolean} todayHighlight default false
+ * @cfg {Boolean} todayBtn default false
+ * @cfg {Boolean} calendarWeeks default false
+ * @cfg {Object} daysOfWeekDisabled default empty
+ * @cfg {Boolean} singleMode default false (true | false)
+ *
+ * @cfg {Boolean} keyboardNavigation default true
+ * @cfg {String} language default en
+ *
* @constructor
- * Create a new Popover
+ * Create a new DateField
* @param {Object} config The config object
*/
-Roo.bootstrap.Popover = function(config){
- Roo.bootstrap.Popover.superclass.constructor.call(this, config);
-
- this.addEvents({
- // raw events
- /**
- * @event show
- * After the popover show
- *
- * @param {Roo.bootstrap.Popover} this
- */
- "show" : true,
- /**
- * @event hide
- * After the popover hide
- *
- * @param {Roo.bootstrap.Popover} this
- */
- "hide" : true
- });
+Roo.bootstrap.form.DateField = function(config){
+ Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event show
+ * Fires when this field show.
+ * @param {Roo.bootstrap.form.DateField} this
+ * @param {Mixed} date The date value
+ */
+ show : true,
+ /**
+ * @event show
+ * Fires when this field hide.
+ * @param {Roo.bootstrap.form.DateField} this
+ * @param {Mixed} date The date value
+ */
+ hide : true,
+ /**
+ * @event select
+ * Fires when select a date.
+ * @param {Roo.bootstrap.form.DateField} this
+ * @param {Mixed} date The date value
+ */
+ select : true,
+ /**
+ * @event beforeselect
+ * Fires when before select a date.
+ * @param {Roo.bootstrap.form.DateField} this
+ * @param {Mixed} date The date value
+ */
+ beforeselect : true
+ });
};
-Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
- title: 'Fill in a title',
- html: false,
+ /**
+ * @cfg {String} format
+ * The default date format string which can be overriden for localization support. The format must be
+ * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
+ */
+ format : "m/d/y",
+ /**
+ * @cfg {String} altFormats
+ * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
+ * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
+ */
+ altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
- placement : 'right',
- trigger : 'hover', // hover
+ weekStart : 0,
- delay : 0,
+ viewMode : '',
- over: 'parent',
+ minViewMode : '',
- can_build_overlaid : false,
+ todayHighlight : false,
- getChildContainer : function()
+ todayBtn: false,
+
+ language: 'en',
+
+ keyboardNavigation: true,
+
+ calendarWeeks: false,
+
+ startDate: -Infinity,
+
+ endDate: Infinity,
+
+ daysOfWeekDisabled: [],
+
+ _events: [],
+
+ singleMode : false,
+
+ UTCDate: function()
{
- return this.el.select('.popover-content',true).first();
+ return new Date(Date.UTC.apply(Date, arguments));
},
- getAutoCreate : function(){
-
- var cfg = {
- cls : 'popover roo-dynamic',
- style: 'display:block',
- cn : [
- {
- cls : 'arrow'
- },
- {
- cls : 'popover-inner',
- cn : [
- {
- tag: 'h3',
- cls: 'popover-title popover-header',
- html : this.title
- },
- {
- cls : 'popover-content popover-body',
- html : this.html
- }
- ]
-
- }
- ]
- };
-
- return cfg;
- },
- setTitle: function(str)
+ UTCToday: function()
{
- this.title = str;
- this.el.select('.popover-title',true).first().dom.innerHTML = str;
+ var today = new Date();
+ return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
},
- setContent: function(str)
+
+ getDate: function() {
+ var d = this.getUTCDate();
+ return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
+ },
+
+ getUTCDate: function() {
+ return this.date;
+ },
+
+ setDate: function(d) {
+ this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
+ },
+
+ setUTCDate: function(d) {
+ this.date = d;
+ this.setValue(this.formatDate(this.date));
+ },
+
+ onRender: function(ct, position)
{
- this.html = str;
- this.el.select('.popover-content',true).first().dom.innerHTML = str;
+
+ Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
+
+ this.language = this.language || 'en';
+ this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
+ this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
+
+ this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
+ this.format = this.format || 'm/d/y';
+ this.isInline = false;
+ this.isInput = true;
+ this.component = this.el.select('.add-on', true).first() || false;
+ this.component = (this.component && this.component.length === 0) ? false : this.component;
+ this.hasInput = this.component && this.inputEl().length;
+
+ if (typeof(this.minViewMode === 'string')) {
+ switch (this.minViewMode) {
+ case 'months':
+ this.minViewMode = 1;
+ break;
+ case 'years':
+ this.minViewMode = 2;
+ break;
+ default:
+ this.minViewMode = 0;
+ break;
+ }
+ }
+
+ if (typeof(this.viewMode === 'string')) {
+ switch (this.viewMode) {
+ case 'months':
+ this.viewMode = 1;
+ break;
+ case 'years':
+ this.viewMode = 2;
+ break;
+ default:
+ this.viewMode = 0;
+ break;
+ }
+ }
+
+ this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
+
+// this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
+
+ this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.picker().on('mousedown', this.onMousedown, this);
+ this.picker().on('click', this.onClick, this);
+
+ this.picker().addClass('datepicker-dropdown');
+
+ this.startViewMode = this.viewMode;
+
+ if(this.singleMode){
+ Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
+ v.setVisibilityMode(Roo.Element.DISPLAY);
+ v.hide();
+ });
+
+ Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
+ v.setStyle('width', '189px');
+ });
+ }
+
+ Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
+ if(!this.calendarWeeks){
+ v.remove();
+ return;
+ }
+
+ v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
+ v.attr('colspan', function(i, val){
+ return parseInt(val) + 1;
+ });
+ });
+
+
+ this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
+
+ this.setStartDate(this.startDate);
+ this.setEndDate(this.endDate);
+
+ this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
+
+ this.fillDow();
+ this.fillMonths();
+ this.update();
+ this.showMode();
+
+ if(this.isInline) {
+ this.showPopup();
+ }
},
- // as it get's added to the bottom of the page.
- onRender : function(ct, position)
+
+ picker : function()
{
- Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
- if(!this.el){
- var cfg = Roo.apply({}, this.getAutoCreate());
- cfg.id = Roo.id();
-
- if (this.cls) {
- cfg.cls += ' ' + this.cls;
- }
- if (this.style) {
- cfg.style = this.style;
- }
- //Roo.log("adding to ");
- this.el = Roo.get(document.body).createChild(cfg, position);
-// Roo.log(this.el);
- }
- this.initEvents();
+ return this.pickerEl;
+// return this.el.select('.datepicker', true).first();
},
- initEvents : function()
+ fillDow: function()
{
- this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
- this.el.enableDisplayMode('block');
- this.el.hide();
- if (this.over === false) {
- return;
- }
- if (this.triggers === false) {
- return;
+ var dowCnt = this.weekStart;
+
+ var dow = {
+ tag: 'tr',
+ cn: [
+
+ ]
+ };
+
+ if(this.calendarWeeks){
+ dow.cn.push({
+ tag: 'th',
+ cls: 'cw',
+ html: ' '
+ })
}
- var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
- var triggers = this.trigger ? this.trigger.split(' ') : [];
- Roo.each(triggers, function(trigger) {
- if (trigger == 'click') {
- on_el.on('click', this.toggle, this);
- } else if (trigger != 'manual') {
- var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
- var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
-
- on_el.on(eventIn ,this.enter, this);
- on_el.on(eventOut, this.leave, this);
- }
- }, this);
+ while (dowCnt < this.weekStart + 7) {
+ dow.cn.push({
+ tag: 'th',
+ cls: 'dow',
+ html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
+ });
+ }
+ this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
},
-
- // private
- timeout : null,
- hoverState : null,
-
- toggle : function () {
- this.hoverState == 'in' ? this.leave() : this.enter();
- },
-
- enter : function () {
+ fillMonths: function()
+ {
+ var i = 0;
+ var months = this.picker().select('>.datepicker-months td', true).first();
- clearTimeout(this.timeout);
-
- this.hoverState = 'in';
-
- if (!this.delay || !this.delay.show) {
- this.show();
- return;
+ months.dom.innerHTML = '';
+
+ while (i < 12) {
+ var month = {
+ tag: 'span',
+ cls: 'month',
+ html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
+ };
+
+ months.createChild(month);
}
- var _t = this;
- this.timeout = setTimeout(function () {
- if (_t.hoverState == 'in') {
- _t.show();
- }
- }, this.delay.show)
+
},
- leave : function() {
- clearTimeout(this.timeout);
-
- this.hoverState = 'out';
-
- if (!this.delay || !this.delay.hide) {
- this.hide();
- return;
+ update: function()
+ {
+ this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
+
+ if (this.date < this.startDate) {
+ this.viewDate = new Date(this.startDate);
+ } else if (this.date > this.endDate) {
+ this.viewDate = new Date(this.endDate);
+ } else {
+ this.viewDate = new Date(this.date);
}
- var _t = this;
- this.timeout = setTimeout(function () {
- if (_t.hoverState == 'out') {
- _t.hide();
- }
- }, this.delay.hide)
+
+ this.fill();
},
- show : function (on_el)
+ fill: function()
{
- if (!on_el) {
- on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
- }
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth(),
+ startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
+ startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
+ endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
+ endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
+ currentDate = this.date && this.date.valueOf(),
+ today = this.UTCToday();
- // set content.
- this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
- if (this.html !== false) {
- this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
- }
- this.el.removeClass([
- 'fade','top','bottom', 'left', 'right','in',
- 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
- ]);
- if (!this.title.length) {
- this.el.select('.popover-title',true).hide();
- }
+ this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
- var placement = typeof this.placement == 'function' ?
- this.placement.call(this, this.el, on_el) :
- this.placement;
-
- var autoToken = /\s?auto?\s?/i;
- var autoPlace = autoToken.test(placement);
- if (autoPlace) {
- placement = placement.replace(autoToken, '') || 'top';
- }
+// this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
- //this.el.detach()
- //this.el.setXY([0,0]);
- this.el.show();
- this.el.dom.style.display='block';
- this.el.addClass(placement);
+// this.picker.select('>tfoot th.today').
+// .text(dates[this.language].today)
+// .toggle(this.todayBtn !== false);
+
+ this.updateNavArrows();
+ this.fillMonths();
+
+ var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
- //this.el.appendTo(on_el);
+ day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
+
+ prevMonth.setUTCDate(day);
- var p = this.getPosition();
- var box = this.el.getBox();
+ prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
- if (autoPlace) {
- // fixme..
- }
- var align = Roo.bootstrap.Popover.alignment[placement];
+ var nextMonth = new Date(prevMonth);
-// Roo.log(align);
- this.el.alignTo(on_el, align[0],align[1]);
- //var arrow = this.el.select('.arrow',true).first();
- //arrow.set(align[2],
+ nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
- this.el.addClass('in');
+ nextMonth = nextMonth.valueOf();
+ var fillMonths = false;
- if (this.el.hasClass('fade')) {
- // fade it?
+ this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
+
+ while(prevMonth.valueOf() <= nextMonth) {
+ var clsName = '';
+
+ if (prevMonth.getUTCDay() === this.weekStart) {
+ if(fillMonths){
+ this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
+ }
+
+ fillMonths = {
+ tag: 'tr',
+ cn: []
+ };
+
+ if(this.calendarWeeks){
+ // ISO 8601: First week contains first thursday.
+ // ISO also states week starts on Monday, but we can be more abstract here.
+ var
+ // Start of current week: based on weekstart/current date
+ ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
+ // Thursday of this week
+ th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
+ // First Thursday of year, year from thursday
+ yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
+ // Calendar week: ms between thursdays, div ms per day, div 7 days
+ calWeek = (th - yth) / 864e5 / 7 + 1;
+
+ fillMonths.cn.push({
+ tag: 'td',
+ cls: 'cw',
+ html: calWeek
+ });
+ }
+ }
+
+ if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
+ clsName += ' old';
+ } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
+ clsName += ' new';
+ }
+ if (this.todayHighlight &&
+ prevMonth.getUTCFullYear() == today.getFullYear() &&
+ prevMonth.getUTCMonth() == today.getMonth() &&
+ prevMonth.getUTCDate() == today.getDate()) {
+ clsName += ' today';
+ }
+
+ if (currentDate && prevMonth.valueOf() === currentDate) {
+ clsName += ' active';
+ }
+
+ if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
+ this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
+ clsName += ' disabled';
+ }
+
+ fillMonths.cn.push({
+ tag: 'td',
+ cls: 'day ' + clsName,
+ html: prevMonth.getDate()
+ });
+
+ prevMonth.setDate(prevMonth.getDate()+1);
}
+
+ var currentYear = this.date && this.date.getUTCFullYear();
+ var currentMonth = this.date && this.date.getUTCMonth();
- this.hoverState = 'in';
+ this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
- this.fireEvent('show', this);
+ Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
+ v.removeClass('active');
+
+ if(currentYear === year && k === currentMonth){
+ v.addClass('active');
+ }
+
+ if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
+ v.addClass('disabled');
+ }
+
+ });
- },
- hide : function()
- {
- this.el.setXY([0,0]);
- this.el.removeClass('in');
- this.el.hide();
- this.hoverState = null;
- this.fireEvent('hide', this);
- }
-
-});
-
-Roo.bootstrap.Popover.alignment = {
- 'left' : ['r-l', [-10,0], 'right bs-popover-right'],
- 'right' : ['l-r', [10,0], 'left bs-popover-left'],
- 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
- 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
-};
-
- /*
- * - LGPL
- *
- * Progress
- *
- */
-
-/**
- * @class Roo.bootstrap.Progress
- * @extends Roo.bootstrap.Component
- * Bootstrap Progress class
- * @cfg {Boolean} striped striped of the progress bar
- * @cfg {Boolean} active animated of the progress bar
- *
- *
- * @constructor
- * Create a new Progress
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.Progress = function(config){
- Roo.bootstrap.Progress.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
-
- striped : false,
- active: false,
-
- getAutoCreate : function(){
- var cfg = {
- tag: 'div',
- cls: 'progress'
- };
+ year = parseInt(year/10, 10) * 10;
+
+ this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
+ this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
- if(this.striped){
- cfg.cls += ' progress-striped';
+ year -= 1;
+ for (var i = -1; i < 11; i++) {
+ this.picker().select('>.datepicker-years tbody td',true).first().createChild({
+ tag: 'span',
+ cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
+ html: year
+ });
+
+ year += 1;
}
-
- if(this.active){
- cfg.cls += ' active';
+ },
+
+ showMode: function(dir)
+ {
+ if (dir) {
+ this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
}
-
- return cfg;
- }
-
-});
-
-
-
- /*
- * - LGPL
- *
- * ProgressBar
- *
- */
-
-/**
- * @class Roo.bootstrap.ProgressBar
- * @extends Roo.bootstrap.Component
- * Bootstrap ProgressBar class
- * @cfg {Number} aria_valuenow aria-value now
- * @cfg {Number} aria_valuemin aria-value min
- * @cfg {Number} aria_valuemax aria-value max
- * @cfg {String} label label for the progress bar
- * @cfg {String} panel (success | info | warning | danger )
- * @cfg {String} role role of the progress bar
- * @cfg {String} sr_only text
- *
- *
- * @constructor
- * Create a new ProgressBar
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.ProgressBar = function(config){
- Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
-
- aria_valuenow : 0,
- aria_valuemin : 0,
- aria_valuemax : 100,
- label : false,
- panel : false,
- role : false,
- sr_only: false,
+ Roo.each(this.picker().select('>div',true).elements, function(v){
+ v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+ v.hide();
+ });
+ this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
+ },
- getAutoCreate : function()
+ place: function()
{
+ if(this.isInline) {
+ return;
+ }
- var cfg = {
- tag: 'div',
- cls: 'progress-bar',
- style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
- };
+ this.picker().removeClass(['bottom', 'top']);
- if(this.sr_only){
- cfg.cn = {
- tag: 'span',
- cls: 'sr-only',
- html: this.sr_only
- }
+ 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(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
+
+ return;
}
- if(this.role){
- cfg.role = this.role;
- }
+ this.picker().addClass('bottom');
- if(this.aria_valuenow){
- cfg['aria-valuenow'] = this.aria_valuenow;
+ this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
+ },
+
+ parseDate : function(value)
+ {
+ if(!value || value instanceof Date){
+ return value;
}
-
- if(this.aria_valuemin){
- cfg['aria-valuemin'] = this.aria_valuemin;
+ var v = Date.parseDate(value, this.format);
+ if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
+ v = Date.parseDate(value, 'Y-m-d');
}
-
- if(this.aria_valuemax){
- cfg['aria-valuemax'] = this.aria_valuemax;
+ if(!v && this.altFormats){
+ if(!this.altFormatsArray){
+ this.altFormatsArray = this.altFormats.split("|");
+ }
+ for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
+ v = Date.parseDate(value, this.altFormatsArray[i]);
+ }
}
+ return v;
+ },
+
+ formatDate : function(date, fmt)
+ {
+ return (!date || !(date instanceof Date)) ?
+ date : date.dateFormat(fmt || this.format);
+ },
+
+ onFocus : function()
+ {
+ Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
+ this.showPopup();
+ },
+
+ onBlur : function()
+ {
+ Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
- if(this.label && !this.sr_only){
- cfg.html = this.label;
- }
+ var d = this.inputEl().getValue();
- if(this.panel){
- cfg.cls += ' progress-bar-' + this.panel;
+ this.setValue(d);
+
+ this.hidePopup();
+ },
+
+ showPopup : function()
+ {
+ this.picker().show();
+ this.update();
+ this.place();
+
+ this.fireEvent('showpopup', this, this.date);
+ },
+
+ hidePopup : function()
+ {
+ if(this.isInline) {
+ return;
}
+ this.picker().hide();
+ this.viewMode = this.startViewMode;
+ this.showMode();
+
+ this.fireEvent('hidepopup', this, this.date);
- return cfg;
},
- update : function(aria_valuenow)
+ onMousedown: function(e)
+ {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ keyup: function(e)
+ {
+ Roo.bootstrap.form.DateField.superclass.keyup.call(this);
+ this.update();
+ },
+
+ setValue: function(v)
{
- this.aria_valuenow = aria_valuenow;
+ if(this.fireEvent('beforeselect', this, v) !== false){
+ var d = new Date(this.parseDate(v) ).clearTime();
- this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
- }
-
-});
+ if(isNaN(d.getTime())){
+ this.date = this.viewDate = '';
+ Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
+ return;
+ }
-
+ v = this.formatDate(d);
- /*
- * - LGPL
- *
- * column
- *
- */
+ Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
-/**
- * @class Roo.bootstrap.TabGroup
- * @extends Roo.bootstrap.Column
- * Bootstrap Column class
- * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
- * @cfg {Boolean} carousel true to make the group behave like a carousel
- * @cfg {Boolean} bullets show bullets for the panels
- * @cfg {Boolean} autoslide (true|false) auto slide .. default false
- * @cfg {Number} timer auto slide timer .. default 0 millisecond
- * @cfg {Boolean} showarrow (true|false) show arrow default true
- *
- * @constructor
- * Create a new TabGroup
- * @param {Object} config The config object
- */
+ this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
-Roo.bootstrap.TabGroup = function(config){
- Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
- if (!this.navId) {
- this.navId = Roo.id();
- }
- this.tabs = [];
- Roo.bootstrap.TabGroup.register(this);
-
-};
+ this.update();
-Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
+ this.fireEvent('select', this, this.date);
+ }
+ },
- carousel : false,
- transition : false,
- bullets : 0,
- timer : 0,
- autoslide : false,
- slideFn : false,
- slideOnTouch : false,
- showarrow : true,
+ getValue: function()
+ {
+ return this.formatDate(this.date);
+ },
- getAutoCreate : function()
+ fireKey: function(e)
{
- var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
-
- cfg.cls += ' tab-content';
+ if (!this.picker().isVisible()){
+ if (e.keyCode == 27) { // allow escape to hide and re-show picker
+ this.showPopup();
+ }
+ return;
+ }
- if (this.carousel) {
- cfg.cls += ' carousel slide';
-
- cfg.cn = [{
- cls : 'carousel-inner',
- cn : []
- }];
+ var dateChanged = false,
+ dir, day, month,
+ newDate, newViewDate;
- if(this.bullets && !Roo.isTouch){
-
- var bullets = {
- cls : 'carousel-bullets',
- cn : []
- };
-
- if(this.bullets_cls){
- bullets.cls = bullets.cls + ' ' + this.bullets_cls;
+ switch(e.keyCode){
+ case 27: // escape
+ this.hidePopup();
+ e.preventDefault();
+ break;
+ case 37: // left
+ case 39: // right
+ if (!this.keyboardNavigation) {
+ break;
}
+ dir = e.keyCode == 37 ? -1 : 1;
- bullets.cn.push({
- cls : 'clear'
- });
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue(this.formatDate(this.date));
+// this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 38: // up
+ case 40: // down
+ if (!this.keyboardNavigation) {
+ break;
+ }
+ dir = e.keyCode == 38 ? -1 : 1;
+ if (e.ctrlKey){
+ newDate = this.moveYear(this.date, dir);
+ newViewDate = this.moveYear(this.viewDate, dir);
+ } else if (e.shiftKey){
+ newDate = this.moveMonth(this.date, dir);
+ newViewDate = this.moveMonth(this.viewDate, dir);
+ } else {
+ newDate = new Date(this.date);
+ newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
+ newViewDate = new Date(this.viewDate);
+ newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
+ }
+ if (this.dateWithinRange(newDate)){
+ this.date = newDate;
+ this.viewDate = newViewDate;
+ this.setValue(this.formatDate(this.date));
+// this.update();
+ e.preventDefault();
+ dateChanged = true;
+ }
+ break;
+ case 13: // enter
+ this.setValue(this.formatDate(this.date));
+ this.hidePopup();
+ e.preventDefault();
+ break;
+ case 9: // tab
+ this.setValue(this.formatDate(this.date));
+ this.hidePopup();
+ break;
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
+ break;
+ default :
+ this.hidePopup();
- cfg.cn[0].cn.push(bullets);
- }
-
- if(this.showarrow){
- cfg.cn[0].cn.push({
- tag : 'div',
- class : 'carousel-arrow',
- cn : [
- {
- tag : 'div',
- class : 'carousel-prev',
- cn : [
- {
- tag : 'i',
- class : 'fa fa-chevron-left'
- }
- ]
- },
- {
- tag : 'div',
- class : 'carousel-next',
- cn : [
- {
- tag : 'i',
- class : 'fa fa-chevron-right'
- }
- ]
- }
- ]
- });
- }
-
}
-
- return cfg;
},
- initEvents: function()
+
+ onClick: function(e)
{
-// if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
-// this.el.on("touchstart", this.onTouchStart, this);
-// }
+ e.stopPropagation();
+ e.preventDefault();
- if(this.autoslide){
- var _this = this;
-
- this.slideFn = window.setInterval(function() {
- _this.showPanelNext();
- }, this.timer);
- }
+ var target = e.getTarget();
- if(this.showarrow){
- this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
- this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
+ if(target.nodeName.toLowerCase() === 'i'){
+ target = Roo.get(target).dom.parentNode;
}
+ var nodeName = target.nodeName;
+ var className = target.className;
+ var html = target.innerHTML;
+ //Roo.log(nodeName);
+ switch(nodeName.toLowerCase()) {
+ case 'th':
+ switch(className) {
+ case 'switch':
+ this.showMode(1);
+ break;
+ case 'prev':
+ case 'next':
+ var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
+ switch(this.viewMode){
+ case 0:
+ this.viewDate = this.moveMonth(this.viewDate, dir);
+ break;
+ case 1:
+ case 2:
+ this.viewDate = this.moveYear(this.viewDate, dir);
+ break;
+ }
+ this.fill();
+ break;
+ case 'today':
+ var date = new Date();
+ this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
+// this.fill()
+ this.setValue(this.formatDate(this.date));
+
+ this.hidePopup();
+ break;
+ }
+ break;
+ case 'span':
+ if (className.indexOf('disabled') < 0) {
+ if (!this.viewDate) {
+ this.viewDate = new Date();
+ }
+ this.viewDate.setUTCDate(1);
+ if (className.indexOf('month') > -1) {
+ this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
+ } else {
+ var year = parseInt(html, 10) || 0;
+ this.viewDate.setUTCFullYear(year);
+
+ }
+
+ if(this.singleMode){
+ this.setValue(this.formatDate(this.viewDate));
+ this.hidePopup();
+ return;
+ }
+
+ this.showMode(-1);
+ this.fill();
+ }
+ break;
+
+ case 'td':
+ //Roo.log(className);
+ if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
+ var day = parseInt(html, 10) || 1;
+ var year = (this.viewDate || new Date()).getUTCFullYear(),
+ month = (this.viewDate || new Date()).getUTCMonth();
+
+ if (className.indexOf('old') > -1) {
+ if(month === 0 ){
+ month = 11;
+ year -= 1;
+ }else{
+ month -= 1;
+ }
+ } else if (className.indexOf('new') > -1) {
+ if (month == 11) {
+ month = 0;
+ year += 1;
+ } else {
+ month += 1;
+ }
+ }
+ //Roo.log([year,month,day]);
+ this.date = this.UTCDate(year, month, day,0,0,0,0);
+ this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
+// this.fill();
+ //Roo.log(this.formatDate(this.date));
+ this.setValue(this.formatDate(this.date));
+ this.hidePopup();
+ }
+ break;
+ }
},
-// onTouchStart : function(e, el, o)
-// {
-// if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
-// return;
-// }
-//
-// this.showPanelNext();
-// },
-
-
- getChildContainer : function()
+ setStartDate: function(startDate)
{
- return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
+ this.startDate = startDate || -Infinity;
+ if (this.startDate !== -Infinity) {
+ this.startDate = this.parseDate(this.startDate);
+ }
+ this.update();
+ this.updateNavArrows();
},
-
- /**
- * register a Navigation item
- * @param {Roo.bootstrap.NavItem} the navitem to add
- */
- register : function(item)
+
+ setEndDate: function(endDate)
{
- this.tabs.push( item);
- item.navId = this.navId; // not really needed..
- this.addBullet();
+ this.endDate = endDate || Infinity;
+ if (this.endDate !== Infinity) {
+ this.endDate = this.parseDate(this.endDate);
+ }
+ this.update();
+ this.updateNavArrows();
+ },
+ setDaysOfWeekDisabled: function(daysOfWeekDisabled)
+ {
+ this.daysOfWeekDisabled = daysOfWeekDisabled || [];
+ if (typeof(this.daysOfWeekDisabled) !== 'object') {
+ this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
+ }
+ this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
+ return parseInt(d, 10);
+ });
+ this.update();
+ this.updateNavArrows();
},
- getActivePanel : function()
+ updateNavArrows: function()
{
- var r = false;
- Roo.each(this.tabs, function(t) {
- if (t.active) {
- r = t;
- return false;
+ if(this.singleMode){
+ return;
+ }
+
+ var d = new Date(this.viewDate),
+ year = d.getUTCFullYear(),
+ month = d.getUTCMonth();
+
+ Roo.each(this.picker().select('.prev', true).elements, function(v){
+ v.show();
+ switch (this.viewMode) {
+ case 0:
+
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
+ v.hide();
+ }
+ break;
+ case 1:
+ case 2:
+ if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
+ v.hide();
+ }
+ break;
}
- return null;
});
- return r;
+ Roo.each(this.picker().select('.next', true).elements, function(v){
+ v.show();
+ switch (this.viewMode) {
+ case 0:
+
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
+ v.hide();
+ }
+ break;
+ case 1:
+ case 2:
+ if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
+ v.hide();
+ }
+ break;
+ }
+ })
},
- getPanelByName : function(n)
+
+ moveMonth: function(date, dir)
{
- var r = false;
- Roo.each(this.tabs, function(t) {
- if (t.tabId == n) {
- r = t;
- return false;
+ if (!dir) {
+ return date;
+ }
+ var new_date = new Date(date.valueOf()),
+ day = new_date.getUTCDate(),
+ month = new_date.getUTCMonth(),
+ mag = Math.abs(dir),
+ new_month, test;
+ dir = dir > 0 ? 1 : -1;
+ if (mag == 1){
+ test = dir == -1
+ // If going back one month, make sure month is not current month
+ // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
+ ? function(){
+ return new_date.getUTCMonth() == month;
}
- return null;
- });
- return r;
+ // If going forward one month, make sure month is as expected
+ // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
+ : function(){
+ return new_date.getUTCMonth() != new_month;
+ };
+ new_month = month + dir;
+ new_date.setUTCMonth(new_month);
+ // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
+ if (new_month < 0 || new_month > 11) {
+ new_month = (new_month + 12) % 12;
+ }
+ } else {
+ // For magnitudes >1, move one month at a time...
+ for (var i=0; i<mag; i++) {
+ // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
+ new_date = this.moveMonth(new_date, dir);
+ }
+ // ...then reset the day, keeping it in the new month
+ new_month = new_date.getUTCMonth();
+ new_date.setUTCDate(day);
+ test = function(){
+ return new_month != new_date.getUTCMonth();
+ };
+ }
+ // Common date-resetting loop -- if date is beyond end of month, make it
+ // end of month
+ while (test()){
+ new_date.setUTCDate(--day);
+ new_date.setUTCMonth(new_month);
+ }
+ return new_date;
},
- indexOfPanel : function(p)
+
+ moveYear: function(date, dir)
{
- var r = false;
- Roo.each(this.tabs, function(t,i) {
- if (t.tabId == p.tabId) {
- r = i;
- return false;
- }
- return null;
- });
- return r;
+ return this.moveMonth(date, dir*12);
},
- /**
- * show a specific panel
- * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
- * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
- */
- showPanel : function (pan)
+
+ dateWithinRange: function(date)
{
- if(this.transition || typeof(pan) == 'undefined'){
- Roo.log("waiting for the transitionend");
+ return date >= this.startDate && date <= this.endDate;
+ },
+
+
+ remove: function()
+ {
+ this.picker().remove();
+ },
+
+ validateValue : function(value)
+ {
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
+ }
+
+ if(value.length < 1) {
+ if(this.allowBlank){
+ return true;
+ }
return false;
}
- if (typeof(pan) == 'number') {
- pan = this.tabs[pan];
+ if(value.length < this.minLength){
+ return false;
+ }
+ if(value.length > this.maxLength){
+ return false;
+ }
+ if(this.vtype){
+ var vt = Roo.form.VTypes;
+ if(!vt[this.vtype](value, this)){
+ return false;
+ }
}
-
- if (typeof(pan) == 'string') {
- pan = this.getPanelByName(pan);
+ if(typeof this.validator == "function"){
+ var msg = this.validator(value);
+ if(msg !== true){
+ return false;
+ }
}
- var cur = this.getActivePanel();
-
- if(!pan || !cur){
- Roo.log('pan or acitve pan is undefined');
+ if(this.regex && !this.regex.test(value)){
return false;
}
- if (pan.tabId == this.getActivePanel().tabId) {
- return true;
- }
-
- if (false === cur.fireEvent('beforedeactivate')) {
+ if(typeof(this.parseDate(value)) == 'undefined'){
return false;
}
- if(this.bullets > 0 && !Roo.isTouch){
- this.setActiveBullet(this.indexOfPanel(pan));
- }
+ if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
+ return false;
+ }
- if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
-
- //class="carousel-item carousel-item-next carousel-item-left"
-
- this.transition = true;
- var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
- var lr = dir == 'next' ? 'left' : 'right';
- pan.el.addClass(dir); // or prev
- pan.el.addClass('carousel-item-' + dir); // or prev
- pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
- cur.el.addClass(lr); // or right
- pan.el.addClass(lr);
- cur.el.addClass('carousel-item-' +lr); // or right
- pan.el.addClass('carousel-item-' +lr);
-
-
- var _this = this;
- cur.el.on('transitionend', function() {
- Roo.log("trans end?");
-
- pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
- pan.setActive(true);
-
- cur.el.removeClass([lr, 'carousel-item-' + lr]);
- cur.setActive(false);
-
- _this.transition = false;
-
- }, this, { single: true } );
-
- return true;
- }
+ if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
+ return false;
+ }
- cur.setActive(false);
- pan.setActive(true);
return true;
-
},
- showPanelNext : function()
+
+ reset : function()
{
- var i = this.indexOfPanel(this.getActivePanel());
-
- if (i >= this.tabs.length - 1 && !this.autoslide) {
- return;
- }
+ this.date = this.viewDate = '';
- if (i >= this.tabs.length - 1 && this.autoslide) {
- i = -1;
+ Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
+ }
+
+});
+
+Roo.apply(Roo.bootstrap.form.DateField, {
+
+ head : {
+ tag: 'thead',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'th',
+ cls: 'prev',
+ html: '<i class="fa fa-arrow-left"/>'
+ },
+ {
+ tag: 'th',
+ cls: 'switch',
+ colspan: '5'
+ },
+ {
+ tag: 'th',
+ cls: 'next',
+ html: '<i class="fa fa-arrow-right"/>'
+ }
+
+ ]
}
-
- this.showPanel(this.tabs[i+1]);
+ ]
},
- showPanelPrev : function()
- {
- var i = this.indexOfPanel(this.getActivePanel());
-
- if (i < 1 && !this.autoslide) {
- return;
+ content : {
+ tag: 'tbody',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ colspan: '7'
+ }
+ ]
}
-
- if (i < 1 && this.autoslide) {
- i = this.tabs.length;
+ ]
+ },
+
+ footer : {
+ tag: 'tfoot',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'th',
+ colspan: '7',
+ cls: 'today'
+ }
+
+ ]
}
-
- this.showPanel(this.tabs[i-1]);
+ ]
},
+ dates:{
+ en: {
+ days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
+ daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
+ daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
+ today: "Today"
+ }
+ },
- addBullet: function()
+ modes: [
{
- if(!this.bullets || Roo.isTouch){
- return;
+ clsName: 'days',
+ navFnc: 'Month',
+ navStep: 1
+ },
+ {
+ clsName: 'months',
+ navFnc: 'FullYear',
+ navStep: 1
+ },
+ {
+ clsName: 'years',
+ navFnc: 'FullYear',
+ navStep: 10
+ }]
+});
+
+Roo.apply(Roo.bootstrap.form.DateField, {
+
+ template : {
+ tag: 'div',
+ cls: 'datepicker dropdown-menu roo-dynamic shadow',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'datepicker-days',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.form.DateField.head,
+ {
+ tag: 'tbody'
+ },
+ Roo.bootstrap.form.DateField.footer
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'div',
+ cls: 'datepicker-months',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.form.DateField.head,
+ Roo.bootstrap.form.DateField.content,
+ Roo.bootstrap.form.DateField.footer
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'div',
+ cls: 'datepicker-years',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.form.DateField.head,
+ Roo.bootstrap.form.DateField.content,
+ Roo.bootstrap.form.DateField.footer
+ ]
+ }
+ ]
}
- var ctr = this.el.select('.carousel-bullets',true).first();
- var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
- var bullet = ctr.createChild({
- cls : 'bullet bullet-' + i
- },ctr.dom.lastChild);
-
-
- var _this = this;
-
- bullet.on('click', (function(e, el, o, ii, t){
+ ]
+ }
+});
- e.preventDefault();
+
- this.showPanel(ii);
+ /*
+ * - LGPL
+ *
+ * TimeField
+ *
+ */
- if(this.autoslide && this.slideFn){
- clearInterval(this.slideFn);
- this.slideFn = window.setInterval(function() {
- _this.showPanelNext();
- }, this.timer);
- }
+/**
+ * @class Roo.bootstrap.form.TimeField
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap DateField class
+ *
+ *
+ * @constructor
+ * Create a new TimeField
+ * @param {Object} config The config object
+ */
- }).createDelegate(this, [i, bullet], true));
-
+Roo.bootstrap.form.TimeField = function(config){
+ Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
+ this.addEvents({
+ /**
+ * @event show
+ * Fires when this field show.
+ * @param {Roo.bootstrap.form.DateField} thisthis
+ * @param {Mixed} date The date value
+ */
+ show : true,
+ /**
+ * @event show
+ * Fires when this field hide.
+ * @param {Roo.bootstrap.form.DateField} this
+ * @param {Mixed} date The date value
+ */
+ hide : true,
+ /**
+ * @event select
+ * Fires when select a date.
+ * @param {Roo.bootstrap.form.DateField} this
+ * @param {Mixed} date The date value
+ */
+ select : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
+
+ /**
+ * @cfg {String} format
+ * The default time format string which can be overriden for localization support. The format must be
+ * valid according to {@link Date#parseDate} (defaults to 'H:i').
+ */
+ format : "H:i",
+
+ getAutoCreate : function()
+ {
+ this.after = '<i class="fa far fa-clock"></i>';
+ return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
+
},
-
- setActiveBullet : function(i)
+ onRender: function(ct, position)
{
- if(Roo.isTouch){
- return;
- }
- Roo.each(this.el.select('.bullet', true).elements, function(el){
- el.removeClass('selected');
- });
-
- var bullet = this.el.select('.bullet-' + i, true).first();
+ Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
+
+ this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
- if(!bullet){
- return;
- }
+ this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- bullet.addClass('selected');
- }
-
+ this.pop = this.picker().select('>.datepicker-time',true).first();
+ this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.picker().on('mousedown', this.onMousedown, this);
+ this.picker().on('click', this.onClick, this);
+
+ this.picker().addClass('datepicker-dropdown');
-
-});
+ this.fillTime();
+ this.update();
+
+ this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
+ this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
+ this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
+ this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
+ this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
+ this.pop.select('button.ok', true).first().on('click', this.setTime, this);
-
+ },
+
+ fireKey: function(e){
+ if (!this.picker().isVisible()){
+ if (e.keyCode == 27) { // allow escape to hide and re-show picker
+ this.show();
+ }
+ return;
+ }
-
-
-Roo.apply(Roo.bootstrap.TabGroup, {
+ e.preventDefault();
+
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ break;
+ case 37: // left
+ case 39: // right
+ this.onTogglePeriod();
+ break;
+ case 38: // up
+ this.onIncrementMinutes();
+ break;
+ case 40: // down
+ this.onDecrementMinutes();
+ break;
+ case 13: // enter
+ case 9: // tab
+ this.setTime();
+ break;
+ }
+ },
- groups: {},
- /**
- * register a Navigation Group
- * @param {Roo.bootstrap.NavGroup} the navgroup to add
- */
- register : function(navgrp)
+ onClick: function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ picker : function()
{
- this.groups[navgrp.navId] = navgrp;
-
+ return this.pickerEl;
+ },
+
+ fillTime: function()
+ {
+ var time = this.pop.select('tbody', true).first();
+
+ time.dom.innerHTML = '';
+
+ time.createChild({
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'btn',
+ cn: [
+ {
+ tag: 'i',
+ cls: 'hours-up fa fas fa-chevron-up'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
+ },
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'btn',
+ cn: [
+ {
+ tag: 'i',
+ cls: 'minutes-up fa fas fa-chevron-up'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
+ }
+ ]
+ });
+
+ time.createChild({
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'timepicker-hour',
+ html: '00'
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator',
+ html: ':'
+ },
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'timepicker-minute',
+ html: '00'
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
+ },
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'button',
+ type: 'button',
+ cls: 'btn btn-primary period',
+ html: 'AM'
+
+ }
+ ]
+ }
+ ]
+ });
+
+ time.createChild({
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'btn',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'hours-down fa fas fa-chevron-down'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
+ },
+ {
+ tag: 'td',
+ cn: [
+ {
+ tag: 'a',
+ href: '#',
+ cls: 'btn',
+ cn: [
+ {
+ tag: 'span',
+ cls: 'minutes-down fa fas fa-chevron-down'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'td',
+ cls: 'separator'
+ }
+ ]
+ });
+
},
- /**
- * fetch a Navigation Group based on the navigation ID
- * if one does not exist , it will get created.
- * @param {string} the navgroup to add
- * @returns {Roo.bootstrap.NavGroup} the navgroup
- */
- get: function(navId) {
- if (typeof(this.groups[navId]) == 'undefined') {
- this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
- }
- return this.groups[navId] ;
- }
-
-
-
-});
-
- /*
- * - LGPL
- *
- * TabPanel
- *
- */
-
-/**
- * @class Roo.bootstrap.TabPanel
- * @extends Roo.bootstrap.Component
- * Bootstrap TabPanel class
- * @cfg {Boolean} active panel active
- * @cfg {String} html panel content
- * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
- * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
- * @cfg {String} href click to link..
- *
- *
- * @constructor
- * Create a new TabPanel
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.TabPanel = function(config){
- Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event changed
- * Fires when the active status changes
- * @param {Roo.bootstrap.TabPanel} this
- * @param {Boolean} state the new state
-
- */
- 'changed': true,
- /**
- * @event beforedeactivate
- * Fires before a tab is de-activated - can be used to do validation on a form.
- * @param {Roo.bootstrap.TabPanel} this
- * @return {Boolean} false if there is an error
-
- */
- 'beforedeactivate': true
- });
-
- this.tabId = this.tabId || Roo.id();
-
-};
-
-Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
- active: false,
- html: false,
- tabId: false,
- navId : false,
- href : '',
+ update: function()
+ {
+
+ this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
+
+ this.fill();
+ },
- getAutoCreate : function(){
+ fill: function()
+ {
+ var hours = this.time.getHours();
+ var minutes = this.time.getMinutes();
+ var period = 'AM';
-
- var cfg = {
- tag: 'div',
- // item is needed for carousel - not sure if it has any effect otherwise
- cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
- html: this.html || ''
- };
+ if(hours > 11){
+ period = 'PM';
+ }
- if(this.active){
- cfg.cls += ' active';
+ if(hours == 0){
+ hours = 12;
}
- if(this.tabId){
- cfg.tabId = this.tabId;
+
+ if(hours > 12){
+ hours = hours - 12;
}
-
+ if(hours < 10){
+ hours = '0' + hours;
+ }
+
+ if(minutes < 10){
+ minutes = '0' + minutes;
+ }
+
+ this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
+ this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
+ this.pop.select('button', true).first().dom.innerHTML = period;
- return cfg;
},
- initEvents: function()
- {
- var p = this.parent();
+ place: function()
+ {
+ this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
- this.navId = this.navId || p.navId;
+ var cls = ['bottom'];
- if (typeof(this.navId) != 'undefined') {
- // not really needed.. but just in case.. parent should be a NavGroup.
- var tg = Roo.bootstrap.TabGroup.get(this.navId);
-
- tg.register(this);
-
- var i = tg.tabs.length - 1;
-
- if(this.active && tg.bullets > 0 && i < tg.bullets){
- tg.setActiveBullet(i);
- }
+ 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.el.on('click', this.onClick, this);
+ cls.push('right');
- if(Roo.isTouch){
- this.el.on("touchstart", this.onTouchStart, this);
- this.el.on("touchmove", this.onTouchMove, this);
- this.el.on("touchend", this.onTouchEnd, this);
+ 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().setXY(20000,20000);
+ this.picker().addClass(cls.join('-'));
+
+ var _this = this;
+
+ Roo.each(cls, function(c){
+ if(c == 'bottom'){
+ (function() {
+ //
+ }).defer(200);
+ _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
+ //_this.picker().setTop(_this.inputEl().getHeight());
+ return;
+ }
+ if(c == 'top'){
+ _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
+
+ //_this.picker().setTop(0 - _this.picker().getHeight());
+ return;
+ }
+ /*
+ if(c == 'left'){
+ _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
+ return;
+ }
+ if(c == 'right'){
+ _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
+ return;
+ }
+ */
+ });
},
+
+ onFocus : function()
+ {
+ Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
+ this.show();
+ },
- onRender : function(ct, position)
+ onBlur : function()
{
- Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
+ Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
+ this.hide();
},
- setActive : function(state)
+ show : function()
{
- Roo.log("panel - set active " + this.tabId + "=" + state);
+ this.picker().show();
+ this.pop.show();
+ this.update();
+ this.place();
- this.active = state;
- if (!state) {
- this.el.removeClass('active');
-
- } else if (!this.el.hasClass('active')) {
- this.el.addClass('active');
- }
+ this.fireEvent('show', this, this.date);
+ },
+
+ hide : function()
+ {
+ this.picker().hide();
+ this.pop.hide();
- this.fireEvent('changed', this, state);
+ this.fireEvent('hide', this, this.date);
},
- onClick : function(e)
+ setTime : function()
{
- e.preventDefault();
+ this.hide();
+ this.setValue(this.time.format(this.format));
+
+ this.fireEvent('select', this, this.date);
- if(!this.href.length){
- return;
- }
- window.location.href = this.href;
},
- startX : 0,
- startY : 0,
- endX : 0,
- endY : 0,
- swiping : false,
+ onMousedown: function(e){
+ e.stopPropagation();
+ e.preventDefault();
+ },
- onTouchStart : function(e)
+ onIncrementHours: function()
{
- this.swiping = false;
+ Roo.log('onIncrementHours');
+ this.time = this.time.add(Date.HOUR, 1);
+ this.update();
- this.startX = e.browserEvent.touches[0].clientX;
- this.startY = e.browserEvent.touches[0].clientY;
},
- onTouchMove : function(e)
+ onDecrementHours: function()
{
- this.swiping = true;
-
- this.endX = e.browserEvent.touches[0].clientX;
- this.endY = e.browserEvent.touches[0].clientY;
+ Roo.log('onDecrementHours');
+ this.time = this.time.add(Date.HOUR, -1);
+ this.update();
},
- onTouchEnd : function(e)
+ onIncrementMinutes: function()
{
- if(!this.swiping){
- this.onClick(e);
- return;
- }
-
- var tabGroup = this.parent();
-
- if(this.endX > this.startX){ // swiping right
- tabGroup.showPanelPrev();
- return;
- }
-
- if(this.startX > this.endX){ // swiping left
- tabGroup.showPanelNext();
- return;
- }
- }
+ Roo.log('onIncrementMinutes');
+ this.time = this.time.add(Date.MINUTE, 1);
+ this.update();
+ },
+
+ onDecrementMinutes: function()
+ {
+ Roo.log('onDecrementMinutes');
+ this.time = this.time.add(Date.MINUTE, -1);
+ this.update();
+ },
+ onTogglePeriod: function()
+ {
+ Roo.log('onTogglePeriod');
+ this.time = this.time.add(Date.HOUR, 12);
+ this.update();
+ }
+
});
+Roo.apply(Roo.bootstrap.form.TimeField, {
+
+ template : {
+ tag: 'div',
+ cls: 'datepicker dropdown-menu',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'datepicker-time',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ {
+ tag: 'tbody',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ colspan: '7'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag: 'tfoot',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'th',
+ colspan: '7',
+ cls: '',
+ cn: [
+ {
+ tag: 'button',
+ cls: 'btn btn-info ok',
+ html: 'OK'
+ }
+ ]
+ }
+
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+});
+
/*
* - LGPL
*
- * DateField
+ * MonthField
*
*/
/**
- * @class Roo.bootstrap.DateField
- * @extends Roo.bootstrap.Input
- * Bootstrap DateField class
- * @cfg {Number} weekStart default 0
- * @cfg {String} viewMode default empty, (months|years)
- * @cfg {String} minViewMode default empty, (months|years)
- * @cfg {Number} startDate default -Infinity
- * @cfg {Number} endDate default Infinity
- * @cfg {Boolean} todayHighlight default false
- * @cfg {Boolean} todayBtn default false
- * @cfg {Boolean} calendarWeeks default false
- * @cfg {Object} daysOfWeekDisabled default empty
- * @cfg {Boolean} singleMode default false (true | false)
+ * @class Roo.bootstrap.form.MonthField
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap MonthField class
*
- * @cfg {Boolean} keyboardNavigation default true
* @cfg {String} language default en
*
* @constructor
- * Create a new DateField
+ * Create a new MonthField
* @param {Object} config The config object
*/
-Roo.bootstrap.DateField = function(config){
- Roo.bootstrap.DateField.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event show
- * Fires when this field show.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- show : true,
- /**
- * @event show
- * Fires when this field hide.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- hide : true,
- /**
- * @event select
- * Fires when select a date.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- select : true,
- /**
- * @event beforeselect
- * Fires when before select a date.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- beforeselect : true
- });
+Roo.bootstrap.form.MonthField = function(config){
+ Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ /**
+ * @event show
+ * Fires when this field show.
+ * @param {Roo.bootstrap.form.MonthField} this
+ * @param {Mixed} date The date value
+ */
+ show : true,
+ /**
+ * @event show
+ * Fires when this field hide.
+ * @param {Roo.bootstrap.form.MonthField} this
+ * @param {Mixed} date The date value
+ */
+ hide : true,
+ /**
+ * @event select
+ * Fires when select a date.
+ * @param {Roo.bootstrap.form.MonthField} this
+ * @param {String} oldvalue The old value
+ * @param {String} newvalue The new value
+ */
+ select : true
+ });
};
-Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
-
- /**
- * @cfg {String} format
- * The default date format string which can be overriden for localization support. The format must be
- * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
- */
- format : "m/d/y",
- /**
- * @cfg {String} altFormats
- * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
- * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
- */
- altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
-
- weekStart : 0,
-
- viewMode : '',
-
- minViewMode : '',
-
- todayHighlight : false,
-
- todayBtn: false,
-
- language: 'en',
-
- keyboardNavigation: true,
-
- calendarWeeks: false,
-
- startDate: -Infinity,
-
- endDate: Infinity,
-
- daysOfWeekDisabled: [],
-
- _events: [],
+Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
- singleMode : false,
-
- UTCDate: function()
- {
- return new Date(Date.UTC.apply(Date, arguments));
- },
-
- UTCToday: function()
- {
- var today = new Date();
- return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
- },
-
- getDate: function() {
- var d = this.getUTCDate();
- return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
- },
-
- getUTCDate: function() {
- return this.date;
- },
-
- setDate: function(d) {
- this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
- },
-
- setUTCDate: function(d) {
- this.date = d;
- this.setValue(this.formatDate(this.date));
- },
-
onRender: function(ct, position)
{
- Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
+ Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
this.language = this.language || 'en';
- this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
- this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
+ this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
+ this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
- this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
- this.format = this.format || 'm/d/y';
+ this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
this.isInline = false;
this.isInput = true;
this.component = this.el.select('.add-on', true).first() || false;
- this.component = (this.component && this.component.length === 0) ? false : this.component;
- this.hasInput = this.component && this.inputEl().length;
-
- if (typeof(this.minViewMode === 'string')) {
- switch (this.minViewMode) {
- case 'months':
- this.minViewMode = 1;
- break;
- case 'years':
- this.minViewMode = 2;
- break;
- default:
- this.minViewMode = 0;
- break;
- }
- }
-
- if (typeof(this.viewMode === 'string')) {
- switch (this.viewMode) {
- case 'months':
- this.viewMode = 1;
- break;
- case 'years':
- this.viewMode = 2;
- break;
- default:
- this.viewMode = 0;
- break;
- }
- }
-
- this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
-
-// this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
-
- this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-
- this.picker().on('mousedown', this.onMousedown, this);
- this.picker().on('click', this.onClick, this);
-
- this.picker().addClass('datepicker-dropdown');
-
- this.startViewMode = this.viewMode;
-
- if(this.singleMode){
- Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
- v.setVisibilityMode(Roo.Element.DISPLAY);
- v.hide();
- });
-
- Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
- v.setStyle('width', '189px');
- });
- }
-
- Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
- if(!this.calendarWeeks){
- v.remove();
- return;
- }
-
- v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
- v.attr('colspan', function(i, val){
- return parseInt(val) + 1;
- });
- });
-
+ this.component = (this.component && this.component.length === 0) ? false : this.component;
+ this.hasInput = this.component && this.inputEL().length;
- this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
+ this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
- this.setStartDate(this.startDate);
- this.setEndDate(this.endDate);
+ this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
+ this.picker().on('mousedown', this.onMousedown, this);
+ this.picker().on('click', this.onClick, this);
+
+ this.picker().addClass('datepicker-dropdown');
+
+ Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
+ v.setStyle('width', '189px');
+ });
- this.fillDow();
this.fillMonths();
+
this.update();
- this.showMode();
if(this.isInline) {
- this.showPopup();
+ this.show();
}
+
},
- picker : function()
+ setValue: function(v, suppressEvent)
+ {
+ var o = this.getValue();
+
+ Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
+
+ this.update();
+
+ if(suppressEvent !== true){
+ this.fireEvent('select', this, o, v);
+ }
+
+ },
+
+ getValue: function()
{
- return this.pickerEl;
-// return this.el.select('.datepicker', true).first();
+ return this.value;
},
- fillDow: function()
+ onClick: function(e)
{
- var dowCnt = this.weekStart;
+ e.stopPropagation();
+ e.preventDefault();
- var dow = {
- tag: 'tr',
- cn: [
-
- ]
- };
+ var target = e.getTarget();
- if(this.calendarWeeks){
- dow.cn.push({
- tag: 'th',
- cls: 'cw',
- html: ' '
- })
+ if(target.nodeName.toLowerCase() === 'i'){
+ target = Roo.get(target).dom.parentNode;
}
- while (dowCnt < this.weekStart + 7) {
- dow.cn.push({
- tag: 'th',
- cls: 'dow',
- html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
- });
+ var nodeName = target.nodeName;
+ var className = target.className;
+ var html = target.innerHTML;
+
+ if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
+ return;
}
- this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
+ this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
+
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+
+ this.hide();
+
+ },
+
+ picker : function()
+ {
+ return this.pickerEl;
},
fillMonths: function()
var month = {
tag: 'span',
cls: 'month',
- html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
+ html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
};
months.createChild(month);
update: function()
{
- this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
+ var _this = this;
- if (this.date < this.startDate) {
- this.viewDate = new Date(this.startDate);
- } else if (this.date > this.endDate) {
- this.viewDate = new Date(this.endDate);
- } else {
- this.viewDate = new Date(this.date);
+ if(typeof(this.vIndex) == 'undefined' && this.value.length){
+ this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
}
- this.fill();
+ Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
+ e.removeClass('active');
+
+ if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
+ e.addClass('active');
+ }
+ })
},
- fill: function()
+ place: function()
{
- var d = new Date(this.viewDate),
- year = d.getUTCFullYear(),
- month = d.getUTCMonth(),
- startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
- startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
- endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
- endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
- currentDate = this.date && this.date.valueOf(),
- today = this.UTCToday();
+ if(this.isInline) {
+ return;
+ }
+
+ this.picker().removeClass(['bottom', 'top']);
- this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
+ 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(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
+
+ return;
+ }
-// this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
+ this.picker().addClass('bottom');
-// this.picker.select('>tfoot th.today').
-// .text(dates[this.language].today)
-// .toggle(this.todayBtn !== false);
+ this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
+ },
- this.updateNavArrows();
- this.fillMonths();
-
- var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
+ onFocus : function()
+ {
+ Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
+ this.show();
+ },
+
+ onBlur : function()
+ {
+ Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
- day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
-
- prevMonth.setUTCDate(day);
+ var d = this.inputEl().getValue();
- prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
+ this.setValue(d);
+
+ this.hide();
+ },
+
+ show : function()
+ {
+ this.picker().show();
+ this.picker().select('>.datepicker-months', true).first().show();
+ this.update();
+ this.place();
- var nextMonth = new Date(prevMonth);
+ this.fireEvent('show', this, this.date);
+ },
+
+ hide : function()
+ {
+ if(this.isInline) {
+ return;
+ }
+ this.picker().hide();
+ this.fireEvent('hide', this, this.date);
- nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
+ },
+
+ onMousedown: function(e)
+ {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ keyup: function(e)
+ {
+ Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
+ this.update();
+ },
+
+ fireKey: function(e)
+ {
+ if (!this.picker().isVisible()){
+ if (e.keyCode == 27) {// allow escape to hide and re-show picker
+ this.show();
+ }
+ return;
+ }
- nextMonth = nextMonth.valueOf();
+ var dir;
- var fillMonths = false;
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ e.preventDefault();
+ break;
+ case 37: // left
+ case 39: // right
+ dir = e.keyCode == 37 ? -1 : 1;
+
+ this.vIndex = this.vIndex + dir;
+
+ if(this.vIndex < 0){
+ this.vIndex = 0;
+ }
+
+ if(this.vIndex > 11){
+ this.vIndex = 11;
+ }
+
+ if(isNaN(this.vIndex)){
+ this.vIndex = 0;
+ }
+
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+
+ break;
+ case 38: // up
+ case 40: // down
+
+ dir = e.keyCode == 38 ? -1 : 1;
+
+ this.vIndex = this.vIndex + dir * 4;
+
+ if(this.vIndex < 0){
+ this.vIndex = 0;
+ }
+
+ if(this.vIndex > 11){
+ this.vIndex = 11;
+ }
+
+ if(isNaN(this.vIndex)){
+ this.vIndex = 0;
+ }
+
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+ break;
+
+ case 13: // enter
+
+ if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+ }
+
+ this.hide();
+ e.preventDefault();
+ break;
+ case 9: // tab
+ if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
+ this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
+ }
+ this.hide();
+ break;
+ case 16: // shift
+ case 17: // ctrl
+ case 18: // alt
+ break;
+ default :
+ this.hide();
+
+ }
+ },
+
+ remove: function()
+ {
+ this.picker().remove();
+ }
+
+});
+
+Roo.apply(Roo.bootstrap.form.MonthField, {
+
+ content : {
+ tag: 'tbody',
+ cn: [
+ {
+ tag: 'tr',
+ cn: [
+ {
+ tag: 'td',
+ colspan: '7'
+ }
+ ]
+ }
+ ]
+ },
+
+ dates:{
+ en: {
+ months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
+ monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ }
+ }
+});
+
+Roo.apply(Roo.bootstrap.form.MonthField, {
+
+ template : {
+ tag: 'div',
+ cls: 'datepicker dropdown-menu roo-dynamic',
+ cn: [
+ {
+ tag: 'div',
+ cls: 'datepicker-months',
+ cn: [
+ {
+ tag: 'table',
+ cls: 'table-condensed',
+ cn:[
+ Roo.bootstrap.form.DateField.content
+ ]
+ }
+ ]
+ }
+ ]
+ }
+});
+
+
+
+
+ /*
+ * - LGPL
+ *
+ * CheckBox
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.form.CheckBox
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap CheckBox class
+ *
+ * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
+ * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
+ * @cfg {String} boxLabel The text that appears beside the checkbox
+ * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
+ * @cfg {Boolean} checked initnal the element
+ * @cfg {Boolean} inline inline the element (default false)
+ * @cfg {String} groupId the checkbox group id // normal just use for checkbox
+ * @cfg {String} tooltip label tooltip
+ *
+ * @constructor
+ * Create a new CheckBox
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.form.CheckBox = function(config){
+ Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
+
+ this.addEvents({
+ /**
+ * @event check
+ * Fires when the element is checked or unchecked.
+ * @param {Roo.bootstrap.form.CheckBox} this This input
+ * @param {Boolean} checked The new checked value
+ */
+ check : true,
+ /**
+ * @event click
+ * Fires when the element is click.
+ * @param {Roo.bootstrap.form.CheckBox} this This input
+ */
+ click : true
+ });
+
+};
+
+Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
+
+ inputType: 'checkbox',
+ inputValue: 1,
+ valueOff: 0,
+ boxLabel: false,
+ checked: false,
+ weight : false,
+ inline: false,
+ tooltip : '',
+
+ // checkbox success does not make any sense really..
+ invalidClass : "",
+ validClass : "",
+
+
+ getAutoCreate : function()
+ {
+ var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
- this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
+ var id = Roo.id();
- while(prevMonth.valueOf() <= nextMonth) {
- var clsName = '';
+ var cfg = {};
+
+ cfg.cls = 'form-group form-check ' + this.inputType; //input-group
+
+ if(this.inline){
+ cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
+ }
+
+ var input = {
+ tag: 'input',
+ id : id,
+ type : this.inputType,
+ value : this.inputValue,
+ cls : 'roo-' + this.inputType, //'form-box',
+ placeholder : this.placeholder || ''
- if (prevMonth.getUTCDay() === this.weekStart) {
- if(fillMonths){
- this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
- }
-
- fillMonths = {
- tag: 'tr',
- cn: []
- };
-
- if(this.calendarWeeks){
- // ISO 8601: First week contains first thursday.
- // ISO also states week starts on Monday, but we can be more abstract here.
- var
- // Start of current week: based on weekstart/current date
- ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
- // Thursday of this week
- th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
- // First Thursday of year, year from thursday
- yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
- // Calendar week: ms between thursdays, div ms per day, div 7 days
- calWeek = (th - yth) / 864e5 / 7 + 1;
-
- fillMonths.cn.push({
- tag: 'td',
- cls: 'cw',
- html: calWeek
- });
+ };
+
+ if(this.inputType != 'radio'){
+ var hidden = {
+ tag: 'input',
+ type : 'hidden',
+ cls : 'roo-hidden-value',
+ value : this.checked ? this.inputValue : this.valueOff
+ };
+ }
+
+
+ if (this.weight) { // Validity check?
+ cfg.cls += " " + this.inputType + "-" + this.weight;
+ }
+
+ if (this.disabled) {
+ input.disabled=true;
+ }
+
+ if(this.checked){
+ input.checked = this.checked;
+ }
+
+ if (this.name) {
+
+ input.name = this.name;
+
+ if(this.inputType != 'radio'){
+ hidden.name = this.name;
+ input.name = '_hidden_' + 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.inputType != 'radio'){
+ inputblock.cn.push(hidden);
+ }
+
+ if (this.after) {
+ inputblock.cn.push({
+ tag :'span',
+ cls : 'input-group-addon',
+ html : this.after
+ });
+ }
+
+ }
+ var boxLabelCfg = false;
+
+ if(this.boxLabel){
+
+ boxLabelCfg = {
+ tag: 'label',
+ //'for': id, // box label is handled by onclick - so no for...
+ cls: 'box-label',
+ html: this.boxLabel
+ };
+ if(this.tooltip){
+ boxLabelCfg.tooltip = this.tooltip;
+ }
+
+ }
+
+
+ if (align ==='left' && this.fieldLabel.length) {
+// Roo.log("left and has label");
+ cfg.cn = [
+ {
+ tag: 'label',
+ 'for' : id,
+ cls : 'control-label',
+ html : this.fieldLabel
+ },
+ {
+ cls : "",
+ cn: [
+ inputblock
+ ]
}
- }
+ ];
- if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
- clsName += ' old';
- } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
- clsName += ' new';
- }
- if (this.todayHighlight &&
- prevMonth.getUTCFullYear() == today.getFullYear() &&
- prevMonth.getUTCMonth() == today.getMonth() &&
- prevMonth.getUTCDate() == today.getDate()) {
- clsName += ' today';
+ if (boxLabelCfg) {
+ cfg.cn[1].cn.push(boxLabelCfg);
}
- if (currentDate && prevMonth.valueOf() === currentDate) {
- clsName += ' active';
+ if(this.labelWidth > 12){
+ cfg.cn[0].style = "width: " + this.labelWidth + 'px';
}
- if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
- this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
- clsName += ' disabled';
+ if(this.labelWidth < 13 && this.labelmd == 0){
+ this.labelmd = this.labelWidth;
}
- fillMonths.cn.push({
- tag: 'td',
- cls: 'day ' + clsName,
- html: prevMonth.getDate()
- });
+ if(this.labellg > 0){
+ cfg.cn[0].cls += ' col-lg-' + this.labellg;
+ cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
+ }
- prevMonth.setDate(prevMonth.getDate()+1);
- }
-
- var currentYear = this.date && this.date.getUTCFullYear();
- var currentMonth = this.date && this.date.getUTCMonth();
-
- this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
-
- Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
- v.removeClass('active');
+ if(this.labelmd > 0){
+ cfg.cn[0].cls += ' col-md-' + this.labelmd;
+ cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
+ }
- if(currentYear === year && k === currentMonth){
- v.addClass('active');
+ if(this.labelsm > 0){
+ cfg.cn[0].cls += ' col-sm-' + this.labelsm;
+ cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
}
- if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
- v.addClass('disabled');
+ if(this.labelxs > 0){
+ cfg.cn[0].cls += ' col-xs-' + this.labelxs;
+ cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
}
- });
-
+ } 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
+
+ ];
+ if (boxLabelCfg) {
+ cfg.cn.push(boxLabelCfg);
+ }
+
+ } else {
+
+// Roo.log(" no label && no align");
+ cfg.cn = [ inputblock ] ;
+ if (boxLabelCfg) {
+ cfg.cn.push(boxLabelCfg);
+ }
+
+
+ }
- year = parseInt(year/10, 10) * 10;
+
- this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
+ if(this.inputType != 'radio'){
+ cfg.cn.push(hidden);
+ }
- this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
+ return cfg;
- year -= 1;
- for (var i = -1; i < 11; i++) {
- this.picker().select('>.datepicker-years tbody td',true).first().createChild({
- tag: 'span',
- cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
- html: year
- });
-
- year += 1;
- }
},
- showMode: function(dir)
+ /**
+ * return the real input element.
+ */
+ inputEl: function ()
{
- if (dir) {
- this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
- }
-
- Roo.each(this.picker().select('>div',true).elements, function(v){
- v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- v.hide();
- });
- this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
+ return this.el.select('input.roo-' + this.inputType,true).first();
},
-
- place: function()
+ hiddenEl: function ()
{
- if(this.isInline) {
- return;
- }
-
- this.picker().removeClass(['bottom', 'top']);
-
- if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
- /*
- * place to the top of element!
- *
- */
-
- this.picker().addClass('top');
- this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
-
- return;
- }
-
- this.picker().addClass('bottom');
-
- this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
+ return this.el.select('input.roo-hidden-value',true).first();
},
- parseDate : function(value)
+ labelEl: function()
{
- if(!value || value instanceof Date){
- return value;
- }
- var v = Date.parseDate(value, this.format);
- if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
- v = Date.parseDate(value, 'Y-m-d');
- }
- if(!v && this.altFormats){
- if(!this.altFormatsArray){
- this.altFormatsArray = this.altFormats.split("|");
- }
- for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
- v = Date.parseDate(value, this.altFormatsArray[i]);
- }
- }
- return v;
+ return this.el.select('label.control-label',true).first();
},
+ /* depricated... */
- formatDate : function(date, fmt)
- {
- return (!date || !(date instanceof Date)) ?
- date : date.dateFormat(fmt || this.format);
+ label: function()
+ {
+ return this.labelEl();
},
- onFocus : function()
+ boxLabelEl: function()
{
- Roo.bootstrap.DateField.superclass.onFocus.call(this);
- this.showPopup();
+ return this.el.select('label.box-label',true).first();
},
- onBlur : function()
+ initEvents : function()
{
- Roo.bootstrap.DateField.superclass.onBlur.call(this);
+// Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
- var d = this.inputEl().getValue();
+ this.inputEl().on('click', this.onClick, this);
- this.setValue(d);
-
- this.hidePopup();
+ if (this.boxLabel) {
+ this.el.select('label.box-label',true).first().on('click', this.onClick, this);
+ }
+
+ this.startValue = this.getValue();
+
+ if(this.groupId){
+ Roo.bootstrap.form.CheckBox.register(this);
+ }
},
- showPopup : function()
- {
- this.picker().show();
- this.update();
- this.place();
+ onClick : function(e)
+ {
+ if(this.fireEvent('click', this, e) !== false){
+ this.setChecked(!this.checked);
+ }
- this.fireEvent('showpopup', this, this.date);
},
- hidePopup : function()
+ setChecked : function(state,suppressEvent)
{
- if(this.isInline) {
+ this.startValue = this.getValue();
+
+ if(this.inputType == 'radio'){
+
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ e.dom.checked = false;
+ });
+
+ this.inputEl().dom.checked = true;
+
+ this.inputEl().dom.value = this.inputValue;
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, true);
+ }
+
+ this.validate();
+
return;
}
- this.picker().hide();
- this.viewMode = this.startViewMode;
- this.showMode();
- this.fireEvent('hidepopup', this, this.date);
+ this.checked = state;
+ this.inputEl().dom.checked = state;
+
+
+ this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, state);
+ }
+
+ this.validate();
},
- onMousedown: function(e)
- {
- e.stopPropagation();
- e.preventDefault();
- },
-
- keyup: function(e)
- {
- Roo.bootstrap.DateField.superclass.keyup.call(this);
- this.update();
- },
-
- setValue: function(v)
+ getValue : function()
{
- if(this.fireEvent('beforeselect', this, v) !== false){
- var d = new Date(this.parseDate(v) ).clearTime();
-
- if(isNaN(d.getTime())){
- this.date = this.viewDate = '';
- Roo.bootstrap.DateField.superclass.setValue.call(this, '');
- return;
- }
-
- v = this.formatDate(d);
-
- Roo.bootstrap.DateField.superclass.setValue.call(this, v);
-
- this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
-
- this.update();
-
- this.fireEvent('select', this, this.date);
+ if(this.inputType == 'radio'){
+ return this.getGroupValue();
}
+
+ return this.hiddenEl().dom.value;
+
},
- getValue: function()
+ getGroupValue : function()
{
- return this.formatDate(this.date);
+ if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
+ return '';
+ }
+
+ return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
},
- fireKey: function(e)
+ setValue : function(v,suppressEvent)
{
- if (!this.picker().isVisible()){
- if (e.keyCode == 27) { // allow escape to hide and re-show picker
- this.showPopup();
- }
+ if(this.inputType == 'radio'){
+ this.setGroupValue(v, suppressEvent);
return;
}
- var dateChanged = false,
- dir, day, month,
- newDate, newViewDate;
+ this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
- switch(e.keyCode){
- case 27: // escape
- this.hidePopup();
- e.preventDefault();
- break;
- case 37: // left
- case 39: // right
- if (!this.keyboardNavigation) {
- break;
- }
- dir = e.keyCode == 37 ? -1 : 1;
-
- if (e.ctrlKey){
- newDate = this.moveYear(this.date, dir);
- newViewDate = this.moveYear(this.viewDate, dir);
- } else if (e.shiftKey){
- newDate = this.moveMonth(this.date, dir);
- newViewDate = this.moveMonth(this.viewDate, dir);
- } else {
- newDate = new Date(this.date);
- newDate.setUTCDate(this.date.getUTCDate() + dir);
- newViewDate = new Date(this.viewDate);
- newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
- }
- if (this.dateWithinRange(newDate)){
- this.date = newDate;
- this.viewDate = newViewDate;
- this.setValue(this.formatDate(this.date));
-// this.update();
- e.preventDefault();
- dateChanged = true;
- }
- break;
- case 38: // up
- case 40: // down
- if (!this.keyboardNavigation) {
- break;
- }
- dir = e.keyCode == 38 ? -1 : 1;
- if (e.ctrlKey){
- newDate = this.moveYear(this.date, dir);
- newViewDate = this.moveYear(this.viewDate, dir);
- } else if (e.shiftKey){
- newDate = this.moveMonth(this.date, dir);
- newViewDate = this.moveMonth(this.viewDate, dir);
- } else {
- newDate = new Date(this.date);
- newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
- newViewDate = new Date(this.viewDate);
- newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
- }
- if (this.dateWithinRange(newDate)){
- this.date = newDate;
- this.viewDate = newViewDate;
- this.setValue(this.formatDate(this.date));
-// this.update();
- e.preventDefault();
- dateChanged = true;
- }
- break;
- case 13: // enter
- this.setValue(this.formatDate(this.date));
- this.hidePopup();
- e.preventDefault();
- break;
- case 9: // tab
- this.setValue(this.formatDate(this.date));
- this.hidePopup();
- break;
- case 16: // shift
- case 17: // ctrl
- case 18: // alt
- break;
- default :
- this.hidePopup();
-
- }
+ this.validate();
},
-
- onClick: function(e)
+ setGroupValue : function(v, suppressEvent)
{
- e.stopPropagation();
- e.preventDefault();
+ this.startValue = this.getValue();
- var target = e.getTarget();
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ e.dom.checked = false;
+
+ if(e.dom.value == v){
+ e.dom.checked = true;
+ }
+ });
- if(target.nodeName.toLowerCase() === 'i'){
- target = Roo.get(target).dom.parentNode;
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, true);
}
-
- var nodeName = target.nodeName;
- var className = target.className;
- var html = target.innerHTML;
- //Roo.log(nodeName);
-
- switch(nodeName.toLowerCase()) {
- case 'th':
- switch(className) {
- case 'switch':
- this.showMode(1);
- break;
- case 'prev':
- case 'next':
- var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
- switch(this.viewMode){
- case 0:
- this.viewDate = this.moveMonth(this.viewDate, dir);
- break;
- case 1:
- case 2:
- this.viewDate = this.moveYear(this.viewDate, dir);
- break;
- }
- this.fill();
- break;
- case 'today':
- var date = new Date();
- this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
-// this.fill()
- this.setValue(this.formatDate(this.date));
-
- this.hidePopup();
- break;
- }
- break;
- case 'span':
- if (className.indexOf('disabled') < 0) {
- this.viewDate.setUTCDate(1);
- if (className.indexOf('month') > -1) {
- this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
- } else {
- var year = parseInt(html, 10) || 0;
- this.viewDate.setUTCFullYear(year);
-
- }
-
- if(this.singleMode){
- this.setValue(this.formatDate(this.viewDate));
- this.hidePopup();
- return;
- }
-
- this.showMode(-1);
- this.fill();
- }
- break;
-
- case 'td':
- //Roo.log(className);
- if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
- var day = parseInt(html, 10) || 1;
- var year = this.viewDate.getUTCFullYear(),
- month = this.viewDate.getUTCMonth();
- if (className.indexOf('old') > -1) {
- if(month === 0 ){
- month = 11;
- year -= 1;
- }else{
- month -= 1;
- }
- } else if (className.indexOf('new') > -1) {
- if (month == 11) {
- month = 0;
- year += 1;
- } else {
- month += 1;
- }
- }
- //Roo.log([year,month,day]);
- this.date = this.UTCDate(year, month, day,0,0,0,0);
- this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
-// this.fill();
- //Roo.log(this.formatDate(this.date));
- this.setValue(this.formatDate(this.date));
- this.hidePopup();
- }
- break;
- }
+ this.validate();
+
+ return;
},
- setStartDate: function(startDate)
- {
- this.startDate = startDate || -Infinity;
- if (this.startDate !== -Infinity) {
- this.startDate = this.parseDate(this.startDate);
- }
- this.update();
- this.updateNavArrows();
- },
-
- setEndDate: function(endDate)
+ validate : function()
{
- this.endDate = endDate || Infinity;
- if (this.endDate !== Infinity) {
- this.endDate = this.parseDate(this.endDate);
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
}
- this.update();
- this.updateNavArrows();
- },
-
- setDaysOfWeekDisabled: function(daysOfWeekDisabled)
- {
- this.daysOfWeekDisabled = daysOfWeekDisabled || [];
- if (typeof(this.daysOfWeekDisabled) !== 'object') {
- this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
+
+ if(
+ this.disabled ||
+ (this.inputType == 'radio' && this.validateRadio()) ||
+ (this.inputType == 'checkbox' && this.validateCheckbox())
+ ){
+ this.markValid();
+ return true;
}
- this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
- return parseInt(d, 10);
- });
- this.update();
- this.updateNavArrows();
+
+ this.markInvalid();
+ return false;
},
- updateNavArrows: function()
+ validateRadio : function()
{
- if(this.singleMode){
- return;
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
}
- var d = new Date(this.viewDate),
- year = d.getUTCFullYear(),
- month = d.getUTCMonth();
+ if(this.allowBlank){
+ return true;
+ }
- Roo.each(this.picker().select('.prev', true).elements, function(v){
- v.show();
- switch (this.viewMode) {
- case 0:
-
- if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
- v.hide();
- }
- break;
- case 1:
- case 2:
- if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
- v.hide();
- }
- break;
+ var valid = false;
+
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ if(!e.dom.checked){
+ return;
}
+
+ valid = true;
+
+ return false;
});
- Roo.each(this.picker().select('.next', true).elements, function(v){
- v.show();
- switch (this.viewMode) {
- case 0:
-
- if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
- v.hide();
- }
- break;
- case 1:
- case 2:
- if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
- v.hide();
- }
- break;
- }
- })
+ return valid;
},
- moveMonth: function(date, dir)
+ validateCheckbox : function()
{
- if (!dir) {
- return date;
+ if(!this.groupId){
+ return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
+ //return (this.getValue() == this.inputValue) ? true : false;
}
- var new_date = new Date(date.valueOf()),
- day = new_date.getUTCDate(),
- month = new_date.getUTCMonth(),
- mag = Math.abs(dir),
- new_month, test;
- dir = dir > 0 ? 1 : -1;
- if (mag == 1){
- test = dir == -1
- // If going back one month, make sure month is not current month
- // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
- ? function(){
- return new_date.getUTCMonth() == month;
- }
- // If going forward one month, make sure month is as expected
- // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
- : function(){
- return new_date.getUTCMonth() != new_month;
- };
- new_month = month + dir;
- new_date.setUTCMonth(new_month);
- // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
- if (new_month < 0 || new_month > 11) {
- new_month = (new_month + 12) % 12;
- }
- } else {
- // For magnitudes >1, move one month at a time...
- for (var i=0; i<mag; i++) {
- // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
- new_date = this.moveMonth(new_date, dir);
+
+ var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
+
+ if(!group){
+ return false;
+ }
+
+ var r = false;
+
+ for(var i in group){
+ if(group[i].el.isVisible(true)){
+ r = false;
+ break;
}
- // ...then reset the day, keeping it in the new month
- new_month = new_date.getUTCMonth();
- new_date.setUTCDate(day);
- test = function(){
- return new_month != new_date.getUTCMonth();
- };
+
+ r = true;
}
- // Common date-resetting loop -- if date is beyond end of month, make it
- // end of month
- while (test()){
- new_date.setUTCDate(--day);
- new_date.setUTCMonth(new_month);
+
+ for(var i in group){
+ if(r){
+ break;
+ }
+
+ r = (group[i].getValue() == group[i].inputValue) ? true : false;
}
- return new_date;
+
+ return r;
},
-
- moveYear: function(date, dir)
+
+ /**
+ * Mark this field as valid
+ */
+ markValid : function()
{
- return this.moveMonth(date, dir*12);
- },
+ var _this = this;
+
+ this.fireEvent('valid', this);
+
+ var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
+
+ if(this.groupId){
+ label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
+ }
+
+ if(label){
+ label.markValid();
+ }
- dateWithinRange: function(date)
- {
- return date >= this.startDate && date <= this.endDate;
- },
+ if(this.inputType == 'radio'){
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ var fg = e.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([_this.invalidClass, _this.validClass]);
+ fg.addClass(_this.validClass);
+ } else {
+ fg.removeClass(['is-valid', 'is-invalid']);
+ fg.addClass('is-valid');
+ }
+ });
+
+ return;
+ }
-
- remove: function()
- {
- this.picker().remove();
+ if(!this.groupId){
+ var fg = this.el.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([this.invalidClass, this.validClass]);
+ fg.addClass(this.validClass);
+ } else {
+ fg.removeClass(['is-valid', 'is-invalid']);
+ fg.addClass('is-valid');
+ }
+ return;
+ }
+
+ var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
+
+ if(!group){
+ return;
+ }
+
+ for(var i in group){
+ var fg = group[i].el.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([this.invalidClass, this.validClass]);
+ fg.addClass(this.validClass);
+ } else {
+ fg.removeClass(['is-valid', 'is-invalid']);
+ fg.addClass('is-valid');
+ }
+ }
},
- validateValue : function(value)
+ /**
+ * Mark this field as invalid
+ * @param {String} msg The validation message
+ */
+ markInvalid : function(msg)
{
- if(this.getVisibilityEl().hasClass('hidden')){
- return true;
+ if(this.allowBlank){
+ return;
}
- if(value.length < 1) {
- if(this.allowBlank){
- return true;
- }
- return false;
+ var _this = this;
+
+ this.fireEvent('invalid', this, msg);
+
+ var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
+
+ if(this.groupId){
+ label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
}
- if(value.length < this.minLength){
- return false;
+ if(label){
+ label.markInvalid();
}
- if(value.length > this.maxLength){
- return false;
+
+ if(this.inputType == 'radio'){
+
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ var fg = e.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([_this.invalidClass, _this.validClass]);
+ fg.addClass(_this.invalidClass);
+ } else {
+ fg.removeClass(['is-invalid', 'is-valid']);
+ fg.addClass('is-invalid');
+ }
+ });
+
+ return;
}
- if(this.vtype){
- var vt = Roo.form.VTypes;
- if(!vt[this.vtype](value, this)){
- return false;
+
+ if(!this.groupId){
+ var fg = this.el.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([_this.invalidClass, _this.validClass]);
+ fg.addClass(_this.invalidClass);
+ } else {
+ fg.removeClass(['is-invalid', 'is-valid']);
+ fg.addClass('is-invalid');
}
+ return;
}
- if(typeof this.validator == "function"){
- var msg = this.validator(value);
- if(msg !== true){
- return false;
+
+ var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
+
+ if(!group){
+ return;
+ }
+
+ for(var i in group){
+ var fg = group[i].el.findParent('.form-group', false, true);
+ if (Roo.bootstrap.version == 3) {
+ fg.removeClass([_this.invalidClass, _this.validClass]);
+ fg.addClass(_this.invalidClass);
+ } else {
+ fg.removeClass(['is-invalid', 'is-valid']);
+ fg.addClass('is-invalid');
}
}
- if(this.regex && !this.regex.test(value)){
- return false;
+ },
+
+ clearInvalid : function()
+ {
+ Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
+
+ // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
+
+ var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
+
+ if (label && label.iconEl) {
+ label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
+ label.iconEl.removeClass(['is-invalid', 'is-valid']);
+ }
+ },
+
+ disable : function()
+ {
+ if(this.inputType != 'radio'){
+ Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
+ return;
}
- if(typeof(this.parseDate(value)) == 'undefined'){
- return false;
+ var _this = this;
+
+ if(this.rendered){
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ _this.getActionEl().addClass(this.disabledClass);
+ e.dom.disabled = true;
+ });
}
- if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
- return false;
- }
+ this.disabled = true;
+ this.fireEvent("disable", this);
+ return this;
+ },
+
+ enable : function()
+ {
+ if(this.inputType != 'radio'){
+ Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
+ return;
+ }
- if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
- return false;
- }
+ var _this = this;
+ if(this.rendered){
+ Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
+ _this.getActionEl().removeClass(this.disabledClass);
+ e.dom.disabled = false;
+ });
+ }
- return true;
+ this.disabled = false;
+ this.fireEvent("enable", this);
+ return this;
},
- reset : function()
+ setBoxLabel : function(v)
{
- this.date = this.viewDate = '';
+ this.boxLabel = v;
- Roo.bootstrap.DateField.superclass.setValue.call(this, '');
+ if(this.rendered){
+ this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+ }
}
-
+
});
-Roo.apply(Roo.bootstrap.DateField, {
+Roo.apply(Roo.bootstrap.form.CheckBox, {
- head : {
- tag: 'thead',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'th',
- cls: 'prev',
- html: '<i class="fa fa-arrow-left"/>'
- },
- {
- tag: 'th',
- cls: 'switch',
- colspan: '5'
- },
- {
- tag: 'th',
- cls: 'next',
- html: '<i class="fa fa-arrow-right"/>'
- }
-
- ]
- }
- ]
- },
+ groups: {},
- content : {
- tag: 'tbody',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- colspan: '7'
- }
- ]
+ /**
+ * register a CheckBox Group
+ * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
+ */
+ register : function(checkbox)
+ {
+ if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
+ this.groups[checkbox.groupId] = {};
}
- ]
- },
-
- footer : {
- tag: 'tfoot',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'th',
- colspan: '7',
- cls: 'today'
- }
-
- ]
+
+ if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
+ return;
}
- ]
+
+ this.groups[checkbox.groupId][checkbox.name] = checkbox;
+
},
-
- dates:{
- en: {
- days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
- daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
- daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
- months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
- monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
- today: "Today"
+ /**
+ * fetch a CheckBox Group based on the group ID
+ * @param {string} the group ID
+ * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
+ */
+ get: function(groupId) {
+ if (typeof(this.groups[groupId]) == 'undefined') {
+ return false;
}
- },
+
+ return this.groups[groupId] ;
+ }
+
- modes: [
- {
- clsName: 'days',
- navFnc: 'Month',
- navStep: 1
- },
- {
- clsName: 'months',
- navFnc: 'FullYear',
- navStep: 1
- },
- {
- clsName: 'years',
- navFnc: 'FullYear',
- navStep: 10
- }]
});
+/*
+ * - LGPL
+ *
+ * RadioItem
+ *
+ */
-Roo.apply(Roo.bootstrap.DateField, {
-
- template : {
- tag: 'div',
- cls: 'datepicker dropdown-menu roo-dynamic',
- cn: [
- {
- tag: 'div',
- cls: 'datepicker-days',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.DateField.head,
- {
- tag: 'tbody'
- },
- Roo.bootstrap.DateField.footer
- ]
- }
- ]
- },
- {
- tag: 'div',
- cls: 'datepicker-months',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.DateField.head,
- Roo.bootstrap.DateField.content,
- Roo.bootstrap.DateField.footer
- ]
- }
- ]
- },
- {
- tag: 'div',
- cls: 'datepicker-years',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.DateField.head,
- Roo.bootstrap.DateField.content,
- Roo.bootstrap.DateField.footer
- ]
- }
+/**
+ * @class Roo.bootstrap.form.Radio
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Radio class
+ * @cfg {String} boxLabel - the label associated
+ * @cfg {String} value - the value of radio
+ *
+ * @constructor
+ * Create a new Radio
+ * @param {Object} config The config object
+ */
+Roo.bootstrap.form.Radio = function(config){
+ Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
+
+};
+
+Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
+
+ boxLabel : '',
+
+ value : '',
+
+ getAutoCreate : function()
+ {
+ var cfg = {
+ tag : 'div',
+ cls : 'form-group radio',
+ cn : [
+ {
+ tag : 'label',
+ cls : 'box-label',
+ html : this.boxLabel
+ }
]
+ };
+
+ return cfg;
+ },
+
+ initEvents : function()
+ {
+ this.parent().register(this);
+
+ this.el.on('click', this.onClick, this);
+
+ },
+
+ onClick : function(e)
+ {
+ if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
+ this.setChecked(true);
+ }
+ },
+
+ setChecked : function(state, suppressEvent)
+ {
+ this.parent().setValue(this.value, suppressEvent);
+
+ },
+
+ setBoxLabel : function(v)
+ {
+ this.boxLabel = v;
+
+ if(this.rendered){
+ this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
}
- ]
}
+
});
-
/*
* - LGPL
*
- * TimeField
+ * Input
*
*/
/**
- * @class Roo.bootstrap.TimeField
- * @extends Roo.bootstrap.Input
- * Bootstrap DateField class
- *
+ * @class Roo.bootstrap.form.SecurePass
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap SecurePass class
+ *
*
* @constructor
- * Create a new TimeField
+ * Create a new SecurePass
* @param {Object} config The config object
*/
+
+Roo.bootstrap.form.SecurePass = function (config) {
+ // these go here, so the translation tool can replace them..
+ this.errors = {
+ PwdEmpty: "Please type a password, and then retype it to confirm.",
+ PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
+ PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
+ PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
+ IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
+ FNInPwd: "Your password can't contain your first name. Please type a different password.",
+ LNInPwd: "Your password can't contain your last name. Please type a different password.",
+ TooWeak: "Your password is Too Weak."
+ },
+ this.meterLabel = "Password strength:";
+ this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
+ this.meterClass = [
+ "roo-password-meter-tooweak",
+ "roo-password-meter-weak",
+ "roo-password-meter-medium",
+ "roo-password-meter-strong",
+ "roo-password-meter-grey"
+ ];
+
+ this.errors = {};
+
+ Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
+}
-Roo.bootstrap.TimeField = function(config){
- Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event show
- * Fires when this field show.
- * @param {Roo.bootstrap.DateField} thisthis
- * @param {Mixed} date The date value
- */
- show : true,
- /**
- * @event show
- * Fires when this field hide.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- hide : true,
- /**
- * @event select
- * Fires when select a date.
- * @param {Roo.bootstrap.DateField} this
- * @param {Mixed} date The date value
- */
- select : true
- });
-};
-
-Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
+Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
+ /**
+ * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
+ * {
+ * PwdEmpty: "Please type a password, and then retype it to confirm.",
+ * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
+ * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
+ * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
+ * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
+ * FNInPwd: "Your password can't contain your first name. Please type a different password.",
+ * LNInPwd: "Your password can't contain your last name. Please type a different password."
+ * })
+ */
+ // private
+ meterWidth: 300,
+ errorMsg :'',
+ errors: false,
+ imageRoot: '/',
/**
- * @cfg {String} format
- * The default time format string which can be overriden for localization support. The format must be
- * valid according to {@link Date#parseDate} (defaults to 'H:i').
+ * @cfg {String/Object} Label for the strength meter (defaults to
+ * 'Password strength:')
*/
- format : "H:i",
-
- onRender: function(ct, position)
+ // private
+ meterLabel: '',
+ /**
+ * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
+ * ['Weak', 'Medium', 'Strong'])
+ */
+ // private
+ pwdStrengths: false,
+ // private
+ strength: 0,
+ // private
+ _lastPwd: null,
+ // private
+ kCapitalLetter: 0,
+ kSmallLetter: 1,
+ kDigit: 2,
+ kPunctuation: 3,
+
+ insecure: false,
+ // private
+ initEvents: function ()
+ {
+ Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
+
+ if (this.el.is('input[type=password]') && Roo.isSafari) {
+ this.el.on('keydown', this.SafariOnKeyDown, this);
+ }
+
+ this.el.on('keyup', this.checkStrength, this, {buffer: 50});
+ },
+ // private
+ onRender: function (ct, position)
+ {
+ Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
+ this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
+ this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
+
+ this.trigger.createChild({
+ cn: [
+ {
+ //id: 'PwdMeter',
+ tag: 'div',
+ cls: 'roo-password-meter-grey col-xs-12',
+ style: {
+ //width: 0,
+ //width: this.meterWidth + 'px'
+ }
+ },
+ {
+ cls: 'roo-password-meter-text'
+ }
+ ]
+ });
+
+
+ if (this.hideTrigger) {
+ this.trigger.setDisplayed(false);
+ }
+ this.setSize(this.width || '', this.height || '');
+ },
+ // private
+ onDestroy: function ()
+ {
+ if (this.trigger) {
+ this.trigger.removeAllListeners();
+ this.trigger.remove();
+ }
+ if (this.wrap) {
+ this.wrap.remove();
+ }
+ Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
+ },
+ // private
+ checkStrength: function ()
{
+ var pwd = this.inputEl().getValue();
+ if (pwd == this._lastPwd) {
+ return;
+ }
+
+ var strength;
+ if (this.ClientSideStrongPassword(pwd)) {
+ strength = 3;
+ } else if (this.ClientSideMediumPassword(pwd)) {
+ strength = 2;
+ } else if (this.ClientSideWeakPassword(pwd)) {
+ strength = 1;
+ } else {
+ strength = 0;
+ }
+
+ Roo.log('strength1: ' + strength);
- Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
+ //var pm = this.trigger.child('div/div/div').dom;
+ var pm = this.trigger.child('div/div');
+ pm.removeClass(this.meterClass);
+ pm.addClass(this.meterClass[strength]);
- this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
- this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+ var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
+
+ pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
- this.pop = this.picker().select('>.datepicker-time',true).first();
- this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+ this._lastPwd = pwd;
+ },
+ reset: function ()
+ {
+ Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
- this.picker().on('mousedown', this.onMousedown, this);
- this.picker().on('click', this.onClick, this);
+ this._lastPwd = '';
- this.picker().addClass('datepicker-dropdown');
-
- this.fillTime();
- this.update();
-
- this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
- this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
- this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
- this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
- this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
- this.pop.select('button.ok', true).first().on('click', this.setTime, this);
-
+ var pm = this.trigger.child('div/div');
+ pm.removeClass(this.meterClass);
+ pm.addClass('roo-password-meter-grey');
+
+
+ var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
+
+ pt.innerHTML = '';
+ this.inputEl().dom.type='password';
},
-
- fireKey: function(e){
- if (!this.picker().isVisible()){
- if (e.keyCode == 27) { // allow escape to hide and re-show picker
- this.show();
+ // private
+ validateValue: function (value)
+ {
+ if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
+ return false;
+ }
+ if (value.length == 0) {
+ if (this.allowBlank) {
+ this.clearInvalid();
+ return true;
}
- return;
+
+ this.markInvalid(this.errors.PwdEmpty);
+ this.errorMsg = this.errors.PwdEmpty;
+ return false;
+ }
+
+ if(this.insecure){
+ return true;
+ }
+
+ if (!value.match(/[\x21-\x7e]+/)) {
+ this.markInvalid(this.errors.PwdBadChar);
+ this.errorMsg = this.errors.PwdBadChar;
+ return false;
+ }
+ if (value.length < 6) {
+ this.markInvalid(this.errors.PwdShort);
+ this.errorMsg = this.errors.PwdShort;
+ return false;
+ }
+ if (value.length > 16) {
+ this.markInvalid(this.errors.PwdLong);
+ this.errorMsg = this.errors.PwdLong;
+ return false;
+ }
+ var strength;
+ if (this.ClientSideStrongPassword(value)) {
+ strength = 3;
+ } else if (this.ClientSideMediumPassword(value)) {
+ strength = 2;
+ } else if (this.ClientSideWeakPassword(value)) {
+ strength = 1;
+ } else {
+ strength = 0;
}
- e.preventDefault();
- switch(e.keyCode){
- case 27: // escape
- this.hide();
- break;
- case 37: // left
- case 39: // right
- this.onTogglePeriod();
- break;
- case 38: // up
- this.onIncrementMinutes();
- break;
- case 40: // down
- this.onDecrementMinutes();
- break;
- case 13: // enter
- case 9: // tab
- this.setTime();
- break;
+ if (strength < 2) {
+ //this.markInvalid(this.errors.TooWeak);
+ this.errorMsg = this.errors.TooWeak;
+ //return false;
}
+
+
+ console.log('strength2: ' + strength);
+
+ //var pm = this.trigger.child('div/div/div').dom;
+
+ var pm = this.trigger.child('div/div');
+ pm.removeClass(this.meterClass);
+ pm.addClass(this.meterClass[strength]);
+
+ var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
+
+ pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
+
+ this.errorMsg = '';
+ return true;
},
-
- onClick: function(e) {
- e.stopPropagation();
- e.preventDefault();
- },
-
- picker : function()
+ // private
+ CharacterSetChecks: function (type)
{
- return this.el.select('.datepicker', true).first();
+ this.type = type;
+ this.fResult = false;
},
-
- fillTime: function()
- {
- var time = this.pop.select('tbody', true).first();
-
- time.dom.innerHTML = '';
-
- time.createChild({
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'hours-up glyphicon glyphicon-chevron-up'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- },
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'minutes-up glyphicon glyphicon-chevron-up'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
+ // private
+ isctype: function (character, type)
+ {
+ switch (type) {
+ case this.kCapitalLetter:
+ if (character >= 'A' && character <= 'Z') {
+ return true;
}
- ]
- });
-
- time.createChild({
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- cn: [
- {
- tag: 'span',
- cls: 'timepicker-hour',
- html: '00'
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator',
- html: ':'
- },
- {
- tag: 'td',
- cn: [
- {
- tag: 'span',
- cls: 'timepicker-minute',
- html: '00'
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- },
- {
- tag: 'td',
- cn: [
- {
- tag: 'button',
- type: 'button',
- cls: 'btn btn-primary period',
- html: 'AM'
-
- }
- ]
+ break;
+
+ case this.kSmallLetter:
+ if (character >= 'a' && character <= 'z') {
+ return true;
}
- ]
- });
-
- time.createChild({
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'hours-down glyphicon glyphicon-chevron-down'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- },
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'minutes-down glyphicon glyphicon-chevron-down'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
+ break;
+
+ case this.kDigit:
+ if (character >= '0' && character <= '9') {
+ return true;
}
- ]
- });
-
+ break;
+
+ case this.kPunctuation:
+ if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
+ return true;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
},
-
- update: function()
+ // private
+ IsLongEnough: function (pwd, size)
{
-
- this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
-
- this.fill();
+ return !(pwd == null || isNaN(size) || pwd.length < size);
},
-
- fill: function()
+ // private
+ SpansEnoughCharacterSets: function (word, nb)
{
- var hours = this.time.getHours();
- var minutes = this.time.getMinutes();
- var period = 'AM';
-
- if(hours > 11){
- period = 'PM';
- }
-
- if(hours == 0){
- hours = 12;
+ if (!this.IsLongEnough(word, nb))
+ {
+ return false;
}
+
+ var characterSetChecks = new Array(
+ new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
+ new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
+ );
-
- if(hours > 12){
- hours = hours - 12;
+ for (var index = 0; index < word.length; ++index) {
+ for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
+ if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
+ characterSetChecks[nCharSet].fResult = true;
+ break;
+ }
+ }
}
-
- if(hours < 10){
- hours = '0' + hours;
+
+ var nCharSets = 0;
+ for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
+ if (characterSetChecks[nCharSet].fResult) {
+ ++nCharSets;
+ }
}
-
- if(minutes < 10){
- minutes = '0' + minutes;
+
+ if (nCharSets < nb) {
+ return false;
}
-
- this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
- this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
- this.pop.select('button', true).first().dom.innerHTML = period;
-
+ return true;
+ },
+ // private
+ ClientSideStrongPassword: function (pwd)
+ {
+ return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
},
+ // private
+ ClientSideMediumPassword: function (pwd)
+ {
+ return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
+ },
+ // private
+ ClientSideWeakPassword: function (pwd)
+ {
+ return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
+ }
+
+});
+Roo.htmleditor = {};
+
+/**
+ * @class Roo.htmleditor.Filter
+ * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
+ * @cfg {DomElement} node The node to iterate and filter
+ * @cfg {boolean|String|Array} tag Tags to replace
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
+
+
+
+Roo.htmleditor.Filter = function(cfg) {
+ Roo.apply(this.cfg);
+ // this does not actually call walk as it's really just a abstract class
+}
+
+
+Roo.htmleditor.Filter.prototype = {
- place: function()
- {
- this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
-
- var cls = ['bottom'];
-
- if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
- cls.pop();
- cls.push('top');
- }
-
- cls.push('right');
+ node: false,
+
+ tag: false,
+
+ // overrride to do replace comments.
+ replaceComment : false,
+
+ // overrride to do replace or do stuff with tags..
+ replaceTag : false,
+
+ walk : function(dom)
+ {
+ Roo.each( Array.from(dom.childNodes), function( e ) {
+ switch(true) {
+
+ case e.nodeType == 8 && this.replaceComment !== false: // comment
+ this.replaceComment(e);
+ return;
+
+ case e.nodeType != 1: //not a node.
+ return;
+
+ case this.tag === true: // everything
+ case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
+ case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
+ if (this.replaceTag && false === this.replaceTag(e)) {
+ return;
+ }
+ if (e.hasChildNodes()) {
+ this.walk(e);
+ }
+ return;
+
+ default: // tags .. that do not match.
+ if (e.hasChildNodes()) {
+ this.walk(e);
+ }
+ }
+
+ }, this);
- if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
- cls.pop();
- cls.push('left');
+ }
+};
+
+/**
+ * @class Roo.htmleditor.FilterAttributes
+ * clean attributes and styles including http:// etc.. in attribute
+ * @constructor
+* Run a new Attribute Filter
+* @param {Object} config Configuration options
+ */
+Roo.htmleditor.FilterAttributes = function(cfg)
+{
+ Roo.apply(this, cfg);
+ this.attrib_black = this.attrib_black || [];
+ this.attrib_white = this.attrib_white || [];
+
+ this.attrib_clean = this.attrib_clean || [];
+ this.style_white = this.style_white || [];
+ this.style_black = this.style_black || [];
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
+{
+ tag: true, // all tags
+
+ attrib_black : false, // array
+ attrib_clean : false,
+ attrib_white : false,
+
+ style_white : false,
+ style_black : false,
+
+
+ replaceTag : function(node)
+ {
+ if (!node.attributes || !node.attributes.length) {
+ return true;
}
- this.picker().addClass(cls.join('-'));
-
- var _this = this;
-
- Roo.each(cls, function(c){
- if(c == 'bottom'){
- _this.picker().setTop(_this.inputEl().getHeight());
- return;
+ for (var i = node.attributes.length-1; i > -1 ; i--) {
+ var a = node.attributes[i];
+ //console.log(a);
+ if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
+ node.removeAttribute(a.name);
+ continue;
}
- if(c == 'top'){
- _this.picker().setTop(0 - _this.picker().getHeight());
- return;
+
+
+
+ if (a.name.toLowerCase().substr(0,2)=='on') {
+ node.removeAttribute(a.name);
+ continue;
}
- if(c == 'left'){
- _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
- return;
+
+ if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
+ node.removeAttribute(a.name);
+ continue;
}
- if(c == 'right'){
- _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
- return;
+ if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
+ this.cleanAttr(node,a.name,a.value); // fixme..
+ continue;
}
- });
-
- },
-
- onFocus : function()
- {
- Roo.bootstrap.TimeField.superclass.onFocus.call(this);
- this.show();
- },
-
- onBlur : function()
- {
- Roo.bootstrap.TimeField.superclass.onBlur.call(this);
- this.hide();
+ if (a.name == 'style') {
+ this.cleanStyle(node,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.removeAttribute('class');
+ }
+
+ if (a.value.match(/^body$/)) {
+ node.removeAttribute('class');
+ }
+ continue;
+ }
+
+
+ // style cleanup!?
+ // class cleanup?
+
+ }
+ return true; // clean children
},
-
- show : function()
- {
- this.picker().show();
- this.pop.show();
- this.update();
- this.place();
- this.fireEvent('show', this, this.date);
- },
-
- hide : function()
+ cleanAttr: function(node, n,v)
{
- this.picker().hide();
- this.pop.hide();
- this.fireEvent('hide', this, this.date);
+ if (v.match(/^\./) || v.match(/^\//)) {
+ return;
+ }
+ if (v.match(/^(http|https):\/\//)
+ || v.match(/^mailto:/)
+ || v.match(/^ftp:/)
+ || v.match(/^data:/)
+ ) {
+ return;
+ }
+ if (v.match(/^#/)) {
+ return;
+ }
+ if (v.match(/^\{/)) { // allow template editing.
+ return;
+ }
+// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
+ node.removeAttribute(n);
+
},
-
- setTime : function()
+ cleanStyle : function(node, n,v)
{
- this.hide();
- this.setValue(this.time.format(this.format));
+ if (v.match(/expression/)) { //XSS?? should we even bother..
+ node.removeAttribute(n);
+ return;
+ }
- this.fireEvent('select', this, this.date);
+ 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 ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
+ return true;
+ }
+ //Roo.log()
+ // only allow 'c whitelisted system attributes'
+ if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
+ return true;
+ }
+
+
+ clean.push(p);
+ return true;
+ },this);
+ if (clean.length) {
+ node.setAttribute(n, clean.join(';'));
+ } else {
+ node.removeAttribute(n);
+ }
+
+ }
- },
-
- onMousedown: function(e){
- e.stopPropagation();
- e.preventDefault();
- },
-
- onIncrementHours: function()
- {
- Roo.log('onIncrementHours');
- this.time = this.time.add(Date.HOUR, 1);
- this.update();
- },
-
- onDecrementHours: function()
- {
- Roo.log('onDecrementHours');
- this.time = this.time.add(Date.HOUR, -1);
- this.update();
- },
-
- onIncrementMinutes: function()
- {
- Roo.log('onIncrementMinutes');
- this.time = this.time.add(Date.MINUTE, 1);
- this.update();
- },
-
- onDecrementMinutes: function()
- {
- Roo.log('onDecrementMinutes');
- this.time = this.time.add(Date.MINUTE, -1);
- this.update();
- },
- onTogglePeriod: function()
+});/**
+ * @class Roo.htmleditor.FilterBlack
+ * remove blacklisted elements.
+ * @constructor
+ * Run a new Blacklisted Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterBlack = function(cfg)
+{
+ Roo.apply(this, cfg);
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
+{
+ tag : true, // all elements.
+
+ replaceTag : function(n)
{
- Roo.log('onTogglePeriod');
- this.time = this.time.add(Date.HOUR, 12);
- this.update();
+ n.parentNode.removeChild(n);
}
-
-
});
+/**
+ * @class Roo.htmleditor.FilterComment
+ * remove comments.
+ * @constructor
+* Run a new Comments Filter
+* @param {Object} config Configuration options
+ */
+Roo.htmleditor.FilterComment = function(cfg)
+{
+ this.walk(cfg.node);
+}
-Roo.apply(Roo.bootstrap.TimeField, {
-
- content : {
- tag: 'tbody',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- colspan: '7'
- }
- ]
- }
- ]
- },
-
- footer : {
- tag: 'tfoot',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'th',
- colspan: '7',
- cls: '',
- cn: [
- {
- tag: 'button',
- cls: 'btn btn-info ok',
- html: 'OK'
- }
- ]
- }
+Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
+{
+
+ replaceComment : function(n)
+ {
+ n.parentNode.removeChild(n);
+ }
+});/**
+ * @class Roo.htmleditor.FilterKeepChildren
+ * remove tags but keep children
+ * @constructor
+ * Run a new Keep Children Filter
+ * @param {Object} config Configuration options
+ */
- ]
- }
- ]
+Roo.htmleditor.FilterKeepChildren = function(cfg)
+{
+ Roo.apply(this, cfg);
+ if (this.tag === false) {
+ return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
}
-});
+ this.walk(cfg.node);
+}
-Roo.apply(Roo.bootstrap.TimeField, {
+Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
+{
+
- template : {
- tag: 'div',
- cls: 'datepicker dropdown-menu',
- cn: [
- {
- tag: 'div',
- cls: 'datepicker-time',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.TimeField.content,
- Roo.bootstrap.TimeField.footer
- ]
+ replaceTag : function(node)
+ {
+ // walk children...
+ //Roo.log(node);
+ var ar = Array.from(node.childNodes);
+ //remove first..
+ for (var i = 0; i < ar.length; i++) {
+ if (ar[i].nodeType == 1) {
+ if (
+ (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
+ || // array and it matches
+ (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
+ ) {
+ this.replaceTag(ar[i]); // child is blacklisted as well...
+ continue;
}
- ]
}
- ]
+ }
+ ar = Array.from(node.childNodes);
+ for (var i = 0; i < ar.length; i++) {
+
+ node.removeChild(ar[i]);
+ // what if we need to walk these???
+ node.parentNode.insertBefore(ar[i], node);
+ if (this.tag !== false) {
+ this.walk(ar[i]);
+
+ }
+ }
+ node.parentNode.removeChild(node);
+ return false; // don't walk children
+
+
}
-});
+});/**
+ * @class Roo.htmleditor.FilterParagraph
+ * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
+ * like on 'push' to remove the <p> tags and replace them with line breaks.
+ * @constructor
+ * Run a new Paragraph Filter
+ * @param {Object} config Configuration options
+ */
-
+Roo.htmleditor.FilterParagraph = function(cfg)
+{
+ // no need to apply config.
+ this.walk(cfg.node);
+}
- /*
- * - LGPL
- *
- * MonthField
- *
+Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
+{
+
+
+ tag : 'P',
+
+
+ replaceTag : function(node)
+ {
+
+ if (node.childNodes.length == 1 &&
+ node.childNodes[0].nodeType == 3 &&
+ node.childNodes[0].textContent.trim().length < 1
+ ) {
+ // remove and replace with '<BR>';
+ node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
+ return false; // no need to walk..
+ }
+ var ar = Array.from(node.childNodes);
+ for (var i = 0; i < ar.length; i++) {
+ node.removeChild(ar[i]);
+ // what if we need to walk these???
+ node.parentNode.insertBefore(ar[i], node);
+ }
+ // now what about this?
+ // <p> </p>
+
+ // double BR.
+ node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
+ node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
+ node.parentNode.removeChild(node);
+
+ return false;
+
+ }
+
+});/**
+ * @class Roo.htmleditor.FilterSpan
+ * filter span's with no attributes out..
+ * @constructor
+ * Run a new Span Filter
+ * @param {Object} config Configuration options
*/
-/**
- * @class Roo.bootstrap.MonthField
- * @extends Roo.bootstrap.Input
- * Bootstrap MonthField class
- *
- * @cfg {String} language default en
+Roo.htmleditor.FilterSpan = function(cfg)
+{
+ // no need to apply config.
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
+{
+
+ tag : 'SPAN',
+
+
+ replaceTag : function(node)
+ {
+ if (node.attributes && node.attributes.length > 0) {
+ return true; // walk if there are any.
+ }
+ Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
+ return false;
+
+ }
+
+});/**
+ * @class Roo.htmleditor.FilterTableWidth
+ try and remove table width data - as that frequently messes up other stuff.
*
+ * was cleanTableWidths.
+ *
+ * Quite often pasting from word etc.. results in tables with column and widths.
+ * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
+ *
* @constructor
- * Create a new MonthField
- * @param {Object} config The config object
+ * Run a new Table Filter
+ * @param {Object} config Configuration options
*/
-Roo.bootstrap.MonthField = function(config){
- Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
-
- this.addEvents({
- /**
- * @event show
- * Fires when this field show.
- * @param {Roo.bootstrap.MonthField} this
- * @param {Mixed} date The date value
- */
- show : true,
- /**
- * @event show
- * Fires when this field hide.
- * @param {Roo.bootstrap.MonthField} this
- * @param {Mixed} date The date value
- */
- hide : true,
- /**
- * @event select
- * Fires when select a date.
- * @param {Roo.bootstrap.MonthField} this
- * @param {String} oldvalue The old value
- * @param {String} newvalue The new value
- */
- select : true
- });
-};
+Roo.htmleditor.FilterTableWidth = function(cfg)
+{
+ // no need to apply config.
+ this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
+ this.walk(cfg.node);
+}
-Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
+Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
+{
+
+
- onRender: function(ct, position)
- {
-
- Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
-
- this.language = this.language || 'en';
- this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
- this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
-
- this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
- this.isInline = false;
- this.isInput = true;
- this.component = this.el.select('.add-on', true).first() || false;
- this.component = (this.component && this.component.length === 0) ? false : this.component;
- this.hasInput = this.component && this.inputEL().length;
-
- this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
-
- this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
-
- this.picker().on('mousedown', this.onMousedown, this);
- this.picker().on('click', this.onClick, this);
-
- this.picker().addClass('datepicker-dropdown');
-
- Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
- v.setStyle('width', '189px');
- });
+ replaceTag: function(node) {
- this.fillMonths();
- this.update();
+
+ if (node.hasAttribute('width')) {
+ node.removeAttribute('width');
+ }
- if(this.isInline) {
- this.show();
+
+ if (node.hasAttribute("style")) {
+ // pretty basic...
+
+ var styles = node.getAttribute("style").split(";");
+ var nstyle = [];
+ Roo.each(styles, function(s) {
+ if (!s.match(/:/)) {
+ return;
+ }
+ var kv = s.split(":");
+ if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
+ return;
+ }
+ // what ever is left... we allow.
+ nstyle.push(s);
+ });
+ node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+ if (!nstyle.length) {
+ node.removeAttribute('style');
+ }
}
- },
-
- setValue: function(v, suppressEvent)
- {
- var o = this.getValue();
-
- Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
-
- this.update();
+ return true; // continue doing children..
+ }
+});/**
+ * @class Roo.htmleditor.FilterWord
+ * try and clean up all the mess that Word generates.
+ *
+ * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
+
+ * @constructor
+ * Run a new Span Filter
+ * @param {Object} config Configuration options
+ */
- if(suppressEvent !== true){
- this.fireEvent('select', this, o, v);
- }
-
- },
+Roo.htmleditor.FilterWord = function(cfg)
+{
+ // no need to apply config.
+ this.replaceDocBullets(cfg.node);
- getValue: function()
- {
- return this.value;
- },
+ this.walk(cfg.node);
- onClick: function(e)
+
+}
+
+Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
+{
+ tag: true,
+
+
+ /**
+ * Clean up MS wordisms...
+ */
+ replaceTag : function(node)
{
- e.stopPropagation();
- e.preventDefault();
+
+ // no idea what this does - span with text, replaceds with just text.
+ if(
+ node.nodeName == 'SPAN' &&
+ !node.hasAttributes() &&
+ node.childNodes.length == 1 &&
+ node.firstChild.nodeName == "#text"
+ ) {
+ var textNode = node.firstChild;
+ node.removeChild(textNode);
+ if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
+ node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
+ }
+ node.parentNode.insertBefore(textNode, node);
+ if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
+ node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
+ }
+
+ node.parentNode.removeChild(node);
+ return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
+ }
- var target = e.getTarget();
+
- if(target.nodeName.toLowerCase() === 'i'){
- target = Roo.get(target).dom.parentNode;
+ if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
+ node.parentNode.removeChild(node);
+ return false; // dont do chidlren
+ }
+ //Roo.log(node.tagName);
+ // remove - but keep children..
+ if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
+ //Roo.log('-- removed');
+ while (node.childNodes.length) {
+ var cn = node.childNodes[0];
+ node.removeChild(cn);
+ node.parentNode.insertBefore(cn, node);
+ // move node to parent - and clean it..
+ if (cn.nodeType == 1) {
+ this.replaceTag(cn);
+ }
+
+ }
+ node.parentNode.removeChild(node);
+ /// no need to iterate chidlren = it's got none..
+ //this.iterateChildren(node, this.cleanWord);
+ return false; // no need to iterate children.
+ }
+ // 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");
+ }
}
- var nodeName = target.nodeName;
- var className = target.className;
- var html = target.innerHTML;
+ if (node.hasAttribute("lang")) {
+ node.removeAttribute("lang");
+ }
- if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
- return;
+ if (node.hasAttribute("style")) {
+
+ var styles = node.getAttribute("style").split(";");
+ var nstyle = [];
+ Roo.each(styles, function(s) {
+ if (!s.match(/:/)) {
+ return;
+ }
+ var kv = s.split(":");
+ if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
+ return;
+ }
+ // what ever is left... we allow.
+ nstyle.push(s);
+ });
+ node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
+ if (!nstyle.length) {
+ node.removeAttribute('style');
+ }
}
+ return true; // do children
- this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
- this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
- this.hide();
-
},
- picker : function()
+ styleToObject: function(node)
{
- return this.pickerEl;
+ var styles = node.getAttribute("style").split(";");
+ var ret = {};
+ Roo.each(styles, function(s) {
+ if (!s.match(/:/)) {
+ return;
+ }
+ var kv = s.split(":");
+
+ // what ever is left... we allow.
+ ret[kv[0]] = kv[1];
+ });
+ return ret;
},
- fillMonths: function()
- {
- var i = 0;
- var months = this.picker().select('>.datepicker-months td', true).first();
-
- months.dom.innerHTML = '';
-
- while (i < 12) {
- var month = {
- tag: 'span',
- cls: 'month',
- html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
- };
-
- months.createChild(month);
- }
-
- },
- update: function()
+ replaceDocBullets : function(doc)
{
- var _this = this;
-
- if(typeof(this.vIndex) == 'undefined' && this.value.length){
- this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
+ var listpara = doc.getElementsByClassName('MsoListParagraph');
+ while(listpara.length) {
+ this.replaceDocBullet(listpara.item(0));
+ //code
}
-
- Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
- e.removeClass('active');
-
- if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
- e.addClass('active');
- }
- })
},
- place: function()
+ replaceDocBullet : function(p)
{
- if(this.isInline) {
- return;
+ // gather all the siblings.
+ var ns = p,
+ parent = p.parentNode,
+ doc = parent.ownerDocument,
+ items = [];
+ while (ns) {
+ if (ns.nodeType != 1) {
+ ns = ns.nextSibling;
+ continue;
+ }
+ if (!ns.className.match(/MsoListParagraph/i)) {
+ break;
+ }
+ items.push(ns);
+ ns = ns.nextSibling;
+
}
-
- this.picker().removeClass(['bottom', 'top']);
-
- if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
- /*
- * place to the top of element!
- *
- */
+ var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
+ parent.insertBefore(ul, p);
+ var lvl = 0;
+ var stack = [ ul ];
+ var last_li = false;
+ items.forEach(function(n) {
+ parent.removeChild(n);
+ var spans = n.getElementsByTagName('span');
+ if (!spans.length || !n.isEqualNode(spans.item(0).parentNode)) {
+ return; // skip it...
+ }
- this.picker().addClass('top');
- this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
+ var style = this.styleToObject(n);
+ if (typeof(style['mso-list']) == 'undefined') {
+ return; // skip it.
+ }
+ n.removeChild(spans.item(0)); // remove the fake bullet.
+ var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1;
+ if (nlvl > lvl) {
+ //new indent
+ var nul = doc.createElement('ul'); // what about number lists...
+ last_li.appendChild(nul);
+ stack[nlvl] = nul;
+ }
+ lvl = nlvl;
- return;
- }
+ var nli = stack[nlvl].appendChild(doc.createElement('li'));
+ last_li = nli;
+ // copy children of p into nli
+ while(n.firstChild) {
+ var fc = n.firstChild;
+ n.removeChild(fc);
+ nli.appendChild(fc);
+ }
+
+
+ },this);
- this.picker().addClass('bottom');
- this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
- },
+
+
+ }
- onFocus : function()
- {
- Roo.bootstrap.MonthField.superclass.onFocus.call(this);
- this.show();
- },
- onBlur : function()
- {
- Roo.bootstrap.MonthField.superclass.onBlur.call(this);
+
+});
+/**
+ * @class Roo.htmleditor.FilterStyleToTag
+ * part of the word stuff... - certain 'styles' should be converted to tags.
+ * eg.
+ * font-weight: bold -> bold
+ * ?? super / subscrit etc..
+ *
+ * @constructor
+* Run a new style to tag filter.
+* @param {Object} config Configuration options
+ */
+Roo.htmleditor.FilterStyleToTag = function(cfg)
+{
+
+ this.tags = {
+ B : [ 'fontWeight' , 'bold'],
+ I : [ 'fontStyle' , 'italic'],
+ //pre : [ 'font-style' , 'italic'],
+ // h1.. h6 ?? font-size?
+ SUP : [ 'verticalAlign' , 'super' ],
+ SUB : [ 'verticalAlign' , 'sub' ]
- var d = this.inputEl().getValue();
- this.setValue(d);
-
- this.hide();
- },
+ };
- show : function()
- {
- this.picker().show();
- this.picker().select('>.datepicker-months', true).first().show();
- this.update();
- this.place();
-
- this.fireEvent('show', this, this.date);
- },
+ Roo.apply(this, cfg);
+
- hide : function()
- {
- if(this.isInline) {
- return;
- }
- this.picker().hide();
- this.fireEvent('hide', this, this.date);
-
- },
+ this.walk(cfg.node);
- onMousedown: function(e)
- {
- e.stopPropagation();
- e.preventDefault();
- },
- keyup: function(e)
- {
- Roo.bootstrap.MonthField.superclass.keyup.call(this);
- this.update();
- },
+
+}
- fireKey: function(e)
+
+Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
+{
+ tag: true, // all tags
+
+ tags : false,
+
+
+ replaceTag : function(node)
{
- if (!this.picker().isVisible()){
- if (e.keyCode == 27) {// allow escape to hide and re-show picker
- this.show();
- }
- return;
- }
- var dir;
- switch(e.keyCode){
- case 27: // escape
- this.hide();
- e.preventDefault();
- break;
- case 37: // left
- case 39: // right
- dir = e.keyCode == 37 ? -1 : 1;
-
- this.vIndex = this.vIndex + dir;
-
- if(this.vIndex < 0){
- this.vIndex = 0;
- }
-
- if(this.vIndex > 11){
- this.vIndex = 11;
- }
-
- if(isNaN(this.vIndex)){
- this.vIndex = 0;
- }
-
- this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
-
- break;
- case 38: // up
- case 40: // down
-
- dir = e.keyCode == 38 ? -1 : 1;
-
- this.vIndex = this.vIndex + dir * 4;
-
- if(this.vIndex < 0){
- this.vIndex = 0;
- }
-
- if(this.vIndex > 11){
- this.vIndex = 11;
- }
-
- if(isNaN(this.vIndex)){
- this.vIndex = 0;
- }
-
- this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
- break;
-
- case 13: // enter
-
- if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
- this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
- }
-
- this.hide();
- e.preventDefault();
- break;
- case 9: // tab
- if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
- this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
- }
- this.hide();
- break;
- case 16: // shift
- case 17: // ctrl
- case 18: // alt
- break;
- default :
- this.hide();
-
+ if (node.getAttribute("style") === null) {
+ return true;
}
- },
-
- remove: function()
- {
- this.picker().remove();
- }
-
-});
-
-Roo.apply(Roo.bootstrap.MonthField, {
-
- content : {
- tag: 'tbody',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- colspan: '7'
+ var inject = [];
+ for (var k in this.tags) {
+ if (node.style[this.tags[k][0]] == this.tags[k][1]) {
+ inject.push(k);
+ node.style.removeProperty(this.tags[k][0]);
}
- ]
}
- ]
- },
-
- dates:{
- en: {
- months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
- monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
+ if (!inject.length) {
+ return true;
}
+ var cn = Array.from(node.childNodes);
+ var nn = node;
+ Roo.each(inject, function(t) {
+ var nc = node.ownerDocument.createElement(t);
+ nn.appendChild(nc);
+ nn = nc;
+ });
+ for(var i = 0;i < cn.length;cn++) {
+ node.removeChild(cn[i]);
+ nn.appendChild(cn[i]);
+ }
+ return true /// iterate thru
}
-});
-
-Roo.apply(Roo.bootstrap.MonthField, {
-
- template : {
- tag: 'div',
- cls: 'datepicker dropdown-menu roo-dynamic',
- cn: [
- {
- tag: 'div',
- cls: 'datepicker-months',
- cn: [
- {
- tag: 'table',
- cls: 'table-condensed',
- cn:[
- Roo.bootstrap.DateField.content
- ]
- }
- ]
- }
- ]
- }
-});
-
-
-
-
- /*
- * - LGPL
- *
- * CheckBox
- *
- */
-
-/**
- * @class Roo.bootstrap.CheckBox
- * @extends Roo.bootstrap.Input
- * Bootstrap CheckBox class
- *
- * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
- * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
- * @cfg {String} boxLabel The text that appears beside the checkbox
- * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
- * @cfg {Boolean} checked initnal the element
- * @cfg {Boolean} inline inline the element (default false)
- * @cfg {String} groupId the checkbox group id // normal just use for checkbox
- * @cfg {String} tooltip label tooltip
- *
+
+})/**
+ * @class Roo.htmleditor.FilterLongBr
+ * BR/BR/BR - keep a maximum of 2...
* @constructor
- * Create a new CheckBox
- * @param {Object} config The config object
+ * Run a new Long BR Filter
+ * @param {Object} config Configuration options
*/
-Roo.bootstrap.CheckBox = function(config){
- Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
-
- this.addEvents({
- /**
- * @event check
- * Fires when the element is checked or unchecked.
- * @param {Roo.bootstrap.CheckBox} this This input
- * @param {Boolean} checked The new checked value
- */
- check : true,
- /**
- * @event click
- * Fires when the element is click.
- * @param {Roo.bootstrap.CheckBox} this This input
- */
- click : true
- });
-
-};
+Roo.htmleditor.FilterLongBr = function(cfg)
+{
+ // no need to apply config.
+ this.walk(cfg.node);
+}
-Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
-
- inputType: 'checkbox',
- inputValue: 1,
- valueOff: 0,
- boxLabel: false,
- checked: false,
- weight : false,
- inline: false,
- tooltip : '',
+Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
+{
- getAutoCreate : function()
+
+ tag : 'BR',
+
+
+ replaceTag : function(node)
{
- var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
-
- var id = Roo.id();
- var cfg = {};
-
- cfg.cls = 'form-group ' + this.inputType; //input-group
-
- if(this.inline){
- cfg.cls += ' ' + this.inputType + '-inline';
+ var ps = node.nextSibling;
+ while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
+ ps = ps.nextSibling;
}
- var input = {
- tag: 'input',
- id : id,
- type : this.inputType,
- value : this.inputValue,
- cls : 'roo-' + this.inputType, //'form-box',
- placeholder : this.placeholder || ''
-
- };
-
- if(this.inputType != 'radio'){
- var hidden = {
- tag: 'input',
- type : 'hidden',
- cls : 'roo-hidden-value',
- value : this.checked ? this.inputValue : this.valueOff
- };
+ if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
+ node.parentNode.removeChild(node); // remove last BR inside one fo these tags
+ return false;
}
-
- if (this.weight) { // Validity check?
- cfg.cls += " " + this.inputType + "-" + this.weight;
+ if (!ps || ps.nodeType != 1) {
+ return false;
}
- if (this.disabled) {
- input.disabled=true;
+ if (!ps || ps.tagName != 'BR') {
+
+ return false;
}
- if(this.checked){
- input.checked = this.checked;
- }
- if (this.name) {
-
- input.name = this.name;
-
- if(this.inputType != 'radio'){
- hidden.name = this.name;
- input.name = '_hidden_' + 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];
- }
- });
+ if (!node.previousSibling) {
+ return false;
+ }
+ var ps = node.previousSibling;
- 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.inputType != 'radio'){
- inputblock.cn.push(hidden);
- }
-
- if (this.after) {
- inputblock.cn.push({
- tag :'span',
- cls : 'input-group-addon',
- html : this.after
- });
- }
-
+ while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
+ ps = ps.previousSibling;
+ }
+ if (!ps || ps.nodeType != 1) {
+ return false;
+ }
+ // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
+ if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
+ return false;
}
- if (align ==='left' && this.fieldLabel.length) {
-// Roo.log("left and has label");
- cfg.cn = [
- {
- tag: 'label',
- 'for' : id,
- cls : 'control-label',
- html : this.fieldLabel
- },
- {
- cls : "",
- cn: [
- inputblock
- ]
- }
- ];
-
- if(this.labelWidth > 12){
- cfg.cn[0].style = "width: " + this.labelWidth + 'px';
- }
-
- if(this.labelWidth < 13 && this.labelmd == 0){
- this.labelmd = this.labelWidth;
- }
-
- if(this.labellg > 0){
- cfg.cn[0].cls += ' col-lg-' + this.labellg;
- cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
- }
-
- if(this.labelmd > 0){
- cfg.cn[0].cls += ' col-md-' + this.labelmd;
- cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
- }
-
- if(this.labelsm > 0){
- cfg.cn[0].cls += ' col-sm-' + this.labelsm;
- cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
- }
-
- if(this.labelxs > 0){
- cfg.cn[0].cls += ' col-xs-' + this.labelxs;
- cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
- }
-
- } 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
-
- ];
+ node.parentNode.removeChild(node); // remove me...
+
+ return false; // no need to do children
- } else {
-
-// Roo.log(" no label && no align");
- cfg.cn = [ inputblock ] ;
-
-
+ }
+
+});
+
+/**
+ * @class Roo.htmleditor.FilterBlock
+ * removes id / data-block and contenteditable that are associated with blocks
+ * usage should be done on a cloned copy of the dom
+ * @constructor
+* Run a new Attribute Filter { node : xxxx }}
+* @param {Object} config Configuration options
+ */
+Roo.htmleditor.FilterBlock = function(cfg)
+{
+ Roo.apply(this, cfg);
+ var qa = cfg.node.querySelectorAll;
+ this.removeAttributes('data-block');
+ this.removeAttributes('contenteditable');
+ this.removeAttributes('id');
+
+}
+
+Roo.apply(Roo.htmleditor.FilterBlock.prototype,
+{
+ node: true, // all tags
+
+
+ removeAttributes : function(attr)
+ {
+ var ar = this.node.querySelectorAll('*[' + attr + ']');
+ for (var i =0;i<ar.length;i++) {
+ ar[i].removeAttribute(attr);
}
+ }
- if(this.boxLabel){
- var boxLabelCfg = {
- tag: 'label',
- //'for': id, // box label is handled by onclick - so no for...
- cls: 'box-label',
- html: this.boxLabel
- };
-
- if(this.tooltip){
- boxLabelCfg.tooltip = this.tooltip;
- }
-
- cfg.cn.push(boxLabelCfg);
+
+
+
+});
+/**
+ * @class Roo.htmleditor.KeyEnter
+ * Handle Enter press..
+ * @cfg {Roo.HtmlEditorCore} core the editor.
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
+
+
+
+
+
+Roo.htmleditor.KeyEnter = function(cfg) {
+ Roo.apply(this, cfg);
+ // this does not actually call walk as it's really just a abstract class
+
+ Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
+}
+
+//Roo.htmleditor.KeyEnter.i = 0;
+
+
+Roo.htmleditor.KeyEnter.prototype = {
+
+ core : false,
+
+ keypress : function(e)
+ {
+ if (e.charCode != 13 && e.charCode != 10) {
+ Roo.log([e.charCode,e]);
+ return true;
+ }
+ e.preventDefault();
+ // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
+ var doc = this.core.doc;
+ //add a new line
+
+
+ var sel = this.core.getSelection();
+ var range = sel.getRangeAt(0);
+ var n = range.commonAncestorContainer;
+ var pc = range.closest([ 'ol', 'ul']);
+ var pli = range.closest('li');
+ if (!pc || e.ctrlKey) {
+ sel.insertNode('br', 'after');
+
+ this.core.undoManager.addEvent();
+ this.core.fireEditorEvent(e);
+ return false;
}
- if(this.inputType != 'radio'){
- cfg.cn.push(hidden);
+ // deal with <li> insetion
+ if (pli.innerText.trim() == '' &&
+ pli.previousSibling &&
+ pli.previousSibling.nodeName == 'LI' &&
+ pli.previousSibling.innerText.trim() == '') {
+ pli.parentNode.removeChild(pli.previousSibling);
+ sel.cursorAfter(pc);
+ this.core.undoManager.addEvent();
+ this.core.fireEditorEvent(e);
+ return false;
+ }
+
+ var li = doc.createElement('LI');
+ li.innerHTML = ' ';
+ if (!pli || !pli.firstSibling) {
+ pc.appendChild(li);
+ } else {
+ pli.parentNode.insertBefore(li, pli.firstSibling);
}
+ sel.cursorText (li.firstChild);
+
+ this.core.undoManager.addEvent();
+ this.core.fireEditorEvent(e);
+
+ return false;
- return cfg;
+
- },
+
+
+ }
+};
+
+/**
+ * @class Roo.htmleditor.Block
+ * Base class for html editor blocks - do not use it directly .. extend it..
+ * @cfg {DomElement} node The node to apply stuff to.
+ * @cfg {String} friendly_name the name that appears in the context bar about this block
+ * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
+
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.Block = function(cfg)
+{
+ // do nothing .. should not be called really.
+}
+/**
+ * factory method to get the block from an element (using cache if necessary)
+ * @static
+ * @param {HtmlElement} the dom element
+ */
+Roo.htmleditor.Block.factory = function(node)
+{
+ var cc = Roo.htmleditor.Block.cache;
+ var id = Roo.get(node).id;
+ if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
+ Roo.htmleditor.Block.cache[id].readElement(node);
+ return Roo.htmleditor.Block.cache[id];
+ }
+ var db = node.getAttribute('data-block');
+ if (!db) {
+ db = node.nodeName.toLowerCase().toUpperCaseFirst();
+ }
+ var cls = Roo.htmleditor['Block' + db];
+ if (typeof(cls) == 'undefined') {
+ //Roo.log(node.getAttribute('data-block'));
+ Roo.log("OOps missing block : " + 'Block' + db);
+ return false;
+ }
+ Roo.htmleditor.Block.cache[id] = new cls({ node: node });
+ return Roo.htmleditor.Block.cache[id]; /// should trigger update element
+};
+
+/**
+ * initalize all Elements from content that are 'blockable'
+ * @static
+ * @param the body element
+ */
+Roo.htmleditor.Block.initAll = function(body, type)
+{
+ if (typeof(type) == 'undefined') {
+ var ia = Roo.htmleditor.Block.initAll;
+ ia(body,'table');
+ ia(body,'td');
+ ia(body,'figure');
+ return;
+ }
+ Roo.each(Roo.get(body).query(type), function(e) {
+ Roo.htmleditor.Block.factory(e);
+ },this);
+};
+// question goes here... do we need to clear out this cache sometimes?
+// or show we make it relivant to the htmleditor.
+Roo.htmleditor.Block.cache = {};
+
+Roo.htmleditor.Block.prototype = {
+
+ node : false,
+
+ // used by context menu
+ friendly_name : 'Based Block',
+ // text for button to delete this element
+ deleteTitle : false,
+
+ context : false,
/**
- * return the real input element.
+ * Update a node with values from this object
+ * @param {DomElement} node
*/
- inputEl: function ()
+ updateElement : function(node)
{
- return this.el.select('input.roo-' + this.inputType,true).first();
+ Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
},
- hiddenEl: function ()
+ /**
+ * convert to plain HTML for calling insertAtCursor..
+ */
+ toHTML : function()
{
- return this.el.select('input.roo-hidden-value',true).first();
+ return Roo.DomHelper.markup(this.toObject());
},
-
- labelEl: function()
+ /**
+ * used by readEleemnt to extract data from a node
+ * may need improving as it's pretty basic
+
+ * @param {DomElement} node
+ * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
+ * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
+ * @param {String} style the style property - eg. text-align
+ */
+ getVal : function(node, tag, attr, style)
{
- return this.el.select('label.control-label',true).first();
+ var n = node;
+ if (tag !== true && n.tagName != tag.toUpperCase()) {
+ // in theory we could do figure[3] << 3rd figure? or some more complex search..?
+ // but kiss for now.
+ n = node.getElementsByTagName(tag).item(0);
+ }
+ if (!n) {
+ return '';
+ }
+ if (attr === false) {
+ return n;
+ }
+ if (attr == 'html') {
+ return n.innerHTML;
+ }
+ if (attr == 'style') {
+ return n.style[style];
+ }
+
+ return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
+
},
- /* depricated... */
-
- label: function()
+ /**
+ * create a DomHelper friendly object - for use with
+ * Roo.DomHelper.markup / overwrite / etc..
+ * (override this)
+ */
+ toObject : function()
{
- return this.labelEl();
+ return {};
},
-
- boxLabelEl: function()
+ /**
+ * Read a node that has a 'data-block' property - and extract the values from it.
+ * @param {DomElement} node - the node
+ */
+ readElement : function(node)
{
- return this.el.select('label.box-label',true).first();
- },
+
+ }
- initEvents : function()
+
+};
+
+
+
+/**
+ * @class Roo.htmleditor.BlockFigure
+ * Block that has an image and a figcaption
+ * @cfg {String} image_src the url for the image
+ * @cfg {String} align (left|right) alignment for the block default left
+ * @cfg {String} caption the text to appear below (and in the alt tag)
+ * @cfg {String} caption_display (block|none) display or not the caption
+ * @cfg {String|number} image_width the width of the image number or %?
+ * @cfg {String|number} image_height the height of the image number or %?
+ *
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.BlockFigure = function(cfg)
+{
+ if (cfg.node) {
+ this.readElement(cfg.node);
+ this.updateElement(cfg.node);
+ }
+ Roo.apply(this, cfg);
+}
+Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
+
+
+ // setable values.
+ image_src: '',
+ align: 'center',
+ caption : '',
+ caption_display : 'block',
+ width : '100%',
+ cls : '',
+ href: '',
+ video_url : '',
+
+ // margin: '2%', not used
+
+ text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
+
+
+ // used by context menu
+ friendly_name : 'Image with caption',
+ deleteTitle : "Delete Image and Caption",
+
+ contextMenu : function(toolbar)
{
-// Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
- this.inputEl().on('click', this.onClick, this);
+ var block = function() {
+ return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
+ };
- if (this.boxLabel) {
- this.el.select('label.box-label',true).first().on('click', this.onClick, this);
- }
- this.startValue = this.getValue();
+ var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
- if(this.groupId){
- Roo.bootstrap.CheckBox.register(this);
- }
- },
-
- onClick : function(e)
- {
- if(this.fireEvent('click', this, e) !== false){
- this.setChecked(!this.checked);
- }
+ var syncValue = toolbar.editorcore.syncValue;
- },
-
- setChecked : function(state,suppressEvent)
- {
- this.startValue = this.getValue();
-
- if(this.inputType == 'radio'){
+ var fields = {};
+
+ return [
+ {
+ xtype : 'TextItem',
+ text : "Source: ",
+ xns : rooui.Toolbar //Boostrap?
+ },
+ {
+ xtype : 'Button',
+ text: 'Change Image URL',
+
+ listeners : {
+ click: function (btn, state)
+ {
+ var b = block();
+
+ Roo.MessageBox.show({
+ title : "Image Source URL",
+ msg : "Enter the url for the image",
+ buttons: Roo.MessageBox.OKCANCEL,
+ fn: function(btn, val){
+ if (btn != 'ok') {
+ return;
+ }
+ b.image_src = val;
+ b.updateElement();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ },
+ minWidth:250,
+ prompt:true,
+ //multiline: multiline,
+ modal : true,
+ value : b.image_src
+ });
+ }
+ },
+ xns : rooui.Toolbar
+ },
+
+ {
+ xtype : 'Button',
+ text: 'Change Link URL',
+
+ listeners : {
+ click: function (btn, state)
+ {
+ var b = block();
+
+ Roo.MessageBox.show({
+ title : "Link URL",
+ msg : "Enter the url for the link - leave blank to have no link",
+ buttons: Roo.MessageBox.OKCANCEL,
+ fn: function(btn, val){
+ if (btn != 'ok') {
+ return;
+ }
+ b.href = val;
+ b.updateElement();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ },
+ minWidth:250,
+ prompt:true,
+ //multiline: multiline,
+ modal : true,
+ value : b.href
+ });
+ }
+ },
+ xns : rooui.Toolbar
+ },
+ {
+ xtype : 'Button',
+ text: 'Show Video URL',
+
+ listeners : {
+ click: function (btn, state)
+ {
+ Roo.MessageBox.alert("Video URL",
+ block().video_url == '' ? 'This image is not linked ot a video' :
+ 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
+ }
+ },
+ xns : rooui.Toolbar
+ },
- Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
- e.dom.checked = false;
- });
- this.inputEl().dom.checked = true;
+ {
+ xtype : 'TextItem',
+ text : "Width: ",
+ xns : rooui.Toolbar //Boostrap?
+ },
+ {
+ xtype : 'ComboBox',
+ allowBlank : false,
+ displayField : 'val',
+ editable : true,
+ listWidth : 100,
+ triggerAction : 'all',
+ typeAhead : true,
+ valueField : 'val',
+ width : 70,
+ name : 'width',
+ listeners : {
+ select : function (combo, r, index)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ var b = block();
+ b.width = r.get('val');
+ b.updateElement();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.form,
+ store : {
+ xtype : 'SimpleStore',
+ data : [
+ ['50%'],
+ ['80%'],
+ ['100%']
+ ],
+ fields : [ 'val'],
+ xns : Roo.data
+ }
+ },
+ {
+ xtype : 'TextItem',
+ text : "Align: ",
+ xns : rooui.Toolbar //Boostrap?
+ },
+ {
+ xtype : 'ComboBox',
+ allowBlank : false,
+ displayField : 'val',
+ editable : true,
+ listWidth : 100,
+ triggerAction : 'all',
+ typeAhead : true,
+ valueField : 'val',
+ width : 70,
+ name : 'align',
+ listeners : {
+ select : function (combo, r, index)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ var b = block();
+ b.align = r.get('val');
+ b.updateElement();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.form,
+ store : {
+ xtype : 'SimpleStore',
+ data : [
+ ['left'],
+ ['right'],
+ ['center']
+ ],
+ fields : [ 'val'],
+ xns : Roo.data
+ }
+ },
- this.inputEl().dom.value = this.inputValue;
- if(suppressEvent !== true){
- this.fireEvent('check', this, true);
+ {
+ xtype : 'Button',
+ text: 'Hide Caption',
+ name : 'caption_display',
+ pressed : false,
+ enableToggle : true,
+ setValue : function(v) {
+ // this trigger toggle.
+
+ this.setText(v ? "Hide Caption" : "Show Caption");
+ this.setPressed(v != 'block');
+ },
+ listeners : {
+ toggle: function (btn, state)
+ {
+ var b = block();
+ b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
+ this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
+ b.updateElement();
+ syncValue();
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
}
-
- this.validate();
-
- return;
- }
+ ];
- this.checked = state;
+ },
+ /**
+ * create a DomHelper friendly object - for use with
+ * Roo.DomHelper.markup / overwrite / etc..
+ */
+ toObject : function()
+ {
+ var d = document.createElement('div');
+ d.innerHTML = this.caption;
- this.inputEl().dom.checked = state;
+ var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
+ var iw = this.align == 'center' ? this.width : '100%';
+ var img = {
+ tag : 'img',
+ contenteditable : 'false',
+ src : this.image_src,
+ alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
+ style: {
+ width : iw,
+ maxWidth : iw + ' !important', // this is not getting rendered?
+ margin : m
+
+ }
+ };
+ /*
+ '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
+ '<a href="{2}">' +
+ '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
+ '</a>' +
+ '</div>',
+ */
+
+ if (this.href.length > 0) {
+ img = {
+ tag : 'a',
+ href: this.href,
+ contenteditable : 'true',
+ cn : [
+ img
+ ]
+ };
+ }
- this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
- if(suppressEvent !== true){
- this.fireEvent('check', this, state);
+ if (this.video_url.length > 0) {
+ img = {
+ tag : 'div',
+ cls : this.cls,
+ frameborder : 0,
+ allowfullscreen : true,
+ width : 420, // these are for video tricks - that we replace the outer
+ height : 315,
+ src : this.video_url,
+ cn : [
+ img
+ ]
+ };
}
+ // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
+ var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
- this.validate();
+
+ var ret = {
+ tag: 'figure',
+ 'data-block' : 'Figure',
+ 'data-width' : this.width,
+ contenteditable : 'false',
+
+ style : {
+ display: 'block',
+ float : this.align ,
+ maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
+ width : this.align == 'center' ? '100%' : this.width,
+ margin: '0px',
+ padding: this.align == 'center' ? '0' : '0 10px' ,
+ textAlign : this.align // seems to work for email..
+
+ },
+
+
+ align : this.align,
+ cn : [
+ img,
+
+ {
+ tag: 'figcaption',
+ 'data-display' : this.caption_display,
+ style : {
+ textAlign : 'left',
+ fontSize : '16px',
+ lineHeight : '24px',
+ display : this.caption_display,
+ maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
+ margin: m,
+ width: this.align == 'center' ? this.width : '100%'
+
+
+ },
+ cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
+ cn : [
+ {
+ tag: 'div',
+ style : {
+ marginTop : '16px',
+ textAlign : 'left'
+ },
+ align: 'left',
+ cn : [
+ {
+ // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
+ tag : 'i',
+ contenteditable : true,
+ html : captionhtml
+ }
+
+ ]
+ }
+
+ ]
+
+ }
+ ]
+ };
+ return ret;
+
},
- getValue : function()
+ readElement : function(node)
{
- if(this.inputType == 'radio'){
- return this.getGroupValue();
- }
-
- return this.hiddenEl().dom.value;
+ // this should not really come from the link...
+ this.video_url = this.getVal(node, 'div', 'src');
+ this.cls = this.getVal(node, 'div', 'class');
+ this.href = this.getVal(node, 'a', 'href');
- },
-
- getGroupValue : function()
- {
- if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
- return '';
- }
- return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
- },
-
- setValue : function(v,suppressEvent)
- {
- if(this.inputType == 'radio'){
- this.setGroupValue(v, suppressEvent);
- return;
+ this.image_src = this.getVal(node, 'img', 'src');
+
+ this.align = this.getVal(node, 'figure', 'align');
+ var figcaption = this.getVal(node, 'figcaption', false);
+ if (figcaption !== '') {
+ this.caption = this.getVal(figcaption, 'i', 'html');
}
- this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
+
+ this.caption_display = this.getVal(node, 'figcaption', 'data-display');
+ //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
+ this.width = this.getVal(node, true, 'data-width');
+ //this.margin = this.getVal(node, 'figure', 'style', 'margin');
- this.validate();
},
-
- setGroupValue : function(v, suppressEvent)
+ removeNode : function()
{
- this.startValue = this.getValue();
-
- Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
- e.dom.checked = false;
-
- if(e.dom.value == v){
- e.dom.checked = true;
+ return this.node;
+ }
+
+
+
+
+
+
+
+
+})
+
+
+
+/**
+ * @class Roo.htmleditor.BlockTable
+ * Block that manages a table
+ *
+ * @constructor
+ * Create a new Filter.
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.BlockTable = function(cfg)
+{
+ if (cfg.node) {
+ this.readElement(cfg.node);
+ this.updateElement(cfg.node);
+ }
+ Roo.apply(this, cfg);
+ if (!cfg.node) {
+ this.rows = [];
+ for(var r = 0; r < this.no_row; r++) {
+ this.rows[r] = [];
+ for(var c = 0; c < this.no_col; c++) {
+ this.rows[r][c] = this.emptyCell();
}
- });
-
- if(suppressEvent !== true){
- this.fireEvent('check', this, true);
}
-
- this.validate();
-
- return;
- },
+ }
- validate : function()
+
+}
+Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
+
+ rows : false,
+ no_col : 1,
+ no_row : 1,
+
+
+ width: '100%',
+
+ // used by context menu
+ friendly_name : 'Table',
+ deleteTitle : 'Delete Table',
+ // context menu is drawn once..
+
+ contextMenu : function(toolbar)
{
- if(this.getVisibilityEl().hasClass('hidden')){
- return true;
- }
- if(
- this.disabled ||
- (this.inputType == 'radio' && this.validateRadio()) ||
- (this.inputType == 'checkbox' && this.validateCheckbox())
- ){
- this.markValid();
- return true;
- }
+ var block = function() {
+ return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
+ };
- this.markInvalid();
- return false;
- },
-
- validateRadio : function()
- {
- if(this.getVisibilityEl().hasClass('hidden')){
- return true;
- }
- if(this.allowBlank){
- return true;
- }
+ var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
- var valid = false;
+ var syncValue = toolbar.editorcore.syncValue;
- Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
- if(!e.dom.checked){
- return;
- }
+ var fields = {};
+
+ return [
+ {
+ xtype : 'TextItem',
+ text : "Width: ",
+ xns : rooui.Toolbar //Boostrap?
+ },
+ {
+ xtype : 'ComboBox',
+ allowBlank : false,
+ displayField : 'val',
+ editable : true,
+ listWidth : 100,
+ triggerAction : 'all',
+ typeAhead : true,
+ valueField : 'val',
+ width : 100,
+ name : 'width',
+ listeners : {
+ select : function (combo, r, index)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ var b = block();
+ b.width = r.get('val');
+ b.updateElement();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.form,
+ store : {
+ xtype : 'SimpleStore',
+ data : [
+ ['100%'],
+ ['auto']
+ ],
+ fields : [ 'val'],
+ xns : Roo.data
+ }
+ },
+ // -------- Cols
+
+ {
+ xtype : 'TextItem',
+ text : "Columns: ",
+ xns : rooui.Toolbar //Boostrap?
+ },
+
+ {
+ xtype : 'Button',
+ text: '-',
+ listeners : {
+ click : function (_self, e)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ block().removeColumn();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ },
+ {
+ xtype : 'Button',
+ text: '+',
+ listeners : {
+ click : function (_self, e)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ block().addColumn();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ },
+ // -------- ROWS
+ {
+ xtype : 'TextItem',
+ text : "Rows: ",
+ xns : rooui.Toolbar //Boostrap?
+ },
+
+ {
+ xtype : 'Button',
+ text: '-',
+ listeners : {
+ click : function (_self, e)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ block().removeRow();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ },
+ {
+ xtype : 'Button',
+ text: '+',
+ listeners : {
+ click : function (_self, e)
+ {
+ block().addRow();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ },
+ // -------- ROWS
+ {
+ xtype : 'Button',
+ text: 'Reset Column Widths',
+ listeners : {
+
+ click : function (_self, e)
+ {
+ block().resetWidths();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ }
- valid = true;
- return false;
- });
+
+ ];
- return valid;
},
- validateCheckbox : function()
+
+ /**
+ * create a DomHelper friendly object - for use with
+ * Roo.DomHelper.markup / overwrite / etc..
+ * ?? should it be called with option to hide all editing features?
+ */
+ toObject : function()
{
- if(!this.groupId){
- return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
- //return (this.getValue() == this.inputValue) ? true : false;
- }
- var group = Roo.bootstrap.CheckBox.get(this.groupId);
-
- if(!group){
- return false;
- }
-
- var r = false;
+ var ret = {
+ tag : 'table',
+ contenteditable : 'false', // this stops cell selection from picking the table.
+ 'data-block' : 'Table',
+ style : {
+ width: this.width,
+ border : 'solid 1px #000', // ??? hard coded?
+ 'border-collapse' : 'collapse'
+ },
+ cn : [
+ { tag : 'tbody' , cn : [] }
+ ]
+ };
- for(var i in group){
- if(group[i].el.isVisible(true)){
- r = false;
- break;
- }
+ // do we have a head = not really
+ var ncols = 0;
+ Roo.each(this.rows, function( row ) {
+ var tr = {
+ tag: 'tr',
+ style : {
+ margin: '6px',
+ border : 'solid 1px #000',
+ textAlign : 'left'
+ },
+ cn : [ ]
+ };
- r = true;
- }
-
- for(var i in group){
- if(r){
- break;
- }
+ ret.cn[0].cn.push(tr);
+ // does the row have any properties? ?? height?
+ var nc = 0;
+ Roo.each(row, function( cell ) {
+
+ var td = {
+ tag : 'td',
+ contenteditable : 'true',
+ 'data-block' : 'Td',
+ html : cell.html,
+ style : cell.style
+ };
+ if (cell.colspan > 1) {
+ td.colspan = cell.colspan ;
+ nc += cell.colspan;
+ } else {
+ nc++;
+ }
+ if (cell.rowspan > 1) {
+ td.rowspan = cell.rowspan ;
+ }
+
+
+ // widths ?
+ tr.cn.push(td);
+
+
+ }, this);
+ ncols = Math.max(nc, ncols);
- r = (group[i].getValue() == group[i].inputValue) ? true : false;
- }
+
+ }, this);
+ // add the header row..
- return r;
+ ncols++;
+
+
+ return ret;
+
},
- /**
- * Mark this field as valid
- */
- markValid : function()
+ readElement : function(node)
{
- var _this = this;
+ node = node ? node : this.node ;
+ this.width = this.getVal(node, true, 'style', 'width') || '100%';
- this.fireEvent('valid', this);
-
- var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
+ this.rows = [];
+ this.no_row = 0;
+ var trs = Array.from(node.rows);
+ trs.forEach(function(tr) {
+ var row = [];
+ this.rows.push(row);
+
+ this.no_row++;
+ var no_column = 0;
+ Array.from(tr.cells).forEach(function(td) {
+
+ var add = {
+ colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
+ rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
+ style : td.hasAttribute('style') ? td.getAttribute('style') : '',
+ html : td.innerHTML
+ };
+ no_column += add.colspan;
+
+
+ row.push(add);
+
+
+ },this);
+ this.no_col = Math.max(this.no_col, no_column);
+
+
+ },this);
- if(this.groupId){
- label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
- }
- if(label){
- label.markValid();
- }
-
- if(this.inputType == 'radio'){
- Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
- var fg = e.findParent('.form-group', false, true);
- if (Roo.bootstrap.version == 3) {
- fg.removeClass([_this.invalidClass, _this.validClass]);
- fg.addClass(_this.validClass);
- } else {
- fg.removeClass(['is-valid', 'is-invalid']);
- fg.addClass('is-valid');
+ },
+ normalizeRows: function()
+ {
+ var ret= [];
+ var rid = -1;
+ this.rows.forEach(function(row) {
+ rid++;
+ ret[rid] = [];
+ row = this.normalizeRow(row);
+ var cid = 0;
+ row.forEach(function(c) {
+ while (typeof(ret[rid][cid]) != 'undefined') {
+ cid++;
+ }
+ if (typeof(ret[rid]) == 'undefined') {
+ ret[rid] = [];
+ }
+ ret[rid][cid] = c;
+ c.row = rid;
+ c.col = cid;
+ if (c.rowspan < 2) {
+ return;
+ }
+
+ for(var i = 1 ;i < c.rowspan; i++) {
+ if (typeof(ret[rid+i]) == 'undefined') {
+ ret[rid+i] = [];
+ }
+ ret[rid+i][cid] = c;
}
});
-
- return;
- }
-
- if(!this.groupId){
- var fg = this.el.findParent('.form-group', false, true);
- if (Roo.bootstrap.version == 3) {
- fg.removeClass([this.invalidClass, this.validClass]);
- fg.addClass(this.validClass);
- } else {
- fg.removeClass(['is-valid', 'is-invalid']);
- fg.addClass('is-valid');
+ }, this);
+ return ret;
+
+ },
+
+ normalizeRow: function(row)
+ {
+ var ret= [];
+ row.forEach(function(c) {
+ if (c.colspan < 2) {
+ ret.push(c);
+ return;
+ }
+ for(var i =0 ;i < c.colspan; i++) {
+ ret.push(c);
}
+ });
+ return ret;
+
+ },
+
+ deleteColumn : function(sel)
+ {
+ if (!sel || sel.type != 'col') {
return;
}
-
- var group = Roo.bootstrap.CheckBox.get(this.groupId);
-
- if(!group){
+ if (this.no_col < 2) {
return;
}
- for(var i in group){
- var fg = group[i].el.findParent('.form-group', false, true);
- if (Roo.bootstrap.version == 3) {
- fg.removeClass([this.invalidClass, this.validClass]);
- fg.addClass(this.validClass);
+ this.rows.forEach(function(row) {
+ var cols = this.normalizeRow(row);
+ var col = cols[sel.col];
+ if (col.colspan > 1) {
+ col.colspan --;
} else {
- fg.removeClass(['is-valid', 'is-invalid']);
- fg.addClass('is-valid');
+ row.remove(col);
}
- }
+
+ }, this);
+ this.no_col--;
+
+ },
+ removeColumn : function()
+ {
+ this.deleteColumn({
+ type: 'col',
+ col : this.no_col-1
+ });
+ this.updateElement();
},
- /**
- * Mark this field as invalid
- * @param {String} msg The validation message
- */
- markInvalid : function(msg)
+
+ addColumn : function()
{
- if(this.allowBlank){
+
+ this.rows.forEach(function(row) {
+ row.push(this.emptyCell());
+
+ }, this);
+ this.updateElement();
+ },
+
+ deleteRow : function(sel)
+ {
+ if (!sel || sel.type != 'row') {
return;
}
- var _this = this;
-
- this.fireEvent('invalid', this, msg);
+ if (this.no_row < 2) {
+ return;
+ }
- var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
+ var rows = this.normalizeRows();
- if(this.groupId){
- label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
- }
- if(label){
- label.markInvalid();
- }
-
- if(this.inputType == 'radio'){
+ rows[sel.row].forEach(function(col) {
+ if (col.rowspan > 1) {
+ col.rowspan--;
+ } else {
+ col.remove = 1; // flage it as removed.
+ }
- Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
- var fg = e.findParent('.form-group', false, true);
- if (Roo.bootstrap.version == 3) {
- fg.removeClass([_this.invalidClass, _this.validClass]);
- fg.addClass(_this.invalidClass);
- } else {
- fg.removeClass(['is-invalid', 'is-valid']);
- fg.addClass('is-invalid');
+ }, this);
+ var newrows = [];
+ this.rows.forEach(function(row) {
+ newrow = [];
+ row.forEach(function(c) {
+ if (typeof(c.remove) == 'undefined') {
+ newrow.push(c);
}
+
});
-
- return;
- }
-
- if(!this.groupId){
- var fg = this.el.findParent('.form-group', false, true);
- if (Roo.bootstrap.version == 3) {
- fg.removeClass([_this.invalidClass, _this.validClass]);
- fg.addClass(_this.invalidClass);
- } else {
- fg.removeClass(['is-invalid', 'is-valid']);
- fg.addClass('is-invalid');
+ if (newrow.length > 0) {
+ newrows.push(row);
}
- return;
- }
+ });
+ this.rows = newrows;
- var group = Roo.bootstrap.CheckBox.get(this.groupId);
- if(!group){
- return;
- }
- for(var i in group){
- var fg = group[i].el.findParent('.form-group', false, true);
- if (Roo.bootstrap.version == 3) {
- fg.removeClass([_this.invalidClass, _this.validClass]);
- fg.addClass(_this.invalidClass);
- } else {
- fg.removeClass(['is-invalid', 'is-valid']);
- fg.addClass('is-invalid');
- }
- }
+ this.no_row--;
+ this.updateElement();
},
-
- clearInvalid : function()
+ removeRow : function()
{
- Roo.bootstrap.Input.prototype.clearInvalid.call(this);
-
- // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
-
- var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
+ this.deleteRow({
+ type: 'row',
+ row : this.no_row-1
+ });
- if (label && label.iconEl) {
- label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
- label.iconEl.removeClass(['is-invalid', 'is-valid']);
- }
},
- disable : function()
+
+ addRow : function()
{
- if(this.inputType != 'radio'){
- Roo.bootstrap.CheckBox.superclass.disable.call(this);
- return;
- }
-
- var _this = this;
- if(this.rendered){
- Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
- _this.getActionEl().addClass(this.disabledClass);
- e.dom.disabled = true;
- });
+ var row = [];
+ for (var i = 0; i < this.no_col; i++ ) {
+
+ row.push(this.emptyCell());
+
}
+ this.rows.push(row);
+ this.updateElement();
- this.disabled = true;
- this.fireEvent("disable", this);
- return this;
},
-
- enable : function()
- {
- if(this.inputType != 'radio'){
- Roo.bootstrap.CheckBox.superclass.enable.call(this);
- return;
- }
-
- var _this = this;
-
- if(this.rendered){
- Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
- _this.getActionEl().removeClass(this.disabledClass);
- e.dom.disabled = false;
- });
- }
+
+ // the default cell object... at present...
+ emptyCell : function() {
+ return (new Roo.htmleditor.BlockTd({})).toObject();
- this.disabled = false;
- this.fireEvent("enable", this);
- return this;
+
},
- setBoxLabel : function(v)
+ removeNode : function()
{
- this.boxLabel = v;
-
- if(this.rendered){
- this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
- }
- }
-
-});
-
-Roo.apply(Roo.bootstrap.CheckBox, {
+ return this.node;
+ },
- groups: {},
- /**
- * register a CheckBox Group
- * @param {Roo.bootstrap.CheckBox} the CheckBox to add
- */
- register : function(checkbox)
+
+ resetWidths : function()
{
- if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
- this.groups[checkbox.groupId] = {};
- }
-
- if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
- return;
- }
-
- this.groups[checkbox.groupId][checkbox.name] = checkbox;
-
- },
- /**
- * fetch a CheckBox Group based on the group ID
- * @param {string} the group ID
- * @returns {Roo.bootstrap.CheckBox} the CheckBox group
- */
- get: function(groupId) {
- if (typeof(this.groups[groupId]) == 'undefined') {
- return false;
- }
-
- return this.groups[groupId] ;
+ Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
+ var nn = Roo.htmleditor.Block.factory(n);
+ nn.width = '';
+ nn.updateElement(n);
+ });
}
-});
-/*
- * - LGPL
+
+
+})
+
+/**
+ *
+ * editing a TD?
+ *
+ * since selections really work on the table cell, then editing really should work from there
+ *
+ * The original plan was to support merging etc... - but that may not be needed yet..
+ *
+ * So this simple version will support:
+ * add/remove cols
+ * adjust the width +/-
+ * reset the width...
+ *
*
- * RadioItem
- *
*/
+
+
+
/**
- * @class Roo.bootstrap.Radio
- * @extends Roo.bootstrap.Component
- * Bootstrap Radio class
- * @cfg {String} boxLabel - the label associated
- * @cfg {String} value - the value of radio
+ * @class Roo.htmleditor.BlockTable
+ * Block that manages a table
*
* @constructor
- * Create a new Radio
- * @param {Object} config The config object
+ * Create a new Filter.
+ * @param {Object} config Configuration options
*/
-Roo.bootstrap.Radio = function(config){
- Roo.bootstrap.Radio.superclass.constructor.call(this, config);
-
-};
-Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
+Roo.htmleditor.BlockTd = function(cfg)
+{
+ if (cfg.node) {
+ this.readElement(cfg.node);
+ this.updateElement(cfg.node);
+ }
+ Roo.apply(this, cfg);
+
- boxLabel : '',
- value : '',
+}
+Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
+
+ node : false,
- getAutoCreate : function()
- {
- var cfg = {
- tag : 'div',
- cls : 'form-group radio',
- cn : [
- {
- tag : 'label',
- cls : 'box-label',
- html : this.boxLabel
- }
- ]
- };
-
- return cfg;
- },
+ width: '',
+ textAlign : 'left',
+ valign : 'top',
- initEvents : function()
- {
- this.parent().register(this);
-
- this.el.on('click', this.onClick, this);
-
- },
+ colspan : 1,
+ rowspan : 1,
- onClick : function(e)
- {
- if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
- this.setChecked(true);
- }
- },
- setChecked : function(state, suppressEvent)
- {
- this.parent().setValue(this.value, suppressEvent);
-
- },
+ // used by context menu
+ friendly_name : 'Table Cell',
+ deleteTitle : false, // use our customer delete
- setBoxLabel : function(v)
+ // context menu is drawn once..
+
+ contextMenu : function(toolbar)
{
- this.boxLabel = v;
- if(this.rendered){
- this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
+ var cell = function() {
+ return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
+ };
+
+ var table = function() {
+ return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
+ };
+
+ var lr = false;
+ var saveSel = function()
+ {
+ lr = toolbar.editorcore.getSelection().getRangeAt(0);
}
- }
-
-});
-
-
- /*
- * - LGPL
- *
- * Input
- *
- */
-
-/**
- * @class Roo.bootstrap.SecurePass
- * @extends Roo.bootstrap.Input
- * Bootstrap SecurePass class
- *
- *
- * @constructor
- * Create a new SecurePass
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.SecurePass = function (config) {
- // these go here, so the translation tool can replace them..
- this.errors = {
- PwdEmpty: "Please type a password, and then retype it to confirm.",
- PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
- PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
- PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
- IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
- FNInPwd: "Your password can't contain your first name. Please type a different password.",
- LNInPwd: "Your password can't contain your last name. Please type a different password.",
- TooWeak: "Your password is Too Weak."
+ var restoreSel = function()
+ {
+ if (lr) {
+ (function() {
+ toolbar.editorcore.focus();
+ var cr = toolbar.editorcore.getSelection();
+ cr.removeAllRanges();
+ cr.addRange(lr);
+ toolbar.editorcore.onEditorEvent();
+ }).defer(10, this);
+
+
+ }
+ }
+
+ var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
+
+ var syncValue = toolbar.editorcore.syncValue;
+
+ var fields = {};
+
+ return [
+ {
+ xtype : 'Button',
+ text : 'Edit Table',
+ listeners : {
+ click : function() {
+ var t = toolbar.tb.selectedNode.closest('table');
+ toolbar.editorcore.selectNode(t);
+ toolbar.editorcore.onEditorEvent();
+ }
+ }
+
+ },
+
+
+
+ {
+ xtype : 'TextItem',
+ text : "Column Width: ",
+ xns : rooui.Toolbar
+
+ },
+ {
+ xtype : 'Button',
+ text: '-',
+ listeners : {
+ click : function (_self, e)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ cell().shrinkColumn();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ },
+ {
+ xtype : 'Button',
+ text: '+',
+ listeners : {
+ click : function (_self, e)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ cell().growColumn();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ },
+
+ {
+ xtype : 'TextItem',
+ text : "Vertical Align: ",
+ xns : rooui.Toolbar //Boostrap?
+ },
+ {
+ xtype : 'ComboBox',
+ allowBlank : false,
+ displayField : 'val',
+ editable : true,
+ listWidth : 100,
+ triggerAction : 'all',
+ typeAhead : true,
+ valueField : 'val',
+ width : 100,
+ name : 'valign',
+ listeners : {
+ select : function (combo, r, index)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ var b = cell();
+ b.valign = r.get('val');
+ b.updateElement();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.form,
+ store : {
+ xtype : 'SimpleStore',
+ data : [
+ ['top'],
+ ['middle'],
+ ['bottom'] // there are afew more...
+ ],
+ fields : [ 'val'],
+ xns : Roo.data
+ }
+ },
+
+ {
+ xtype : 'TextItem',
+ text : "Merge Cells: ",
+ xns : rooui.Toolbar
+
+ },
+
+
+ {
+ xtype : 'Button',
+ text: 'Right',
+ listeners : {
+ click : function (_self, e)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ cell().mergeRight();
+ //block().growColumn();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ },
+
+ {
+ xtype : 'Button',
+ text: 'Below',
+ listeners : {
+ click : function (_self, e)
+ {
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ cell().mergeBelow();
+ //block().growColumn();
+ syncValue();
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.Toolbar
+ },
+ {
+ xtype : 'TextItem',
+ text : "| ",
+ xns : rooui.Toolbar
+
+ },
+
+ {
+ xtype : 'Button',
+ text: 'Split',
+ listeners : {
+ click : function (_self, e)
+ {
+ //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ cell().split();
+ syncValue();
+ toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
+ toolbar.editorcore.onEditorEvent();
+
+ }
+ },
+ xns : rooui.Toolbar
+ },
+ {
+ xtype : 'Fill',
+ xns : rooui.Toolbar
+
+ },
+
+
+ {
+ xtype : 'Button',
+ text: 'Delete',
+
+ xns : rooui.Toolbar,
+ menu : {
+ xtype : 'Menu',
+ xns : rooui.menu,
+ items : [
+ {
+ xtype : 'Item',
+ html: 'Column',
+ listeners : {
+ click : function (_self, e)
+ {
+ var t = table();
+
+ cell().deleteColumn();
+ syncValue();
+ toolbar.editorcore.selectNode(t.node);
+ toolbar.editorcore.onEditorEvent();
+ }
+ },
+ xns : rooui.menu
+ },
+ {
+ xtype : 'Item',
+ html: 'Row',
+ listeners : {
+ click : function (_self, e)
+ {
+ var t = table();
+ cell().deleteRow();
+ syncValue();
+
+ toolbar.editorcore.selectNode(t.node);
+ toolbar.editorcore.onEditorEvent();
+
+ }
+ },
+ xns : rooui.menu
+ },
+ {
+ xtype : 'Separator',
+ xns : rooui.menu
+ },
+ {
+ xtype : 'Item',
+ html: 'Table',
+ listeners : {
+ click : function (_self, e)
+ {
+ var t = table();
+ var nn = t.node.nextSibling || t.node.previousSibling;
+ t.node.parentNode.removeChild(t.node);
+ if (nn) {
+ toolbar.editorcore.selectNode(nn, true);
+ }
+ toolbar.editorcore.onEditorEvent();
+
+ }
+ },
+ xns : rooui.menu
+ }
+ ]
+ }
+ }
+
+ // align... << fixme
+
+ ];
+
},
- this.meterLabel = "Password strength:";
- this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
- this.meterClass = [
- "roo-password-meter-tooweak",
- "roo-password-meter-weak",
- "roo-password-meter-medium",
- "roo-password-meter-strong",
- "roo-password-meter-grey"
- ];
- this.errors = {};
-
- Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
-}
-
-Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
- /**
- * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
- * {
- * PwdEmpty: "Please type a password, and then retype it to confirm.",
- * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
- * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
- * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
- * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
- * FNInPwd: "Your password can't contain your first name. Please type a different password.",
- * LNInPwd: "Your password can't contain your last name. Please type a different password."
- * })
- */
- // private
- meterWidth: 300,
- errorMsg :'',
- errors: false,
- imageRoot: '/',
- /**
- * @cfg {String/Object} Label for the strength meter (defaults to
- * 'Password strength:')
- */
- // private
- meterLabel: '',
- /**
- * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
- * ['Weak', 'Medium', 'Strong'])
+ /**
+ * create a DomHelper friendly object - for use with
+ * Roo.DomHelper.markup / overwrite / etc..
+ * ?? should it be called with option to hide all editing features?
*/
- // private
- pwdStrengths: false,
- // private
- strength: 0,
- // private
- _lastPwd: null,
- // private
- kCapitalLetter: 0,
- kSmallLetter: 1,
- kDigit: 2,
- kPunctuation: 3,
-
- insecure: false,
- // private
- initEvents: function ()
- {
- Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
-
- if (this.el.is('input[type=password]') && Roo.isSafari) {
- this.el.on('keydown', this.SafariOnKeyDown, this);
+ /**
+ * create a DomHelper friendly object - for use with
+ * Roo.DomHelper.markup / overwrite / etc..
+ * ?? should it be called with option to hide all editing features?
+ */
+ toObject : function()
+ {
+
+ var ret = {
+ tag : 'td',
+ contenteditable : 'true', // this stops cell selection from picking the table.
+ 'data-block' : 'Td',
+ valign : this.valign,
+ style : {
+ 'text-align' : this.textAlign,
+ border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
+ 'border-collapse' : 'collapse',
+ padding : '6px', // 8 for desktop / 4 for mobile
+ 'vertical-align': this.valign
+ },
+ html : this.html
+ };
+ if (this.width != '') {
+ ret.width = this.width;
+ ret.style.width = this.width;
}
-
- this.el.on('keyup', this.checkStrength, this, {buffer: 50});
+
+
+ if (this.colspan > 1) {
+ ret.colspan = this.colspan ;
+ }
+ if (this.rowspan > 1) {
+ ret.rowspan = this.rowspan ;
+ }
+
+
+
+ return ret;
+
},
- // private
- onRender: function (ct, position)
+
+ readElement : function(node)
{
- Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
- this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
- this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
-
- this.trigger.createChild({
- cn: [
- {
- //id: 'PwdMeter',
- tag: 'div',
- cls: 'roo-password-meter-grey col-xs-12',
- style: {
- //width: 0,
- //width: this.meterWidth + 'px'
- }
- },
- {
- cls: 'roo-password-meter-text'
- }
- ]
- });
-
-
- if (this.hideTrigger) {
- this.trigger.setDisplayed(false);
- }
- this.setSize(this.width || '', this.height || '');
+ node = node ? node : this.node ;
+ this.width = node.style.width;
+ this.colspan = Math.max(1,1*node.getAttribute('colspan'));
+ this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
+ this.html = node.innerHTML;
+
+
},
- // private
- onDestroy: function ()
+
+ // the default cell object... at present...
+ emptyCell : function() {
+ return {
+ colspan : 1,
+ rowspan : 1,
+ textAlign : 'left',
+ html : " " // is this going to be editable now?
+ };
+
+ },
+
+ removeNode : function()
{
- if (this.trigger) {
- this.trigger.removeAllListeners();
- this.trigger.remove();
- }
- if (this.wrap) {
- this.wrap.remove();
- }
- Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
+ return this.node.closest('table');
+
},
- // private
- checkStrength: function ()
+
+ cellData : false,
+
+ colWidths : false,
+
+ toTableArray : function()
{
- var pwd = this.inputEl().getValue();
- if (pwd == this._lastPwd) {
- return;
- }
-
- var strength;
- if (this.ClientSideStrongPassword(pwd)) {
- strength = 3;
- } else if (this.ClientSideMediumPassword(pwd)) {
- strength = 2;
- } else if (this.ClientSideWeakPassword(pwd)) {
- strength = 1;
- } else {
- strength = 0;
- }
-
- Roo.log('strength1: ' + strength);
-
- //var pm = this.trigger.child('div/div/div').dom;
- var pm = this.trigger.child('div/div');
- pm.removeClass(this.meterClass);
- pm.addClass(this.meterClass[strength]);
+ var ret = [];
+ var tab = this.node.closest('tr').closest('table');
+ Array.from(tab.rows).forEach(function(r, ri){
+ ret[ri] = [];
+ });
+ var rn = 0;
+ this.colWidths = [];
+ var all_auto = true;
+ Array.from(tab.rows).forEach(function(r, ri){
+
+ var cn = 0;
+ Array.from(r.cells).forEach(function(ce, ci){
+ var c = {
+ cell : ce,
+ row : rn,
+ col: cn,
+ colspan : ce.colSpan,
+ rowspan : ce.rowSpan
+ };
+ if (ce.isEqualNode(this.node)) {
+ this.cellData = c;
+ }
+ // if we have been filled up by a row?
+ if (typeof(ret[rn][cn]) != 'undefined') {
+ while(typeof(ret[rn][cn]) != 'undefined') {
+ cn++;
+ }
+ c.col = cn;
+ }
-
- var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
+ if (typeof(this.colWidths[cn]) == 'undefined') {
+ this.colWidths[cn] = ce.style.width;
+ if (this.colWidths[cn] != '') {
+ all_auto = false;
+ }
+ }
- pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
-
- this._lastPwd = pwd;
- },
- reset: function ()
- {
- Roo.bootstrap.SecurePass.superclass.reset.call(this);
-
- this._lastPwd = '';
+
+ if (c.colspan < 2 && c.rowspan < 2 ) {
+ ret[rn][cn] = c;
+ cn++;
+ return;
+ }
+ for(var j = 0; j < c.rowspan; j++) {
+ if (typeof(ret[rn+j]) == 'undefined') {
+ continue; // we have a problem..
+ }
+ ret[rn+j][cn] = c;
+ for(var i = 0; i < c.colspan; i++) {
+ ret[rn+j][cn+i] = c;
+ }
+ }
+
+ cn += c.colspan;
+ }, this);
+ rn++;
+ }, this);
- var pm = this.trigger.child('div/div');
- pm.removeClass(this.meterClass);
- pm.addClass('roo-password-meter-grey');
+ // initalize widths.?
+ // either all widths or no widths..
+ if (all_auto) {
+ this.colWidths[0] = false; // no widths flag.
+ }
- var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
+ return ret;
- pt.innerHTML = '';
- this.inputEl().dom.type='password';
},
- // private
- validateValue: function (value)
+
+
+
+
+ mergeRight: function()
{
+
+ // get the contents of the next cell along..
+ var tr = this.node.closest('tr');
+ var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
+ if (i >= tr.childNodes.length - 1) {
+ return; // no cells on right to merge with.
+ }
+ var table = this.toTableArray();
- if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
- return false;
+ if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
+ return; // nothing right?
}
- if (value.length == 0) {
- if (this.allowBlank) {
- this.clearInvalid();
- return true;
- }
-
- this.markInvalid(this.errors.PwdEmpty);
- this.errorMsg = this.errors.PwdEmpty;
- return false;
+ var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
+ // right cell - must be same rowspan and on the same row.
+ if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
+ return; // right hand side is not same rowspan.
}
- if(this.insecure){
- return true;
- }
- if ('[\x21-\x7e]*'.match(value)) {
- this.markInvalid(this.errors.PwdBadChar);
- this.errorMsg = this.errors.PwdBadChar;
- return false;
+
+ this.node.innerHTML += ' ' + rc.cell.innerHTML;
+ tr.removeChild(rc.cell);
+ this.colspan += rc.colspan;
+ this.node.setAttribute('colspan', this.colspan);
+
+ },
+
+
+ mergeBelow : function()
+ {
+ var table = this.toTableArray();
+ if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
+ return; // no row below
}
- if (value.length < 6) {
- this.markInvalid(this.errors.PwdShort);
- this.errorMsg = this.errors.PwdShort;
- return false;
+ if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
+ return; // nothing right?
}
- if (value.length > 16) {
- this.markInvalid(this.errors.PwdLong);
- this.errorMsg = this.errors.PwdLong;
- return false;
+ var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
+
+ if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
+ return; // right hand side is not same rowspan.
}
- var strength;
- if (this.ClientSideStrongPassword(value)) {
- strength = 3;
- } else if (this.ClientSideMediumPassword(value)) {
- strength = 2;
- } else if (this.ClientSideWeakPassword(value)) {
- strength = 1;
- } else {
- strength = 0;
+ this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
+ rc.cell.parentNode.removeChild(rc.cell);
+ this.rowspan += rc.rowspan;
+ this.node.setAttribute('rowspan', this.rowspan);
+ },
+
+ split: function()
+ {
+ if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
+ return;
}
-
+ var table = this.toTableArray();
+ var cd = this.cellData;
+ this.rowspan = 1;
+ this.colspan = 1;
- if (strength < 2) {
- //this.markInvalid(this.errors.TooWeak);
- this.errorMsg = this.errors.TooWeak;
- //return false;
+ for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
+
+
+
+ for(var c = cd.col; c < cd.col + cd.colspan; c++) {
+ if (r == cd.row && c == cd.col) {
+ this.node.removeAttribute('rowspan');
+ this.node.removeAttribute('colspan');
+ continue;
+ }
+
+ var ntd = this.node.cloneNode(); // which col/row should be 0..
+ ntd.removeAttribute('id'); //
+ //ntd.style.width = '';
+ ntd.innerHTML = '';
+ table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
+ }
+
}
+ this.redrawAllCells(table);
+
- console.log('strength2: ' + strength);
-
- //var pm = this.trigger.child('div/div/div').dom;
-
- var pm = this.trigger.child('div/div');
- pm.removeClass(this.meterClass);
- pm.addClass(this.meterClass[strength]);
-
- var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
-
- pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
-
- this.errorMsg = '';
- return true;
- },
- // private
- CharacterSetChecks: function (type)
- {
- this.type = type;
- this.fResult = false;
},
- // private
- isctype: function (character, type)
+
+
+
+ redrawAllCells: function(table)
{
- switch (type) {
- case this.kCapitalLetter:
- if (character >= 'A' && character <= 'Z') {
- return true;
- }
- break;
-
- case this.kSmallLetter:
- if (character >= 'a' && character <= 'z') {
- return true;
- }
- break;
+
+
+ var tab = this.node.closest('tr').closest('table');
+ var ctr = tab.rows[0].parentNode;
+ Array.from(tab.rows).forEach(function(r, ri){
- case this.kDigit:
- if (character >= '0' && character <= '9') {
- return true;
- }
- break;
+ Array.from(r.cells).forEach(function(ce, ci){
+ ce.parentNode.removeChild(ce);
+ });
+ r.parentNode.removeChild(r);
+ });
+ for(var r = 0 ; r < table.length; r++) {
+ var re = tab.rows[r];
- case this.kPunctuation:
- if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
- return true;
+ var re = tab.ownerDocument.createElement('tr');
+ ctr.appendChild(re);
+ for(var c = 0 ; c < table[r].length; c++) {
+ if (table[r][c].cell === false) {
+ continue;
}
- break;
-
- default:
- return false;
+
+ re.appendChild(table[r][c].cell);
+
+ table[r][c].cell = false;
+ }
}
-
+
},
- // private
- IsLongEnough: function (pwd, size)
+ updateWidths : function(table)
{
- return !(pwd == null || isNaN(size) || pwd.length < size);
+ for(var r = 0 ; r < table.length; r++) {
+
+ for(var c = 0 ; c < table[r].length; c++) {
+ if (table[r][c].cell === false) {
+ continue;
+ }
+
+ if (this.colWidths[0] != false && table[r][c].colspan < 2) {
+ var el = Roo.htmleditor.Block.factory(table[r][c].cell);
+ el.width = Math.floor(this.colWidths[c]) +'%';
+ el.updateElement(el.node);
+ }
+ table[r][c].cell = false; // done
+ }
+ }
},
- // private
- SpansEnoughCharacterSets: function (word, nb)
+ normalizeWidths : function(table)
{
- if (!this.IsLongEnough(word, nb))
- {
- return false;
+
+ if (this.colWidths[0] === false) {
+ var nw = 100.0 / this.colWidths.length;
+ this.colWidths.forEach(function(w,i) {
+ this.colWidths[i] = nw;
+ },this);
+ return;
}
-
- var characterSetChecks = new Array(
- new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
- new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
- );
+
+ var t = 0, missing = [];
- for (var index = 0; index < word.length; ++index) {
- for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
- if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
- characterSetChecks[nCharSet].fResult = true;
- break;
- }
- }
- }
-
- var nCharSets = 0;
- for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
- if (characterSetChecks[nCharSet].fResult) {
- ++nCharSets;
+ this.colWidths.forEach(function(w,i) {
+ //if you mix % and
+ this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
+ var add = this.colWidths[i];
+ if (add > 0) {
+ t+=add;
+ return;
}
+ missing.push(i);
+
+
+ },this);
+ var nc = this.colWidths.length;
+ if (missing.length) {
+ var mult = (nc - missing.length) / (1.0 * nc);
+ var t = mult * t;
+ var ew = (100 -t) / (1.0 * missing.length);
+ this.colWidths.forEach(function(w,i) {
+ if (w > 0) {
+ this.colWidths[i] = w * mult;
+ return;
+ }
+
+ this.colWidths[i] = ew;
+ }, this);
+ // have to make up numbers..
+
}
-
- if (nCharSets < nb) {
- return false;
+ // now we should have all the widths..
+
+
+ },
+
+ shrinkColumn : function()
+ {
+ var table = this.toTableArray();
+ this.normalizeWidths(table);
+ var col = this.cellData.col;
+ var nw = this.colWidths[col] * 0.8;
+ if (nw < 5) {
+ return;
}
- return true;
+ var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
+ this.colWidths.forEach(function(w,i) {
+ if (i == col) {
+ this.colWidths[i] = nw;
+ return;
+ }
+ this.colWidths[i] += otherAdd
+ }, this);
+ this.updateWidths(table);
+
},
- // private
- ClientSideStrongPassword: function (pwd)
+ growColumn : function()
{
- return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
+ var table = this.toTableArray();
+ this.normalizeWidths(table);
+ var col = this.cellData.col;
+ var nw = this.colWidths[col] * 1.2;
+ if (nw > 90) {
+ return;
+ }
+ var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
+ this.colWidths.forEach(function(w,i) {
+ if (i == col) {
+ this.colWidths[i] = nw;
+ return;
+ }
+ this.colWidths[i] -= otherSub
+ }, this);
+ this.updateWidths(table);
+
},
- // private
- ClientSideMediumPassword: function (pwd)
+ deleteRow : function()
{
- return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
+ // delete this rows 'tr'
+ // if any of the cells in this row have a rowspan > 1 && row!= this row..
+ // then reduce the rowspan.
+ var table = this.toTableArray();
+ // this.cellData.row;
+ for (var i =0;i< table[this.cellData.row].length ; i++) {
+ var c = table[this.cellData.row][i];
+ if (c.row != this.cellData.row) {
+
+ c.rowspan--;
+ c.cell.setAttribute('rowspan', c.rowspan);
+ continue;
+ }
+ if (c.rowspan > 1) {
+ c.rowspan--;
+ c.cell.setAttribute('rowspan', c.rowspan);
+ }
+ }
+ table.splice(this.cellData.row,1);
+ this.redrawAllCells(table);
+
},
- // private
- ClientSideWeakPassword: function (pwd)
+ deleteColumn : function()
{
- return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
+ var table = this.toTableArray();
+
+ for (var i =0;i< table.length ; i++) {
+ var c = table[i][this.cellData.col];
+ if (c.col != this.cellData.col) {
+ table[i][this.cellData.col].colspan--;
+ } else if (c.colspan > 1) {
+ c.colspan--;
+ c.cell.setAttribute('colspan', c.colspan);
+ }
+ table[i].splice(this.cellData.col,1);
+ }
+
+ this.redrawAllCells(table);
}
-
-})//<script type="text/javascript">
+
+
+
+
+})
+
+//<script type="text/javascript">
/*
* Based Ext JS Library 1.1.1
* Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
* @param {Roo.HtmlEditorCore} this
*/
- editorevent: true
+ editorevent: true
+
});
* @cfg {Number} width (in pixels)
*/
width: 500,
+ /**
+ * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
+ * if you are doing an email editor, this probably needs disabling, it's designed
+ */
+ autoClean: true,
+ /**
+ * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
+ */
+ enableBlocks : true,
/**
* @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
*
*/
stylesheets: false,
+ /**
+ * @cfg {String} language default en - language of text (usefull for rtl languages)
+ *
+ */
+ language: 'en',
+ /**
+ * @cfg {boolean} allowComments - default false - allow comments in HTML source
+ * - by default they are stripped - if you are editing email you may need this.
+ */
+ allowComments: false,
// id of frame..
frameId: false,
bodyCls : '',
+
+ undoManager : false,
/**
* 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
st = '<style type="text/css">' +
'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
'</style>';
- } else {
- st = '<style type="text/css">' +
- this.stylesheets +
- '</style>';
+ } else {
+ for (var i in this.stylesheets) {
+ if (typeof(this.stylesheets[i]) != 'string') {
+ continue;
+ }
+ st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
+ }
+
}
st += '<style type="text/css">' +
'IMG { cursor: pointer } ' +
'</style>';
-
- var cls = 'roo-htmleditor-body';
+
+ st += '<meta name="google" content="notranslate">';
+
+ var cls = 'notranslate roo-htmleditor-body';
if(this.bodyCls.length){
cls += ' ' + this.bodyCls;
}
- return '<html><head>' + st +
+ return '<html class="notranslate" translate="no"><head>' + st +
//<style type="text/css">' +
//'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
//'</style>' +
- ' </head><body class="' + cls + '"></body></html>';
+ ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
},
// private
this.iframe = iframe.dom;
- this.assignDocWin();
+ this.assignDocWin();
this.doc.designMode = 'on';
if(this.doc.body || this.doc.readyState == 'complete'){
try {
this.doc.designMode="on";
+
} catch (e) {
return;
}
if(this.sourceEditMode){
- Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
+ Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
}else{
- Roo.get(this.iframe).removeClass(['x-hidden','hide']);
+ Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
//this.iframe.className = '';
this.deferFocus();
}
* @param {String} html The HTML to be cleaned
* return {String} The cleaned HTML
*/
- cleanHtml : function(html){
+ cleanHtml : function(html)
+ {
html = String(html);
if(html.length > 5){
if(Roo.isSafari){ // strip safari nonsense
* Protected method that will not generally be called directly. Syncs the contents
* of the editor iframe with the textarea.
*/
- syncValue : function(){
+ syncValue : function()
+ {
+ //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
if(this.initialized){
+
+ if (this.undoManager) {
+ this.undoManager.addEvent();
+ }
+
+
var bd = (this.doc.body || this.doc.documentElement);
- //this.cleanUpPaste(); -- this is done else where and causes havoc..
- var html = bd.innerHTML;
+
+
+ var sel = this.win.getSelection();
+
+ var div = document.createElement('div');
+ div.innerHTML = bd.innerHTML;
+ var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
+ if (gtx.length > 0) {
+ var rm = gtx.item(0).parentNode;
+ rm.parentNode.removeChild(rm);
+ }
+
+
+ if (this.enableBlocks) {
+ new Roo.htmleditor.FilterBlock({ node : div });
+ }
+ //?? tidy?
+ var tidy = new Roo.htmleditor.TidySerializer({
+ inner: true
+ });
+ var html = tidy.serialize(div);
+
+
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;
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 (
+ html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
+
+ var cc = match.charCodeAt();
+
+ // Get the character value, handling surrogate pairs
+ if (match.length == 2) {
+ // It's a surrogate pair, calculate the Unicode code point
+ var high = match.charCodeAt(0) - 0xD800;
+ var low = match.charCodeAt(1) - 0xDC00;
+ cc = (high * 0x400) + low + 0x10000;
+ } else if (
(cc >= 0x4E00 && cc < 0xA000 ) ||
(cc >= 0x3400 && cc < 0x4E00 ) ||
(cc >= 0xf900 && cc < 0xfb00 )
) {
- return b;
- }
- return "&#"+cc+";"
+ return match;
+ }
+
+ // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
+ return "&#" + cc + ";";
+
+
});
+
+
+
if(this.owner.fireEvent('beforesync', this, html) !== false){
this.el.dom.value = html;
this.owner.fireEvent('sync', this, html);
},
/**
+ * TEXTAREA -> EDITABLE
* Protected method that will not generally be called directly. Pushes the value of the textarea
* into the iframe editor.
*/
- pushValue : function(){
+ pushValue : function()
+ {
+ //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
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);
}
+ if (this.autoClean) {
+ new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
+ new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
+ }
+ if (this.enableBlocks) {
+ Roo.htmleditor.Block.initAll(this.doc.body);
+ }
+
+ this.updateLanguage();
+
+ var lc = this.doc.body.lastChild;
+ if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
+ // add an extra line at the end.
+ this.doc.body.appendChild(this.doc.createElement('br'));
+ }
+
+
}
},
//var ss = this.el.getStyles( 'background-image', 'background-repeat');
//ss['background-attachment'] = 'fixed'; // w3c
dbody.bgProperties = 'fixed'; // ie
+ dbody.setAttribute("translate", "no");
+
//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
});
+ Roo.EventManager.on(this.doc, {
+ 'paste': this.onPasteEvent,
+ scope : this
+ });
if(Roo.isGecko){
Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
}
+ //??? needed???
if(Roo.isIE || Roo.isSafari || Roo.isOpera){
Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
}
this.initialized = true;
+
+ // initialize special key events - enter
+ new Roo.htmleditor.KeyEnter({core : this});
+
+
+
this.owner.fireEvent('initialize', this);
this.pushValue();
},
-
+ // this is to prevent a href clicks resulting in a redirect?
+
+ onPasteEvent : function(e,v)
+ {
+ // I think we better assume paste is going to be a dirty load of rubish from word..
+
+ // even pasting into a 'email version' of this widget will have to clean up that mess.
+ var cd = (e.browserEvent.clipboardData || window.clipboardData);
+
+ // check what type of paste - if it's an image, then handle it differently.
+ if (cd.files && cd.files.length > 0) {
+ // pasting images?
+ var urlAPI = (window.createObjectURL && window) ||
+ (window.URL && URL.revokeObjectURL && URL) ||
+ (window.webkitURL && webkitURL);
+
+ var url = urlAPI.createObjectURL( cd.files[0]);
+ this.insertAtCursor('<img src=" + url + ">');
+ return false;
+ }
+ if (cd.types.indexOf('text/html') < 0 ) {
+ return false;
+ }
+ var images = [];
+ var html = cd.getData('text/html'); // clipboard event
+ if (cd.types.indexOf('text/rtf') > -1) {
+ var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
+ images = parser.doc ? parser.doc.getElementsByType('pict') : [];
+ }
+ //Roo.log(images);
+ //Roo.log(imgs);
+ // fixme..
+ images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
+ .map(function(g) { return g.toDataURL(); })
+ .filter(function(g) { return g != 'about:blank'; });
+
+
+ html = this.cleanWordChars(html);
+
+ var d = (new DOMParser().parseFromString(html, 'text/html')).body;
+
+
+ var sn = this.getParentElement();
+ // check if d contains a table, and prevent nesting??
+ //Roo.log(d.getElementsByTagName('table'));
+ //Roo.log(sn);
+ //Roo.log(sn.closest('table'));
+ if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
+ e.preventDefault();
+ this.insertAtCursor("You can not nest tables");
+ //Roo.log("prevent?"); // fixme -
+ return false;
+ }
+
+ if (images.length > 0) {
+ Roo.each(d.getElementsByTagName('img'), function(img, i) {
+ img.setAttribute('src', images[i]);
+ });
+ }
+ if (this.autoClean) {
+ new Roo.htmleditor.FilterWord({ node : d });
+
+ new Roo.htmleditor.FilterStyleToTag({ node : d });
+ new Roo.htmleditor.FilterAttributes({
+ node : d,
+ attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
+ attrib_clean : ['href', 'src' ]
+ });
+ new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
+ // should be fonts..
+ new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
+ new Roo.htmleditor.FilterParagraph({ node : d });
+ new Roo.htmleditor.FilterSpan({ node : d });
+ new Roo.htmleditor.FilterLongBr({ node : d });
+ new Roo.htmleditor.FilterComment({ node : d });
+
+
+ }
+ if (this.enableBlocks) {
+
+ Array.from(d.getElementsByTagName('img')).forEach(function(img) {
+ if (img.closest('figure')) { // assume!! that it's aready
+ return;
+ }
+ var fig = new Roo.htmleditor.BlockFigure({
+ image_src : img.src
+ });
+ fig.updateElement(img); // replace it..
+
+ });
+ }
+
+
+ this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
+ if (this.enableBlocks) {
+ Roo.htmleditor.Block.initAll(this.doc.body);
+ }
+
+
+ e.preventDefault();
+ return false;
+ // default behaveiour should be our local cleanup paste? (optional?)
+ // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
+ //this.owner.fireEvent('paste', e, v);
+ },
// private
onDestroy : function(){
onFirstFocus : function(){
this.assignDocWin();
-
+ this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
this.activated = true;
onEditorEvent : function(e)
{
- this.owner.fireEvent('editorevent', this, e);
+
+
+ if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
+ return; // we do not handle this.. (undo manager does..)
+ }
+ // in theory this detects if the last element is not a br, then we try and do that.
+ // its so clicking in space at bottom triggers adding a br and moving the cursor.
+ if (e &&
+ e.target.nodeName == 'BODY' &&
+ e.type == "mouseup" &&
+ this.doc.body.lastChild
+ ) {
+ var lc = this.doc.body.lastChild;
+ // gtx-trans is google translate plugin adding crap.
+ while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
+ lc = lc.previousSibling;
+ }
+ if (lc.nodeType == 1 && lc.nodeName != 'BR') {
+ // if last element is <BR> - then dont do anything.
+
+ var ns = this.doc.createElement('br');
+ this.doc.body.appendChild(ns);
+ range = this.doc.createRange();
+ range.setStartAfter(ns);
+ range.collapse(true);
+ var sel = this.win.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+ }
+
+
+
+ this.fireEditorEvent(e);
// this.updateToolbar();
this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
},
+
+ fireEditorEvent: function(e)
+ {
+ this.owner.fireEvent('editorevent', this, e);
+ },
insertTag : function(tg)
{
// could be a bit smarter... -> wrap the current selected tRoo..
- if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
+ if (tg.toLowerCase() == 'span' ||
+ tg.toLowerCase() == 'code' ||
+ tg.toLowerCase() == 'sup' ||
+ tg.toLowerCase() == 'sub'
+ ) {
range = this.createRange(this.getSelection());
var wrappingNode = this.doc.createElement(tg.toLowerCase());
}
this.execCmd("formatblock", tg);
-
+ this.undoManager.addEvent();
},
insertText : function(txt)
//alert(Sender.getAttribute('label'));
range.insertNode(this.doc.createTextNode(txt));
+ this.undoManager.addEvent();
} ,
* @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){
+ relayCmd : function(cmd, value)
+ {
+
+ switch (cmd) {
+ case 'justifyleft':
+ case 'justifyright':
+ case 'justifycenter':
+ // if we are in a cell, then we will adjust the
+ var n = this.getParentElement();
+ var td = n.closest('td');
+ if (td) {
+ var bl = Roo.htmleditor.Block.factory(td);
+ bl.textAlign = cmd.replace('justify','');
+ bl.updateElement();
+ this.owner.fireEvent('editorevent', this);
+ return;
+ }
+ this.execCmd('styleWithCSS', true); //
+ break;
+ case 'bold':
+ case 'italic':
+ // if there is no selection, then we insert, and set the curson inside it..
+ this.execCmd('styleWithCSS', false);
+ break;
+
+
+ default:
+ break;
+ }
+
+
this.win.focus();
this.execCmd(cmd, value);
this.owner.fireEvent('editorevent', this);
if(!this.activated){
return;
}
- /*
- if(Roo.isIE){
- this.win.focus();
- var r = this.doc.selection.createRange();
- if(r){
- r.collapse(true);
- r.pasteHTML(text);
- this.syncValue();
- this.deferFocus();
-
- }
- return;
- }
- */
+
if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
this.win.focus();
var win = this.win;
if (win.getSelection && win.getSelection().getRangeAt) {
+
+ // delete the existing?
+
+ this.createRange(this.getSelection()).deleteContents();
range = win.getSelection().getRangeAt(0);
node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
range.insertNode(node);
+ range = range.cloneRange();
+ range.collapse(false);
+
+ win.getSelection().removeAllRanges();
+ win.getSelection().addRange(range);
+
+
+
} 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();
cmd = 'underline';
break;
- case 'v':
- this.cleanUpPaste.defer(100, this);
- return;
+ //case 'v':
+ // this.cleanUpPaste.defer(100, this);
+ // return;
}
if(cmd){
- this.win.focus();
- this.execCmd(cmd);
- this.deferFocus();
+
+ this.relayCmd(cmd);
+ //this.win.focus();
+ //this.execCmd(cmd);
+ //this.deferFocus();
e.preventDefault();
}
// private
fixKeys : function(){ // load time branching for fastest keydown performance
+
+
if(Roo.isIE){
return function(e){
var k = e.getKey(), r;
}
return;
}
-
+ /// this is handled by Roo.htmleditor.KeyEnter
+ /*
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.pasteHTML('<br/>');
r.collapse(false);
r.select();
}
}
}
- if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
- this.cleanUpPaste.defer(100, this);
- return;
- }
+ */
+ //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+ // this.cleanUpPaste.defer(100, this);
+ // return;
+ //}
};
this.execCmd('InsertHTML','    ');
this.deferFocus();
}
- if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
- this.cleanUpPaste.defer(100, this);
- return;
- }
+
+ //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+ // this.cleanUpPaste.defer(100, this);
+ // return;
+ //}
};
}else if(Roo.isSafari){
this.deferFocus();
return;
}
- if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
- this.cleanUpPaste.defer(100, this);
- return;
- }
+ this.mozKeyPress(e);
+
+ //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
+ // this.cleanUpPaste.defer(100, this);
+ // return;
+ // }
};
}
getSelection : function()
{
this.assignDocWin();
- return Roo.isIE ? this.doc.selection : this.win.getSelection();
+ return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
+ },
+ /**
+ * Select a dom node
+ * @param {DomElement} node the node to select
+ */
+ selectNode : function(node, collapse)
+ {
+ var nodeRange = node.ownerDocument.createRange();
+ try {
+ nodeRange.selectNode(node);
+ } catch (e) {
+ nodeRange.selectNodeContents(node);
+ }
+ if (collapse === true) {
+ nodeRange.collapse(true);
+ }
+ //
+ var s = this.win.getSelection();
+ s.removeAllRanges();
+ s.addRange(nodeRange);
},
getSelectedNode: function()
// should we cache this!!!!
-
-
+
var range = this.createRange(this.getSelection()).cloneRange();
return nodes[0];
},
+
+
createRange: function(sel)
{
// this has strange effects when using with
// 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;
- }
-
- },
-
+
cleanWordChars : function(input) {// change the chars to hex code
- var he = Roo.HtmlEditorCore;
+ var swapCodes = [
+ [ 8211, "–" ],
+ [ 8212, "—" ],
+ [ 8216, "'" ],
+ [ 8217, "'" ],
+ [ 8220, '"' ],
+ [ 8221, '"' ],
+ [ 8226, "*" ],
+ [ 8230, "..." ]
+ ];
var output = input;
- Roo.each(he.swapCodes, function(sw) {
+ Roo.each(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]);
- }
- },
-
+
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;
- }
- var lcname = node.tagName.toLowerCase();
- // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
- // whitelist of tags..
-
- if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
- // remove node.
- node.parentNode.removeChild(node);
- return;
-
- }
-
- 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:/) || v.match(/^ftp:/)) {
- return;
- }
- if (v.match(/^#/)) {
- return;
- }
-// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
- node.removeAttribute(n);
-
- }
-
- var cwhite = this.cwhite;
- var cblack = this.cblack;
-
- function cleanStyle(n,v)
- {
- if (v.match(/expression/)) { //XSS?? should we even bother..
- node.removeAttribute(n);
- return;
- }
-
- 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 ( cwhite.length && 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);
+ new Roo.htmleditor.FilterComment({node : node});
+ new Roo.htmleditor.FilterAttributes({
+ node : node,
+ attrib_black : this.ablack,
+ attrib_clean : this.aclean,
+ style_white : this.cwhite,
+ style_black : this.cblack
+ });
+ new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
+ new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
+
},
/**
* Clean up MS wordisms...
+ * @deprecated - use filter directly
*/
cleanWord : function(node)
{
-
-
- 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);
- this.iterateChildren(node, this.cleanWord);
- return;
- }
- // clean styles
- if (node.className.length) {
-
- var cn = node.className.split(/\W+/);
- var cna = [];
- Roo.each(cn, function(cls) {
- if (cls.match(/Mso[a-zA-Z]+/)) {
- return;
- }
- cna.push(cls);
- });
- node.className = cna.length ? cna.join(' ') : '';
- if (!cna.length) {
- node.removeAttribute("class");
- }
- }
-
- if (node.hasAttribute("lang")) {
- node.removeAttribute("lang");
- }
-
- if (node.hasAttribute("style")) {
-
- var styles = node.getAttribute("style").split(";");
- var nstyle = [];
- Roo.each(styles, function(s) {
- if (!s.match(/:/)) {
- return;
- }
- var kv = s.split(":");
- if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
- return;
- }
- // what ever is left... we allow.
- nstyle.push(s);
- });
- node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
- if (!nstyle.length) {
- node.removeAttribute('style');
- }
- }
- this.iterateChildren(node, this.cleanWord);
-
-
+ new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
},
- /**
- * iterateChildren of a Node, calling fn each time, using this as the scole..
- * @param {DomNode} node node to iterate children of.
- * @param {Function} fn method of this class to call on each item.
- */
- iterateChildren : function(node, fn)
- {
- if (!node.childNodes.length) {
- return;
- }
- for (var i = node.childNodes.length-1; i > -1 ; i--) {
- fn.call(this, node.childNodes[i])
- }
- },
-
+
/**
- * cleanTableWidths.
- *
- * Quite often pasting from word etc.. results in tables with column and widths.
- * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
- *
+
+ * @deprecated - use filters
*/
cleanTableWidths : function(node)
{
-
-
- if (!node) {
- this.cleanTableWidths(this.doc.body);
- return;
- }
-
- // ignore list...
- if (node.nodeName == "#text" || node.nodeName == "#comment") {
- return;
- }
- Roo.log(node.tagName);
- if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
- this.iterateChildren(node, this.cleanTableWidths);
- return;
- }
- if (node.hasAttribute('width')) {
- node.removeAttribute('width');
- }
-
-
- if (node.hasAttribute("style")) {
- // pretty basic...
-
- var styles = node.getAttribute("style").split(";");
- var nstyle = [];
- Roo.each(styles, function(s) {
- if (!s.match(/:/)) {
- return;
- }
- var kv = s.split(":");
- if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
- return;
- }
- // what ever is left... we allow.
- nstyle.push(s);
- });
- node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
- if (!nstyle.length) {
- node.removeAttribute('style');
- }
- }
-
- this.iterateChildren(node, this.cleanTableWidths);
-
+ new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
+
},
-
-
-
- domToHTML : function(currentElement, depth, nopadtext) {
-
- depth = depth || 0;
- nopadtext = nopadtext || false;
-
- if (!currentElement) {
- return this.domToHTML(this.doc.body);
- }
-
- //Roo.log(currentElement);
- var j;
- var allText = false;
- var nodeName = currentElement.nodeName;
- var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
-
- if (nodeName == '#text') {
-
- return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
- }
-
-
- var ret = '';
- if (nodeName != 'BODY') {
-
- var i = 0;
- // Prints the node tagName, such as <A>, <IMG>, etc
- if (tagName) {
- var attr = [];
- for(i = 0; i < currentElement.attributes.length;i++) {
- // quoting?
- var aname = currentElement.attributes.item(i).name;
- if (!currentElement.attributes.item(i).value.length) {
- continue;
- }
- attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
- }
-
- ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
- }
- else {
-
- // eack
- }
- } else {
- tagName = false;
- }
- if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
- return ret;
- }
- if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
- nopadtext = true;
- }
-
-
- // Traverse the tree
- i = 0;
- var currentElementChild = currentElement.childNodes.item(i);
- var allText = true;
- var innerHTML = '';
- lastnode = '';
- while (currentElementChild) {
- // Formatting code (indent the tree so it looks nice on the screen)
- var nopad = nopadtext;
- if (lastnode == 'SPAN') {
- nopad = true;
- }
- // text
- if (currentElementChild.nodeName == '#text') {
- var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
- toadd = nopadtext ? toadd : toadd.trim();
- if (!nopad && toadd.length > 80) {
- innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
- }
- innerHTML += toadd;
-
- i++;
- currentElementChild = currentElement.childNodes.item(i);
- lastNode = '';
- continue;
- }
- allText = false;
-
- innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
-
- // Recursively traverse the tree structure of the child node
- innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
- lastnode = currentElementChild.nodeName;
- i++;
- currentElementChild=currentElement.childNodes.item(i);
- }
-
- ret += innerHTML;
-
- if (!allText) {
- // The remaining code is mostly for formatting the tree
- ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
- }
-
-
- if (tagName) {
- ret+= "</"+tagName+">";
- }
- return ret;
-
- },
+
applyBlacklists : function()
{
var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
+ this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
+ this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
+ this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
+
this.white = [];
this.black = [];
Roo.each(Roo.HtmlEditorCore.white, function(tag) {
},
+
+ updateLanguage : function()
+ {
+ if (!this.iframe || !this.iframe.contentDocument) {
+ return;
+ }
+ Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
+ },
+
+
removeStylesheets : function()
{
var _this = this;
});
Roo.HtmlEditorCore.white = [
- 'area', 'br', 'img', 'input', 'hr', 'wbr',
+ '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',
+ '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',
+ 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
+ 'THEAD', 'TR',
- 'dir', 'menu', 'ol', 'ul', 'dl',
+ 'DIR', 'MENU', 'OL', 'UL', 'DL',
- 'embed', 'object'
+ '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..
+ 'APPLET', //
+ 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
+ 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
+ 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
+ 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
+ //'FONT' // CLEAN LATER..
+ 'COLGROUP', 'COL' // messy tables.
+
+
];
-Roo.HtmlEditorCore.clean = [
- 'script', 'style', 'title', 'xml'
+Roo.HtmlEditorCore.clean = [ // ?? needed???
+ 'SCRIPT', 'STYLE', 'TITLE', 'XML'
];
-Roo.HtmlEditorCore.remove = [
- 'font'
+Roo.HtmlEditorCore.tag_remove = [
+ 'FONT', 'TBODY'
];
// attributes..
];
-Roo.HtmlEditorCore.swapCodes =[
- [ 8211, "--" ],
- [ 8212, "--" ],
- [ 8216, "'" ],
- [ 8217, "'" ],
- [ 8220, '"' ],
- [ 8221, '"' ],
- [ 8226, "*" ],
- [ 8230, "..." ]
-];
+
/*
* - LGPL
*/
/**
- * @class Roo.bootstrap.HtmlEditor
- * @extends Roo.bootstrap.TextArea
+ * @class Roo.bootstrap.form.HtmlEditor
+ * @extends Roo.bootstrap.form.TextArea
* Bootstrap HtmlEditor class
* @constructor
* @param {Object} config The config object
*/
-Roo.bootstrap.HtmlEditor = function(config){
- Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
+Roo.bootstrap.form.HtmlEditor = function(config){
+ Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
if (!this.toolbars) {
this.toolbars = [];
}
};
-Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
+Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
/**
Roo.log('renewing');
Roo.log("create toolbars");
- this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
+ this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({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?
+// editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // 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);
+// Roo.bootstrap.form.HtmlEditor);
// editor.toolbars[i].init(editor);
// }
},
{
// Roo.log("Call onRender: " + this.xtype);
var _t = this;
- Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
+ Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
this.wrap = this.inputEl().wrap({
cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
onResize : function(w, h)
{
Roo.log('resize: ' +w + ',' + h );
- Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
+ Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
var ew = false;
var eh = false;
// clearInvalid : Roo.emptyFn,
setValue : function(v){
- Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
+ Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
this.editorcore.pushValue();
},
-Roo.namespace('Roo.bootstrap.htmleditor');
+Roo.namespace('Roo.bootstrap.form.HtmlEditor');
/**
- * @class Roo.bootstrap.HtmlEditorToolbar1
+ * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
+ * @parent Roo.bootstrap.form.HtmlEditor
+ * @extends Roo.bootstrap.nav.Simplebar
* Basic Toolbar
*
+ * @example
* Usage:
*
- new Roo.bootstrap.HtmlEditor({
+ new Roo.bootstrap.form.HtmlEditor({
....
toolbars : [
- new Roo.bootstrap.HtmlEditorToolbar1({
+ new Roo.bootstrap.form.HtmlEditorToolbarStandard({
disable : { fonts: 1 , format: 1, ..., ... , ...],
btns : [ .... ]
})
* .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
*/
-Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
+Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
{
Roo.apply(this, config);
colors : true,
specialElements : true
});
- Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
+ Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
this.editor = config.editor;
this.editorcore = config.editor.editorcore;
//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, {
+Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
bar : true,
{
// Roo.log("Call onRender: " + this.xtype);
- Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
+ Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
Roo.log(this.el);
this.el.dom.style.marginBottom = '0';
var _this = this;
// hides menus... - so this cant be on a menu...
Roo.bootstrap.MenuMgr.hideAll();
*/
- Roo.bootstrap.MenuMgr.hideAll();
+ Roo.bootstrap.menu.Manager.hideAll();
//this.editorsyncValue();
},
onFirstFocus: function() {
-
-/**
- * @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
+
+/*
+ * - LGPL
*/
-Roo.bootstrap.Table.AbstractSelectionModel = function(){
- this.locked = false;
- Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
-};
-
-
-Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
- /** @ignore Called by the grid automatically. Do not call directly. */
- init : function(grid){
- this.grid = grid;
- this.initEvents();
- },
-
- /**
- * Locks the selections.
- */
- lock : function(){
- this.locked = true;
- },
-
- /**
- * Unlocks the selections.
- */
- unlock : function(){
- this.locked = false;
- },
- /**
- * Returns true if the selections are locked.
- * @return {Boolean}
- */
- isLocked : function(){
- return this.locked;
- }
-});
/**
- * @extends Roo.bootstrap.Table.AbstractSelectionModel
- * @class Roo.bootstrap.Table.RowSelectionModel
- * The default SelectionModel used by {@link Roo.bootstrap.Table}.
- * It supports multiple selections and keyboard selection/navigation.
+ * @class Roo.bootstrap.form.Markdown
+ * @extends Roo.bootstrap.form.TextArea
+ * Bootstrap Showdown editable area
+ * @cfg {string} content
+ *
* @constructor
- * @param {Object} config
+ * Create a new Showdown
*/
-Roo.bootstrap.Table.RowSelectionModel = function(config){
- Roo.apply(this, config);
- this.selections = new Roo.util.MixedCollection(false, function(o){
- return o.id;
- });
-
- this.last = false;
- this.lastActive = false;
-
- this.addEvents({
- /**
- * @event selectionchange
- * Fires when the selection changes
- * @param {SelectionModel} this
- */
- "selectionchange" : true,
- /**
- * @event afterselectionchange
- * Fires after the selection changes (eg. by key press or clicking)
- * @param {SelectionModel} this
- */
- "afterselectionchange" : true,
- /**
- * @event beforerowselect
- * Fires when a row is selected being selected, return false to cancel.
- * @param {SelectionModel} this
- * @param {Number} rowIndex The selected index
- * @param {Boolean} keepExisting False if other selections will be cleared
- */
- "beforerowselect" : true,
- /**
- * @event rowselect
- * Fires when a row is selected.
- * @param {SelectionModel} this
- * @param {Number} rowIndex The selected index
- * @param {Roo.data.Record} r The record
- */
- "rowselect" : true,
- /**
- * @event rowdeselect
- * Fires when a row is deselected.
- * @param {SelectionModel} this
- * @param {Number} rowIndex The selected index
- */
- "rowdeselect" : true
- });
- Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
- this.locked = false;
- };
-
-Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
- /**
- * @cfg {Boolean} singleSelect
- * True to allow selection of only one row at a time (defaults to false)
- */
- singleSelect : false,
+Roo.bootstrap.form.Markdown = function(config){
+ Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
+
+};
- // private
+Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
+
+ editing :false,
+
initEvents : function()
{
-
- //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
- // this.growclickrid.on("mousedown", this.handleMouseDown, this);
- //}else{ // allow click to work like normal
- // this.grid.on("rowclick", this.handleDragableRowClick, this);
- //}
- //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
- this.grid.on("rowclick", this.handleMouseDown, this);
- this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
- "up" : function(e){
- if(!e.shiftKey){
- this.selectPrevious(e.shiftKey);
- }else if(this.last !== false && this.lastActive !== false){
- var last = this.last;
- this.selectRange(this.last, this.lastActive-1);
- this.grid.getView().focusRow(this.lastActive);
- if(last !== false){
- this.last = last;
- }
- }else{
- this.selectFirstRow();
- }
- this.fireEvent("afterselectionchange", this);
- },
- "down" : function(e){
- if(!e.shiftKey){
- this.selectNext(e.shiftKey);
- }else if(this.last !== false && this.lastActive !== false){
- var last = this.last;
- this.selectRange(this.last, this.lastActive+1);
- this.grid.getView().focusRow(this.lastActive);
- if(last !== false){
- this.last = last;
- }
- }else{
- this.selectFirstRow();
- }
- this.fireEvent("afterselectionchange", this);
- },
- scope: this
- });
- this.grid.store.on('load', function(){
- this.selections.clear();
- },this);
- /*
- var view = this.grid.view;
- view.on("refresh", this.onRefresh, this);
- view.on("rowupdated", this.onRowUpdated, this);
- view.on("rowremoved", this.onRemove, this);
- */
- },
-
- // private
- onRefresh : function()
- {
- var ds = this.grid.store, i, v = this.grid.view;
- var s = this.selections;
- s.each(function(r){
- if((i = ds.indexOfId(r.id)) != -1){
- v.onRowSelect(i);
- }else{
- s.remove(r);
- }
+ Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
+ this.markdownEl = this.el.createChild({
+ cls : 'roo-markdown-area'
});
- },
-
- // private
- onRemove : function(v, index, r){
- this.selections.remove(r);
- },
-
- // private
- onRowUpdated : function(v, index, r){
- if(this.isSelected(r)){
- v.onRowSelect(index);
- }
- },
-
- /**
- * Select records.
- * @param {Array} records The records to select
- * @param {Boolean} keepExisting (optional) True to keep existing selections
- */
- selectRecords : function(records, keepExisting)
- {
- if(!keepExisting){
- this.clearSelections();
- }
- var ds = this.grid.store;
- for(var i = 0, len = records.length; i < len; i++){
- this.selectRow(ds.indexOf(records[i]), true);
- }
- },
-
- /**
- * Gets the number of selected rows.
- * @return {Number}
- */
- getCount : function(){
- return this.selections.length;
- },
-
- /**
- * Selects the first row in the grid.
- */
- selectFirstRow : function(){
- this.selectRow(0);
- },
-
- /**
- * Select the last row.
- * @param {Boolean} keepExisting (optional) True to keep existing selections
- */
- selectLastRow : function(keepExisting){
- //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
- this.selectRow(this.grid.store.getCount() - 1, keepExisting);
- },
-
- /**
- * Selects the row immediately following the last selected row.
- * @param {Boolean} keepExisting (optional) True to keep existing selections
- */
- selectNext : function(keepExisting)
- {
- if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
- this.selectRow(this.last+1, keepExisting);
- this.grid.getView().focusRow(this.last);
- }
- },
-
- /**
- * Selects the row that precedes the last selected row.
- * @param {Boolean} keepExisting (optional) True to keep existing selections
- */
- selectPrevious : function(keepExisting){
- if(this.last){
- this.selectRow(this.last-1, keepExisting);
- this.grid.getView().focusRow(this.last);
+ this.inputEl().addClass('d-none');
+ if (this.getValue() == '') {
+ this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
+
+ } else {
+ this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
}
+ this.markdownEl.on('click', this.toggleTextEdit, this);
+ this.on('blur', this.toggleTextEdit, this);
+ this.on('specialkey', this.resizeTextArea, this);
},
-
- /**
- * Returns the selected records
- * @return {Array} Array of selected records
- */
- getSelections : function(){
- return [].concat(this.selections.items);
- },
-
- /**
- * Returns the first selected record.
- * @return {Record}
- */
- getSelected : function(){
- return this.selections.itemAt(0);
- },
-
-
- /**
- * Clears all selections.
- */
- clearSelections : function(fast)
+
+ toggleTextEdit : function()
{
- if(this.locked) {
- return;
- }
- if(fast !== true){
- var ds = this.grid.store;
- var s = this.selections;
- s.each(function(r){
- this.deselectRow(ds.indexOfId(r.id));
- }, this);
- s.clear();
- }else{
- this.selections.clear();
- }
- this.last = false;
- },
-
-
- /**
- * Selects all rows.
- */
- selectAll : function(){
- if(this.locked) {
+ var sh = this.markdownEl.getHeight();
+ this.inputEl().addClass('d-none');
+ this.markdownEl.addClass('d-none');
+ if (!this.editing) {
+ // show editor?
+ this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
+ this.inputEl().removeClass('d-none');
+ this.inputEl().focus();
+ this.editing = true;
return;
}
- this.selections.clear();
- for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
- this.selectRow(i, true);
- }
- },
-
- /**
- * Returns True if there is a selection.
- * @return {Boolean}
- */
- hasSelection : function(){
- return this.selections.length > 0;
- },
-
- /**
- * Returns True if the specified row is selected.
- * @param {Number/Record} record The record or index of the record to check
- * @return {Boolean}
- */
- isSelected : function(index){
- var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
- return (r && this.selections.key(r.id) ? true : false);
- },
-
- /**
- * Returns True if the specified record id is selected.
- * @param {String} id The id of record to check
- * @return {Boolean}
- */
- isIdSelected : function(id){
- return (this.selections.key(id) ? true : false);
- },
-
-
- // private
- handleMouseDBClick : function(e, t){
-
+ // show showdown...
+ this.updateMarkdown();
+ this.markdownEl.removeClass('d-none');
+ this.editing = false;
+ return;
},
- // private
- handleMouseDown : function(e, t)
+ updateMarkdown : function()
{
- var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
- if(this.isLocked() || rowIndex < 0 ){
+ if (this.getValue() == '') {
+ this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
return;
- };
- if(e.shiftKey && this.last !== false){
- var last = this.last;
- this.selectRange(last, rowIndex, e.ctrlKey);
- this.last = last; // reset the last
- t.focus();
-
- }else{
- var isSelected = this.isSelected(rowIndex);
- //Roo.log("select row:" + rowIndex);
- if(isSelected){
- this.deselectRow(rowIndex);
- } else {
- this.selectRow(rowIndex, true);
- }
-
- /*
- if(e.button !== 0 && isSelected){
- alert('rowIndex 2: ' + rowIndex);
- view.focusRow(rowIndex);
- }else if(e.ctrlKey && isSelected){
- this.deselectRow(rowIndex);
- }else if(!isSelected){
- this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
- view.focusRow(rowIndex);
- }
- */
- }
- this.fireEvent("afterselectionchange", this);
- },
- // private
- handleDragableRowClick : function(grid, rowIndex, e)
- {
- if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
- this.selectRow(rowIndex, false);
- grid.view.focusRow(rowIndex);
- this.fireEvent("afterselectionchange", this);
}
+
+ this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
},
- /**
- * Selects multiple rows.
- * @param {Array} rows Array of the indexes of the row to select
- * @param {Boolean} keepExisting (optional) True to keep existing selections
- */
- selectRows : function(rows, keepExisting){
- if(!keepExisting){
- this.clearSelections();
- }
- for(var i = 0, len = rows.length; i < len; i++){
- this.selectRow(rows[i], true);
- }
- },
-
- /**
- * Selects a range of rows. All rows in between startRow and endRow are also selected.
- * @param {Number} startRow The index of the first row in the range
- * @param {Number} endRow The index of the last row in the range
- * @param {Boolean} keepExisting (optional) True to retain existing selections
- */
- selectRange : function(startRow, endRow, keepExisting){
- if(this.locked) {
- return;
- }
- if(!keepExisting){
- this.clearSelections();
- }
- if(startRow <= endRow){
- for(var i = startRow; i <= endRow; i++){
- this.selectRow(i, true);
- }
- }else{
- for(var i = startRow; i >= endRow; i--){
- this.selectRow(i, true);
- }
- }
- },
-
- /**
- * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
- * @param {Number} startRow The index of the first row in the range
- * @param {Number} endRow The index of the last row in the range
- */
- deselectRange : function(startRow, endRow, preventViewNotify){
- if(this.locked) {
- return;
- }
- for(var i = startRow; i <= endRow; i++){
- this.deselectRow(i, preventViewNotify);
- }
+ resizeTextArea: function () {
+
+ var sh = 100;
+ Roo.log([sh, this.getValue().split("\n").length * 30]);
+ this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
},
-
- /**
- * 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)
+ setValue : function(val)
{
- if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
- return;
- }
- if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
- if(!keepExisting || this.singleSelect){
- this.clearSelections();
- }
-
- var r = this.grid.store.getAt(index);
- //console.log('selectRow - record id :' + r.id);
-
- this.selections.add(r);
- this.last = this.lastActive = index;
- if(!preventViewNotify){
- var proxy = new Roo.Element(
- this.grid.getRowDom(index)
- );
- proxy.addClass('bg-info info');
- }
- this.fireEvent("rowselect", this, index, r);
- this.fireEvent("selectionchange", this);
+ Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
+ if (!this.editing) {
+ this.updateMarkdown();
}
+
},
-
- /**
- * Deselects a row.
- * @param {Number} row The index of the row to deselect
- */
- deselectRow : function(index, preventViewNotify)
+ focus : function()
{
- if(this.locked) {
- return;
- }
- if(this.last == index){
- this.last = false;
- }
- if(this.lastActive == index){
- this.lastActive = false;
- }
-
- var r = this.grid.store.getAt(index);
- if (!r) {
- return;
+ if (!this.editing) {
+ this.toggleTextEdit();
}
- this.selections.remove(r);
- //.console.log('deselectRow - record id :' + r.id);
- if(!preventViewNotify){
-
- var proxy = new Roo.Element(
- this.grid.getRowDom(index)
- );
- proxy.removeClass('bg-info info');
- }
- this.fireEvent("rowdeselect", this, index);
- this.fireEvent("selectionchange", this);
- },
-
- // 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.
/**
* @class Roo.bootstrap.PagingToolbar
- * @extends Roo.bootstrap.NavSimplebar
+ * @extends Roo.bootstrap.nav.Simplebar
* A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
* @constructor
* Create a new PagingToolbar
if (Roo.bootstrap.version == 4) {
this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
} else {
- this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
+ this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
}
};
-Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
+Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
/**
- * @cfg {Roo.data.Store} dataSource
+ * @cfg {Roo.bootstrap.Button} buttons[]
+ * Buttons for the toolbar
+ */
+ /**
+ * @cfg {Roo.data.Store} store
* The underlying data store providing the paged data
*/
/**
// private
onLoad : function(ds, r, o)
{
- this.cursor = o.params.start ? o.params.start : 0;
+ this.cursor = o.params && o.params.start ? o.params.start : 0;
var d = this.getPageData(),
ap = d.activePage,
},
// private
- onLoadError : function(){
+ onLoadError : function(proxy, o){
this.loading.enable();
+ if (this.ds.events.loadexception.listeners.length < 2) {
+ // nothing has been assigned to loadexception except this...
+ // so
+ Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
+
+ }
},
// private
});
-/*
+Roo.bootstrap.dash = {};/*
* - LGPL
*
* numberBox
/**
* @class Roo.bootstrap.dash.TabBox
* @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.dash.TabPane
* Bootstrap TabBox class
* @cfg {String} title Title of the TabBox
* @cfg {String} icon Icon of the TabBox
/**
* @class Roo.bootstrap.TabPane
* @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Graph Roo.bootstrap.Column
* Bootstrap TabPane class
* @cfg {Boolean} active (false | true) Default false
* @cfg {String} title title of panel
- /*
- * - LGPL
- *
- * menu
- *
- */
-Roo.bootstrap.menu = Roo.bootstrap.menu || {};
-
-/**
- * @class Roo.bootstrap.menu.Menu
- * @extends Roo.bootstrap.Component
- * Bootstrap Menu class - container for Menu
- * @cfg {String} html Text of the menu
- * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
- * @cfg {String} icon Font awesome icon
- * @cfg {String} pos Menu align to (top | bottom) default bottom
- *
- *
- * @constructor
- * Create a new Menu
- * @param {Object} config The config object
- */
-
-
-Roo.bootstrap.menu.Menu = function(config){
- Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
-
- this.addEvents({
- /**
- * @event beforeshow
- * Fires before this menu is displayed
- * @param {Roo.bootstrap.menu.Menu} this
- */
- beforeshow : true,
- /**
- * @event beforehide
- * Fires before this menu is hidden
- * @param {Roo.bootstrap.menu.Menu} this
- */
- beforehide : true,
- /**
- * @event show
- * Fires after this menu is displayed
- * @param {Roo.bootstrap.menu.Menu} this
- */
- show : true,
- /**
- * @event hide
- * Fires after this menu is hidden
- * @param {Roo.bootstrap.menu.Menu} this
- */
- hide : true,
- /**
- * @event click
- * Fires when this menu is clicked (or when the enter key is pressed while it is active)
- * @param {Roo.bootstrap.menu.Menu} this
- * @param {Roo.EventObject} e
- */
- click : true
- });
-
-};
-
-Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
-
- submenu : false,
- html : '',
- weight : 'default',
- icon : false,
- pos : 'bottom',
-
-
- getChildContainer : function() {
- if(this.isSubMenu){
- return this.el;
- }
-
- return this.el.select('ul.dropdown-menu', true).first();
- },
-
- getAutoCreate : function()
- {
- var text = [
- {
- tag : 'span',
- cls : 'roo-menu-text',
- html : this.html
- }
- ];
-
- if(this.icon){
- text.unshift({
- tag : 'i',
- cls : 'fa ' + this.icon
- })
- }
-
-
- var cfg = {
- tag : 'div',
- cls : 'btn-group',
- cn : [
- {
- tag : 'button',
- cls : 'dropdown-button btn btn-' + this.weight,
- cn : text
- },
- {
- tag : 'button',
- cls : 'dropdown-toggle btn btn-' + this.weight,
- cn : [
- {
- tag : 'span',
- cls : 'caret'
- }
- ]
- },
- {
- tag : 'ul',
- cls : 'dropdown-menu'
- }
- ]
-
- };
-
- if(this.pos == 'top'){
- cfg.cls += ' dropup';
- }
-
- if(this.isSubMenu){
- cfg = {
- tag : 'ul',
- cls : 'dropdown-menu'
- }
- }
-
- return cfg;
- },
-
- onRender : function(ct, position)
- {
- this.isSubMenu = ct.hasClass('dropdown-submenu');
-
- Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
- },
-
- initEvents : function()
- {
- if(this.isSubMenu){
- return;
- }
-
- this.hidden = true;
-
- this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
- this.triggerEl.on('click', this.onTriggerPress, this);
-
- this.buttonEl = this.el.select('button.dropdown-button', true).first();
- this.buttonEl.on('click', this.onClick, this);
-
- },
-
- list : function()
- {
- if(this.isSubMenu){
- return this.el;
- }
-
- return this.el.select('ul.dropdown-menu', true).first();
- },
-
- onClick : function(e)
- {
- this.fireEvent("click", this, e);
- },
-
- onTriggerPress : function(e)
- {
- if (this.isVisible()) {
- this.hide();
- } else {
- this.show();
- }
- },
-
- isVisible : function(){
- return !this.hidden;
- },
-
- show : function()
- {
- this.fireEvent("beforeshow", this);
-
- this.hidden = false;
- this.el.addClass('open');
-
- Roo.get(document).on("mouseup", this.onMouseUp, this);
-
- this.fireEvent("show", this);
-
-
- },
-
- hide : function()
- {
- this.fireEvent("beforehide", this);
-
- this.hidden = true;
- this.el.removeClass('open');
-
- Roo.get(document).un("mouseup", this.onMouseUp);
-
- this.fireEvent("hide", this);
- },
-
- onMouseUp : function()
- {
- this.hide();
- }
-
-});
-
-
- /*
- * - LGPL
- *
- * menu item
- *
- */
-Roo.bootstrap.menu = Roo.bootstrap.menu || {};
-
-/**
- * @class Roo.bootstrap.menu.Item
- * @extends Roo.bootstrap.Component
- * Bootstrap MenuItem class
- * @cfg {Boolean} submenu (true | false) default false
- * @cfg {String} html text of the item
- * @cfg {String} href the link
- * @cfg {Boolean} disable (true | false) default false
- * @cfg {Boolean} preventDefault (true | false) default true
- * @cfg {String} icon Font awesome icon
- * @cfg {String} pos Submenu align to (left | right) default right
- *
- *
- * @constructor
- * Create a new Item
- * @param {Object} config The config object
- */
-
-
-Roo.bootstrap.menu.Item = function(config){
- Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
- this.addEvents({
- /**
- * @event mouseover
- * Fires when the mouse is hovering over this menu
- * @param {Roo.bootstrap.menu.Item} this
- * @param {Roo.EventObject} e
- */
- mouseover : true,
- /**
- * @event mouseout
- * Fires when the mouse exits this menu
- * @param {Roo.bootstrap.menu.Item} this
- * @param {Roo.EventObject} e
- */
- mouseout : true,
- // raw events
- /**
- * @event click
- * The raw click event for the entire grid.
- * @param {Roo.EventObject} e
- */
- click : true
- });
-};
-
-Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
-
- submenu : false,
- href : '',
- html : '',
- preventDefault: true,
- disable : false,
- icon : false,
- pos : 'right',
-
- getAutoCreate : function()
- {
- var text = [
- {
- tag : 'span',
- cls : 'roo-menu-item-text',
- html : this.html
- }
- ];
-
- if(this.icon){
- text.unshift({
- tag : 'i',
- cls : 'fa ' + this.icon
- })
- }
-
- var cfg = {
- tag : 'li',
- cn : [
- {
- tag : 'a',
- href : this.href || '#',
- cn : text
- }
- ]
- };
-
- if(this.disable){
- cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
- }
-
- if(this.submenu){
- cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
-
- if(this.pos == 'left'){
- cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
- }
- }
-
- return cfg;
- },
-
- initEvents : function()
- {
- this.el.on('mouseover', this.onMouseOver, this);
- this.el.on('mouseout', this.onMouseOut, this);
-
- this.el.select('a', true).first().on('click', this.onClick, this);
-
- },
-
- onClick : function(e)
- {
- if(this.preventDefault){
- e.preventDefault();
- }
-
- this.fireEvent("click", this, e);
- },
-
- onMouseOver : function(e)
- {
- if(this.submenu && this.pos == 'left'){
- this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
- }
-
- this.fireEvent("mouseover", this, e);
- },
-
- onMouseOut : function(e)
- {
- this.fireEvent("mouseout", this, e);
- }
-});
-
-
-
- /*
- * - LGPL
- *
- * menu separator
- *
- */
-Roo.bootstrap.menu = Roo.bootstrap.menu || {};
-
-/**
- * @class Roo.bootstrap.menu.Separator
- * @extends Roo.bootstrap.Component
- * Bootstrap Separator class
- *
- * @constructor
- * Create a new Separator
- * @param {Object} config The config object
- */
-
-
-Roo.bootstrap.menu.Separator = function(config){
- Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
-
- getAutoCreate : function(){
- var cfg = {
- tag : 'li',
- cls: 'divider'
- };
-
- return cfg;
- }
-
-});
-
-
-
/*
* - LGPL
*
return;
}
- var bindEl = el;
+ var bindEl = el;
+ var pel = false;
+ if (!el.attr('tooltip')) {
+ pel = el.findParent("[tooltip]");
+ if (pel) {
+ bindEl = Roo.get(pel);
+ }
+ }
+
+
// you can not look for children, as if el is the body.. then everythign is the child..
- if (!el.attr('tooltip')) { //
+ if (!pel && !el.attr('tooltip')) { //
if (!el.select("[tooltip]").elements.length) {
return;
}
//Roo.log("child element over..");
}
- this.currentEl = bindEl;
+ this.currentEl = el;
this.currentTip.bind(bindEl);
this.currentRegion = Roo.lib.Region.getRegion(dom);
this.currentTip.enter();
getAutoCreate : function(){
var cfg = {
- cls : 'tooltip',
+ cls : 'tooltip',
role : 'tooltip',
cn : [
{
- cls : 'tooltip-arrow'
+ cls : 'tooltip-arrow arrow'
},
{
cls : 'tooltip-inner'
{
this.bindEl = el;
},
-
+
+ initEvents : function()
+ {
+ this.arrowEl = this.el.select('.arrow', true).first();
+ this.innerEl = this.el.select('.tooltip-inner', true).first();
+ },
enter : function () {
this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
- this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
+ this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
+ 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
var placement = typeof this.placement == 'function' ?
this.placement.call(this, this.el, on_el) :
}
align = this.alignment[placement];
+
+ this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
+
}
+ var elems = document.getElementsByTagName('div');
+ var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
+ for (var i = 0; i < elems.length; i++) {
+ var zindex = Number.parseInt(
+ document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
+ 10
+ );
+ if (zindex > highest) {
+ highest = zindex;
+ }
+ }
+
+
+
+ this.el.dom.style.zIndex = highest;
+
this.el.alignTo(this.bindEl, align[0],align[1]);
//var arrow = this.el.select('.arrow',true).first();
//arrow.set(align[2],
this.el.addClass(placement);
+ this.el.addClass("bs-tooltip-"+ placement);
- this.el.addClass('in fade');
+ this.el.addClass('in fade show');
this.hoverState = null;
// fade it?
}
+
+
+
+
},
hide : function()
{
return;
}
//this.el.setXY([0,0]);
- this.el.removeClass('in');
+ this.el.removeClass(['show', 'in']);
//this.el.hide();
}
}
-});/*
- * - LGPL
- *
- * Alert
- *
- */
-
-/**
+});/**
* @class Roo.bootstrap.Alert
* @extends Roo.bootstrap.Component
- * Bootstrap Alert class
+ * Bootstrap Alert class - shows an alert area box
+ * eg
+ * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
+ Enter a valid email address
+</div>
+ * @licence LGPL
* @cfg {String} title The title of alert
* @cfg {String} html The content of alert
- * @cfg {String} weight ( success | info | warning | danger )
- * @cfg {String} faicon font-awesomeicon
+ * @cfg {String} weight (success|info|warning|danger) Weight of the message
+ * @cfg {String} fa font-awesomeicon
+ * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
+ * @cfg {Boolean} close true to show a x closer
+ *
*
* @constructor
* Create a new alert
title: '',
html: '',
weight: false,
- faicon: false,
+ fa: false,
+ faicon: false, // BC
+ close : false,
+
getAutoCreate : function()
{
tag : 'div',
cls : 'alert',
cn : [
+ {
+ tag: 'button',
+ type : "button",
+ cls: "close",
+ html : '×',
+ style : this.close ? '' : 'display:none'
+ },
{
tag : 'i',
cls : 'roo-alert-icon'
if(this.faicon){
cfg.cn[0].cls += ' fa ' + this.faicon;
}
+ if(this.fa){
+ cfg.cn[0].cls += ' fa ' + this.fa;
+ }
if(this.weight){
cfg.cls += ' alert-' + this.weight;
initEvents: function()
{
this.el.setVisibilityMode(Roo.Element.DISPLAY);
+ this.titleEl = this.el.select('.roo-alert-title',true).first();
+ this.iconEl = this.el.select('.roo-alert-icon',true).first();
+ this.htmlEl = this.el.select('.roo-alert-text',true).first();
+ if (this.seconds > 0) {
+ this.hide.defer(this.seconds, this);
+ }
},
-
+ /**
+ * Set the Title Message HTML
+ * @param {String} html
+ */
setTitle : function(str)
{
- this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
+ this.titleEl.dom.innerHTML = str;
},
-
- setText : function(str)
+
+ /**
+ * Set the Body Message HTML
+ * @param {String} html
+ */
+ setHtml : function(str)
{
- this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
+ this.htmlEl.dom.innerHTML = str;
},
+ /**
+ * Set the Weight of the alert
+ * @param {String} (success|info|warning|danger) weight
+ */
setWeight : function(weight)
{
if(this.weight){
- this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
+ this.el.removeClass('alert-' + this.weight);
}
this.weight = weight;
- this.el.select('.alert',true).first().addClass('alert-' + this.weight);
+ this.el.addClass('alert-' + this.weight);
},
-
+ /**
+ * Set the Icon of the alert
+ * @param {String} see fontawsome names (name without the 'fa-' bit)
+ */
setIcon : function(icon)
{
if(this.faicon){
- this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
+ this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
}
this.faicon = icon;
- this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
+ this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
},
-
+ /**
+ * Hide the Alert
+ */
hide: function()
{
this.el.hide();
},
-
+ /**
+ * Show the Alert
+ */
show: function()
{
this.el.show();
});
/*
- * - LGPL
- *
- * nav progress bar
- *
- */
-
-/**
- * @class Roo.bootstrap.NavProgressBar
- * @extends Roo.bootstrap.Component
- * Bootstrap NavProgressBar class
- *
- * @constructor
- * Create a new nav progress bar
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.NavProgressBar = function(config){
- Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
-
- this.bullets = this.bullets || [];
-
-// Roo.bootstrap.NavProgressBar.register(this);
- this.addEvents({
- /**
- * @event changed
- * Fires when the active item changes
- * @param {Roo.bootstrap.NavProgressBar} this
- * @param {Roo.bootstrap.NavProgressItem} selected The item selected
- * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
- */
- 'changed': true
- });
-
-};
-
-Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
-
- bullets : [],
- barItems : [],
-
- getAutoCreate : function()
- {
- var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
-
- cfg = {
- tag : 'div',
- cls : 'roo-navigation-bar-group',
- cn : [
- {
- tag : 'div',
- cls : 'roo-navigation-top-bar'
- },
- {
- tag : 'div',
- cls : 'roo-navigation-bullets-bar',
- cn : [
- {
- tag : 'ul',
- cls : 'roo-navigation-bar'
- }
- ]
- },
-
- {
- tag : 'div',
- cls : 'roo-navigation-bottom-bar'
- }
- ]
-
- };
-
- return cfg;
-
- },
-
- initEvents: function()
- {
-
- },
-
- onRender : function(ct, position)
- {
- Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
-
- if(this.bullets.length){
- Roo.each(this.bullets, function(b){
- this.addItem(b);
- }, this);
- }
-
- this.format();
-
- },
-
- addItem : function(cfg)
- {
- var item = new Roo.bootstrap.NavProgressItem(cfg);
-
- item.parentId = this.id;
- item.render(this.el.select('.roo-navigation-bar', true).first(), null);
-
- if(cfg.html){
- var top = new Roo.bootstrap.Element({
- tag : 'div',
- cls : 'roo-navigation-bar-text'
- });
-
- var bottom = new Roo.bootstrap.Element({
- tag : 'div',
- cls : 'roo-navigation-bar-text'
- });
-
- top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
- bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
-
- var topText = new Roo.bootstrap.Element({
- tag : 'span',
- html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
- });
-
- var bottomText = new Roo.bootstrap.Element({
- tag : 'span',
- html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
- });
-
- topText.onRender(top.el, null);
- bottomText.onRender(bottom.el, null);
-
- item.topEl = top;
- item.bottomEl = bottom;
- }
-
- this.barItems.push(item);
-
- return item;
- },
-
- getActive : function()
- {
- var active = false;
-
- Roo.each(this.barItems, function(v){
-
- if (!v.isActive()) {
- return;
- }
-
- active = v;
- return false;
-
- });
-
- return active;
- },
-
- setActiveItem : function(item)
- {
- var prev = false;
-
- Roo.each(this.barItems, function(v){
- if (v.rid == item.rid) {
- return ;
- }
-
- if (v.isActive()) {
- v.setActive(false);
- prev = v;
- }
- });
-
- item.setActive(true);
-
- this.fireEvent('changed', this, item, prev);
- },
-
- getBarItem: function(rid)
- {
- var ret = false;
-
- Roo.each(this.barItems, function(e) {
- if (e.rid != rid) {
- return;
- }
-
- ret = e;
- return false;
- });
-
- return ret;
- },
-
- indexOfItem : function(item)
- {
- var index = false;
-
- Roo.each(this.barItems, function(v, i){
-
- if (v.rid != item.rid) {
- return;
- }
-
- index = i;
- return false
- });
-
- return index;
- },
-
- setActiveNext : function()
- {
- var i = this.indexOfItem(this.getActive());
-
- if (i > this.barItems.length) {
- return;
- }
-
- this.setActiveItem(this.barItems[i+1]);
- },
-
- setActivePrev : function()
- {
- var i = this.indexOfItem(this.getActive());
-
- if (i < 1) {
- return;
- }
-
- this.setActiveItem(this.barItems[i-1]);
- },
-
- format : function()
- {
- if(!this.barItems.length){
- return;
- }
-
- var width = 100 / this.barItems.length;
-
- Roo.each(this.barItems, function(i){
- i.el.setStyle('width', width + '%');
- i.topEl.el.setStyle('width', width + '%');
- i.bottomEl.el.setStyle('width', width + '%');
- }, this);
-
- }
-
-});
-/*
- * - LGPL
- *
- * Nav Progress Item
- *
- */
-
-/**
- * @class Roo.bootstrap.NavProgressItem
- * @extends Roo.bootstrap.Component
- * Bootstrap NavProgressItem class
- * @cfg {String} rid the reference id
- * @cfg {Boolean} active (true|false) Is item active default false
- * @cfg {Boolean} disabled (true|false) Is item active default false
- * @cfg {String} html
- * @cfg {String} position (top|bottom) text position default bottom
- * @cfg {String} icon show icon instead of number
- *
- * @constructor
- * Create a new NavProgressItem
- * @param {Object} config The config object
- */
-Roo.bootstrap.NavProgressItem = function(config){
- Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
- this.addEvents({
- // raw events
- /**
- * @event click
- * The raw click event for the entire grid.
- * @param {Roo.bootstrap.NavProgressItem} this
- * @param {Roo.EventObject} e
- */
- "click" : true
- });
-
-};
-
-Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
-
- rid : '',
- active : false,
- disabled : false,
- html : '',
- position : 'bottom',
- icon : false,
-
- getAutoCreate : function()
- {
- var iconCls = 'roo-navigation-bar-item-icon';
-
- iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
-
- var cfg = {
- tag: 'li',
- cls: 'roo-navigation-bar-item',
- cn : [
- {
- tag : 'i',
- cls : iconCls
- }
- ]
- };
-
- if(this.active){
- cfg.cls += ' active';
- }
- if(this.disabled){
- cfg.cls += ' disabled';
- }
-
- return cfg;
- },
-
- disable : function()
- {
- this.setDisabled(true);
- },
-
- enable : function()
- {
- this.setDisabled(false);
- },
-
- initEvents: function()
- {
- this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
-
- this.iconEl.on('click', this.onClick, this);
- },
-
- onClick : function(e)
- {
- e.preventDefault();
-
- if(this.disabled){
- return;
- }
-
- if(this.fireEvent('click', this, e) === false){
- return;
- };
-
- this.parent().setActiveItem(this);
- },
-
- isActive: function ()
- {
- return this.active;
- },
-
- setActive : function(state)
- {
- if(this.active == state){
- return;
- }
-
- this.active = state;
-
- if (state) {
- this.el.addClass('active');
- return;
- }
-
- this.el.removeClass('active');
-
- return;
- },
-
- setDisabled : function(state)
- {
- if(this.disabled == state){
- return;
- }
-
- this.disabled = state;
-
- if (state) {
- this.el.addClass('disabled');
- return;
- }
-
- this.el.removeClass('disabled');
- },
-
- tooltipEl : function()
- {
- return this.el.select('.roo-navigation-bar-item-icon', true).first();;
- }
-});
-
-
- /*
* - LGPL
*
* FieldLabel
*/
/**
- * @class Roo.bootstrap.FieldLabel
+ * @class Roo.bootstrap.form.FieldLabel
* @extends Roo.bootstrap.Component
* Bootstrap FieldLabel class
* @cfg {String} html contents of the element
* @param {Object} config The config object
*/
-Roo.bootstrap.FieldLabel = function(config){
+Roo.bootstrap.form.FieldLabel = function(config){
Roo.bootstrap.Element.superclass.constructor.call(this, config);
this.addEvents({
});
};
-Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
tag: 'label',
cls: '',
this.indicator.addClass('invisible');
}
- Roo.bootstrap.FieldLabel.register(this);
+ Roo.bootstrap.form.FieldLabel.register(this);
},
indicatorEl : function()
});
-Roo.apply(Roo.bootstrap.FieldLabel, {
+Roo.apply(Roo.bootstrap.form.FieldLabel, {
groups: {},
/**
* register a FieldLabel Group
- * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
+ * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
*/
register : function(label)
{
/**
* fetch a FieldLabel Group based on the target
* @param {string} target
- * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
+ * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
*/
get: function(target) {
if (typeof(this.groups[target]) == 'undefined') {
/**
- * @class Roo.bootstrap.DateSplitField
+ * @class Roo.bootstrap.form.DateSplitField
* @extends Roo.bootstrap.Component
* Bootstrap DateSplitField class
* @cfg {string} fieldLabel - the label associated
* @param {Object} config The config object
*/
-Roo.bootstrap.DateSplitField = function(config){
- Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
+Roo.bootstrap.form.DateSplitField = function(config){
+ Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
this.addEvents({
// raw events
/**
* @event years
* getting the data of years
- * @param {Roo.bootstrap.DateSplitField} this
+ * @param {Roo.bootstrap.form.DateSplitField} this
* @param {Object} years
*/
"years" : true,
/**
* @event days
* getting the data of days
- * @param {Roo.bootstrap.DateSplitField} this
+ * @param {Roo.bootstrap.form.DateSplitField} this
* @param {Object} days
*/
"days" : true,
});
};
-Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
fieldLabel : '',
labelAlign : 'top',
{
var _this = this;
- Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
+ Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
- this.dayField = new Roo.bootstrap.ComboBox({
+ this.dayField = new Roo.bootstrap.form.ComboBox({
allowBlank : this.dayAllowBlank,
alwaysQuery : true,
displayField : 'value',
this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
- this.monthField = new Roo.bootstrap.MonthField({
+ this.monthField = new Roo.bootstrap.form.MonthField({
after : '<i class=\"fa fa-calendar\"></i>',
allowBlank : this.monthAllowBlank,
placeholder : this.monthPlaceholder,
this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
- this.yearField = new Roo.bootstrap.ComboBox({
+ this.yearField = new Roo.bootstrap.form.ComboBox({
allowBlank : this.yearAllowBlank,
alwaysQuery : true,
displayField : 'value',
});
- /**
+
+
+/**
+ * @class Roo.bootstrap.LayoutMasonry
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
+ * Bootstrap Layout Masonry class
*
* This is based on
* http://masonry.desandro.com
* The idea is to render all the bricks based on vertical width...
*
* The original code extends 'outlayer' - we might need to use that....
- *
- */
-
-/**
- * @class Roo.bootstrap.LayoutMasonry
- * @extends Roo.bootstrap.Component
- * Bootstrap Layout Masonry class
- *
* @constructor
* Create a new Element
* @param {Object} config The config object
*/
/**
- * @class Roo.bootstrap.NumberField
- * @extends Roo.bootstrap.Input
+ * @class Roo.bootstrap.form.NumberField
+ * @extends Roo.bootstrap.form.Input
* Bootstrap NumberField class
*
*
* @param {Object} config The config object
*/
-Roo.bootstrap.NumberField = function(config){
- Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
+Roo.bootstrap.form.NumberField = function(config){
+ Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
+Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
/**
* @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
this.name = '';
- var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
+ var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
this.name = hiddenInput.name;
// private
initEvents : function()
{
- Roo.bootstrap.NumberField.superclass.initEvents.call(this);
+ Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
var allowed = "0123456789";
validateValue : function(value)
{
- if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
+ if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
return false;
}
*/
/**
- * @class Roo.bootstrap.RadioSet
- * @extends Roo.bootstrap.Input
+ * @class Roo.bootstrap.form.RadioSet
+ * @extends Roo.bootstrap.form.Input
+ * @children Roo.bootstrap.form.Radio
* Bootstrap RadioSet class
* @cfg {String} indicatorpos (left|right) default left
* @cfg {Boolean} inline (true|false) inline the element (default true)
* @param {Object} config The config object
*/
-Roo.bootstrap.RadioSet = function(config){
+Roo.bootstrap.form.RadioSet = function(config){
- Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
+ Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
this.radioes = [];
- Roo.bootstrap.RadioSet.register(this);
+ Roo.bootstrap.form.RadioSet.register(this);
this.addEvents({
/**
* @event check
* Fires when the element is checked or unchecked.
- * @param {Roo.bootstrap.RadioSet} this This radio
- * @param {Roo.bootstrap.Radio} item The checked item
+ * @param {Roo.bootstrap.form.RadioSet} this This radio
+ * @param {Roo.bootstrap.form.Radio} item The checked item
*/
check : true,
/**
* @event click
* Fires when the element is click.
- * @param {Roo.bootstrap.RadioSet} this This radio set
- * @param {Roo.bootstrap.Radio} item The checked item
+ * @param {Roo.bootstrap.form.RadioSet} this This radio set
+ * @param {Roo.bootstrap.form.Radio} item The checked item
* @param {Roo.EventObject} e The event object
*/
click : true
};
-Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
+Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
radioes : false,
});
-Roo.apply(Roo.bootstrap.RadioSet, {
+Roo.apply(Roo.bootstrap.form.RadioSet, {
groups: {},
* @type Number
*/
Roo.bootstrap.SplitBar.BOTTOM = 4;
-Roo.namespace("Roo.bootstrap.layout");/*
+/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
/**
* @class Roo.bootstrap.layout.Manager
* @extends Roo.bootstrap.Component
+ * @abstract
* Base class for layout managers.
*/
Roo.bootstrap.layout.Manager = function(config)
{
+ this.monitorWindowResize = true; // do this before we apply configuration.
+
Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
/** false to disable window resize monitoring @type Boolean */
- this.monitorWindowResize = true;
+
this.regions = {};
this.addEvents({
/**
/**
* @class Roo.bootstrap.layout.Border
* @extends Roo.bootstrap.layout.Manager
+ * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
+ * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
* This class represents a common layout manager used in desktop applications. For screenshots and more details,
* please see: examples/bootstrap/nested.html<br><br>
};
-Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
+Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
+ /**
+ * @cfg {Roo.bootstrap.layout.Region} center region to go in center
+ */
+ /**
+ * @cfg {Roo.bootstrap.layout.Region} west region to go in west
+ */
+ /**
+ * @cfg {Roo.bootstrap.layout.Region} east region to go in east
+ */
+ /**
+ * @cfg {Roo.bootstrap.layout.Region} south region to go in south
+ */
+ /**
+ * @cfg {Roo.bootstrap.layout.Region} north region to go in north
+ */
+
+
+
+
parent : false, // this might point to a 'nest' or a ???
/**
this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
this.split.el.addClass("roo-layout-split-v");
}
- var size = config.initialSize || config.height;
- if(typeof size != "undefined"){
- this.el.setHeight(size);
- }
+ //var size = config.initialSize || config.height;
+ //if(this.el && typeof size != "undefined"){
+ // this.el.setHeight(size);
+ //}
};
Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
{
orientation: Roo.bootstrap.SplitBar.VERTICAL,
+
+
+ onRender : function(ctr, pos)
+ {
+ Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
+ var size = this.config.initialSize || this.config.height;
+ if(this.el && typeof size != "undefined"){
+ this.el.setHeight(size);
+ }
-
+ },
getBox : function(){
if(this.collapsed){
this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
this.split.el.addClass("roo-layout-split-v");
}
- var size = config.initialSize || config.height;
- if(typeof size != "undefined"){
- this.el.setHeight(size);
- }
+
};
Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
orientation: Roo.bootstrap.SplitBar.VERTICAL,
+
+ onRender : function(ctr, pos)
+ {
+ Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
+ var size = this.config.initialSize || this.config.height;
+ if(this.el && typeof size != "undefined"){
+ this.el.setHeight(size);
+ }
+
+ },
+
getBox : function(){
if(this.collapsed){
return this.collapsedEl.getBox();
this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
this.split.el.addClass("roo-layout-split-h");
}
- var size = config.initialSize || config.width;
- if(typeof size != "undefined"){
- this.el.setWidth(size);
- }
+
};
Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
+
+ onRender : function(ctr, pos)
+ {
+ Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
+ var size = this.config.initialSize || this.config.width;
+ if(this.el && typeof size != "undefined"){
+ this.el.setWidth(size);
+ }
+
+ },
+
getBox : function(){
if(this.collapsed){
return this.collapsedEl.getBox();
return this.collapsedEl.getBox();
}
var box = this.el.getBox();
+ if (box.width == 0) {
+ box.width = this.config.width; // kludge?
+ }
if(this.split){
box.width += this.split.el.getWidth();
}
}
Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
}
-});Roo.namespace("Roo.bootstrap.panel");/*
+});/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
* <script type="text/javascript">
*/
/**
- * @class Roo.ContentPanel
+ * @class Roo.bootstrap.paenl.Content
* @extends Roo.util.Observable
- * A basic ContentPanel element.
+ * @children Roo.bootstrap.Component
+ * @parent builder Roo.bootstrap.layout.Border
+ * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
* @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
* @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
* @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
* @cfg {String} title The title for this panel
* @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
* @cfg {String} url Calls {@link #setUrl} with this value
- * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
+ * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
* @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
* @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
* @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
+ * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
* @cfg {Boolean} badges render the badges
-
+ * @cfg {String} cls extra classes to use
+ * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
+
* @constructor
* Create a new ContentPanel.
- * @param {String/HTMLElement/Roo.Element} el The container element for this panel
* @param {String/Object} config A string to set only the title or a config object
- * @param {String} content (optional) Set the HTML content for this panel
- * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
+
*/
Roo.bootstrap.panel.Content = function( config){
this.el = Roo.DomHelper.append(document.body,
config.autoCreate, true);
}else{
- var elcfg = { tag: "div",
- cls: "roo-layout-inactive-content",
- id: config.id||el
- };
+ var elcfg = {
+ tag: "div",
+ cls: (config.cls || '') +
+ (config.background ? ' bg-' + config.background : '') +
+ " roo-layout-inactive-content",
+ id: config.id||el
+ };
+ if (config.iframe) {
+ elcfg.cn = [
+ {
+ tag : 'iframe',
+ style : 'border: 0px',
+ src : 'about:blank'
+ }
+ ];
+ }
+
if (config.html) {
elcfg.html = config.html;
}
this.el = Roo.DomHelper.append(document.body, elcfg , true);
+ if (config.iframe) {
+ this.iframeEl = this.el.select('iframe',true).first();
+ }
+
}
}
this.closable = false;
* Fires when this tab is created
* @param {Roo.ContentPanel} this
*/
- "render" : true
+ "render" : true,
+
+ /**
+ * @event scroll
+ * Fires when this content is scrolled
+ * @param {Roo.ContentPanel} this
+ * @param {Event} scrollEvent
+ */
+ "scroll" : true
- if(this.autoScroll){
+ if(this.autoScroll && !this.iframe){
this.resizeEl.setStyle("overflow", "auto");
+ this.resizeEl.on('scroll', this.onScroll, this);
} else {
// fix randome scrolling
//this.el.on('scroll', function() {
Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
+ cls : '',
+ background : '',
+
tabTip : '',
+ iframe : false,
+ iframeEl : false,
+
+ /* Resize Element - use this to work out scroll etc. */
+ resizeEl : false,
+
setRegion : function(region){
this.region = region;
this.setActiveClass(region && !this.background);
return true;
},
/**
- * Updates this panel's element
+ * Updates this panel's element (not for iframe)
* @param {String} content The new content
* @param {Boolean} loadScripts (optional) true to look for and process scripts
*/
setContent : function(content, loadScripts){
+ if (this.iframe) {
+ return;
+ }
+
this.el.update(content, loadScripts);
},
- ignoreResize : function(w, h){
+ ignoreResize : function(w, h)
+ {
+ //return false; // always resize?
if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
return true;
}else{
* @return {Roo.UpdateManager} The UpdateManager
*/
getUpdateManager : function(){
+ if (this.iframe) {
+ return false;
+ }
return this.el.getUpdateManager();
},
/**
* Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
+ * Does not work with IFRAME contents
* @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
<pre><code>
panel.load({
scripts: false
});
</code></pre>
+
* The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
* are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
* @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&param2=2" or an object {param1: 1, param2: 2}
* @return {Roo.ContentPanel} this
*/
load : function(){
+
+ if (this.iframe) {
+ return this;
+ }
+
var um = this.el.getUpdateManager();
um.update.apply(um, arguments);
return this;
* @param {String/Function} url The URL to load the content from or a function to call to get the URL
* @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
* @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
- * @return {Roo.UpdateManager} The UpdateManager
+ * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
*/
setUrl : function(url, params, loadOnce){
+ if (this.iframe) {
+ this.iframeEl.dom.src = url;
+ return false;
+ }
+
if(this.refreshDelegate){
this.removeListener("activate", this.refreshDelegate);
}
this.el.setSize(width, height);
}
var size = this.adjustForComponents(width, height);
+ if (this.iframe) {
+ this.iframeEl.setSize(width,height);
+ }
+
this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
this.fireEvent('resize', this, size.width, size.height);
+
+
}
},
getChildContainer: function () {
return this.getEl();
+ },
+
+
+ onScroll : function(e)
+ {
+ this.fireEvent('scroll', this, e);
}
* @constructor
* Create a new GridPanel.
* @cfg {Roo.bootstrap.Table} grid The grid for this panel
+ * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
* @param {Object} config A the config object
*/
};
-Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
+Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
+{
+
getId : function(){
return this.grid.id;
},
return this.grid;
},
- setSize : function(width, height){
- if(!this.ignoreResize(width, height)){
+ setSize : function(width, height)
+ {
+
+ //if(!this.ignoreResize(width, height)){
var grid = this.grid;
var size = this.adjustForComponents(width, height);
+ // tfoot is not a footer?
+
+
var gridel = grid.getGridEl();
gridel.setSize(size.width, size.height);
- /*
- var thd = grid.getGridEl().select('thead',true).first();
+
var tbd = grid.getGridEl().select('tbody', true).first();
- if (tbd) {
- tbd.setSize(width, height - thd.getHeight());
+ var thd = grid.getGridEl().select('thead',true).first();
+ var tbf= grid.getGridEl().select('tfoot', true).first();
+
+ if (tbf) {
+ size.height -= tbf.getHeight();
}
- */
+ if (thd) {
+ size.height -= thd.getHeight();
+ }
+
+ tbd.setSize(size.width, size.height );
+ // this is for the account management tab -seems to work there.
+ var thd = grid.getGridEl().select('thead',true).first();
+ //if (tbd) {
+ // tbd.setSize(size.width, size.height - thd.getHeight());
+ //}
+
grid.autoSize();
- }
+ //}
+
},
* Create a new Panel, that can contain a layout.Border.
*
*
- * @param {Roo.BorderLayout} layout The layout for this panel
* @param {String/Object} config A string to set only the title or a config object
*/
Roo.bootstrap.panel.Nest = function(config)
};
Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
+ /**
+ * @cfg {Roo.BorderLayout} layout The layout for this panel
+ */
+ layout : false,
setSize : function(width, height){
if(!this.ignoreResize(width, height)){
* Availability: https://github.com/jackocnr/intl-tel-input.git
**/
-Roo.bootstrap.PhoneInputData = function() {
+Roo.bootstrap.form.PhoneInputData = function() {
var d = [
[
"Afghanistan (افغانستان)",
**/
/**
- * @class Roo.bootstrap.PhoneInput
- * @extends Roo.bootstrap.TriggerField
+ * @class Roo.bootstrap.form.PhoneInput
+ * @extends Roo.bootstrap.form.TriggerField
* An input with International dial-code selection
* @cfg {String} defaultDialCode default '+852'
* @param {Object} config Configuration options
*/
-Roo.bootstrap.PhoneInput = function(config) {
- Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
+Roo.bootstrap.form.PhoneInput = function(config) {
+ Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
-
+Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
+ /**
+ * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
+ */
listWidth: undefined,
selectedClass: 'active',
getAutoCreate : function()
{
- var data = Roo.bootstrap.PhoneInputData();
+ var data = Roo.bootstrap.form.PhoneInputData();
var align = this.labelAlign || this.parentLabelAlign();
var id = Roo.id();
initEvents : function()
{
this.createList();
- Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
+ Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
this.indicator = this.indicatorEl();
this.flag = this.flagEl();
});
/**
- * @class Roo.bootstrap.MoneyField
- * @extends Roo.bootstrap.ComboBox
+ * @class Roo.bootstrap.form.MoneyField
+ * @extends Roo.bootstrap.form.ComboBox
* Bootstrap MoneyField class
*
* @constructor
* @param {Object} config Configuration options
*/
-Roo.bootstrap.MoneyField = function(config) {
+Roo.bootstrap.form.MoneyField = function(config) {
- Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
+ Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
+Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
/**
* @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
inputmd : 9,
inputsm : 9,
inputxs : 6,
-
+ /**
+ * @cfg {Roo.data.Store} store Store to lookup currency??
+ */
store : false,
getAutoCreate : function()
validateValue : function(value)
{
- if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
+ if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
return false;
}
return this.el.select('input.hidden-number-input',true).first();
}
-});
\ No newline at end of file
+});/**
+ * @class Roo.bootstrap.BezierSignature
+ * @extends Roo.bootstrap.Component
+ * Bootstrap BezierSignature class
+ * This script refer to:
+ * Title: Signature Pad
+ * Author: szimek
+ * Availability: https://github.com/szimek/signature_pad
+ *
+ * @constructor
+ * Create a new BezierSignature
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.BezierSignature = function(config){
+ Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
+ this.addEvents({
+ "resize" : true
+ });
+};
+
+Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
+{
+
+ curve_data: [],
+
+ is_empty: true,
+
+ mouse_btn_down: true,
+
+ /**
+ * @cfg {int} canvas height
+ */
+ canvas_height: '200px',
+
+ /**
+ * @cfg {float|function} Radius of a single dot.
+ */
+ dot_size: false,
+
+ /**
+ * @cfg {float} Minimum width of a line. Defaults to 0.5.
+ */
+ min_width: 0.5,
+
+ /**
+ * @cfg {float} Maximum width of a line. Defaults to 2.5.
+ */
+ max_width: 2.5,
+
+ /**
+ * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
+ */
+ throttle: 16,
+
+ /**
+ * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
+ */
+ min_distance: 5,
+
+ /**
+ * @cfg {string} Color used to clear the background. Can be any color format accepted by context.fillStyle. Defaults to "rgba(0,0,0,0)" (transparent black). Use a non-transparent color e.g. "rgb(255,255,255)" (opaque white) if you'd like to save signatures as JPEG images.
+ */
+ bg_color: 'rgba(0, 0, 0, 0)',
+
+ /**
+ * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
+ */
+ dot_color: 'black',
+
+ /**
+ * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
+ */
+ velocity_filter_weight: 0.7,
+
+ /**
+ * @cfg {function} Callback when stroke begin.
+ */
+ onBegin: false,
+
+ /**
+ * @cfg {function} Callback when stroke end.
+ */
+ onEnd: false,
+
+ getAutoCreate : function()
+ {
+ var cls = 'roo-signature column';
+
+ if(this.cls){
+ cls += ' ' + this.cls;
+ }
+
+ var col_sizes = [
+ 'lg',
+ 'md',
+ 'sm',
+ 'xs'
+ ];
+
+ for(var i = 0; i < col_sizes.length; i++) {
+ if(this[col_sizes[i]]) {
+ cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
+ }
+ }
+
+ var cfg = {
+ tag: 'div',
+ cls: cls,
+ cn: [
+ {
+ tag: 'div',
+ cls: 'roo-signature-body',
+ cn: [
+ {
+ tag: 'canvas',
+ cls: 'roo-signature-body-canvas',
+ height: this.canvas_height,
+ width: this.canvas_width
+ }
+ ]
+ },
+ {
+ tag: 'input',
+ type: 'file',
+ style: 'display: none'
+ }
+ ]
+ };
+
+ return cfg;
+ },
+
+ initEvents: function()
+ {
+ Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
+
+ var canvas = this.canvasEl();
+
+ // mouse && touch event swapping...
+ canvas.dom.style.touchAction = 'none';
+ canvas.dom.style.msTouchAction = 'none';
+
+ this.mouse_btn_down = false;
+ canvas.on('mousedown', this._handleMouseDown, this);
+ canvas.on('mousemove', this._handleMouseMove, this);
+ Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
+
+ if (window.PointerEvent) {
+ canvas.on('pointerdown', this._handleMouseDown, this);
+ canvas.on('pointermove', this._handleMouseMove, this);
+ Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
+ }
+
+ if ('ontouchstart' in window) {
+ canvas.on('touchstart', this._handleTouchStart, this);
+ canvas.on('touchmove', this._handleTouchMove, this);
+ canvas.on('touchend', this._handleTouchEnd, this);
+ }
+
+ Roo.EventManager.onWindowResize(this.resize, this, true);
+
+ // file input event
+ this.fileEl().on('change', this.uploadImage, this);
+
+ this.clear();
+
+ this.resize();
+ },
+
+ resize: function(){
+
+ var canvas = this.canvasEl().dom;
+ var ctx = this.canvasElCtx();
+ var img_data = false;
+
+ if(canvas.width > 0) {
+ var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
+ }
+ // setting canvas width will clean img data
+ canvas.width = 0;
+
+ var style = window.getComputedStyle ?
+ getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
+
+ var padding_left = parseInt(style.paddingLeft) || 0;
+ var padding_right = parseInt(style.paddingRight) || 0;
+
+ canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
+
+ if(img_data) {
+ ctx.putImageData(img_data, 0, 0);
+ }
+ },
+
+ _handleMouseDown: function(e)
+ {
+ if (e.browserEvent.which === 1) {
+ this.mouse_btn_down = true;
+ this.strokeBegin(e);
+ }
+ },
+
+ _handleMouseMove: function (e)
+ {
+ if (this.mouse_btn_down) {
+ this.strokeMoveUpdate(e);
+ }
+ },
+
+ _handleMouseUp: function (e)
+ {
+ if (e.browserEvent.which === 1 && this.mouse_btn_down) {
+ this.mouse_btn_down = false;
+ this.strokeEnd(e);
+ }
+ },
+
+ _handleTouchStart: function (e) {
+
+ e.preventDefault();
+ if (e.browserEvent.targetTouches.length === 1) {
+ // var touch = e.browserEvent.changedTouches[0];
+ // this.strokeBegin(touch);
+
+ this.strokeBegin(e); // assume e catching the correct xy...
+ }
+ },
+
+ _handleTouchMove: function (e) {
+ e.preventDefault();
+ // var touch = event.targetTouches[0];
+ // _this._strokeMoveUpdate(touch);
+ this.strokeMoveUpdate(e);
+ },
+
+ _handleTouchEnd: function (e) {
+ var wasCanvasTouched = e.target === this.canvasEl().dom;
+ if (wasCanvasTouched) {
+ e.preventDefault();
+ // var touch = event.changedTouches[0];
+ // _this._strokeEnd(touch);
+ this.strokeEnd(e);
+ }
+ },
+
+ reset: function () {
+ this._lastPoints = [];
+ this._lastVelocity = 0;
+ this._lastWidth = (this.min_width + this.max_width) / 2;
+ this.canvasElCtx().fillStyle = this.dot_color;
+ },
+
+ strokeMoveUpdate: function(e)
+ {
+ this.strokeUpdate(e);
+
+ if (this.throttle) {
+ this.throttleStroke(this.strokeUpdate, this.throttle);
+ }
+ else {
+ this.strokeUpdate(e);
+ }
+ },
+
+ strokeBegin: function(e)
+ {
+ var newPointGroup = {
+ color: this.dot_color,
+ points: []
+ };
+
+ if (typeof this.onBegin === 'function') {
+ this.onBegin(e);
+ }
+
+ this.curve_data.push(newPointGroup);
+ this.reset();
+ this.strokeUpdate(e);
+ },
+
+ strokeUpdate: function(e)
+ {
+ var rect = this.canvasEl().dom.getBoundingClientRect();
+ var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
+ var lastPointGroup = this.curve_data[this.curve_data.length - 1];
+ var lastPoints = lastPointGroup.points;
+ var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
+ var isLastPointTooClose = lastPoint
+ ? point.distanceTo(lastPoint) <= this.min_distance
+ : false;
+ var color = lastPointGroup.color;
+ if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
+ var curve = this.addPoint(point);
+ if (!lastPoint) {
+ this.drawDot({color: color, point: point});
+ }
+ else if (curve) {
+ this.drawCurve({color: color, curve: curve});
+ }
+ lastPoints.push({
+ time: point.time,
+ x: point.x,
+ y: point.y
+ });
+ }
+ },
+
+ strokeEnd: function(e)
+ {
+ this.strokeUpdate(e);
+ if (typeof this.onEnd === 'function') {
+ this.onEnd(e);
+ }
+ },
+
+ addPoint: function (point) {
+ var _lastPoints = this._lastPoints;
+ _lastPoints.push(point);
+ if (_lastPoints.length > 2) {
+ if (_lastPoints.length === 3) {
+ _lastPoints.unshift(_lastPoints[0]);
+ }
+ var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
+ var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
+ _lastPoints.shift();
+ return curve;
+ }
+ return null;
+ },
+
+ calculateCurveWidths: function (startPoint, endPoint) {
+ var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
+ (1 - this.velocity_filter_weight) * this._lastVelocity;
+
+ var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
+ var widths = {
+ end: newWidth,
+ start: this._lastWidth
+ };
+
+ this._lastVelocity = velocity;
+ this._lastWidth = newWidth;
+ return widths;
+ },
+
+ drawDot: function (_a) {
+ var color = _a.color, point = _a.point;
+ var ctx = this.canvasElCtx();
+ var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
+ ctx.beginPath();
+ this.drawCurveSegment(point.x, point.y, width);
+ ctx.closePath();
+ ctx.fillStyle = color;
+ ctx.fill();
+ },
+
+ drawCurve: function (_a) {
+ var color = _a.color, curve = _a.curve;
+ var ctx = this.canvasElCtx();
+ var widthDelta = curve.endWidth - curve.startWidth;
+ var drawSteps = Math.floor(curve.length()) * 2;
+ ctx.beginPath();
+ ctx.fillStyle = color;
+ for (var i = 0; i < drawSteps; i += 1) {
+ var t = i / drawSteps;
+ var tt = t * t;
+ var ttt = tt * t;
+ var u = 1 - t;
+ var uu = u * u;
+ var uuu = uu * u;
+ var x = uuu * curve.startPoint.x;
+ x += 3 * uu * t * curve.control1.x;
+ x += 3 * u * tt * curve.control2.x;
+ x += ttt * curve.endPoint.x;
+ var y = uuu * curve.startPoint.y;
+ y += 3 * uu * t * curve.control1.y;
+ y += 3 * u * tt * curve.control2.y;
+ y += ttt * curve.endPoint.y;
+ var width = curve.startWidth + ttt * widthDelta;
+ this.drawCurveSegment(x, y, width);
+ }
+ ctx.closePath();
+ ctx.fill();
+ },
+
+ drawCurveSegment: function (x, y, width) {
+ var ctx = this.canvasElCtx();
+ ctx.moveTo(x, y);
+ ctx.arc(x, y, width, 0, 2 * Math.PI, false);
+ this.is_empty = false;
+ },
+
+ clear: function()
+ {
+ var ctx = this.canvasElCtx();
+ var canvas = this.canvasEl().dom;
+ ctx.fillStyle = this.bg_color;
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ this.curve_data = [];
+ this.reset();
+ this.is_empty = true;
+ },
+
+ fileEl: function()
+ {
+ return this.el.select('input',true).first();
+ },
+
+ canvasEl: function()
+ {
+ return this.el.select('canvas',true).first();
+ },
+
+ canvasElCtx: function()
+ {
+ return this.el.select('canvas',true).first().dom.getContext('2d');
+ },
+
+ getImage: function(type)
+ {
+ if(this.is_empty) {
+ return false;
+ }
+
+ // encryption ?
+ return this.canvasEl().dom.toDataURL('image/'+type, 1);
+ },
+
+ drawFromImage: function(img_src)
+ {
+ var img = new Image();
+
+ img.onload = function(){
+ this.canvasElCtx().drawImage(img, 0, 0);
+ }.bind(this);
+
+ img.src = img_src;
+
+ this.is_empty = false;
+ },
+
+ selectImage: function()
+ {
+ this.fileEl().dom.click();
+ },
+
+ uploadImage: function(e)
+ {
+ var reader = new FileReader();
+
+ reader.onload = function(e){
+ var img = new Image();
+ img.onload = function(){
+ this.reset();
+ this.canvasElCtx().drawImage(img, 0, 0);
+ }.bind(this);
+ img.src = e.target.result;
+ }.bind(this);
+
+ reader.readAsDataURL(e.target.files[0]);
+ },
+
+ // Bezier Point Constructor
+ Point: (function () {
+ function Point(x, y, time) {
+ this.x = x;
+ this.y = y;
+ this.time = time || Date.now();
+ }
+ Point.prototype.distanceTo = function (start) {
+ return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
+ };
+ Point.prototype.equals = function (other) {
+ return this.x === other.x && this.y === other.y && this.time === other.time;
+ };
+ Point.prototype.velocityFrom = function (start) {
+ return this.time !== start.time
+ ? this.distanceTo(start) / (this.time - start.time)
+ : 0;
+ };
+ return Point;
+ }()),
+
+
+ // Bezier Constructor
+ Bezier: (function () {
+ function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
+ this.startPoint = startPoint;
+ this.control2 = control2;
+ this.control1 = control1;
+ this.endPoint = endPoint;
+ this.startWidth = startWidth;
+ this.endWidth = endWidth;
+ }
+ Bezier.fromPoints = function (points, widths, scope) {
+ var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
+ var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
+ return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
+ };
+ Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
+ var dx1 = s1.x - s2.x;
+ var dy1 = s1.y - s2.y;
+ var dx2 = s2.x - s3.x;
+ var dy2 = s2.y - s3.y;
+ var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
+ var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
+ var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
+ var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
+ var dxm = m1.x - m2.x;
+ var dym = m1.y - m2.y;
+ var k = l2 / (l1 + l2);
+ var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
+ var tx = s2.x - cm.x;
+ var ty = s2.y - cm.y;
+ return {
+ c1: new scope.Point(m1.x + tx, m1.y + ty),
+ c2: new scope.Point(m2.x + tx, m2.y + ty)
+ };
+ };
+ Bezier.prototype.length = function () {
+ var steps = 10;
+ var length = 0;
+ var px;
+ var py;
+ for (var i = 0; i <= steps; i += 1) {
+ var t = i / steps;
+ var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
+ var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
+ if (i > 0) {
+ var xdiff = cx - px;
+ var ydiff = cy - py;
+ length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
+ }
+ px = cx;
+ py = cy;
+ }
+ return length;
+ };
+ Bezier.prototype.point = function (t, start, c1, c2, end) {
+ return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
+ + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
+ + (3.0 * c2 * (1.0 - t) * t * t)
+ + (end * t * t * t);
+ };
+ return Bezier;
+ }()),
+
+ throttleStroke: function(fn, wait) {
+ if (wait === void 0) { wait = 250; }
+ var previous = 0;
+ var timeout = null;
+ var result;
+ var storedContext;
+ var storedArgs;
+ var later = function () {
+ previous = Date.now();
+ timeout = null;
+ result = fn.apply(storedContext, storedArgs);
+ if (!timeout) {
+ storedContext = null;
+ storedArgs = [];
+ }
+ };
+ return function wrapper() {
+ var args = [];
+ for (var _i = 0; _i < arguments.length; _i++) {
+ args[_i] = arguments[_i];
+ }
+ var now = Date.now();
+ var remaining = wait - (now - previous);
+ storedContext = this;
+ storedArgs = args;
+ if (remaining <= 0 || remaining > wait) {
+ if (timeout) {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ previous = now;
+ result = fn.apply(storedContext, storedArgs);
+ if (!timeout) {
+ storedContext = null;
+ storedArgs = [];
+ }
+ }
+ else if (!timeout) {
+ timeout = window.setTimeout(later, remaining);
+ }
+ return result;
+ };
+ }
+
+});
+
+
+
+ // old names for form elements
+Roo.bootstrap.Form = Roo.bootstrap.form.Form;
+Roo.bootstrap.Input = Roo.bootstrap.form.Input;
+Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
+Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
+Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
+Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
+Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
+Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
+Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
+Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
+Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
+Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
+Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
+Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
+Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
+Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
+Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
+Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
+Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
+Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
+Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
+Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
+Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
+Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
+Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
+Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
+
+Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
+Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
+
+Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
+Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
+
+Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
+Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
+Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
+Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator
+