-/**
+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
- };
-
- if(this.specificTitle){
- title = 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'
+
};
-
- 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) - 60
- );
+ 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?
- var _this = this;
- (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;
-
+
+ this.focus();
+ this.fireEvent("show", this);
},
-
- setDefaultButton : function(btn)
- {
- //this.el.select('.modal-footer').()
+
+ focus : function(){
+ return;
+ if(!this.hidden){
+ this.doFocus.defer(50, this);
+ }
},
- diff : false,
-
- resizeTo: function(w,h)
- {
- // skip.. ?? why??
- this.dialogEl.setWidth(w);
- if (this.diff === false) {
- this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
+ doFocus : function(){
+ if(!this.hidden){
+ this.focusEl.focus();
}
-
- this.bodyEl.setHeight(h - this.diff);
-
- 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;
- },
/**
- * 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'
- }
- ],
-
- zIndex : 10001
-});
-/*
- * - LGPL
- *
- * messagebox - can be used as a replace
- *
- */
-/**
- * @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.');
+ "click" : true
+ });
+};
-// Prompt for user data:
-Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
- if (btn == 'ok'){
+Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
+
+ href : false,
+ html : false,
+ preventDefault: false,
+ isContainer : false,
+ active : 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 : [ ]
+ };
+
+ 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;
+ }
+});
+
+
+
+
+
+
+/**
+ * @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
+ */
+
+
+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;
+ }
+
+});
+
+
+
+
+/*
+* 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.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
+ dialogEl: false,
+ bodyEl: false,
+ footerEl: false,
+ titleEl: false,
+ closeEl: false,
+
+ 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);
+ }
+ //var type = this.el.dom.type;
+
+
+ if(this.tabIndex !== undefined){
+ this.el.dom.setAttribute('tabIndex', this.tabIndex);
+ }
+
+ 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])));
+ }
+ }
+
+ 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;
+ }
+
+ var header = [];
+ if (this.allow_close && Roo.bootstrap.version == 3) {
+ header.push({
+ tag: 'button',
+ cls : 'close',
+ html : '×'
+ });
+ }
+
+ header.push(title);
+
+ 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
+ }
+ ]
+
+ } :
+ { // BS4 uses mr-auto on left buttons....
+ cls : 'modal-footer'
+ };
+
+
+
+
+
+ var modal = {
+ cls: "modal",
+ cn : [
+ {
+ cls: "modal-dialog " + size,
+ cn : [
+ {
+ cls : "modal-content",
+ cn : [
+ {
+ cls : 'modal-header',
+ cn : header
+ },
+ bdy,
+ footer
+ ]
+
+ }
+ ]
+
+ }
+ ]
+ };
+
+ 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();
+
+ },
+ 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);
+ }
+
+ },
+
+
+ resize : function()
+ {
+ this.maskEl.setSize(
+ Roo.lib.Dom.getViewWidth(true),
+ Roo.lib.Dom.getViewHeight(true)
+ );
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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)
+ );
+ }
+
+ },
+
+ setSize : function(w,h)
+ {
+ if (!w && !h) {
+ return;
+ }
+
+ this.resizeTo(w,h);
+ // any layout/border etc.. resize..
+ (function () {
+ this.items.forEach( function(e) {
+ e.layout ? e.layout() : false;
+
+ });
+ }).defer(100,this);
+
+ },
+
+ show : function() {
+
+ if (!this.rendered) {
+ this.render();
+ }
+ this.toggleHeaderInput(false);
+ //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');
+ }
+
+ // 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();
+
+ this.fireEvent('show', this);
+
+ // set zindex here - otherwise it appears to be ignored...
+ this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+
+
+ // 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');
+
+ },
+
+ 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) };
+ }
+
+ 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);
+ },
+
+ 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
+ }
+ this.tmpl.overwrite(this.bodyEl, obj);
+ },
+
+ getChildHeight : function(child_nodes)
+ {
+ if(
+ !child_nodes ||
+ child_nodes.length == 0
+ ) {
+ return 0;
+ }
+
+ var child_height = 0;
+
+ 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;
+ }
+ */
+
+ child_height += child_nodes[i].offsetHeight;
+ // Roo.log(child_nodes[i].offsetHeight);
+ }
+
+ return child_height;
+ },
+ toggleHeaderInput : function(is_edit)
+ {
+ if (!this.editableTitle) {
+ return; // not editable.
+ }
+ if (is_edit && this.is_header_editing) {
+ return; // already editing..
+ }
+ if (is_edit) {
+
+ this.headerEditEl.dom.value = this.title;
+ this.headerEditEl.removeClass('d-none');
+ this.headerEditEl.dom.focus();
+ this.titleEl.addClass('d-none');
+
+ 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
+ *
+ * messagebox - can be used as a replace
+ *
+ */
+/**
+ * @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...
}
});
animEl: 'elId'
});
</code></pre>
- * @singleton
+ * @static
*/
Roo.bootstrap.MessageBox = function(){
var dlg, opt, mask, waitTimer;
*/
/**
- * @class Roo.bootstrap.Navbar
+ * @class Roo.bootstrap.nav.Bar
* @extends Roo.bootstrap.Component
+ * @abstract
* Bootstrap Navbar class
* @constructor
*/
-Roo.bootstrap.Navbar = function(config){
- Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
+Roo.bootstrap.nav.Bar = function(config){
+ Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
this.addEvents({
// raw events
/**
});
};
-Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
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);
- }
- }
-
- }, this);
+ this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
var mark = {
tag: "div",
getChildContainer : function()
{
- if (this.el.select('.collapse').getCount()) {
+ if (this.el && this.el.select('.collapse').getCount()) {
return this.el.select('.collapse',true).first();
}
unmask : function()
{
this.maskEl.hide();
- }
+ },
+ onToggle : function()
+ {
+
+ 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();
+ }
+
+
+ },
+ /**
+ * Expand the navbar pulldown
+ */
+ expand : function ()
+ {
+
+ var ce = this.el.select('.navbar-collapse',true).first();
+ if (ce.hasClass('collapsing')) {
+ return;
+ }
+ 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;
+ },
+ /**
+ * Collapse the navbar pulldown
+ */
+ collapse : function()
+ {
+ var ce = this.el.select('.navbar-collapse',true).first();
+
+ if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
+ // it's collapsed or collapsing..
+ return;
+ }
+ ce.removeClass('in'); // old...
+ 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);
+ }
*/
/**
- * @class Roo.bootstrap.NavSimplebar
- * @extends Roo.bootstrap.Navbar
+ * @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
*/
-Roo.bootstrap.NavSimplebar = function(config){
- Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
+Roo.bootstrap.nav.Simplebar = function(config){
+ Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
+Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
inverse: false,
var cfg = {
tag : this.tag || 'div',
- cls : 'navbar navbar-expand-lg roo-navbar-simple'
+ cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
};
if (['light','white'].indexOf(this.weight) > -1) {
cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
// i'm not actually sure these are really used - normally we add a navGroup to a navbar
- //if (Roo.bootstrap.version == 4) {
- // return cfg;
- //}
+ if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
+ return cfg;
+ }
+
+
+
cfg.cn = [
{
- cls: 'nav',
+ cls: 'nav nav-' + this.xtype,
tag : 'ul'
}
];
*/
/**
- * @class Roo.bootstrap.NavHeaderbar
- * @extends Roo.bootstrap.NavSimplebar
+ * @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
*/
-Roo.bootstrap.NavHeaderbar = function(config){
- Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
+Roo.bootstrap.nav.Headerbar = function(config){
+ Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
+Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
position: '',
brand: '',
cn.push({
tag: 'div',
- cls: 'collapse navbar-collapse',
+ cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
cn : []
});
return this.getChildContainer();
},
+ getChildContainer : function()
+ {
+
+ return this.el.select('.roo-navbar-collapse',true).first();
+
+
+ },
initEvents : function()
{
- Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
+ Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
if (this.autohide) {
*/
/**
- * @class Roo.bootstrap.NavSidebar
- * @extends Roo.bootstrap.Navbar
+ * @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
*/
-Roo.bootstrap.NavSidebar = function(config){
- Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
+Roo.bootstrap.nav.Sidebar = function(config){
+ Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
+Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
sidebar : true, // used by Navbar Item and NavbarGroup at present...
*/
/**
- * @class Roo.bootstrap.NavGroup
+ * @class Roo.bootstrap.nav.Group
* @extends Roo.bootstrap.Component
+ * @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 nav group
* @param {Object} config The config object
*/
-Roo.bootstrap.NavGroup = function(config){
- Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
+Roo.bootstrap.nav.Group = function(config){
+ Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
this.navItems = [];
- Roo.bootstrap.NavGroup.register(this);
+ Roo.bootstrap.nav.Group.register(this);
this.addEvents({
/**
* @event changed
* Fires when the active item changes
- * @param {Roo.bootstrap.NavGroup} this
+ * @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
*/
};
-Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
align: '',
inverse: false,
type: 'nav',
navId : '',
// private
+ pilltype : true,
navItems : false,
getAutoCreate : function()
{
- var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
+ var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
cfg = {
tag : 'ul',
if (['tabs','pills'].indexOf(this.type) != -1) {
cfg.cls += ' nav-' + this.type;
} else {
- cfg.cls += ' navbar-nav';
+ // 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
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 {
},
/**
* sets the active Navigation item
- * @param {Roo.bootstrap.NavItem} the new current navitem
+ * @param {Roo.bootstrap.nav.Item} the new current navitem
*/
setActiveItem : function(item)
{
},
/**
* gets the active Navigation item
- * @return {Roo.bootstrap.NavItem} the current navitem
+ * @return {Roo.bootstrap.nav.Item} the current navitem
*/
getActive : function()
{
},
/**
* adds a Navigation item
- * @param {Roo.bootstrap.NavItem} the navitem to add
+ * @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.NavItem(cfg);
+ var cn = new Roo.bootstrap.nav.Item(cfg);
this.register(cn);
cn.parentId = this.id;
cn.onRender(this.el, null);
},
/**
* register a Navigation item
- * @param {Roo.bootstrap.NavItem} the navitem to add
+ * @param {Roo.bootstrap.nav.Item} the navitem to add
*/
register : function(item)
{
});
-Roo.apply(Roo.bootstrap.NavGroup, {
+Roo.apply(Roo.bootstrap.nav.Group, {
groups: {},
/**
* register a Navigation Group
- * @param {Roo.bootstrap.NavGroup} the navgroup to add
+ * @param {Roo.bootstrap.nav.Group} the navgroup to add
*/
register : function(navgrp)
{
/**
* fetch a Navigation Group based on the navigation ID
* @param {string} the navgroup to add
- * @returns {Roo.bootstrap.NavGroup} the navgroup
+ * @returns {Roo.bootstrap.nav.Group} the navgroup
*/
get: function(navId) {
if (typeof(this.groups[navId]) == 'undefined') {
return false;
- //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
+ //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
}
return this.groups[navId] ;
}
});
- /*
- * - LGPL
- *
- * row
- *
- */
-
-/**
- * @class Roo.bootstrap.NavItem
+ /**
+ * @class Roo.bootstrap.nav.Item
* @extends Roo.bootstrap.Component
+ * @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} 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 Navbar Item
* @param {Object} config The config object
*/
-Roo.bootstrap.NavItem = function(config){
- Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
+Roo.bootstrap.nav.Item = function(config){
+ Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
this.addEvents({
// raw events
/**
/**
* @event changed
* Fires when the active item active state changes
- * @param {Roo.bootstrap.NavItem} this
+ * @param {Roo.bootstrap.nav.Item} this
* @param {boolean} state the new state
*/
/**
* @event scrollto
* Fires when scroll to element
- * @param {Roo.bootstrap.NavItem} this
+ * @param {Roo.bootstrap.nav.Item} this
* @param {Object} options
* @param {Roo.EventObject} e
};
-Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
href: false,
html: '',
disabled : false,
animateRef : false,
was_active : false,
+ button_weight : '',
+ button_outline : false,
+ linkcls : '',
+ navLink: false,
getAutoCreate : function(){
var cfg = {
tag: this.tag,
cls: 'nav-item'
-
};
+ cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
+
if (this.active) {
- cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' 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 || ''
+ html: this.html || '',
+ cls : ''
}
];
if (this.tagtype == 'a') {
- cfg.cn[0].cls = 'nav-link';
+ cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
+
}
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 = '<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>';
}
}
this.tag = 'div';
}
- return Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
+ 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;
},
this.menu = this.addxtype(Roo.apply({}, this.menu));
}
- this.el.select('a',true).on('click', this.onClick, this);
+ this.el.on('click', this.onClick, this);
- if(this.tagtype == 'span'){
- this.el.select('span',true).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);
}
if(
- this.preventDefault ||
- this.href == '#'
+ this.preventDefault ||
+ this.href === false ||
+ this.href === '#'
){
- Roo.log("NavItem - prevent Default?");
+ //Roo.log("NavItem - prevent Default?");
e.preventDefault();
}
var p = this.parent();
- if (['tabs','pills'].indexOf(p.type)!==-1) {
+ if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
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');
+ p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
}
},
{
if (this.active && !state && this.navId) {
this.was_active = true;
- var nv = Roo.bootstrap.NavGroup.get(this.navId);
+ var nv = Roo.bootstrap.nav.Group.get(this.navId);
if (nv) {
nv.clearWasActive(this);
}
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);
}
// 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);
+ var nv = Roo.bootstrap.nav.Group.get(this.navId);
if (nv) {
var onav = nv.getWasActive();
if (onav) {
*/
tooltipEl : function()
{
- return this.el.select('' + this.tagtype + '', true).first();
+ return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
},
scrollToElement : function(e)
Roo.get(c).scrollTo('top', options.value, true);
return;
- }
+ },
+ /**
+ * Set the HTML (text content) of the item
+ * @param {string} html content for the nav item
+ */
+ setHtml : function(html)
+ {
+ this.html = html;
+ this.htmlEl.dom.innerHTML = html;
+
+ }
});
*/
/**
- * @class Roo.bootstrap.NavSidebarItem
- * @extends Roo.bootstrap.NavItem
+ * @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)
* Create a new Navbar Button
* @param {Object} config The config object
*/
-Roo.bootstrap.NavSidebarItem = function(config){
- Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
+Roo.bootstrap.nav.SidebarItem = function(config){
+ Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
this.addEvents({
// raw events
/**
/**
* @event changed
* Fires when the active item active state changes
- * @param {Roo.bootstrap.NavSidebarItem} this
+ * @param {Roo.bootstrap.nav.SidebarItem} this
* @param {boolean} state the new state
*/
};
-Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
+Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
badgeWeight : 'default',
e.preventDefault();
}
- this.fireEvent('click', this);
+ this.fireEvent('click', this, e);
+ },
+
+ 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;
+ }
+
+ this.el.removeClass('disabled');
+
+ return;
+ },
+
+ 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;
+ }
+
+
+
+
+});
+
+
+ /*
+ * - LGPL
+ *
+ * nav progress bar
+ *
+ */
+
+/**
+ * @class Roo.bootstrap.nav.ProgressBar
+ * @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.nav.ProgressBarItem
+ * Bootstrap NavProgressBar class
+ *
+ * @constructor
+ * Create a new nav progress bar - a bar indicating step along a process
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.nav.ProgressBar = function(config){
+ Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
+
+ 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, {
+ /**
+ * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
+ * Bullets for the Nav Progress bar for the toolbar
+ */
+ bullets : [],
+ barItems : [],
+
+ getAutoCreate : function()
+ {
+ var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.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.nav.ProgressBar.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.nav.ProgressItem(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.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';
+
+ 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');
},
- disable : function()
+ tooltipEl : function()
+ {
+ return this.el.select('.roo-navigation-bar-item-icon', true).first();;
+ }
+});
+
+
+ /*
+ * - 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);
+
+
+};
+
+Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
+
+ getAutoCreate : function()
{
- this.setDisabled(true);
+
+ var cfg = {
+ tag: 'nav',
+ cn : [
+ {
+ tag : 'ol',
+ cls : 'breadcrumb'
+ }
+ ]
+
+ };
+
+ return cfg;
},
- enable : function()
+ initEvents: function()
{
- this.setDisabled(false);
+ this.olEl = this.el.select('ol',true).first();
},
+ getChildContainer : function()
+ {
+ return this.olEl;
+ }
- setDisabled : function(state)
+});
+
+ /*
+ * - 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 : '',
+
+ getAutoCreate : function()
{
- if(this.disabled == state){
- return;
- }
-
- this.disabled = state;
-
- if (state) {
- this.el.addClass('disabled');
- return;
+
+ 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;
}
- this.el.removeClass('disabled');
-
- return;
+ return cfg;
},
- setActive : function(state)
+ initEvents: function()
{
- if(this.active == state){
- return;
- }
-
- this.active = state;
-
- if (state) {
- this.el.addClass('active');
- return;
+ if (this.href) {
+ this.el.select('a', true).first().on('click',this.onClick, this)
}
- this.el.removeClass('active');
-
- return;
- },
-
- isActive: function ()
- {
- return this.active;
},
-
- setBadge : function(str)
+ onClick : function(e)
{
- if(!this.badgeEl){
- return;
- }
-
- this.badgeEl.dom.innerHTML = str;
+ e.preventDefault();
+ this.fireEvent('click',this, e);
}
-
-
-
});
-
/*
* - LGPL
/**
* @class Roo.bootstrap.Row
* @extends Roo.bootstrap.Component
+ * @children Roo.bootstrap.Component
* Bootstrap Row class (contains columns...)
*
* @constructor
/*
* - LGPL
*
- * element
+ * pagination
*
*/
/**
- * @class Roo.bootstrap.Element
+ * @class Roo.bootstrap.Pagination
* @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
+ * @children Roo.bootstrap.Pagination
+ * Bootstrap Pagination class
+ *
+ * @cfg {String} size (xs|sm|md|lg|xl)
+ * @cfg {Boolean} inverse
*
* @constructor
- * Create a new Element
+ * Create a new Pagination
* @param {Object} config The config object
*/
-Roo.bootstrap.Element = function(config){
- Roo.bootstrap.Element.superclass.constructor.call(this, config);
+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,
+ getAutoCreate : function(){
+ var cfg = {
+ tag: 'ul',
+ cls: 'pagination'
+ };
+ 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
+ *
+ */
+
+
+/**
+ * @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
- * When a element is chick
- * @param {Roo.bootstrap.Element} this
+ * The raw click event for the entire grid.
* @param {Roo.EventObject} e
*/
"click" : true
});
};
-Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
- tag: 'div',
- cls: '',
- html: '',
- preventDefault: false,
- clickable: false,
+ 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(this.cls){
+ cfg.cls = this.cls;
+ }
+
+ if(this.disabled){
+ cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
+ }
+
+ if(this.active){
+ cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
+ }
+
+ return cfg;
+ },
+
+ initEvents: function() {
+
+ this.el.on('click', this.onClick, this);
+
+ },
+ onClick : function(e)
+ {
+ Roo.log('PaginationItem on click ');
+ if(this.preventDefault){
+ e.preventDefault();
+ }
+
+ if(this.disabled){
+ return;
+ }
+
+ this.fireEvent('click', this, e);
+ }
+
+});
+
+
+
+ /*
+ * - LGPL
+ *
+ * slider
+ *
+ */
+
+
+/**
+ * @class Roo.bootstrap.Slider
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Slider class
+ *
+ * @constructor
+ * Create a new Slider
+ * @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: this.tag,
- // cls: this.cls, double assign in parent class Component.js :: onRender
- html: this.html
+ 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">
+ */
+ /**
+ * @extends Roo.dd.DDProxy
+ * @class Roo.grid.SplitDragZone
+ * Support for Column Header resizing
+ * @constructor
+ * @param {Object} config
+ */
+// private
+// This is a support class used internally by the Grid components
+Roo.grid.SplitDragZone = function(grid, hd, hd2){
+ this.grid = grid;
+ this.view = grid.getView();
+ this.proxy = this.view.resizeProxy;
+ Roo.grid.SplitDragZone.superclass.constructor.call(
+ this,
+ hd, // ID
+ "gridSplitters" + this.grid.getGridEl().id, // SGROUP
+ { // CONFIG
+ dragElId : Roo.id(this.proxy.dom),
+ resizeFrame:false
+ }
+ );
+
+ this.setHandleElId(Roo.id(hd));
+ if (hd2 !== false) {
+ this.setOuterHandleElId(Roo.id(hd2));
+ }
+
+ this.scroll = false;
+};
+Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
+ fly: Roo.Element.fly,
+
+ b4StartDrag : function(x, y){
+ this.view.headersDisabled = true;
+ var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
+ this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
+ );
+ this.proxy.setHeight(h);
+
+ // for old system colWidth really stored the actual width?
+ // in bootstrap we tried using xs/ms/etc.. to do % sizing?
+ // which in reality did not work.. - it worked only for fixed sizes
+ // for resizable we need to use actual sizes.
+ var w = this.cm.getColumnWidth(this.cellIndex);
+ if (!this.view.mainWrap) {
+ // bootstrap.
+ w = this.view.getHeaderIndex(this.cellIndex).getWidth();
+ }
+
+
+
+ // this was w-this.grid.minColumnWidth;
+ // doesnt really make sense? - w = thie curren width or the rendered one?
+ var minw = Math.max(w-this.grid.minColumnWidth, 0);
+ this.resetConstraints();
+ this.setXConstraint(minw, 1000);
+ this.setYConstraint(0, 0);
+ this.minX = x - minw;
+ this.maxX = x + 1000;
+ this.startPos = x;
+ if (!this.view.mainWrap) { // this is Bootstrap code..
+ this.getDragEl().style.display='block';
+ }
+
+ Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
+ },
+
+
+ handleMouseDown : function(e){
+ ev = Roo.EventObject.setEvent(e);
+ var t = this.fly(ev.getTarget());
+ if(t.hasClass("x-grid-split")){
+ this.cellIndex = this.view.getCellIndex(t.dom);
+ this.split = t.dom;
+ this.cm = this.grid.colModel;
+ if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
+ Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
+ }
+ }
+ },
+
+ endDrag : function(e){
+ this.view.headersDisabled = false;
+ var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
+ var diff = endX - this.startPos;
+ //
+ var w = this.cm.getColumnWidth(this.cellIndex);
+ if (!this.view.mainWrap) {
+ w = 0;
+ }
+ this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
+ },
+
+ autoOffset : function(){
+ this.setDelta(0,0);
+ }
+});/*
+ * Based on:
+ * Ext JS Library 1.1.1
+ * Copyright(c) 2006-2007, Ext JS, LLC.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+
+/**
+ * @class Roo.grid.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
+ */
+Roo.grid.AbstractSelectionModel = function(){
+ this.locked = false;
+ Roo.grid.AbstractSelectionModel.superclass.constructor.call(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.
+ *
+ * Originally Released Under LGPL - original licence link has changed is not relivant.
+ *
+ * Fork - LGPL
+ * <script type="text/javascript">
+ */
+/**
+ * @extends Roo.grid.AbstractSelectionModel
+ * @class Roo.grid.RowSelectionModel
+ * The default SelectionModel used by {@link Roo.grid.Grid}.
+ * It supports multiple selections and keyboard selection/navigation.
+ * @constructor
+ * @param {Object} config
+ */
+Roo.grid.RowSelectionModel = function(config){
+ Roo.apply(this, config);
+ this.selections = new Roo.util.MixedCollection(false, function(o){
+ return o.id;
+ });
+
+ this.last = false;
+ this.lastActive = false;
+
+ this.addEvents({
+ /**
+ * @event 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;
+};
+
+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(){
+
+ 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);
+ },
+
+ // 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);
+ }
+ });
+ },
+
+ // 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.ds;
+ 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;
},
-
- initEvents: function()
- {
- Roo.bootstrap.Element.superclass.initEvents.call(this);
-
- if(this.clickable){
- this.el.on('click', this.onClick, this);
- }
-
+
+ /**
+ * Selects the first row in the grid.
+ */
+ selectFirstRow : function(){
+ this.selectRow(0);
},
-
- onClick : function(e)
- {
- if(this.preventDefault){
- e.preventDefault();
- }
-
- this.fireEvent('click', this, e);
+
+ /**
+ * Select the last row.
+ * @param {Boolean} keepExisting (optional) True to keep existing selections
+ */
+ selectLastRow : function(keepExisting){
+ this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
},
-
- getValue : function()
- {
- return this.el.dom.innerHTML;
+
+ /**
+ * 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);
+ }
},
-
- setValue : function(value)
- {
- this.el.dom.innerHTML = value;
- }
-
-});
-
+ /**
+ * 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);
+ }
+ },
- /*
- * - LGPL
- *
- * pagination
- *
- */
+ /**
+ * Returns the selected records
+ * @return {Array} Array of selected records
+ */
+ getSelections : function(){
+ return [].concat(this.selections.items);
+ },
-/**
- * @class Roo.bootstrap.Pagination
- * @extends Roo.bootstrap.Component
- * Bootstrap Pagination class
- * @cfg {String} size xs | sm | md | lg
- * @cfg {Boolean} inverse false | true
- *
- * @constructor
- * Create a new Pagination
- * @param {Object} config The config object
- */
+ /**
+ * Returns the first selected record.
+ * @return {Record}
+ */
+ getSelected : function(){
+ return this.selections.itemAt(0);
+ },
-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,
-
- getAutoCreate : function(){
- var cfg = {
- tag: 'ul',
- cls: 'pagination'
- };
- if (this.inverse) {
- cfg.cls += ' inverse';
- }
- if (this.html) {
- cfg.html=this.html;
+ /**
+ * Clears all selections.
+ */
+ clearSelections : function(fast){
+ if(this.locked) {
+ return;
}
- if (this.cls) {
- cfg.cls += " " + this.cls;
+ 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 cfg;
- }
-
-});
-
-
+ this.last = false;
+ },
- /*
- * - LGPL
- *
- * Pagination item
- *
- */
+ /**
+ * 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);
+ }
+ },
-/**
- * @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
- */
+ /**
+ * 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.ds.getAt(index) : index;
+ return (r && this.selections.key(r.id) ? true : false);
+ },
-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
- });
-};
+ /**
+ * 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);
+ },
-Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
-
- 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 : ''
- }
- ]
+ // 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(this.cls){
- cfg.cls = this.cls;
- }
-
- if(this.disabled){
- cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
+ 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);
+ }
}
-
- if(this.active){
- cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
+ 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);
}
-
- return cfg;
},
- initEvents: function() {
-
- this.el.on('click', this.onClick, this);
-
- },
- onClick : function(e)
- {
- Roo.log('PaginationItem on click ');
- if(this.preventDefault){
- e.preventDefault();
+ /**
+ * 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();
}
-
- if(this.disabled){
+ 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;
}
-
- this.fireEvent('click', this, e);
- }
-
-});
+ 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);
+ }
+ },
- /*
- * - LGPL
- *
- * slider
- *
- */
+ /**
+ * 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);
+ }
+ },
+ /**
+ * 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);
+ },
-/**
- * @class Roo.bootstrap.Slider
- * @extends Roo.bootstrap.Component
- * Bootstrap Slider class
- *
- * @constructor
- * Create a new Slider
- * @param {Object} config The config object
- */
+ // private
+ restoreLast : function(){
+ if(this._last){
+ this.last = this._last;
+ }
+ },
-Roo.bootstrap.Slider = function(config){
- Roo.bootstrap.Slider.superclass.constructor.call(this, config);
-};
+ // private
+ acceptsNav : function(row, col, cm){
+ return !cm.isHidden(col) && cm.isCellEditable(col, row);
+ },
-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;
+ // 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.
/**
* The config passed into the constructor
*/
- this.config = config;
+ 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;
+ this.addColumn(config[i]);
+
}
/**
};
Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
/**
- * @cfg {String} header The header text to display in the Grid view.
+ * @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 (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
+ * @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 (Optional) The initial width in pixels of the column. Using this
+ * @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 (Optional) True if sorting is to be allowed on this column.
+ * @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 (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
+ * @cfg {Boolean} locked 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} fixed 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} resizable False to disable column resizing. Defaults to true.
*/
/**
- * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
+ * @cfg {Boolean} hidden True to hide the column. Defaults to false.
*/
/**
- * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
+ * @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 (Optional) For grid editors - returns the grid editor
+ * @cfg {Roo.grid.GridEditor} editor 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} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
*/
/**
- * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
+ * @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 (Optional)
+ * @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 (Optional)
+ * @cfg {String} tooltip mouse over tooltip text
*/
/**
- * @cfg {Number} xs (Optional)
+ * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
*/
/**
- * @cfg {Number} sm (Optional)
+ * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
*/
/**
- * @cfg {Number} md (Optional)
+ * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
*/
/**
- * @cfg {Number} lg (Optional)
+ * @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.
/**
- * Returns the column for a specified dataIndex.
+ * Returns the column Object for a specified dataIndex.
* @param {String} dataIndex The column dataIndex
* @return {Object|Boolean} the column or false if not found
*/
/**
* 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){
- return this.config[col].width * 1 || this.defaultWidth;
+ 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;
+
},
/**
*/
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.grid.ColumnModel.defaultRenderer = function(value)
* 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...')
um.un('failure', this.onLoad, this);
}
}
-};/*
- * - LGPL
- *
- * table
- *
- */
-
-/**
+};/**
* @class Roo.bootstrap.Table
+ * @licence LGBL
* @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)
+ * @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 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
+ * @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.bootstrap.Table = function(config){
+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.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.bootstrap.Table);
+ this.selModel = Roo.factory(this.sm, Roo.grid);
this.sm = this.selModel;
this.sm.xmodule = this.xmodule || false;
}
* @param {Number} columnIndex
* @param {Roo.EventObject} e
*/
- "headercontextmenu" : true
+ "headercontextmenu" : true,
+ /**
+ * @event mousedown
+ * The raw mousedown event for the entire grid.
+ * @param {Roo.EventObject} e
+ */
+ "mousedown" : true
+
});
};
Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
cls: false,
- align: false,
- bgcolor: false,
- border: false,
- cellpadding: false,
- cellspacing: false,
- frame: false,
- rules: false,
- sortable: false,
- summary: false,
- width: false,
+
+ empty_results : '',
striped : false,
scrollBody : false,
bordered: false,
loadMask : false,
footerShow : true,
headerShow : true,
+ enableColumnResize: true,
rowSelection : false,
cellSelection : false,
layout : false,
+
+ minColumnWidth : 50,
// Roo.Element - the tbody
- mainBody: false,
- // Roo.Element - thead element
- mainHead: false,
+ bodyEl: false, // <tbody> Roo.Element - thead element
+ headEl: false, // <thead> Roo.Element - thead element
+ resizeProxy : false, // proxy element for dragging?
+
+
container: false, // used by gridpanel...
auto_hide_footer : false,
+ view: false, // actually points to this..
+
getAutoCreate : function()
{
var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
cfg = {
tag: 'table',
- cls : 'table',
+ cls : 'table',
cn : []
};
+ // this get's auto added by panel.Grid
if (this.scrollBody) {
cfg.cls += ' table-body-fixed';
}
if (this.condensed) {
cfg.cls += ' table-condensed';
}
+
if (this.responsive) {
cfg.cls += ' table-responsive';
}
cfg.cls+= ' ' +this.cls;
}
- // this lot should be simplifed...
- var _t = this;
- var cp = [
- 'align',
- 'bgcolor',
- 'border',
- 'cellpadding',
- 'cellspacing',
- 'frame',
- 'rules',
- 'sortable',
- 'summary',
- 'width'
- ].forEach(function(k) {
- if (_t[k]) {
- cfg[k] = _t[k];
- }
- });
if (this.layout) {
//Roo.log('initEvents with ds!!!!');
- this.mainBody = this.el.select('tbody', true).first();
- this.mainHead = this.el.select('thead', true).first();
+ this.bodyEl = this.el.select('tbody', true).first();
+ this.headEl = this.el.select('thead', true).first();
this.mainFoot = this.el.select('tfoot', true).first();
- var _this = this;
Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
- e.on('click', _this.sort, _this);
- });
+ e.on('click', this.sort, this);
+ }, this);
- this.mainBody.on("click", this.onClick, this);
- this.mainBody.on("dblclick", this.onDblClick, this);
// why is this done????? = it breaks dialogs??
//this.parent().el.setStyle('position', 'relative');
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);
+
+ //?? does bodyEl get replaced on render?
+ this.bodyEl.on("click", this.onClick, this);
+ this.bodyEl.on("dblclick", this.onDblClick, this);
+ this.bodyEl.on('scroll', this.onBodyScroll, this);
+
+ // guessing mainbody will work - this relays usually caught by selmodel at present.
+ this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
+
+
+ this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
+
+
+ if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
+ new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
+ }
+ this.initCSS();
+ },
+ // Compatibility with grid - we implement all the view features at present.
+ getView : function()
+ {
+ return this;
},
+ initCSS : function()
+ {
+
+
+ var cm = this.cm, styles = [];
+ this.CSS.removeStyleSheet(this.id + '-cssrules');
+ var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
+ // we can honour xs/sm/md/xl as widths...
+ // we first have to decide what widht we are currently at...
+ var sz = Roo.getGridSize();
+
+ var total = 0;
+ var last = -1;
+ var cols = []; // visable cols.
+ var total_abs = 0;
+ for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
+ var w = cm.getColumnWidth(i, false);
+ if(cm.isHidden(i)){
+ cols.push( { rel : false, abs : 0 });
+ continue;
+ }
+ if (w !== false) {
+ cols.push( { rel : false, abs : w });
+ total_abs += w;
+ last = i; // not really..
+ continue;
+ }
+ 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++) {
+
+ 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.responsive) {
+ width = '';
+ left = '';
+ hidden = cm.isHidden(i) ? 'display:none;' : '';
+ splithide = 'display: none;';
+ }
+
+ styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
+ if (this.headEl) {
+ if (i == last) {
+ splithide = 'display:none;';
+ }
+
+ styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
+ '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
+ // this is the popover version..
+ '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
+ );
+ }
+
+ }
+ //Roo.log(styles.join(''));
+ this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
+
+ },
+
+
+
onContextMenu : function(e, t)
{
this.processEvent("contextmenu", e);
var rowIndex = this.getRowIndex(row);
// why??? - should these not be based on SelectionModel?
- if(this.cellSelection){
+ //if(this.cellSelection){
this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
- }
+ //}
- if(this.rowSelection){
+ //if(this.rowSelection){
this.fireEvent('rowclick', this, row, rowIndex, e);
- }
-
-
+ //}
+
},
onDblClick : function(e,el)
this.fireEvent('rowdblclick', this, row, rowIndex, e);
}
},
-
+ findRowIndex : function(el)
+ {
+ var cell = Roo.get(el);
+ if(!cell) {
+ return false;
+ }
+ var row = cell.findParent('tr', false, true);
+
+ if(!row || typeof(row) == 'undefined'){
+ return false;
+ }
+ return this.getRowIndex(row);
+ },
sort : function(e,el)
{
var col = Roo.get(el);
var sort = col.attr('sort');
var dir = 'ASC';
- if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
+ if(col.select('i', true).first().hasClass('fa-arrow-up')){
dir = 'DESC';
}
tag: 'th',
cls : 'x-hcol-' + i,
style : '',
+
html: cm.getColumnHeader(i)
};
+ var tooltip = cm.getColumnTooltip(i);
+ if (tooltip) {
+ c.tooltip = tooltip;
+ }
+
+
var hh = '';
if(typeof(config.sortable) != 'undefined' && config.sortable){
- c.cls = 'sortable';
- c.html = '<i class="glyphicon"></i>' + c.html;
+ c.cls += ' sortable';
+ c.html = '<i class="fa"></i>' + c.html;
}
+ // could use BS4 hidden-..-down
+
if(typeof(config.lgHeader) != 'undefined'){
- hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
+ hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
}
if(typeof(config.mdHeader) != 'undefined'){
c.colspan = config.colspan;
}
- if(typeof(config.hidden) != 'undefined' && config.hidden){
- c.style += ' display:none;';
- }
+ // hidden is handled by CSS now
if(typeof(config.dataIndex) != 'undefined'){
c.sort = config.dataIndex;
c.style += ' text-align:' + config.align + ';';
}
- if(typeof(config.width) != 'undefined'){
+ /* width is done in CSS
+ *if(typeof(config.width) != 'undefined'){
c.style += ' width:' + config.width + 'px;';
this.totalWidth += config.width;
} else {
this.totalWidth += 100; // assume minimum of 100 per column?
}
+ */
if(typeof(config.cls) != 'undefined'){
c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
}
+ // 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?
+
+ c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
+
+
- ['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];
-
- });
header.cn.push(c)
}
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']);
+ 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(['glyphicon-arrow-up']);
+ 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(['glyphicon-arrow-down']);
+ e.select('i', true).addClass(['fa-arrow-down']);
}
}
});
- var tbody = this.mainBody;
+ var tbody = this.bodyEl;
if(ds.getCount() > 0){
ds.data.each(function(d,rowIndex){
}
}, this);
+ } else if (this.empty_results.length) {
+ this.el.mask(this.empty_results, 'no-spinner');
}
var tfoot = this.el.select('tfoot', true).first();
this.fireEvent('rowsrendered', this);
this.autoSize();
+
+ this.initCSS(); /// resize cols
+
+
},
if(isUpdate !== true){
this.fireEvent("beforerowremoved", this, index, record);
}
- var bt = this.mainBody.dom;
+ var bt = this.bodyEl.dom;
var rows = this.el.select('tbody > tr', true).elements;
{
//Roo.log('on Add called');
// - note this does not handle multiple adding very well..
- var bt = this.mainBody.dom;
+ 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]);
record = ds.getAt(index);
}else{
index = ds.indexOf(record);
+ if (index < 0) {
+ return; // should not happen - but seems to
+ }
}
this.insertRow(ds, index, true);
this.autoSize();
//this.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);
+ //}
+ },
+
+ /**
+ * Scrolls the specified cell into view
+ * @param {Number} row The row index
+ * @param {Number} col The column index
+ * @param {Boolean} hscroll false to disable horizontal scrolling
+ */
+ ensureVisible : function(row, col, hscroll)
+ {
+ //Roo.log('GridView.ensureVisible,' + row + ',' + col);
+ //return null; //disable for testing.
+ if(typeof row != "number"){
+ row = row.rowIndex;
+ }
+ if(row < 0 && row >= this.ds.getCount()){
+ return null;
+ }
+ col = (col !== undefined ? col : 0);
+ var cm = this.cm;
+ while(cm.isHidden(col)){
+ col++;
+ }
+
+ var el = this.getCellDom(row, col);
+ if(!el){
+ return null;
+ }
+ var c = this.bodyEl.dom;
+
+ var ctop = parseInt(el.offsetTop, 10);
+ var cleft = parseInt(el.offsetLeft, 10);
+ var cbot = ctop + el.offsetHeight;
+ var cright = cleft + el.offsetWidth;
+
+ //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
+ var ch = 0; //?? header is not withing the area?
+ var stop = parseInt(c.scrollTop, 10);
+ var sleft = parseInt(c.scrollLeft, 10);
+ var sbot = stop + ch;
+ var sright = sleft + c.clientWidth;
+ /*
+ Roo.log('GridView.ensureVisible:' +
+ ' ctop:' + ctop +
+ ' c.clientHeight:' + c.clientHeight +
+ ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
+ ' stop:' + stop +
+ ' cbot:' + cbot +
+ ' sbot:' + sbot +
+ ' ch:' + ch
+ );
+ */
+ if(ctop < stop){
+ c.scrollTop = ctop;
+ //Roo.log("set scrolltop to ctop DISABLE?");
+ }else if(cbot > sbot){
+ //Roo.log("set scrolltop to cbot-ch");
+ c.scrollTop = cbot-ch;
+ }
+
+ if(hscroll !== false){
+ if(cleft < sleft){
+ c.scrollLeft = cleft;
+ }else if(cright > sright){
+ c.scrollLeft = cright-c.clientWidth;
+ }
+ }
+
+ return el;
+ },
+
insertRow : function(dm, rowIndex, isUpdate){
//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 e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
var _this = this;
return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
},
+ 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];
+
+ },
+
// returns the object tree for a tr..
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
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];
-
- });
-
+ 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
+ );
+
+
+ });
+ }
row.cn.push(td);
}
onBeforeLoad : function()
{
-
+ this.el.unmask(); // if needed.
},
/**
* Remove all rows
*/
setRowVisibility : function(rowIndex, state)
{
- var bt = this.mainBody.dom;
+ var bt = this.bodyEl.dom;
var rows = this.el.select('tbody > tr', true).elements;
if(typeof(rows[rowIndex]) == 'undefined'){
return;
}
- rows[rowIndex].dom.style.display = state ? '' : 'none';
+ rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
+
},
})
}
},
-
+ /**
+ * 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;
});
return rowIndex;
+ },
+ /**
+ * get the header TH element for columnIndex
+ * @param {Number} columnIndex
+ * @returns {Roo.Element}
+ */
+ getHeaderIndex: function(colIndex)
+ {
+ var cols = this.headEl.select('th', true).elements;
+ return cols[colIndex];
+ },
+ /**
+ * get the Column Index from a dom element. (using regex on x-hcol-{colid})
+ * @param {domElement} cell to look for
+ * @returns {Number} the column
+ */
+ getCellIndex : function(cell)
+ {
+ var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
+ if(id){
+ return parseInt(id[1], 10);
+ }
+ return 0;
},
/**
* Returns the grid's underlying element = used by panel.Grid
var tfd = this.getGridEl().select('tfoot', true).first();
var cw = ctr.getWidth();
+ this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
if (tbd) {
- tbd.setSize(ctr.getWidth(),
- ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
- );
+ tbd.setWidth(ctr.getWidth());
+ // if the body has a max height - and then scrolls - we should perhaps set up the height here
+ // this needs fixing for various usage - currently only hydra job advers I think..
+ //tdb.setHeight(
+ // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
+ //);
var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
cw -= barsize;
}
cw = Math.max(cw, this.totalWidth);
- this.getGridEl().select('tr',true).setWidth(cw);
+ this.getGridEl().select('tbody tr',true).setWidth(cw);
+ this.initCSS();
+
// resize 'expandable coloumn?
return; // we doe not have a view in this design..
},
onBodyScroll: function()
{
- //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
- if(this.mainHead){
- this.mainHead.setStyle({
+ //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
+ if(this.headEl){
+ this.headEl.setStyle({
'position' : 'relative',
- 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
+ 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
});
}
if(this.lazyLoad){
- var scrollHeight = this.mainBody.dom.scrollHeight;
+ var scrollHeight = this.bodyEl.dom.scrollHeight;
- var scrollTop = Math.ceil(this.mainBody.getScroll().top);
+ var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
- var height = this.mainBody.getHeight();
+ var height = this.bodyEl.getHeight();
if(scrollHeight - height == scrollTop) {
}
},
-
+ onColumnSplitterMoved : function(i, diff)
+ {
+ this.userResized = true;
+
+ var cm = this.colModel;
+
+ var w = this.getHeaderIndex(i).getWidth() + diff;
+
+
+ cm.setColumnWidth(i, w, true);
+ this.initCSS();
+ //var cid = cm.getColumnId(i); << not used in this version?
+ /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
+
+ this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
+ this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
+ this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
+*/
+ //this.updateSplitters();
+ //this.layout(); << ??
+ this.fireEvent("columnresize", i, w);
+ },
onHeaderChange : function()
{
var header = this.renderHeader();
var table = this.el.select('table', true).first();
- this.mainHead.remove();
- this.mainHead = table.createChild(header, this.mainBody, false);
+ this.headEl.remove();
+ this.headEl = table.createChild(header, this.bodyEl, false);
+
+ Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
+ e.on('click', this.sort, this);
+ }, this);
+
+ if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
+ new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
+ }
+
},
onHiddenChange : function(colModel, colIndex, hidden)
{
+ /*
+ this.cm.setHidden()
var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
this.CSS.updateRule(thSelector, "display", "none");
this.CSS.updateRule(tdSelector, "display", "none");
}
-
+ */
+ // onload calls initCSS()
this.onHeaderChange();
this.onLoad();
},
}
});
-
+// currently only used to find the split on drag..
+Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
- /*
+/**
+ * @depricated
+*/
+Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
+Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
+/*
* - LGPL
*
* table cell
/**
* @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
/**
* @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
/**
* @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
url:this.getUrl(!isPost),
method: method,
params:isPost ? this.getParams() : null,
- isUpload: this.form.fileUpload
+ isUpload: this.form.fileUpload,
+ formData : this.form.formData
}));
this.uploadProgress();
*/
/**
- * @class Roo.bootstrap.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)
*/
-Roo.bootstrap.Form = function(config){
+Roo.bootstrap.form.Form = function(config){
- Roo.bootstrap.Form.superclass.constructor.call(this, config);
+ Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
- Roo.bootstrap.Form.popover.apply();
+ Roo.bootstrap.form.Form.popover.apply();
this.addEvents({
/**
});
};
-Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
/**
* @cfg {String} method
});
if(this.errorMask && !valid){
- Roo.bootstrap.Form.popover.mask(this, target);
+ Roo.bootstrap.form.Form.popover.mask(this, target);
}
return valid;
});
-Roo.apply(Roo.bootstrap.Form, {
+Roo.apply(Roo.bootstrap.form.Form, {
popover : {
this.toolTip.show(tip);
this.intervalID = window.setInterval(function() {
- Roo.bootstrap.Form.popover.unmask();
+ Roo.bootstrap.form.Form.popover.unmask();
}, 10000);
window.onwheel = function(){ return false;};
/**
* @class Roo.form.VTypes
* Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
- * @singleton
+ * @static
*/
Roo.form.VTypes = function(){
// closure these in so they are only created once.
*/
/**
- * @class Roo.bootstrap.Input
+ * @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} 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} 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 {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
*
* @param {Object} config The config object
*/
-Roo.bootstrap.Input = function(config){
+Roo.bootstrap.form.Input = function(config){
- Roo.bootstrap.Input.superclass.constructor.call(this, config);
+ Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
this.addEvents({
/**
* @param {Roo.form.Field} this
* @param {Roo.EventObject} e The event Object
*/
- keyup : true
+ 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.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
+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").
placeholder : this.placeholder || '',
autocomplete : this.autocomplete || 'new-password'
};
+ if (this.inputType == 'file') {
+ input.style = 'overflow:hidden'; // why not in CSS?
+ }
if(this.capture.length){
input.capture = this.capture;
inputblock.cn.push({
tag :'span',
- cls : 'roo-input-before input-group-prepend input-group-text input-group-' +
+ 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({
tag :'span',
- cls : 'roo-input-after input-group-append input-group-text input-group-' +
+ 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?
});
}
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.allowBlank ) {
+ indicator.style = this.allowBlank ? ' display:none' : '';
}
if (align ==='left' && this.fieldLabel.length) {
}
if(this.labelWidth < 13 && this.labelmd == 0){
- this.labelmd = this.labelWidth;
+ this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
}
if(this.labellg > 0){
} 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'
+ tooltip : 'This field is required',
+ style : this.allowBlank ? ' display:none' : ''
},
{
tag: 'label',
];
if(this.indicatorpos == 'right'){
-
+
cfg.cn = [
{
tag: 'label',
{
tag : 'i',
cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
+ tooltip : 'This field is required',
+ style : this.allowBlank ? ' display:none' : ''
},
inputblock
this.inputEl().on("blur", this.onBlur, this);
this.inputEl().relayEvent('keyup', this);
+ this.inputEl().relayEvent('paste', this);
this.indicator = this.indicatorEl();
return;
}
+
if(this.allowBlank && !this.getRawValue().length){
return;
}
*/
/**
- * @class Roo.bootstrap.TextArea
- * @extends Roo.bootstrap.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
* @param {Object} config The config object
*/
-Roo.bootstrap.TextArea = function(config){
- Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
+Roo.bootstrap.form.TextArea = function(config){
+ Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
+Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
cols : false,
rows : 5,
*/
/**
- * @class Roo.bootstrap.TriggerField
- * @extends Roo.bootstrap.Input
+ * @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.TriggerField();
+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.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
+ * {@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) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
+ * @cfg {String} caret (search|calendar) BS3 only - carat fa name
* @constructor
* Create a new TriggerField.
- * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
+ * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
* to the base TextField)
*/
-Roo.bootstrap.TriggerField = function(config){
+Roo.bootstrap.form.TriggerField = function(config){
this.mimicing = false;
- Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
+ Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
};
-Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
+Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
/**
* @cfg {String} triggerClass A CSS class to apply to the trigger
*/
cls: 'glyphicon form-control-feedback'
};
- if(this.removable && !this.editable && !this.tickable){
+ if(this.removable && !this.editable ){
inputblock = {
cls : 'has-feedback',
cn : [
}
} else {
- if(this.removable && !this.editable && !this.tickable){
+ if(this.removable && !this.editable ){
inputblock = {
cls : 'roo-removable',
cn : [
tag :'span',
cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
cn : [
- caret,
+ Roo.bootstrap.version == 3 ? caret : '',
{
tag: 'span',
cls: 'combobox-clear',
// private
onResize : function(w, h){
-// Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
+// 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.createList();
- Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
+ 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();
{
this.list = Roo.get(document.body).createChild({
tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
- cls: 'typeahead typeahead-long dropdown-menu',
+ cls: 'typeahead typeahead-long dropdown-menu shadow',
style: 'display:none'
});
//if(this.wrap){
// this.wrap.remove();
//}
- Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
+ Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
},
// private
onFocus : function(){
- Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
+ Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
/*
if(!this.mimicing){
this.wrap.addClass('x-trigger-wrap-focus');
this.el.un("keydown", this.checkTab, this);
}
//this.wrap.removeClass('x-trigger-wrap-focus');
- Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
+ Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
},
// private
// private
onDisable : function(){
this.inputEl().dom.disabled = true;
- //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
+ //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.TriggerField.superclass.onEnable.call(this);
+ //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
//if(this.wrap){
// this.el.removeClass('x-item-disabled');
//}
*/
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
+
+ });
+};
+
+Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
+
+
+ errorTimeout : 3000,
+
+ images : false,
+
+ fileCollection : false,
+ allowBlank : true,
+
+ getAutoCreate : function()
+ {
+
+ 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()
+ {
+
+ 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);
+ }
+ }
+ });
+
+
+
+
+ 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.selectorEl.on('change', this.onFileSelected, this);
+ if (this.images) {
+ var t = this;
+ this.images.forEach(function(img) {
+ t.addCard(img)
+ });
+ this.images = false;
+ }
+ this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
+
+
+ },
+
+
+ onClick : function(e)
+ {
+ e.preventDefault();
+
+ this.selectorEl.dom.click();
+
+ },
+
+ onFileSelected : function(e)
+ {
+ e.preventDefault();
+
+ if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
+ return;
+ }
+
+ Roo.each(this.selectorEl.dom.files, function(file){
+ this.addFile(file);
+ }, this);
+
+ },
+
+
+
+
+
+ addFile : function(file)
+ {
+
+ if(typeof(file) === 'string'){
+ throw "Add file by name?"; // should not happen
+ return;
+ }
+
+ if(!file || !this.urlAPI){
+ 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 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');
+
+
+ }
+
+ }
+ );
+ // 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);
+
+
+
+ },
+ 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();
+
+
+ },
+ 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);
+
+
+
+ }
+
+
+});
+
+
+Roo.bootstrap.form.CardUploader.ID = -1;/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
/**
* @class Roo.data.SortTypes
- * @singleton
+ * @static
* Defines the default sorting (casting?) comparison functions used when sorting data.
*/
Roo.data.SortTypes = {
*/
/**
- * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
+ * @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.Reader} reader The Reader object which processes the data object and returns
+ * @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.
*/
/**
* <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
+
+ }
+ </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>
// private
// Called as a callback by the Reader during a load operation.
loadRecords : function(o, options, success){
- if(!o || success === false){
+
+ if(!o){
if(success !== false){
this.fireEvent("load", this, [], options, o);
}
var r = this.reader.readRecords(o);
this.loadRecords(r, {add: append}, true);
},
+
+ /**
+ * 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)
+ {
+ this.loadData(this.reader.toLoadData(rec));
+ },
+
/**
* Gets the number of cached records.
* 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 = function(config)
+{
Roo.data.SimpleStore.superclass.constructor.call(this, {
isLocal : true,
- reader: new Roo.data.ArrayReader({
+ reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
id: config.id
},
Roo.data.Record.create(config.fields)
/**
* @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.prototype = {
+
+
+ readerType : 'Data',
/**
* Create an empty record
* @param {Object} data (optional) - overlay some values
return new this.recordType(Roo.apply(da, d));
}
+
};/*
* Based on:
* Ext JS Library 1.1.1
/**
* @class Roo.data.DataProxy
- * @extends Roo.data.Observable
+ * @extends Roo.util.Observable
+ * @abstract
* This class is an abstract base class for implementations which provide retrieval of
* unformatted data objects.<br>
* <p>
*/
/**
* @class Roo.data.MemoryProxy
+ * @extends Roo.data.DataProxy
* 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.
+ * @param {Object} config A config object containing the objects needed for the Store to access data,
*/
-Roo.data.MemoryProxy = function(data){
- if (data.data) {
- data = data.data;
+Roo.data.MemoryProxy = function(config){
+ var data = config;
+ if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
+ data = config.data;
}
Roo.data.MemoryProxy.superclass.constructor.call(this);
this.data = data;
Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
+ /**
+ * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
+ */
/**
* Load data from the requested source (in this case an in-memory
* data object passed to the constructor), read the data object into
params = params || {};
var result;
try {
- result = reader.readRecords(this.data);
+ result = reader.readRecords(params.data ? params.data :this.data);
}catch(e){
this.fireEvent("loadexception", this, arg, null, e);
callback.call(scope, null, arg, false);
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, null, o.request.arg, false);
+ o.request.callback.call(o.request.scope, o, o.request.arg, false);
return;
}
};
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.
}
var records = [];
for(var i = 0; i < c; i++){
- var n = root[i];
+ var n = root[i];
var values = {};
var id = this.getId(n);
for(var j = 0; j < fl; j++){
f = fi[j];
- var v = this.ef[j](n);
- if (!f.convert) {
- Roo.log('missing convert for ' + f.name);
- Roo.log(f);
- continue;
- }
- values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
- }
+ var 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;
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:
* <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
+ * @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);
+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} data A data block which is used by an Roo.data.Store object as
+ * @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){
+ 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 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
- };
+ 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.ComboBox
- * @extends Roo.bootstrap.TriggerField
+ * @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} 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.ComboBox = function(config){
- Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
+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.ComboBox} combo This combo box
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
*/
'expand' : true,
/**
* @event collapse
* Fires when the dropdown list is collapsed
- * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @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.ComboBox} combo This combo box
+ * @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
*/
/**
* @event select
* Fires when a list item is selected
- * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @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
*/
* @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 {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
/**
* @event add
* Fires when the 'add' icon is pressed (add a listener to enable add button)
- * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @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.ComboBox} combo This combo box
+ * @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.ComboBox} combo This combo box
+ * @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.ComboBox} combo This combo box
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
*/
'afterremove' : true,
/**
* @event specialfilter
* Fires when specialfilter
- * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
*/
'specialfilter' : true,
/**
* @event tick
* Fires when tick the element
- * @param {Roo.bootstrap.ComboBox} combo This combo box
+ * @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.ComboBox} combo This combo box
+ * @param {Roo.bootstrap.form.ComboBox} combo This combo box
* @param {Object} cfg set html .
*/
'touchviewdisplay' : true
}
};
-Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
+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
*/
/**
- * @cfg {String/Roo.Template} tpl The template to use to render the output
+ * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
*/
/**
emptyResultText: 'Empty',
triggerText : 'Select',
emptyTitle : '',
+ width : false,
// element that contains real text value.. (when hidden is used..)
* Normal ComboBox
*/
if(!this.tickable){
- cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
+ cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
return cfg;
}
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',
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;
}
return;
}
- Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
+ Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
if(this.hiddenName){
}
},
- initTickableEvents: function()
- {
- this.createList();
+ 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.hiddenName){
-
- this.hiddenField = this.el.select('input.form-hidden-field',true).first();
+ if(this.multiple){
+ this.addItem(o);
+ return;
+ }
- 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);
-
-
+ 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));
}
-// this.list = this.el.select('ul.dropdown-menu',true).first();
+ if(this.valueField){
+ vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
+ }
- 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});
+ var close = this.closeTriggerEl();
+
+ if(close){
+ if(dv.length || vv * 1 > 0){
+ close.show() ;
+ this.blockFocus=true;
+ } else {
+ close.hide();
+ }
}
-
- 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();
+ 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;
- 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();
+ },
+ // private
+ reset : function(){
+ // overridden so that last data is reset..
- var _this = this;
+ if(this.multiple){
+ this.clearItem();
+ return;
+ }
- (function(){
- var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
- _this.list.setWidth(lw);
- }).defer(100);
+ this.setValue(this.originalValue);
+ //this.clearInvalid();
+ this.lastData = false;
+ if (this.view) {
+ this.view.clearSelections();
+ }
- this.list.on('mouseover', this.onViewOver, this);
- this.list.on('mousemove', this.onViewMove, this);
+ 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 || '');
- this.list.on('scroll', this.onViewScroll, this);
+ },
+ // 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(!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(item){
+ var index = this.view.indexOf(item);
+ this.select(index, false);
}
+ },
- this.view = new Roo.View(this.list, this.tpl, {
- singleSelect:true,
- tickable:true,
- parent:this,
- store: this.store,
- selectedClass: this.selectedClass
- });
+ // private
+ onViewClick : function(view, doFocus, el, e)
+ {
+ var index = this.view.getSelectedIndexes()[0];
- //this.view.wrapEl.setDisplayed(false);
- this.view.on('click', this.onViewClick, this);
+ 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(){
- this.store.on('beforeload', this.onBeforeLoad, this);
- this.store.on('load', this.onLoad, this);
- this.store.on('loadexception', this.onLoadException, this);
+ if(this.tickable && this.editable){
+ this.hasFocus = false;
+ this.restrictHeight();
+ return;
+ }
- 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();
- },
+ this.collapse();
+ },
- "enter" : function(e){
- if(this.fireEvent("specialkey", this, e)){
- this.onViewClick(false);
- }
-
- return true;
- },
+ /**
+ * Returns true if the dropdown list is expanded, else false.
+ */
+ isExpanded : function(){
+ return this.list.isVisible();
+ },
- "esc" : function(e){
- this.onTickableFooterButtonClick(e, false, false);
- },
+ /**
+ * 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;
+ },
- "tab" : function(e){
- this.fireEvent("specialkey", this, e);
-
- this.onTickableFooterButtonClick(e, false, false);
-
- return true;
- },
+ /**
+ * 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);
+ }
+ }
+ },
- scope : this,
+ // 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);
+ }
+ }
+ },
- doRelay : function(e, fn, key){
- if(this.scope.isExpanded()){
- return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
- }
- return true;
- },
+ // 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);
+ }
+ }
+ },
- forceKeyDown: true
- });
+ // 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(){
- 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);
- }
+ var v = this.getRawValue();
- if(this.editable !== false){
- this.tickableInputEl().on("keyup", this.onKeyUp, this);
+ if(this.tickable && this.editable){
+ v = this.tickableInputEl().getValue();
}
- this.indicator = this.indicatorEl();
-
- if(this.indicator){
- this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
- this.indicator.hide();
+ this.doQuery(v);
+ },
+
+ // private
+ doForce : function(){
+ if(this.inputEl().dom.value.length > 0){
+ this.inputEl().dom.value =
+ this.lastSelectionText === undefined ? '' : this.lastSelectionText;
+
}
-
},
- onDestroy : function(){
- if(this.view){
- this.view.setStore(null);
- this.view.el.removeAllListeners();
- this.view.el.remove();
- this.view.purgeListeners();
+ /**
+ * 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 = '';
}
- if(this.list){
- this.list.dom.innerHTML = '';
+ var qe = {
+ query: q,
+ forceAll: forceAll,
+ combo: this,
+ cancel:false
+ };
+ if(this.fireEvent('beforequery', qe)===false || qe.cancel){
+ return false;
}
+ q = qe.query;
- if(this.store){
- this.store.un('beforeload', this.onBeforeLoad, this);
- this.store.un('load', this.onLoad, this);
- this.store.un('loadexception', this.onLoadException, this);
+ 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();
+ }
}
- Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
+
+ this.loadNext = false;
},
-
+
// private
- fireKey : function(e){
- if(e.isNavKeyPress() && !this.list.isVisible()){
- this.fireEvent("specialkey", this, e);
+ getParams : function(q){
+ var p = {};
+ //p[this.queryParam] = q;
+
+ if(this.pageSize){
+ p.start = 0;
+ p.limit = this.pageSize;
}
+ return p;
},
- // 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'));
-// }
+ /**
+ * 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();
},
- /**
- * 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){
+ // 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;
}
- 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');
+
+ if(this.tickable){
+ this.onTickableFooterButtonClick(e, false, false);
}
+
+ this.collapse();
+
},
- // private
-
- onBeforeLoad : function(combo,opts){
- if(!this.hasFocus){
+ /**
+ * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
+ */
+ expand : function(){
+
+ if(this.isExpanded() || !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;
+ var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
+ this.list.setWidth(lw);
- if(!this.hasFocus){
- return;
- }
+ Roo.log('expand');
- if(typeof(this.loading) !== 'undefined' && this.loading !== null){
- this.loading.hide();
- }
+ this.list.show();
- if(this.store.getCount() > 0){
+ this.restrictHeight();
+
+ if(this.tickable){
- 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);
- }
+ this.tickItems = Roo.apply([], this.item);
+
+ this.okBtn.show();
+ this.cancelBtn.show();
+ this.trigger.hide();
+
+ if(this.editable){
+ this.tickableInputEl().focus();
}
- }else{
- this.onEmptyResults();
+
}
- //this.el.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
- onLoadException : function()
+ // Implements the default empty TriggerField.onTriggerClick function
+ onTriggerClick : function(e)
{
- this.hasQuery = false;
-
- if(typeof(this.loading) !== 'undefined' && this.loading !== null){
- this.loading.hide();
- }
+ Roo.log('trigger click');
- if(this.tickable && this.editable){
+ if(this.disabled || !this.triggerList){
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);
- //}
-
+ this.page = 0;
+ this.loadNext = false;
- },
- // 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(this.isExpanded()){
+ this.collapse();
+ if (!this.blockFocus) {
+ this.inputEl().focus();
+ }
- if(selStart != len){
- this.setRawValue(newValue);
- this.selectText(selStart, newValue.length);
+ }else {
+ this.hasFocus = true;
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
+ }
+ if (!this.blockFocus) {
+ this.inputEl().focus();
}
}
},
-
- // private
- onSelect : function(record, index){
+
+ onTickableTriggerClick : function(e)
+ {
+ if(this.disabled){
+ return;
+ }
- if(this.fireEvent('beforeselect', this, record, index) !== false){
+ this.page = 0;
+ this.loadNext = false;
+ this.hasFocus = true;
- this.setFromData(index > -1 ? record.data : false);
-
- this.collapse();
- this.fireEvent('select', this, record, index);
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
}
},
-
- /**
- * Returns the currently selected field value or empty string if no value is set.
- * @return {String} value The selected value
- */
- getValue : function()
+
+ onSearchFieldClick : function(e)
{
- if(Roo.isIOS && this.useNativeIOS){
- return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
+ if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
+ this.onTickableFooterButtonClick(e, false, false);
+ return;
}
- if(this.multiple){
- return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
+ if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
+ return;
}
- if(this.valueField){
- return typeof this.value != 'undefined' ? this.value : '';
- }else{
- return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
+ this.page = 0;
+ this.loadNext = false;
+ this.hasFocus = true;
+
+ if(this.triggerAction == 'all') {
+ this.doQuery(this.allQuery, true);
+ } else {
+ this.doQuery(this.getRawValue());
}
},
- getRawValue : function()
+ listKeyPress : function(e)
{
- if(Roo.isIOS && this.useNativeIOS){
- return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
+ //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;
+ }
+
}
- var v = this.inputEl().getValue();
+ 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);
- return v;
+ 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);
},
-
- /**
- * Clears any text/value currently set in the field
- */
- clearValue : function(){
+
+ onViewScroll : function(e, t){
- if(this.hiddenField){
- this.hiddenField.dom.value = '';
+ 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.value = '';
- this.setRawValue('');
- this.lastSelectionText = '';
- this.lastData = false;
- var close = this.closeTriggerEl();
+ this.hasQuery = true;
- if(close){
- close.hide();
+ 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.validate();
+ this.loading.show();
+
+ var _combo = this;
+
+ this.page++;
+ this.loadNext = true;
+ (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
+
+ return;
},
-
- /**
- * 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);
+
+ 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;
}
- if(this.multiple){
- this.syncValue();
+ 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;
}
- 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.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;
}
- this.lastSelectionText = text;
+
+ 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 = v;
+ this.hiddenField.dom.value = this.value;
}
- Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
- this.value = v;
- var close = this.closeTriggerEl();
+ this.store.fireEvent("datachanged", this.store);
- if(close){
- (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
+ 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();
+ }
},
- /**
- * @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;
+ inputEl: function ()
+ {
+ if(Roo.isIOS && this.useNativeIOS){
+ return this.el.select('select.roo-ios-select', true).first();
}
-
- 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(Roo.isTouch && this.mobileTouchView){
+ return this.el.select('input.form-control',true).first();
}
- if(this.valueField){
- vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
+ if(this.tickable){
+ return this.searchField;
}
- var close = this.closeTriggerEl();
+ return this.el.select('input.form-control',true).first();
+ },
+
+ onTickableFooterButtonClick : function(e, btn, el)
+ {
+ e.preventDefault();
- if(close){
- if(dv.length || vv * 1 > 0){
- close.show() ;
- this.blockFocus=true;
- } else {
- close.hide();
- }
- }
+ this.lastItem = Roo.apply([], this.item);
- if(this.hiddenField){
- this.hiddenField.dom.value = vv;
-
- this.lastSelectionText = dv;
- Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
- this.value = vv;
+ if(btn && btn.name == 'cancel'){
+ this.tickItems = Roo.apply([], this.item);
+ this.collapse();
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;
+ 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;
+ }
- },
- // private
- reset : function(){
- // overridden so that last data is reset..
+ var v = this.getRawValue();
if(this.multiple){
- this.clearItem();
- return;
+ v = this.getValue();
}
- this.setValue(this.originalValue);
- //this.clearInvalid();
- this.lastData = false;
- if (this.view) {
- this.view.clearSelections();
+ if(this.disabled || this.allowBlank || v.length){
+ this.markValid();
+ return true;
}
- this.validate();
+ this.markInvalid();
+ return false;
},
- // 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;
- });
+
+ tickableInputEl : function()
+ {
+ if(!this.tickable || !this.editable){
+ return this.inputEl();
}
- return record;
+
+ return this.inputEl().select('.roo-select2-search-field-input', true).first();
},
- getName: function()
+
+ getAutoCreateTouchView : function()
{
- // returns hidden if it's set..
- if (!this.rendered) {return ''};
- return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
+ var id = Roo.id();
- },
- // 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 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;
}
- var item = this.view.findItemFromChild(t);
- if(item){
- var index = this.view.indexOf(item);
- this.select(index, false);
+ if (this.size) {
+ input.cls += ' input-' + this.size;
}
- },
-
- // private
- onViewClick : function(view, doFocus, el, e)
- {
- var index = this.view.getSelectedIndexes()[0];
- var r = this.store.getAt(index);
+ if (this.disabled) {
+ input.disabled = true;
+ }
- if(this.tickable){
-
- if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
- return;
- }
+ var inputblock = {
+ cls : 'roo-combobox-wrap',
+ cn : [
+ input
+ ]
+ };
+
+ if(this.before){
+ inputblock.cls += ' input-group';
- var rm = false;
- var _this = this;
+ 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';
- 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;
- }
+ inputblock.cn.push({
+ tag: 'button',
+ html : 'x',
+ cls : 'roo-combo-removable-btn close'
});
+ }
+
+ if(this.hasFeedback && !this.allowBlank){
- if(rm){
- return;
- }
+ inputblock.cls += ' has-feedback';
- 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);
- }
+ inputblock.cn.push({
+ tag: 'span',
+ cls: 'glyphicon form-control-feedback'
+ });
- 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();
+ 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
+ });
}
- },
-
- // 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;
- }
+ var ibwrap = inputblock;
- 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;
- },
+ if(this.multiple){
+ ibwrap = {
+ tag: 'ul',
+ cls: 'roo-select2-choices',
+ cn:[
+ {
+ tag: 'li',
+ cls: 'roo-select2-search-field',
+ cn: [
- /**
- * 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);
- }
+ inputblock
+ ]
+ }
+ ]
+ };
+
+
}
- },
-
- // 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);
+
+ 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'
+ }
+ ]
+ }
+ ]
- // 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);
+
+ if(this.multiple){
+ combobox.cls += ' roo-select2-container-multi';
}
- },
-
- // private
- validateBlur : function(){
- return !this.list || !this.list.isVisible();
- },
-
- // private
- initQuery : function(){
- var v = this.getRawValue();
+ 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'
+ };
- if(this.tickable && this.editable){
- v = this.tickableInputEl().getValue();
- }
+ var align = this.labelAlign || this.parentLabelAlign();
- this.doQuery(v);
- },
+ if (align ==='left' && this.fieldLabel.length) {
- // private
- doForce : function(){
- if(this.inputEl().dom.value.length > 0){
- this.inputEl().dom.value =
- this.lastSelectionText === undefined ? '' : this.lastSelectionText;
-
- }
- },
+ cfg.cn = [
+ required,
+ {
+ tag: 'label',
+ cls : 'control-label col-form-label',
+ html : this.fieldLabel
- /**
- * 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)){
+ },
+ {
+ cls : 'roo-combobox-wrap ',
+ cn: [
+ combobox
+ ]
+ }
+ ];
- this.hasQuery = true;
+ var labelCfg = cfg.cn[1];
+ var contentCfg = cfg.cn[2];
- 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;
+
+ 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
+ ]
}
-
- 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();
+
+ ];
+
+ 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
+ ]
}
- }else{
- this.selectedIndex = -1;
- this.onLoad();
+ ];
+
+ if(this.indicatorpos == 'right'){
+ cfg.cn = [
+ {
+ tag: 'label',
+ cls : 'control-label',
+ html : this.fieldLabel,
+ cn : [
+ required
+ ]
+ },
+ {
+ cls : '',
+ cn: [
+ combobox
+ ]
+ }
+ ];
}
+ } else {
+ cfg.cn = combobox;
}
- this.loadNext = false;
+
+ var settings = this;
+
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
+
+ return cfg;
},
- // private
- getParams : function(q){
- var p = {};
- //p[this.queryParam] = q;
+ initTouchView : function()
+ {
+ this.renderTouchView();
- 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.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.list.hide();
- this.hasFocus = 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);
- if(this.tickable){
- this.okBtn.hide();
- this.cancelBtn.hide();
- this.trigger.show();
+ 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.editable){
- this.tickableInputEl().dom.value = '';
- this.tickableInputEl().blur();
- }
+ 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 : '';
- 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.el.dom.removeAttribute('name');
+ this.hiddenField.dom.setAttribute('name', this.hiddenName);
}
- 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.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.tickable){
- this.onTickableFooterButtonClick(e, false, false);
+ if(this.removable && !this.multiple){
+ var close = this.closeTriggerEl();
+ if(close){
+ close.setVisibilityMode(Roo.Element.DISPLAY).hide();
+ close.on('click', this.removeBtnClick, this, close);
+ }
}
-
- this.collapse();
+ /*
+ * 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;
+
},
-
- /**
- * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
- */
- expand : function(){
-
- if(this.isExpanded() || !this.hasFocus){
- return;
- }
+
+ renderTouchView : function()
+ {
+ this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
+ this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
- this.list.setWidth(lw);
+ this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
+ this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- Roo.log('expand');
+ this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
+ this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+ this.touchViewBodyEl.setStyle('overflow', 'auto');
- this.list.show();
+ this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
+ this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- this.restrictHeight();
+ this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
+ this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
- if(this.tickable){
-
- this.tickItems = Roo.apply([], this.item);
-
- this.okBtn.show();
- this.cancelBtn.show();
- this.trigger.hide();
-
- if(this.editable){
- this.tickableInputEl().focus();
- }
-
+ },
+
+ showTouchView : function()
+ {
+ if(this.disabled){
+ return;
}
- 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.touchViewHeaderEl.hide();
+
+ if(this.modalTitle.length){
+ this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
+ this.touchViewHeaderEl.show();
}
-
- this.fireEvent('expand', this);
- },
- // private
- // Implements the default empty TriggerField.onTriggerClick function
- onTriggerClick : function(e)
- {
- Roo.log('trigger click');
+ this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
+ this.touchViewEl.show();
+
+ this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
- if(this.disabled || !this.triggerList){
- return;
+ //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.page = 0;
- this.loadNext = false;
+ 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.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();
- }
+ 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();
+
},
- onTickableTriggerClick : function(e)
+ hideTouchView : function()
{
- if(this.disabled){
- return;
+ 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');
}
- this.page = 0;
- this.loadNext = false;
- this.hasFocus = true;
+ if(this._touchViewMask){
+ this._touchViewMask.removeClass('show');
+ Roo.get(document.body).removeClass("x-body-masked");
+ }
+ },
+
+ setTouchViewValue : function()
+ {
+ if(this.multiple){
+ this.clearItem();
- if(this.triggerAction == 'all') {
- this.doQuery(this.allQuery, true);
- } else {
- this.doQuery(this.getRawValue());
+ var _this = this;
+
+ Roo.each(this.tickItems, function(o){
+ this.addItem(o);
+ }, this);
}
+
+ this.hideTouchView();
},
- onSearchFieldClick : function(e)
+ doTouchViewQuery : function()
{
- if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
- this.onTickableFooterButtonClick(e, false, false);
- return;
+ var qe = {
+ query: '',
+ forceAll: true,
+ combo: this,
+ cancel:false
+ };
+
+ if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
+ return false;
}
- if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
+ if(!this.alwaysQuery || this.mode == 'local'){
+ this.onTouchViewLoad();
return;
}
- this.page = 0;
- this.loadNext = false;
- this.hasFocus = true;
-
- if(this.triggerAction == 'all') {
- this.doQuery(this.allQuery, true);
- } else {
- this.doQuery(this.getRawValue());
- }
+ this.store.load();
},
- listKeyPress : function(e)
+ onTouchViewBeforeLoad : function(combo,opts)
{
- //Roo.log('listkeypress');
- // scroll to first matching element based on key pres..
- if (e.isSpecialKey()) {
- return false;
+ return;
+ },
+
+ // private
+ onTouchViewLoad : function()
+ {
+ if(this.store.getCount() < 1){
+ this.onTouchViewEmptyResults();
+ return;
}
- 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.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);
}
- }
-
- this.store.each(function(v) {
- if (cselitem) {
- // start at existing selection.
- if (cselitem.id == v.id) {
- cselitem = false;
+ 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;
}
- return true;
}
+ 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)
+ {
- if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
- match = this.store.indexOf(v);
- return false;
+ // checkboxes...
+ row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
+ this.tickItems.push(d.data);
}
- return true;
+
+ row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
+
}, 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){
+ var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
- 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;
+ 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;
- this.hasQuery = true;
+ if(this.mobile_restrict_height && listHeight < bodyHeight){
+ this.touchViewBodyEl.setHeight(listHeight);
+ }
- this.loading = this.list.select('.loading', true).first();
+ var _this = this;
- 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(firstChecked && listHeight > bodyHeight){
+ (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
}
- this.loading.show();
-
- var _combo = this;
+ },
+
+ onTouchViewLoadException : function()
+ {
+ this.hideTouchView();
+ },
+
+ onTouchViewEmptyResults : function()
+ {
+ this.clearTouchView();
- this.page++;
- this.loadNext = true;
+ this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
- (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
+ this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
- return;
},
- addItem : function(o)
- {
- var dv = ''; // display value
+ clearTouchView : function()
+ {
+ this.touchViewListGroup.dom.innerHTML = '';
+ },
+
+ onTouchViewClick : function(e, el, o)
+ {
+ e.preventDefault();
- 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));
- }
+ var row = o.row;
+ var rowIndex = o.rowIndex;
- if(!dv.length){
- return;
- }
+ var r = this.store.getAt(rowIndex);
- 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'
- }
- ]
+ if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
- }, this.searchField);
+ 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 close = choice.select('a.roo-select2-search-choice-close', true).first();
+ var combobox = {
+ tag: 'select',
+ cls : 'roo-ios-select'
+ };
- close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
+ if (this.name) {
+ combobox.name = this.name;
+ }
+
+ if (this.disabled) {
+ combobox.disabled = true;
+ }
- this.item.push(o);
+ var settings = this;
- this.lastData = o;
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
- this.syncValue();
+ cfg.cn = combobox;
- this.inputEl().dom.value = '';
+ return cfg;
- this.validate();
},
- onRemoveItem : function(e, _self, o)
+ initIOSView : function()
{
- e.preventDefault();
-
- this.lastItem = Roo.apply([], this.item);
-
- var index = this.item.indexOf(o.data) * 1;
+ this.store.on('load', this.onIOSViewLoad, this);
- if( index < 0){
- Roo.log('not this item?!');
+ return;
+ },
+
+ onIOSViewLoad : function()
+ {
+ if(this.store.getCount() < 1){
return;
}
- this.item.splice(index, 1);
- o.item.remove();
+ this.clearIOSView();
- this.syncValue();
+ 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.fireEvent('remove', this, e);
+ 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.validate();
+ this.inputEl().on('change', function(){
+ this.fireEvent('select', this);
+ }, this);
},
- syncValue : function()
+ clearIOSView: function()
{
- if(!this.item.length){
- this.clearValue();
+ this.inputEl().dom.innerHTML = '';
+
+ this.ios_options = [];
+ },
+
+ setIOSValue: function(v)
+ {
+ this.value = v;
+
+ if(!this.ios_options){
return;
}
-
- var value = [];
- var _this = this;
- Roo.each(this.item, function(i){
- if(_this.valueField){
- value.push(i[_this.valueField]);
- 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">
+ */
- value.push(i);
+/**
+ * @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.value = value.join(',');
- if(this.hiddenField){
- this.hiddenField.dom.value = this.value;
- }
+
+ 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.store.fireEvent("datachanged", this.store);
+ this.footer.dataSource = this.store;
+ this.footer.container = fctr;
+ this.footer = Roo.factory(this.footer, Roo);
+ fctr.insertFirst(this.el);
- this.validate();
- },
+ // 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,
- 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();
- }
+ /**
+ * Returns the element this view is bound to.
+ * @return {Roo.Element}
+ */
+ getEl : function(){
+ return this.wrapEl;
},
- 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();
- }
+
+
+ /**
+ * Refreshes the view. - called by datachanged on the store. - do not call directly.
+ */
+ refresh : function(){
+ //Roo.log('refresh');
+ var t = this.tpl;
- if(this.tickable){
- return this.searchField;
- }
+ // 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> ?????
- 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();
+ 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;
}
-
- 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 el = this.el;
+ if (this.dataName) {
+ this.el.update(t.apply(this.store.meta)); //????
+ el = this.el.child('.roo-tpl-' + this.dataName);
}
- var v = this.getRawValue();
-
- if(this.multiple){
- v = this.getValue();
+ 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 d = Roo.apply({}, data);
+
+ if(this.tickable){
+ Roo.apply(d, {'roo-id' : Roo.id()});
+
+ var _this = this;
+
+ 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.disabled || this.allowBlank || v.length){
- this.markValid();
- return true;
- }
- this.markInvalid();
- return false;
+
+ el.update(html.join(""));
+ this.nodes = el.dom.childNodes;
+ this.updateIndexes(0);
},
- tickableInputEl : function()
+
+ /**
+ * 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)
{
- if(!this.tickable || !this.editable){
- return this.inputEl();
- }
-
- return this.inputEl().select('.roo-select2-search-field-input', true).first();
+ 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);
},
+
- getAutoCreateTouchView : function()
+// --------- FIXME
+ onAdd : function(ds, records, index)
{
- 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 : '',
- 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
- });
+ //Roo.log(['on Add', ds, records, index] );
+ this.clearSelections();
+ if(this.nodes.length == 0){
+ this.refresh();
+ return;
}
-
- if(this.removable && !this.multiple){
- inputblock.cls += ' roo-removable';
-
- inputblock.cn.push({
- tag: 'button',
- html : 'x',
- cls : 'roo-combo-removable-btn close'
- });
+ 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);
+ },
- if(this.hasFeedback && !this.allowBlank){
-
- inputblock.cls += ' has-feedback';
-
- inputblock.cn.push({
- tag: 'span',
- cls: 'glyphicon form-control-feedback'
- });
-
- }
+ onRemove : function(ds, record, index){
+ // Roo.log('onRemove');
+ this.clearSelections();
+ var el = this.dataName ?
+ this.el.child('.roo-tpl-' + this.dataName) :
+ this.el;
- if (this.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
- });
- }
+ el.dom.removeChild(this.nodes[index]);
+ this.updateIndexes(index);
+ },
-
- var ibwrap = inputblock;
-
- if(this.multiple){
- ibwrap = {
- tag: 'ul',
- cls: 'roo-select2-choices',
- cn:[
- {
- tag: 'li',
- cls: 'roo-select2-search-field',
- cn: [
+ /**
+ * Refresh an individual node.
+ * @param {Number} index
+ */
+ refreshNode : function(index){
+ this.onUpdate(this.store, this.store.getAt(index));
+ },
- inputblock
- ]
- }
- ]
- };
-
-
+ 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;
}
-
- 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 = {
- 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 : [
- caret,
- {
- tag: 'span',
- cls: 'combobox-clear',
- cn : [
- {
- tag : 'i',
- cls: 'icon-remove'
- }
- ]
- }
- ]
+ },
- })
+ /**
+ * 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(this.multiple){
- combobox.cls += ' roo-select2-container-multi';
+ 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);
}
- 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
-
- },
- {
- 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
- },
- {
- tag : 'i',
- cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
- tooltip : 'This field is required'
- }
- ]
- },
- {
- 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) {
- 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
+ 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();
+ },
+
- },
- {
- 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
- ]
- }
- ];
+ /**
+ * 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();
- 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);
- }
- }
+ Roo.each(more, function(m){
+ var cfg = {
+ cls : 'fc-event-hori fc-event-draggable',
+ html : m.title
+ };
+ var cg = ctr.createChild(cfg);
+
+ cg.on('click', _this.onEventClick, _this, m);
+ });
+ this.calpopover.show(el);
- },
- /**
- * 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);
- 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];
+
+ onLoad: function ()
+ {
+ this.calevents = [];
+ var cal = this;
+
+ if(this.store.getCount() > 0){
+ this.store.data.each(function(d){
+ cal.addItem({
+ id : d.data.id,
+ start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
+ end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
+ time : d.data.start_time,
+ title : d.data.title,
+ description : d.data.description,
+ venue : d.data.venue
+ });
+ });
}
- 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,
+
+ placement : 'right',
+ trigger : 'hover', // hover
+ modal : false,
+ delay : 0,
+
+ 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;
+
+ },
+ getPopoverHeader : function()
+ {
+ this.title = true; // flag not to hide it..
+ this.headerEl.addClass('p-0');
+ return this.headerEl
+ },
- loadMask : false,
- header : false,
-
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 || ''
+ }
+ ]
+
+ }
+ ]
+ };
+ return cfg;
+ },
+ /**
+ * @param {string} the title
+ */
+ setTitle: function(str)
+ {
+ this.title = str;
+ if (this.el) {
+ this.headerEl.dom.innerHTML = str;
+ }
- 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'
- });
- };
+ },
+ /**
+ * @param {string} the body content
+ */
+ setContent: function(str)
+ {
+ this.html = str;
+ if (this.contentEl) {
+ this.contentEl.dom.innerHTML = str;
+ }
- var 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);
- 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' )
- */
+
+
+ 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.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;
- ]
- }
+ for(var i =0;i < items.length;i++) {
+ nitems.push(this.addxtype(Roo.apply({}, items[i])));
+ }
+ }
- ]
- }
- ]
- };
+ this.items = nitems;
+
+ 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);
}
+
+ 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();
+
- header = this.header;
+ 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) {
-
- var cal_heads = function() {
- var ret = [];
- // fixme - handle this.
-
- for (var i =0; i < Date.dayNames.length; i++) {
- var d = Date.dayNames[i];
- ret.push({
- tag: 'th',
- cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
- html : d.substring(0,3)
- });
-
+ 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);
}
- 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;
- }
- ]
- }
-
-
- ]
- }
- ]
-
+ }, 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();
}
- };
- var cal_rows = function() {
-
- var ret = [];
- for (var r = 0; r < 6; r++) {
- var row= {
- tag : 'tr',
- cls : 'fc-week',
- cn : []
- };
-
- for (var i =0; i < Date.dayNames.length; i++) {
- var d = Date.dayNames[i];
- row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
-
- }
- row.cn[0].cls+=' fc-first';
- row.cn[0].cn[0].style = 'min-height:90px';
- row.cn[6].cls+=' fc-last';
- ret.push(row);
-
+ }, 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();
}
- ret[0].cls += ' fc-first';
- ret[4].cls += ' fc-prev-last';
- ret[5].cls += ' fc-last';
- return ret;
-
- };
-
- var cal_table = {
- tag: 'table',
- cls: 'fc-border-separate',
- style : 'width:100%',
- cellspacing : 0,
- cn : [
- {
- tag: 'thead',
- cn : [
- {
- tag: 'tr',
- cls : 'fc-first fc-last',
- cn : cal_heads()
- }
- ]
- },
- {
- tag: 'tbody',
- cn : cal_rows()
- }
-
- ]
- };
-
- var cfg = {
- cls : 'fc fc-ltr',
- cn : [
- header,
- {
- cls : 'fc-content',
- style : "position: relative;",
- cn : [
- {
- cls : 'fc-view fc-view-month fc-grid',
- style : 'position: relative',
- unselectable : 'on',
- cn : [
- {
- cls : 'fc-event-container',
- style : 'position:absolute;z-index:8;top:0;left:0;'
- },
- cal_table
- ]
- }
- ]
+ }, 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;
+ this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
- if (this.el.hasClass('fade')) {
- // fade it?
+ 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);
- if(this.striped){
- cfg.cls += ' progress-striped';
+ this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
+
+ 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)
{
- this.aria_valuenow = aria_valuenow;
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ keyup: function(e)
+ {
+ Roo.bootstrap.form.DateField.superclass.keyup.call(this);
+ this.update();
+ },
+
+ setValue: function(v)
+ {
+ 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;
+ return date >= this.startDate && date <= this.endDate;
+ },
+
+
+ remove: function()
+ {
+ this.picker().remove();
+ },
+
+ validateValue : function(value)
+ {
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
}
- if (typeof(pan) == 'number') {
- pan = this.tabs[pan];
+ if(value.length < 1) {
+ if(this.allowBlank){
+ return true;
+ }
+ return 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;
+ }
}
-
- 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') {
-
- 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.dom.offsetWidth; // find the offset with - causing a reflow?
- cur.el.addClass(lr); // or right
- pan.el.addClass(lr);
-
- var _this = this;
- cur.el.on('transitionend', function() {
- Roo.log("trans end?");
-
- pan.el.removeClass([lr,dir]);
- pan.setActive(true);
-
- cur.el.removeClass([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;
- }
- 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);
-
- if(this.autoslide && this.slideFn){
- clearInterval(this.slideFn);
- this.slideFn = window.setInterval(function() {
- _this.showPanelNext();
- }, this.timer);
- }
-
- }).createDelegate(this, [i, bullet], true));
-
-
+ clsName: 'days',
+ navFnc: 'Month',
+ navStep: 1
},
-
- setActiveBullet : function(i)
{
- if(Roo.isTouch){
- return;
- }
-
- Roo.each(this.el.select('.bullet', true).elements, function(el){
- el.removeClass('selected');
- });
+ clsName: 'months',
+ navFnc: 'FullYear',
+ navStep: 1
+ },
+ {
+ clsName: 'years',
+ navFnc: 'FullYear',
+ navStep: 10
+ }]
+});
- var bullet = this.el.select('.bullet-' + i, true).first();
-
- if(!bullet){
- return;
+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
+ ]
+ }
+ ]
}
-
- bullet.addClass('selected');
+ ]
}
-
-
-
});
-
-
-Roo.apply(Roo.bootstrap.TabGroup, {
-
- 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
- * 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
+ * TimeField
*
*/
/**
- * @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..
+ * @class Roo.bootstrap.form.TimeField
+ * @extends Roo.bootstrap.form.Input
+ * Bootstrap DateField class
*
*
* @constructor
- * Create a new TabPanel
+ * Create a new TimeField
* @param {Object} config The config object
*/
-Roo.bootstrap.TabPanel = function(config){
- Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
+Roo.bootstrap.form.TimeField = function(config){
+ Roo.bootstrap.form.TimeField.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();
-
+ /**
+ * @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.TabPanel, Roo.bootstrap.Component, {
+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);
+
+
+ },
+ onRender: function(ct, position)
+ {
+
+ Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
+
+ this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
+
+ this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.pop = this.picker().select('>.datepicker-time',true).first();
+ this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
+
+ this.picker().on('mousedown', this.onMousedown, this);
+ this.picker().on('click', this.onClick, this);
+
+ this.picker().addClass('datepicker-dropdown');
+
+ 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;
+ }
+
+ e.preventDefault();
+
+ switch(e.keyCode){
+ case 27: // escape
+ this.hide();
+ break;
+ case 37: // left
+ case 39: // right
+ this.onTogglePeriod();
+ break;
+ case 38: // up
+ this.onIncrementMinutes();
+ break;
+ case 40: // down
+ this.onDecrementMinutes();
+ break;
+ case 13: // enter
+ case 9: // tab
+ this.setTime();
+ break;
+ }
+ },
+
+ onClick: function(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ },
+
+ picker : function()
+ {
+ return this.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'
+ }
+ ]
+ });
+
+ },
- 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(){
- var cfg = {
- tag: 'div',
- // item is needed for carousel - not sure if it has any effect otherwise
- cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
- html: this.html || ''
- };
+ fill: function()
+ {
+ var hours = this.time.getHours();
+ var minutes = this.time.getMinutes();
+ var period = 'AM';
- if(this.active){
- cfg.cls += ' active';
+ if(hours > 11){
+ period = 'PM';
}
- if(this.tabId){
- cfg.tabId = this.tabId;
+ if(hours == 0){
+ hours = 12;
}
- return cfg;
+ 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;
+
},
- 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,
+Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
- endDate: Infinity,
-
- daysOfWeekDisabled: [],
-
- _events: [],
-
- 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.hasInput = this.component && this.inputEL().length;
-// this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
+ this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
this.picker().addClass('datepicker-dropdown');
- this.startViewMode = this.viewMode;
+ Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
+ v.setStyle('width', '189px');
+ });
- 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');
- });
- }
+ this.fillMonths();
- 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.update();
- this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
+ if(this.isInline) {
+ this.show();
+ }
- this.setStartDate(this.startDate);
- this.setEndDate(this.endDate);
+ },
+
+ setValue: function(v, suppressEvent)
+ {
+ var o = this.getValue();
- this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
+ Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
- this.fillDow();
- this.fillMonths();
this.update();
- this.showMode();
-
- if(this.isInline) {
- this.showPopup();
+
+ if(suppressEvent !== true){
+ this.fireEvent('select', this, o, v);
}
+
},
- picker : function()
+ 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();
-
- this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
+ if(this.isInline) {
+ return;
+ }
-// this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
+ this.picker().removeClass(['bottom', 'top']);
-// 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),
+ 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;
+ }
- day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
-
- prevMonth.setUTCDate(day);
+ this.picker().addClass('bottom');
- prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
+ this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
+ },
+
+ onFocus : function()
+ {
+ Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
+ this.show();
+ },
+
+ onBlur : function()
+ {
+ Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
- var nextMonth = new Date(prevMonth);
+ var d = this.inputEl().getValue();
- nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
+ this.setValue(d);
+
+ this.hide();
+ },
+
+ show : function()
+ {
+ this.picker().show();
+ this.picker().select('>.datepicker-months', true).first().show();
+ this.update();
+ this.place();
- nextMonth = nextMonth.valueOf();
+ this.fireEvent('show', this, this.date);
+ },
+
+ hide : function()
+ {
+ if(this.isInline) {
+ return;
+ }
+ this.picker().hide();
+ this.fireEvent('hide', this, this.date);
- var fillMonths = false;
+ },
+
+ 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;
+ }
- this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
+ var dir;
- while(prevMonth.valueOf() <= nextMonth) {
- var clsName = '';
-
- if (prevMonth.getUTCDay() === this.weekStart) {
- if(fillMonths){
- this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
+ 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;
}
-
- 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.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'
}
-
- 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';
+ ]
+ }
+ ]
+ },
+
+ 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;
+
+ var id = Roo.id();
+
+ 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 || ''
- fillMonths.cn.push({
- tag: 'td',
- cls: 'day ' + clsName,
- html: prevMonth.getDate()
- });
+ };
+
+ if(this.inputType != 'radio'){
+ var hidden = {
+ tag: 'input',
+ type : 'hidden',
+ cls : 'roo-hidden-value',
+ value : this.checked ? this.inputValue : this.valueOff
+ };
+ }
+
- prevMonth.setDate(prevMonth.getDate()+1);
+ if (this.weight) { // Validity check?
+ cfg.cls += " " + this.inputType + "-" + this.weight;
}
-
- 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;
+ if (this.disabled) {
+ input.disabled=true;
+ }
- Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
- v.removeClass('active');
+ if(this.checked){
+ input.checked = this.checked;
+ }
+
+ if (this.name) {
- if(currentYear === year && k === currentMonth){
- v.addClass('active');
- }
+ input.name = this.name;
- if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
- v.addClass('disabled');
+ if(this.inputType != 'radio'){
+ hidden.name = this.name;
+ input.name = '_hidden_' + this.name;
}
-
- });
-
+ }
- year = parseInt(year/10, 10) * 10;
+ if (this.size) {
+ input.cls += ' input-' + this.size;
+ }
- this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
+ var settings=this;
- this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
+ ['xs','sm','md','lg'].map(function(size){
+ if (settings[size]) {
+ cfg.cls += ' col-' + size + '-' + settings[size];
+ }
+ });
- 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
- });
+ 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
+ });
+ }
- year += 1;
- }
- },
-
- showMode: function(dir)
- {
- if (dir) {
- this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
}
+ var boxLabelCfg = 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.DateField.modes[this.viewMode].clsName, true).first().show();
- },
-
- place: function()
- {
- if(this.isInline) {
- return;
+ 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;
+ }
+
}
- 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!
- *
- */
+ 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
+ ]
+ }
+ ];
- this.picker().addClass('top');
- this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
+ if (boxLabelCfg) {
+ cfg.cn[1].cn.push(boxLabelCfg);
+ }
- return;
- }
-
- this.picker().addClass('bottom');
-
- this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
- },
-
- parseDate : function(value)
- {
- 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("|");
+ if(this.labelWidth > 12){
+ cfg.cn[0].style = "width: " + this.labelWidth + 'px';
}
- for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
- v = Date.parseDate(value, this.altFormatsArray[i]);
+
+ 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
+
+ ];
+ if (boxLabelCfg) {
+ cfg.cn.push(boxLabelCfg);
+ }
+
+ } else {
+
+// Roo.log(" no label && no align");
+ cfg.cn = [ inputblock ] ;
+ if (boxLabelCfg) {
+ cfg.cn.push(boxLabelCfg);
+ }
+
+
}
- return v;
- },
-
- formatDate : function(date, fmt)
- {
- return (!date || !(date instanceof Date)) ?
- date : date.dateFormat(fmt || this.format);
- },
-
- onFocus : function()
- {
- Roo.bootstrap.DateField.superclass.onFocus.call(this);
- this.showPopup();
- },
-
- onBlur : function()
- {
- Roo.bootstrap.DateField.superclass.onBlur.call(this);
-
- var d = this.inputEl().getValue();
- 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;
+ if(this.inputType != 'radio'){
+ cfg.cn.push(hidden);
}
- this.picker().hide();
- this.viewMode = this.startViewMode;
- this.showMode();
- this.fireEvent('hidepopup', this, this.date);
+ return cfg;
},
- onMousedown: function(e)
+ /**
+ * return the real input element.
+ */
+ inputEl: function ()
{
- e.stopPropagation();
- e.preventDefault();
+ return this.el.select('input.roo-' + this.inputType,true).first();
},
-
- keyup: function(e)
+ hiddenEl: function ()
{
- Roo.bootstrap.DateField.superclass.keyup.call(this);
- this.update();
+ return this.el.select('input.roo-hidden-value',true).first();
},
-
- setValue: function(v)
+
+ labelEl: 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);
- }
+ return this.el.select('label.control-label',true).first();
},
+ /* depricated... */
- getValue: function()
+ label: function()
{
- return this.formatDate(this.date);
+ return this.labelEl();
},
- fireKey: function(e)
+ boxLabelEl: function()
{
- if (!this.picker().isVisible()){
- if (e.keyCode == 27) { // allow escape to hide and re-show picker
- this.showPopup();
- }
- return;
- }
-
- var dateChanged = false,
- dir, day, month,
- newDate, newViewDate;
-
- 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();
-
- }
+ return this.el.select('label.box-label',true).first();
},
-
- onClick: function(e)
+ initEvents : function()
{
- e.stopPropagation();
- e.preventDefault();
+// Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
- var target = e.getTarget();
+ this.inputEl().on('click', this.onClick, this);
- if(target.nodeName.toLowerCase() === 'i'){
- target = Roo.get(target).dom.parentNode;
+ if (this.boxLabel) {
+ this.el.select('label.box-label',true).first().on('click', this.onClick, this);
}
- var nodeName = target.nodeName;
- var className = target.className;
- var html = target.innerHTML;
- //Roo.log(nodeName);
+ this.startValue = this.getValue();
- 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;
- }
- },
-
- setStartDate: function(startDate)
- {
- this.startDate = startDate || -Infinity;
- if (this.startDate !== -Infinity) {
- this.startDate = this.parseDate(this.startDate);
- }
- this.update();
- this.updateNavArrows();
- },
-
- setEndDate: function(endDate)
- {
- this.endDate = endDate || Infinity;
- if (this.endDate !== Infinity) {
- this.endDate = this.parseDate(this.endDate);
+ if(this.groupId){
+ Roo.bootstrap.form.CheckBox.register(this);
}
- this.update();
- this.updateNavArrows();
},
- setDaysOfWeekDisabled: function(daysOfWeekDisabled)
- {
- this.daysOfWeekDisabled = daysOfWeekDisabled || [];
- if (typeof(this.daysOfWeekDisabled) !== 'object') {
- this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
+ onClick : function(e)
+ {
+ if(this.fireEvent('click', this, e) !== false){
+ this.setChecked(!this.checked);
}
- this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
- return parseInt(d, 10);
- });
- this.update();
- this.updateNavArrows();
+
},
- updateNavArrows: function()
+ setChecked : function(state,suppressEvent)
{
- if(this.singleMode){
+ 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;
}
- var d = new Date(this.viewDate),
- year = d.getUTCFullYear(),
- month = d.getUTCMonth();
+ this.checked = state;
- Roo.each(this.picker().select('.prev', true).elements, function(v){
- v.show();
- switch (this.viewMode) {
- case 0:
-
- if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
- v.hide();
- }
- break;
- case 1:
- case 2:
- if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
- v.hide();
- }
- break;
- }
- });
+ this.inputEl().dom.checked = state;
- Roo.each(this.picker().select('.next', true).elements, function(v){
- v.show();
- switch (this.viewMode) {
- case 0:
-
- if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
- v.hide();
- }
- break;
- case 1:
- case 2:
- if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
- v.hide();
- }
- break;
- }
- })
- },
-
- moveMonth: function(date, dir)
- {
- if (!dir) {
- return date;
- }
- var new_date = new Date(date.valueOf()),
- day = new_date.getUTCDate(),
- month = new_date.getUTCMonth(),
- mag = Math.abs(dir),
- new_month, test;
- dir = dir > 0 ? 1 : -1;
- if (mag == 1){
- test = dir == -1
- // If going back one month, make sure month is not current month
- // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
- ? function(){
- return new_date.getUTCMonth() == month;
- }
- // If going forward one month, make sure month is as expected
- // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
- : function(){
- return new_date.getUTCMonth() != new_month;
- };
- new_month = month + dir;
- new_date.setUTCMonth(new_month);
- // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
- if (new_month < 0 || new_month > 11) {
- new_month = (new_month + 12) % 12;
- }
- } else {
- // For magnitudes >1, move one month at a time...
- for (var i=0; i<mag; i++) {
- // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
- new_date = this.moveMonth(new_date, dir);
- }
- // ...then reset the day, keeping it in the new month
- new_month = new_date.getUTCMonth();
- new_date.setUTCDate(day);
- test = function(){
- return new_month != new_date.getUTCMonth();
- };
+
+ this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, state);
}
- // 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);
+
+ this.validate();
+ },
+
+ getValue : function()
+ {
+ if(this.inputType == 'radio'){
+ return this.getGroupValue();
}
- return new_date;
+
+ return this.hiddenEl().dom.value;
+
},
-
- moveYear: function(date, dir)
+
+ getGroupValue : function()
{
- return this.moveMonth(date, dir*12);
+ 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;
},
-
- dateWithinRange: function(date)
+
+ setValue : function(v,suppressEvent)
{
- return date >= this.startDate && date <= this.endDate;
+ if(this.inputType == 'radio'){
+ this.setGroupValue(v, suppressEvent);
+ return;
+ }
+
+ this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
+
+ this.validate();
},
-
- remove: function()
+ setGroupValue : function(v, suppressEvent)
{
- this.picker().remove();
+ 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;
+ }
+ });
+
+ if(suppressEvent !== true){
+ this.fireEvent('check', this, true);
+ }
+
+ this.validate();
+
+ return;
},
- validateValue : function(value)
+ validate : function()
{
if(this.getVisibilityEl().hasClass('hidden')){
return true;
}
- if(value.length < 1) {
- if(this.allowBlank){
- return true;
- }
- return false;
+ if(
+ this.disabled ||
+ (this.inputType == 'radio' && this.validateRadio()) ||
+ (this.inputType == 'checkbox' && this.validateCheckbox())
+ ){
+ this.markValid();
+ return true;
}
- 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;
- }
+ this.markInvalid();
+ return false;
+ },
+
+ validateRadio : function()
+ {
+ if(this.getVisibilityEl().hasClass('hidden')){
+ return true;
}
- if(typeof this.validator == "function"){
- var msg = this.validator(value);
- if(msg !== true){
- return false;
- }
+
+ if(this.allowBlank){
+ return true;
}
- if(this.regex && !this.regex.test(value)){
+ 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;
+ });
+
+ return valid;
+ },
+
+ validateCheckbox : function()
+ {
+ if(!this.groupId){
+ return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
+ //return (this.getValue() == this.inputValue) ? true : false;
}
- if(typeof(this.parseDate(value)) == 'undefined'){
+ var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
+
+ if(!group){
return false;
}
- if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
- return false;
- }
+ var r = false;
- if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
- return false;
- }
+ for(var i in group){
+ if(group[i].el.isVisible(true)){
+ r = false;
+ break;
+ }
+
+ r = true;
+ }
+ for(var i in group){
+ if(r){
+ break;
+ }
+
+ r = (group[i].getValue() == group[i].inputValue) ? true : false;
+ }
- return true;
+ return r;
},
- reset : function()
+ /**
+ * Mark this field as valid
+ */
+ markValid : function()
{
- this.date = this.viewDate = '';
+ var _this = this;
- Roo.bootstrap.DateField.superclass.setValue.call(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();
+ }
-Roo.apply(Roo.bootstrap.DateField, {
-
- head : {
- tag: 'thead',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'th',
- cls: 'prev',
- html: '<i class="fa fa-arrow-left"/>'
- },
- {
- tag: 'th',
- cls: 'switch',
- colspan: '5'
- },
- {
- tag: 'th',
- cls: 'next',
- html: '<i class="fa fa-arrow-right"/>'
- }
+ 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;
+ }
- ]
+ 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;
}
- ]
- },
-
- content : {
- tag: 'tbody',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- colspan: '7'
+
+ 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');
}
- ]
}
- ]
},
- footer : {
- tag: 'tfoot',
- cn: [
- {
- tag: 'tr',
- cn: [
- {
- tag: 'th',
- colspan: '7',
- cls: 'today'
+ /**
+ * Mark this field as invalid
+ * @param {String} msg The validation message
+ */
+ markInvalid : function(msg)
+ {
+ if(this.allowBlank){
+ return;
+ }
+
+ 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(label){
+ label.markInvalid();
+ }
+
+ 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.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;
}
- ]
+
+ 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');
+ }
+ }
+
},
- 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"
+ 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']);
}
},
- modes: [
+ disable : function()
{
- clsName: 'days',
- navFnc: 'Month',
- navStep: 1
+ if(this.inputType != 'radio'){
+ Roo.bootstrap.form.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;
+ });
+ }
+
+ this.disabled = true;
+ this.fireEvent("disable", this);
+ return this;
},
+
+ enable : function()
{
- clsName: 'months',
- navFnc: 'FullYear',
- navStep: 1
+ if(this.inputType != 'radio'){
+ Roo.bootstrap.form.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;
+ });
+ }
+
+ this.disabled = false;
+ this.fireEvent("enable", this);
+ return this;
},
+
+ setBoxLabel : function(v)
{
- clsName: 'years',
- navFnc: 'FullYear',
- navStep: 10
- }]
+ 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.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,
+Roo.apply(Roo.bootstrap.form.CheckBox, {
+
+ groups: {},
+
+ /**
+ * 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] = {};
+ }
+
+ 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.form.CheckBox} the CheckBox group
+ */
+ get: function(groupId) {
+ if (typeof(this.groups[groupId]) == 'undefined') {
+ return false;
+ }
+
+ return this.groups[groupId] ;
+ }
+
+
+});
+/*
+ * - LGPL
+ *
+ * RadioItem
+ *
+ */
+
+/**
+ * @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: '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
- ]
- }
+ 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();
+ 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;
+ },
+ // private
+ CharacterSetChecks: function (type)
+ {
+ this.type = type;
+ this.fResult = false;
+ },
+ // private
+ isctype: function (character, type)
+ {
+ switch (type) {
+ case this.kCapitalLetter:
+ if (character >= 'A' && character <= 'Z') {
+ return true;
+ }
break;
- case 38: // up
- this.onIncrementMinutes();
+
+ case this.kSmallLetter:
+ if (character >= 'a' && character <= 'z') {
+ return true;
+ }
break;
- case 40: // down
- this.onDecrementMinutes();
+
+ case this.kDigit:
+ if (character >= '0' && character <= '9') {
+ return true;
+ }
break;
- case 13: // enter
- case 9: // tab
- this.setTime();
+
+ case this.kPunctuation:
+ if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
+ return true;
+ }
break;
+
+ default:
+ return false;
}
+
},
-
- onClick: function(e) {
- e.stopPropagation();
- e.preventDefault();
- },
-
- picker : function()
+ // private
+ IsLongEnough: function (pwd, size)
{
- return this.el.select('.datepicker', true).first();
+ return !(pwd == null || isNaN(size) || pwd.length < size);
},
-
- fillTime: function()
- {
- var time = this.pop.select('tbody', true).first();
-
- time.dom.innerHTML = '';
-
- time.createChild({
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'hours-up glyphicon glyphicon-chevron-up'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- },
- {
- tag: 'td',
- cn: [
- {
- tag: 'a',
- href: '#',
- cls: 'btn',
- cn: [
- {
- tag: 'span',
- cls: 'minutes-up glyphicon glyphicon-chevron-up'
- }
- ]
- }
- ]
- },
- {
- tag: 'td',
- cls: 'separator'
- }
- ]
- });
-
- time.createChild({
- tag: 'tr',
- cn: [
- {
- tag: 'td',
- cn: [
- {
- tag: 'span',
- cls: 'timepicker-hour',
- html: '00'
- }
- ]
- },
- {
- 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 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'
- }
- ]
- });
+ // private
+ SpansEnoughCharacterSets: function (word, nb)
+ {
+ 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)
+ );
+ 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;
+ }
+ }
+
+ if (nCharSets < nb) {
+ return false;
+ }
+ return true;
},
-
- update: function()
+ // private
+ ClientSideStrongPassword: function (pwd)
{
-
- this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
-
- this.fill();
+ 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 = {
- fill: function()
+ 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)
{
- var hours = this.time.getHours();
- var minutes = this.time.getMinutes();
- var period = 'AM';
+ 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 e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
+ case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
+ 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(hours > 11){
- period = 'PM';
+ }
+};
+
+/**
+ * @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;
}
- if(hours == 0){
- hours = 12;
+ 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 (a.name.toLowerCase().substr(0,2)=='on') {
+ node.removeAttribute(a.name);
+ continue;
+ }
+
+
+ if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
+ node.removeAttribute(a.name);
+ continue;
+ }
+ if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
+ this.cleanAttr(node,a.name,a.value); // fixme..
+ continue;
+ }
+ 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
+ },
+ cleanAttr: function(node, n,v)
+ {
- if(hours > 12){
- hours = hours - 12;
+ if (v.match(/^\./) || v.match(/^\//)) {
+ return;
}
-
- if(hours < 10){
- hours = '0' + hours;
+ if (v.match(/^(http|https):\/\//)
+ || v.match(/^mailto:/)
+ || v.match(/^ftp:/)
+ || v.match(/^data:/)
+ ) {
+ return;
}
-
- if(minutes < 10){
- minutes = '0' + minutes;
+ if (v.match(/^#/)) {
+ return;
}
-
- this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
- this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
- this.pop.select('button', true).first().dom.innerHTML = period;
-
- },
-
- place: function()
- {
- this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
-
- var cls = ['bottom'];
-
- if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
- cls.pop();
- cls.push('top');
+ if (v.match(/^\{/)) { // allow template editing.
+ return;
}
+// Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
+ node.removeAttribute(n);
- cls.push('right');
-
- if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
- cls.pop();
- cls.push('left');
+ },
+ cleanStyle : function(node, n,v)
+ {
+ if (v.match(/expression/)) { //XSS?? should we even bother..
+ node.removeAttribute(n);
+ return;
}
- this.picker().addClass(cls.join('-'));
-
- var _this = this;
+ var parts = v.split(/;/);
+ var clean = [];
- Roo.each(cls, function(c){
- if(c == 'bottom'){
- _this.picker().setTop(_this.inputEl().getHeight());
- return;
- }
- if(c == 'top'){
- _this.picker().setTop(0 - _this.picker().getHeight());
- return;
+ 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(c == 'left'){
- _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
- return;
+ if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
+ return true;
}
- if(c == 'right'){
- _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
- return;
+ //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;
}
- });
-
- },
-
- onFocus : function()
- {
- Roo.bootstrap.TimeField.superclass.onFocus.call(this);
- this.show();
- },
-
- onBlur : function()
- {
- Roo.bootstrap.TimeField.superclass.onBlur.call(this);
- this.hide();
- },
-
- show : function()
- {
- this.picker().show();
- this.pop.show();
- this.update();
- this.place();
-
- this.fireEvent('show', this, this.date);
- },
-
- hide : function()
- {
- this.picker().hide();
- this.pop.hide();
-
- this.fireEvent('hide', this, this.date);
- },
-
- setTime : function()
- {
- this.hide();
- this.setValue(this.time.format(this.format));
+
+
+ clean.push(p);
+ return true;
+ },this);
+ if (clean.length) {
+ node.setAttribute(n, clean.join(';'));
+ } else {
+ node.removeAttribute(n);
+ }
- this.fireEvent('select', this, this.date);
+ }
- },
-
- onMousedown: function(e){
- e.stopPropagation();
- e.preventDefault();
- },
-
- onIncrementHours: function()
- {
- Roo.log('onIncrementHours');
- this.time = this.time.add(Date.HOUR, 1);
- this.update();
- },
-
- onDecrementHours: function()
- {
- Roo.log('onDecrementHours');
- this.time = this.time.add(Date.HOUR, -1);
- this.update();
- },
-
- onIncrementMinutes: function()
- {
- Roo.log('onIncrementMinutes');
- this.time = this.time.add(Date.MINUTE, 1);
- this.update();
- },
-
- onDecrementMinutes: function()
- {
- Roo.log('onDecrementMinutes');
- this.time = this.time.add(Date.MINUTE, -1);
- this.update();
- },
- onTogglePeriod: function()
+});/**
+ * @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
+
+
}
-});
-
-
-
- /*
- * - LGPL
- *
- * MonthField
- *
- */
-
-/**
- * @class Roo.bootstrap.MonthField
- * @extends Roo.bootstrap.Input
- * Bootstrap MonthField class
- *
- * @cfg {String} language default en
- *
+});/**
+ * @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
- * Create a new MonthField
- * @param {Object} config The config object
+ * Run a new Paragraph 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.FilterParagraph = function(cfg)
+{
+ // no need to apply config.
+ this.walk(cfg.node);
+}
-Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
+Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
+{
- onRender: function(ct, position)
+
+ tag : 'P',
+
+
+ replaceTag : function(node)
{
- 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');
- });
-
- this.fillMonths();
-
- this.update();
-
- if(this.isInline) {
- this.show();
+ 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>
- },
-
- setValue: function(v, suppressEvent)
- {
- var o = this.getValue();
-
- Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
+ // double BR.
+ node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
+ node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
+ node.parentNode.removeChild(node);
- this.update();
+ return false;
- if(suppressEvent !== true){
- this.fireEvent('select', this, o, v);
- }
-
- },
+ }
- getValue: function()
+});/**
+ * @class Roo.htmleditor.FilterSpan
+ * filter span's with no attributes out..
+ * @constructor
+ * Run a new Span Filter
+ * @param {Object} config Configuration options
+ */
+
+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)
{
- return this.value;
- },
+ if (node.attributes && node.attributes.length > 0) {
+ return true; // walk if there are any.
+ }
+ Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
+ return false;
+
+ }
- onClick: function(e)
- {
- e.stopPropagation();
- e.preventDefault();
+});/**
+ * @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
+ * Run a new Table Filter
+ * @param {Object} config Configuration options
+ */
+
+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.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
+{
+
+
+
+ replaceTag: function(node) {
- var target = e.getTarget();
- if(target.nodeName.toLowerCase() === 'i'){
- target = Roo.get(target).dom.parentNode;
+
+ if (node.hasAttribute('width')) {
+ node.removeAttribute('width');
}
- 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;
+
+ 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.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
-
- this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
-
- this.hide();
-
- },
+ 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
+ */
+
+Roo.htmleditor.FilterWord = function(cfg)
+{
+ // no need to apply config.
+ this.replaceDocBullets(cfg.node);
- picker : function()
- {
- return this.pickerEl;
- },
+ // this is disabled as the removal is done by other filters;
+ // this.walk(cfg.node);
- 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++]
- };
+
+}
+
+Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
+{
+ tag: true,
+
+
+ /**
+ * Clean up MS wordisms...
+ */
+ replaceTag : function(node)
+ {
+
+ // 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);
+ }
- months.createChild(month);
+ node.parentNode.removeChild(node);
+ return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
}
- },
-
- update: function()
- {
- var _this = this;
+
- if(typeof(this.vIndex) == 'undefined' && this.value.length){
- this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
+ if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
+ node.parentNode.removeChild(node);
+ return false; // dont do chidlren
}
-
- Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
- e.removeClass('active');
+ //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) {
- if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
- e.addClass('active');
+ 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");
}
- })
- },
-
- place: function()
- {
- if(this.isInline) {
- return;
}
- this.picker().removeClass(['bottom', 'top']);
+ if (node.hasAttribute("lang")) {
+ node.removeAttribute("lang");
+ }
- 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());
+ if (node.hasAttribute("style")) {
- return;
+ 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.picker().addClass('bottom');
- this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
},
- onFocus : function()
+ styleToObject: function(node)
{
- Roo.bootstrap.MonthField.superclass.onFocus.call(this);
- this.show();
+ 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].trim()] = kv[1];
+ });
+ return ret;
},
- onBlur : function()
- {
- Roo.bootstrap.MonthField.superclass.onBlur.call(this);
-
- var d = this.inputEl().getValue();
-
- this.setValue(d);
-
- this.hide();
- },
- show : function()
+ replaceDocBullets : function(doc)
{
- this.picker().show();
- this.picker().select('>.datepicker-months', true).first().show();
- this.update();
- this.place();
+ // this is a bit odd - but it appears some indents use ql-indent-1
+ //Roo.log(doc.innerHTML);
- this.fireEvent('show', this, this.date);
- },
-
- hide : function()
- {
- if(this.isInline) {
- return;
+ var listpara = doc.getElementsByClassName('MsoListParagraphCxSpFirst');
+ for( var i = 0; i < listpara.length; i ++) {
+ listpara.item(i).className = "MsoListParagraph";
+ }
+ // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
+ var htwo = doc.getElementsByTagName('h2');
+ for( var i = 0; i < htwo.length; i ++) {
+ if (htwo.item(i).getAttribute('style').match(/mso-list:/)) {
+ htwo.item(i).className = "MsoListParagraph";
+ }
}
- this.picker().hide();
- this.fireEvent('hide', this, this.date);
+ listpara = doc.getElementsByClassName('ql-indent-1');
+ while(listpara.length) {
+ this.replaceDocBullet(listpara.item(0));
+ }
+ listpara = doc.getElementsByClassName('MsoListParagraph');
+ while(listpara.length) {
+
+ this.replaceDocBullet(listpara.item(0));
+ }
+
},
- onMousedown: function(e)
- {
- e.stopPropagation();
- e.preventDefault();
- },
+
- keyup: function(e)
- {
- Roo.bootstrap.MonthField.superclass.keyup.call(this);
- this.update();
- },
-
- fireKey: function(e)
+ replaceDocBullet : function(p)
{
- if (!this.picker().isVisible()){
- if (e.keyCode == 27) {// allow escape to hide and re-show picker
- this.show();
+ // 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|ql-indent-1)/i)) {
+ break;
+ }
+ var spans = ns.getElementsByTagName('span');
+ if (!spans.length) {
+ break;
}
+ var has_list = false;
+ for(var i = 0; i < spans.length; i++) {
+ if (spans[i].getAttribute('style').match(/mso-list/)) {
+ has_list = true;
+ break;
+ }
+ }
+ if (!has_list) {
+ break;
+ }
+
+
+ items.push(ns);
+ ns = ns.nextSibling;
+ }
+ if (!items.length) {
+ ns.className = "";
return;
}
- var dir;
+ var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
+ parent.insertBefore(ul, p);
+ var lvl = 0;
+ var stack = [ ul ];
+ var last_li = 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.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;
+
+ items.forEach(function(n, ipos) {
+ //Roo.log("got innertHMLT=" + n.innerHTML);
+
+ var spans = n.getElementsByTagName('span');
+ if (!spans.length) {
+ //Roo.log("No spans found");
+
+ parent.removeChild(n);
- if(this.vIndex < 0){
- this.vIndex = 0;
- }
- if(this.vIndex > 11){
- this.vIndex = 11;
- }
+ return; // skip it...
+ }
+
- if(isNaN(this.vIndex)){
- this.vIndex = 0;
+
+ var style = {};
+ for(var i = 0; i < spans.length; i++) {
+
+ style = this.styleToObject(spans[i]);
+ if (typeof(style['mso-list']) == 'undefined') {
+ continue;
}
- this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
+ spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
break;
+ }
+ //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
+ style = this.styleToObject(n); // mo-list is from the parent node.
+ if (typeof(style['mso-list']) == 'undefined') {
+ //Roo.log("parent is missing level");
- 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]);
+
+ parent.removeChild(n);
+
+ return;
+ }
+
+ 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...
+ if (!last_li) {
+ last_li = doc.createElement('li');
+ stack[lvl].appendChild(last_li);
}
- this.hide();
- break;
- case 16: // shift
- case 17: // ctrl
- case 18: // alt
- break;
- default :
- this.hide();
+ last_li.appendChild(nul);
+ stack[nlvl] = nul;
- }
- },
-
- remove: function()
- {
- this.picker().remove();
+ }
+ lvl = nlvl;
+
+ var nli = stack[nlvl].appendChild(doc.createElement('li'));
+ last_li = nli;
+ nli.innerHTML = n.innerHTML;
+ //Roo.log("innerHTML = " + n.innerHTML);
+ parent.removeChild(n);
+
+
+
+
+ },this);
+
+
+
+
}
-
-});
-
-Roo.apply(Roo.bootstrap.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.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.FilterStyleToTag
+ * part of the word stuff... - certain 'styles' should be converted to tags.
+ * eg.
+ * font-weight: bold -> bold
+ * ?? super / subscrit etc..
*
* @constructor
- * Create a new CheckBox
- * @param {Object} config The config object
+* Run a new style to tag 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.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' ]
+
+
+ };
+
+ Roo.apply(this, cfg);
+
+
+ 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.FilterStyleToTag, Roo.htmleditor.Filter,
+{
+ tag: true, // all tags
- getAutoCreate : function()
+ tags : false,
+
+
+ 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';
+ if (node.getAttribute("style") === null) {
+ return true;
}
-
- 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
- };
+ 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]);
+ }
}
-
-
- if (this.weight) { // Validity check?
- cfg.cls += " " + this.inputType + "-" + this.weight;
+ 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
+ }
+
+})/**
+ * @class Roo.htmleditor.FilterLongBr
+ * BR/BR/BR - keep a maximum of 2...
+ * @constructor
+ * Run a new Long BR Filter
+ * @param {Object} config Configuration options
+ */
+
+Roo.htmleditor.FilterLongBr = function(cfg)
+{
+ // no need to apply config.
+ this.walk(cfg.node);
+}
+
+Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
+{
+
+
+ tag : 'BR',
+
+
+ replaceTag : function(node)
+ {
- if (this.disabled) {
- input.disabled=true;
+ var ps = node.nextSibling;
+ while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
+ ps = ps.nextSibling;
}
- if(this.checked){
- input.checked = this.checked;
+ 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.name) {
-
- input.name = this.name;
-
- if(this.inputType != 'radio'){
- hidden.name = this.name;
- input.name = '_hidden_' + this.name;
- }
+ if (!ps || ps.nodeType != 1) {
+ return false;
}
- if (this.size) {
- input.cls += ' input-' + this.size;
+ if (!ps || ps.tagName != 'BR') {
+
+ return false;
}
- 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
- });
- }
-
+
+
+ if (!node.previousSibling) {
+ return false;
+ }
+ var ps = node.previousSibling;
+
+ 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;
+
+
+
+});
+/**
+ * @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) {
+ // on it list, or ctrl pressed.
+ if (!e.ctrlKey) {
+ sel.insertNode('br', 'after');
+ } else {
+ // only do this if we have ctrl key..
+ var br = doc.createElement('br');
+ br.className = 'clear';
+ br.setAttribute('style', 'clear: both');
+ sel.insertNode(br, 'after');
}
-
- cfg.cn.push(boxLabelCfg);
+
+
+ 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 : [
+ ['100%'],
+ ['80%'],
+ ['50%'],
+ ['20%'],
+ ['10%']
+ ],
+ 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;
-
- this.fireEvent('valid', this);
+ node = node ? node : this.node ;
+ this.width = this.getVal(node, true, 'style', 'width') || '100%';
- 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', ':' ]} );
+ 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 });
+ new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
},
- /**
- * 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();
image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
- image.on('click', this.onClick, this, file);
-
- this.fireEvent('previewrendered', this, file);
-
- return file;
-
- },
-
- onPreviewLoad : function(file, image)
- {
- if(typeof(file.target) == 'undefined' || !file.target){
- return;
- }
-
- var width = image.dom.naturalWidth || image.dom.width;
- var height = image.dom.naturalHeight || image.dom.height;
-
- if(!this.previewResize) {
- return;
- }
-
- if(width > height){
- file.target.addClass('wide');
- return;
- }
-
- file.target.addClass('tall');
- return;
-
- },
-
- uploadFromSource : function(file, crop)
- {
- this.xhr = new XMLHttpRequest();
-
- this.managerEl.createChild({
- tag : 'div',
- cls : 'roo-document-manager-loading',
- cn : [
- {
- tag : 'div',
- tooltip : file.name,
- cls : 'roo-document-manager-thumb',
- html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
- }
- ]
-
- });
-
- this.xhr.open(this.method, this.url, true);
-
- var headers = {
- "Accept": "application/json",
- "Cache-Control": "no-cache",
- "X-Requested-With": "XMLHttpRequest"
- };
-
- for (var headerName in headers) {
- var headerValue = headers[headerName];
- if (headerValue) {
- this.xhr.setRequestHeader(headerName, headerValue);
- }
- }
-
- var _this = this;
-
- this.xhr.onload = function()
- {
- _this.xhrOnLoad(_this.xhr);
- }
-
- this.xhr.onerror = function()
- {
- _this.xhrOnError(_this.xhr);
- }
-
- var formData = new FormData();
-
- formData.append('returnHTML', 'NO');
-
- formData.append('crop', crop);
-
- if(typeof(file.filename) != 'undefined'){
- formData.append('filename', file.filename);
- }
-
- if(typeof(file.mimetype) != 'undefined'){
- formData.append('mimetype', file.mimetype);
- }
-
- Roo.log(formData);
-
- if(this.fireEvent('prepare', this, formData) != false){
- this.xhr.send(formData);
- };
- }
-});
-
-/*
-* Licence: LGPL
-*/
-
-/**
- * @class Roo.bootstrap.DocumentViewer
- * @extends Roo.bootstrap.Component
- * Bootstrap DocumentViewer class
- * @cfg {Boolean} showDownload (true|false) show download button (default true)
- * @cfg {Boolean} showTrash (true|false) show trash button (default true)
- *
- * @constructor
- * Create a new DocumentViewer
- * @param {Object} config The config object
- */
-
-Roo.bootstrap.DocumentViewer = function(config){
- Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
-
- this.addEvents({
- /**
- * @event initial
- * Fire after initEvent
- * @param {Roo.bootstrap.DocumentViewer} this
- */
- "initial" : true,
- /**
- * @event click
- * Fire after click
- * @param {Roo.bootstrap.DocumentViewer} this
- */
- "click" : true,
- /**
- * @event download
- * Fire after download button
- * @param {Roo.bootstrap.DocumentViewer} this
- */
- "download" : true,
- /**
- * @event trash
- * Fire after trash button
- * @param {Roo.bootstrap.DocumentViewer} this
- */
- "trash" : true
-
- });
-};
-
-Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
-
- showDownload : true,
-
- showTrash : true,
-
- getAutoCreate : function()
- {
- var cfg = {
- tag : 'div',
- cls : 'roo-document-viewer',
- cn : [
- {
- tag : 'div',
- cls : 'roo-document-viewer-body',
- cn : [
- {
- tag : 'div',
- cls : 'roo-document-viewer-thumb',
- cn : [
- {
- tag : 'img',
- cls : 'roo-document-viewer-image'
- }
- ]
- }
- ]
- },
- {
- tag : 'div',
- cls : 'roo-document-viewer-footer',
- cn : {
- tag : 'div',
- cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
- cn : [
- {
- tag : 'div',
- cls : 'btn-group roo-document-viewer-download',
- cn : [
- {
- tag : 'button',
- cls : 'btn btn-default',
- html : '<i class="fa fa-download"></i>'
- }
- ]
- },
- {
- tag : 'div',
- cls : 'btn-group roo-document-viewer-trash',
- cn : [
- {
- tag : 'button',
- cls : 'btn btn-default',
- html : '<i class="fa fa-trash"></i>'
- }
- ]
- }
- ]
- }
- }
- ]
- };
-
- return cfg;
- },
-
- initEvents : function()
- {
- this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
- this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
- this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
- this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
- this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
- this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
- this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
-
- this.bodyEl.on('click', this.onClick, this);
- this.downloadBtn.on('click', this.onDownload, this);
- this.trashBtn.on('click', this.onTrash, this);
-
- this.downloadBtn.hide();
- this.trashBtn.hide();
-
- if(this.showDownload){
- this.downloadBtn.show();
- }
-
- if(this.showTrash){
- this.trashBtn.show();
- }
-
- if(!this.showDownload && !this.showTrash) {
- this.footerEl.hide();
- }
-
- },
-
- initial : function()
- {
- this.fireEvent('initial', this);
-
- },
-
- onClick : function(e)
- {
- e.preventDefault();
-
- this.fireEvent('click', this);
- },
-
- onDownload : function(e)
- {
- e.preventDefault();
-
- this.fireEvent('download', this);
- },
-
- onTrash : function(e)
- {
- e.preventDefault();
-
- this.fireEvent('trash', this);
- }
-
-});
-/*
- * - 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;
-
- });
+ image.on('click', this.onClick, this, file);
+
+ this.fireEvent('previewrendered', this, file);
+
+ return file;
- return active;
},
- setActiveItem : function(item)
+ onPreviewLoad : function(file, image)
{
- var prev = false;
+ if(typeof(file.target) == 'undefined' || !file.target){
+ return;
+ }
- Roo.each(this.barItems, function(v){
- if (v.rid == item.rid) {
- return ;
- }
-
- if (v.isActive()) {
- v.setActive(false);
- prev = v;
- }
- });
-
- item.setActive(true);
+ var width = image.dom.naturalWidth || image.dom.width;
+ var height = image.dom.naturalHeight || image.dom.height;
+
+ if(!this.previewResize) {
+ return;
+ }
+
+ if(width > height){
+ file.target.addClass('wide');
+ return;
+ }
+
+ file.target.addClass('tall');
+ return;
- this.fireEvent('changed', this, item, prev);
},
- getBarItem: function(rid)
+ uploadFromSource : function(file, crop)
{
- var ret = false;
+ this.xhr = new XMLHttpRequest();
- Roo.each(this.barItems, function(e) {
- if (e.rid != rid) {
- return;
- }
-
- ret = e;
- return false;
+ this.managerEl.createChild({
+ tag : 'div',
+ cls : 'roo-document-manager-loading',
+ cn : [
+ {
+ tag : 'div',
+ tooltip : file.name,
+ cls : 'roo-document-manager-thumb',
+ html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
+ }
+ ]
+
});
+
+ this.xhr.open(this.method, this.url, true);
- return ret;
- },
-
- indexOfItem : function(item)
- {
- var index = false;
+ var headers = {
+ "Accept": "application/json",
+ "Cache-Control": "no-cache",
+ "X-Requested-With": "XMLHttpRequest"
+ };
- Roo.each(this.barItems, function(v, i){
-
- if (v.rid != item.rid) {
- return;
+ for (var headerName in headers) {
+ var headerValue = headers[headerName];
+ if (headerValue) {
+ this.xhr.setRequestHeader(headerName, headerValue);
}
-
- index = i;
- return false
- });
+ }
- return index;
- },
-
- setActiveNext : function()
- {
- var i = this.indexOfItem(this.getActive());
+ var _this = this;
- if (i > this.barItems.length) {
- return;
+ this.xhr.onload = function()
+ {
+ _this.xhrOnLoad(_this.xhr);
}
- this.setActiveItem(this.barItems[i+1]);
- },
-
- setActivePrev : function()
- {
- var i = this.indexOfItem(this.getActive());
+ this.xhr.onerror = function()
+ {
+ _this.xhrOnError(_this.xhr);
+ }
- if (i < 1) {
- return;
+ var formData = new FormData();
+
+ formData.append('returnHTML', 'NO');
+
+ formData.append('crop', crop);
+
+ if(typeof(file.filename) != 'undefined'){
+ formData.append('filename', file.filename);
}
- this.setActiveItem(this.barItems[i-1]);
- },
-
- format : function()
- {
- if(!this.barItems.length){
- return;
+ if(typeof(file.mimetype) != 'undefined'){
+ formData.append('mimetype', file.mimetype);
}
-
- 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);
+ Roo.log(formData);
+ if(this.fireEvent('prepare', this, formData) != false){
+ this.xhr.send(formData);
+ };
}
-
});
+
/*
- * - LGPL
- *
- * Nav Progress Item
- *
- */
+* Licence: LGPL
+*/
/**
- * @class Roo.bootstrap.NavProgressItem
+ * @class Roo.bootstrap.DocumentViewer
* @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
+ * Bootstrap DocumentViewer class
+ * @cfg {Boolean} showDownload (true|false) show download button (default true)
+ * @cfg {Boolean} showTrash (true|false) show trash button (default true)
*
* @constructor
- * Create a new NavProgressItem
+ * Create a new DocumentViewer
* @param {Object} config The config object
*/
-Roo.bootstrap.NavProgressItem = function(config){
- Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
+
+Roo.bootstrap.DocumentViewer = function(config){
+ Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
+
this.addEvents({
- // raw events
+ /**
+ * @event initial
+ * Fire after initEvent
+ * @param {Roo.bootstrap.DocumentViewer} this
+ */
+ "initial" : true,
/**
* @event click
- * The raw click event for the entire grid.
- * @param {Roo.bootstrap.NavProgressItem} this
- * @param {Roo.EventObject} e
+ * Fire after click
+ * @param {Roo.bootstrap.DocumentViewer} this
*/
- "click" : true
+ "click" : true,
+ /**
+ * @event download
+ * Fire after download button
+ * @param {Roo.bootstrap.DocumentViewer} this
+ */
+ "download" : true,
+ /**
+ * @event trash
+ * Fire after trash button
+ * @param {Roo.bootstrap.DocumentViewer} this
+ */
+ "trash" : true
+
});
-
};
-Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
+Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
- rid : '',
- active : false,
- disabled : false,
- html : '',
- position : 'bottom',
- icon : false,
+ showDownload : true,
+
+ showTrash : true,
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',
+ tag : 'div',
+ cls : 'roo-document-viewer',
cn : [
{
- tag : 'i',
- cls : iconCls
+ tag : 'div',
+ cls : 'roo-document-viewer-body',
+ cn : [
+ {
+ tag : 'div',
+ cls : 'roo-document-viewer-thumb',
+ cn : [
+ {
+ tag : 'img',
+ cls : 'roo-document-viewer-image'
+ }
+ ]
+ }
+ ]
+ },
+ {
+ tag : 'div',
+ cls : 'roo-document-viewer-footer',
+ cn : {
+ tag : 'div',
+ cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
+ cn : [
+ {
+ tag : 'div',
+ cls : 'btn-group roo-document-viewer-download',
+ cn : [
+ {
+ tag : 'button',
+ cls : 'btn btn-default',
+ html : '<i class="fa fa-download"></i>'
+ }
+ ]
+ },
+ {
+ tag : 'div',
+ cls : 'btn-group roo-document-viewer-trash',
+ cn : [
+ {
+ tag : 'button',
+ cls : 'btn btn-default',
+ html : '<i class="fa fa-trash"></i>'
+ }
+ ]
+ }
+ ]
+ }
}
]
};
- 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()
+ initEvents : function()
{
- this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
+ this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
+ this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
- this.iconEl.on('click', this.onClick, this);
- },
-
- onClick : function(e)
- {
- e.preventDefault();
+ this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
+ this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
- if(this.disabled){
- return;
+ this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
+ this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
+ this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
+ this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
+ this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
+
+ this.bodyEl.on('click', this.onClick, this);
+ this.downloadBtn.on('click', this.onDownload, this);
+ this.trashBtn.on('click', this.onTrash, this);
+
+ this.downloadBtn.hide();
+ this.trashBtn.hide();
+
+ if(this.showDownload){
+ this.downloadBtn.show();
}
- if(this.fireEvent('click', this, e) === false){
- return;
- };
+ if(this.showTrash){
+ this.trashBtn.show();
+ }
+
+ if(!this.showDownload && !this.showTrash) {
+ this.footerEl.hide();
+ }
- this.parent().setActiveItem(this);
},
- isActive: function ()
+ initial : function()
{
- return this.active;
+ this.fireEvent('initial', this);
+
},
- setActive : function(state)
+ onClick : function(e)
{
- if(this.active == state){
- return;
- }
-
- this.active = state;
-
- if (state) {
- this.el.addClass('active');
- return;
- }
-
- this.el.removeClass('active');
+ e.preventDefault();
- return;
+ this.fireEvent('click', this);
},
- setDisabled : function(state)
+ onDownload : function(e)
{
- if(this.disabled == state){
- return;
- }
-
- this.disabled = state;
-
- if (state) {
- this.el.addClass('disabled');
- return;
- }
+ e.preventDefault();
- this.el.removeClass('disabled');
+ this.fireEvent('download', this);
},
- tooltipEl : function()
+ onTrash : function(e)
{
- return this.el.select('.roo-navigation-bar-item-icon', true).first();;
+ e.preventDefault();
+
+ this.fireEvent('trash', this);
}
+
});
-
-
- /*
+/*
* - 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 ???
+
/**
* Creates and adds a new region if it doesn't already exist.
* @param {String} target The target region key (north, south, east, west or center).
delete cfg.items;
}
var nb = false;
+
+ if ( region == 'center') {
+ Roo.log("Center: " + cfg.title);
+ }
+
switch(cfg.xtype)
{
case 'Content': // ContentPanel (el, cfg)
case 'Scroll': // ContentPanel (el, cfg)
case 'View':
- cfg.autoCreate = true;
+ cfg.autoCreate = cfg.autoCreate || true;
ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
//} else {
// var el = this.el.createChild();
position: '', // set by wrapper (eg. north/south etc..)
unrendered_panels : null, // unrendered panels.
+
+ tabPosition : false,
+
+ mgr: false, // points to 'Border'
+
+
createBody : function(){
/** This region's body element
* @type Roo.Element */
/** This region's title element
* @type Roo.Element */
- this.titleEl = dh.append(this.el.dom,
- {
- tag: "div",
- unselectable: "on",
- cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
- children:[
- {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
- {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
- ]}, true);
+ this.titleEl = dh.append(this.el.dom, {
+ tag: "div",
+ unselectable: "on",
+ cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
+ children:[
+ {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
+ {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
+ ]
+ }, true);
this.titleEl.enableDisplayMode();
/** This region's title text element
this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
- this.bottomTabs = c.tabPosition != "top";
+ this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
this.autoScroll = c.autoScroll || false;
//this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
var ts = new Roo.bootstrap.panel.Tabs({
- el: this.bodyEl.dom,
- tabPosition: this.bottomTabs ? 'bottom' : 'top',
- disableTooltips: this.config.disableTabTips,
- toolbar : this.config.toolbar
- });
+ el: this.bodyEl.dom,
+ region : this,
+ tabPosition: this.tabPosition ? this.tabPosition : 'top',
+ disableTooltips: this.config.disableTabTips,
+ toolbar : this.config.toolbar
+ });
if(this.config.hideTabs){
ts.stripWrap.setDisplayed(false);
+
Roo.bootstrap.layout.North = function(config)
{
config.region = 'north';
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)
config.layout.monitorWindowResize = false; // turn off autosizing
this.layout = config.layout;
this.layout.getEl().addClass("roo-layout-nested-layout");
+ this.layout.parent = this;
};
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)){
return this.layout.addxtype(cfg);
}
-}); /*
+});/*
* Based on:
* Ext JS Library 1.1.1
* Copyright(c) 2006-2007, Ext JS, LLC.
}
if(this.tabPosition == "bottom"){
+ // if tabs are at the bottom = create the body first.
this.bodyEl = Roo.get(this.createBody(this.el.dom));
this.el.addClass("roo-tabs-bottom");
}
- this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
- this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
- this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
- this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
+ // next create the tabs holders
+
+ if (this.tabPosition == "west"){
+
+ var reg = this.region; // fake it..
+ while (reg) {
+ if (!reg.mgr.parent) {
+ break;
+ }
+ reg = reg.mgr.parent.region;
+ }
+ Roo.log("got nest?");
+ Roo.log(reg);
+ if (reg.mgr.getRegion('west')) {
+ var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
+ this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
+ this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
+ this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
+ this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
+
+
+ }
+
+
+ } else {
+
+ this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
+ this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
+ this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
+ this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
+ }
+
+
if(Roo.isIE){
Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
}
+
+ // finally - if tabs are at the top, then create the body last..
if(this.tabPosition != "bottom"){
/** The body element that contains {@link Roo.TabPanelItem} bodies. +
* @type Roo.Element
/*
*@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
*/
- toolbar : false,
+ toolbar : false, // set by caller..
+
+ region : false, /// set by caller
+
+ disableTooltips : true, // not used yet...
/**
* Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
*/
activate : function(id)
{
+ //Roo.log('activite:' + id);
+
var tab = this.items[id];
if(!tab){
return null;
* 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
+