fix enums
[roojs1] / roojs-bootstrap-debug.js
index f088254..3448660 100644 (file)
@@ -13,6 +13,203 @@ Roo.bootstrap.version = (
                 });
         return ret;
 })(); /*
+ * 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)
+     */
+    /**
+     * @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.
@@ -435,6 +632,189 @@ Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
     }
 });
 
+ /*
+ * - LGPL
+ *
+ * element
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.Element
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Element class
+ * @cfg {String} html contents of the element
+ * @cfg {String} tag tag of the element
+ * @cfg {String} cls class of the element
+ * @cfg {Boolean} preventDefault (true|false) default false
+ * @cfg {Boolean} clickable (true|false) default false
+ * 
+ * @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,
+    
+    getAutoCreate : function(){
+        
+        var cfg = {
+            tag: this.tag,
+            // cls: this.cls, double assign in parent class Component.js :: onRender
+            html: this.html
+        };
+        
+        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);
+    },
+    
+    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
  *
@@ -581,11 +961,11 @@ Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
  * @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;
@@ -595,12 +975,12 @@ Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
  * @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. 
  * 
  * @constructor
  * Create a new button
@@ -610,14 +990,7 @@ Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
 
 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
         /**
@@ -714,8 +1087,8 @@ Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
             
             return cfg;
         }
+             
         
-         
         if (this.theme==='default') {
             cfg.cls = 'btn roo-button';
             
@@ -981,7 +1354,7 @@ Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
     
     setWeight : function(str)
     {
-       this.el.removeClass(this.weightClass);
+       this.el.removeClass(Roo.bootstrap.Button.weightClass );
         this.weight = str;
         var outline = this.outline ? 'outline-' : '';
         if (str == 'default') {
@@ -993,8 +1366,21 @@ Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
     
     
 });
-
- /*
+// fixme - should include btn-outline-*
+Roo.bootstrap.Button.weightClass = [
+                        
+       "btn-default",
+       "btn-outline-secondary",
+       "btn-secondary",        
+       "btn-primary", 
+       "btn-success", 
+       "btn-info", 
+       "btn-warning",
+       "btn-danger",
+       "btn-link",
+       'btn-light',
+       'btn-dark'
+];/*
  * - LGPL
  *
  * column
@@ -1060,7 +1446,8 @@ Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
         };
         
         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) {
@@ -1072,7 +1459,13 @@ Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
             }
             
             if (!settings[size]) { // 0 = hidden
-                cfg.cls += ' hidden-' + size + ' hidden' + size + '-down';;
+                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] + (
@@ -1415,78 +1808,883 @@ Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
             return;
         }
         
-        return this.el.select('.panel-heading .fa',true).first();
+        return this.el.select('.panel-heading .fa',true).first();
+    },
+    
+    headerEl : function()
+    {
+        if(!this.el || !this.panel.length || !this.header.length){
+            return;
+        }
+        
+        return this.el.select('.panel-heading',true).first()
+    },
+    
+    bodyEl : function()
+    {
+        if(!this.el || !this.panel.length){
+            return;
+        }
+        
+        return this.el.select('.panel-body',true).first()
+    },
+    
+    titleEl : function()
+    {
+        if(!this.el || !this.panel.length || !this.header.length){
+            return;
+        }
+        
+        return this.el.select('.panel-title',true).first();
+    },
+    
+    setTitle : function(v)
+    {
+        var titleEl = this.titleEl();
+        
+        if(!titleEl){
+            return;
+        }
+        
+        titleEl.dom.innerHTML = v;
+    },
+    
+    getTitle : function()
+    {
+        
+        var titleEl = this.titleEl();
+        
+        if(!titleEl){
+            return '';
+        }
+        
+        return titleEl.dom.innerHTML;
+    },
+    
+    setRightTitle : function(v)
+    {
+        var t = this.el.select('.panel-header-right',true).first();
+        
+        if(!t){
+            return;
+        }
+        
+        t.dom.innerHTML = v;
+    },
+    
+    onClick : function(e)
+    {
+        e.preventDefault();
+        
+        this.fireEvent('click', this, e);
+    }
+});
+
+ /*
+ *  - LGPL
+ *
+ *  This is BS4's Card element.. - similar to our containers probably..
+ * 
+ */
+/**
+ * @class Roo.bootstrap.Card
+ * @extends Roo.bootstrap.Component
+ * Bootstrap Card class
+ *
+ *
+ * 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 {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)
+ * @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 Container
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.Card = function(config){
+    Roo.bootstrap.Card.superclass.constructor.call(this, config);
+    
+    this.addEvents({
+         // raw events
+        /**
+         * @event drop
+         * When a element a card is dropped
+         * @param {Roo.bootstrap.Element} this
+         * @param {Roo.Element} n the node being dropped?
+         * @param {Object} dd Drag and drop data
+         * @param {Roo.EventObject} e
+         * @param {Roo.EventObject} data  the data passed via getDragData
+         */
+        'drop' : true,
+         /**
+         * @event rotate
+         * When a element a card is rotate
+         * @param {Roo.bootstrap.Element} this
+         * @param {Roo.Element} n the node being dropped?
+         * @param {Boolean} rotate status
+         */
+        'rotate' : true
+        
+    });
+};
+
+
+Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
+    
+    
+    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: '',
+
+    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.
+    
+    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['margin' + (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 : 'card',
+            cn : [ ]
+        };
+        
+        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;
+        if (this.header.length) {
+            hdr = {
+                tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
+                cls : 'card-header',
+                cn : []
+            };
+            cfg.cn.push(hdr);
+            hdr_ctr = hdr;
+        } else {
+            hdr = {
+                tag : 'div',
+                cls : 'card-header d-none',
+                cn : []
+            };
+            cfg.cn.push(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' 
+                });
+        }
+            
+        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
+            });
+            
+        } 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.el.select('.card-img-top',true).first();
+        if (ret.hasClass('d-none')) {
+            ret.removeClass('d-none');
+        }
+            
+        return ret;
+    },
+    
+    getChildContainer : function()
+    {
+        
+        if(!this.el){
+            return false;
+        }
+        return this.el.select('.roo-card-body-ctr',true).first();    
+    },
+    
+    initEvents: function() 
+    {
+        
+        this.bodyEl = 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').first();
+         
+        this.footerEl = this.el.select('.card-footer').first();
+        this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
+        this.headerEl = this.el.select('.roo-card-header-ctr').first();
+        
+        if (this.rotated) {
+            this.el.addClass('roo-card-rotated');
+            this.fireEvent('rotate', this, true);
+        }
+        
+    },
+    getDragData : function(e)
+    {
+        var target = this.getEl();
+        if (target) {
+            //this.handleSelection(e);
+            
+            var dragData = {
+                source: this,
+                copy: false,
+                nodes: this.getEl(),
+                records: []
+            };
+            
+            
+            dragData.ddel = target.dom ;    // the div element
+            Roo.log(target.getWidth( ));
+            dragData.ddel.style.width = target.getWidth() + 'px';
+            
+            return dragData;
+        }
+        return false;
+    },
+    /**
+    *    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.
+    */
+    getTargetFromEvent : function(e, dragged_card_el)
+    {
+        var target = e.getTarget();
+        while ((target !== null) && (target.parentNode != this.bodyEl.dom)) {
+            target = target.parentNode;
+        }
+        
+        var ret = {
+            position: '',
+            cards : [],
+            card_n : -1,
+            items_n : -1,
+            card : false 
+        };
+        
+        //Roo.log([ 'target' , target ? target.id : '--nothing--']);
+        // see if target is one of the 'cards'...
+        
+        
+        //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.items[ret.items_n].el == dragged_card_el) {
+            return false;
+        }
+        
+        if (ret.position == 'below') {
+            var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
+            
+            if (card_after  && card_after.el == dragged_card_el) {
+                return false;
+            }
+            return ret;
+        }
+        
+        // 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;
+    },
+    
+    onNodeEnter : function(n, dd, e, data){
+        return false;
+    },
+    onNodeOver : function(n, dd, e, data)
+    {
+       
+        var target_info = this.getTargetFromEvent(e,data.source.el);
+        if (target_info === false) {
+            this.dropPlaceHolder('hide');
+            return false;
+        }
+        Roo.log(['getTargetFromEvent', target_info ]);
+        
+         
+        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;
+        }
+        
+        if (this.fireEvent("drop", this, n, dd, e, data) === false) {
+            return false;
+        }
+         
+        this.dropPlaceHolder('hide');
+        
+        // do the dom manipulation first..
+        var dom = data.source.el.dom;
+        dom.parentNode.removeChild(dom);
+        
+        
+        if (info.card !== true) {
+            var cardel = info.card.el.dom;
+            
+            if (info.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.bodyEl.dom.append(dom);
+        }
+        
+        //FIXME HANDLE card = true 
+        
+        // add this to the correct place in items.
+        
+        
+        
+        // remove Card from items.
+        
+        var old_parent = data.source.parent();
+        
+        old_parent.items = old_parent.items.filter(function(e) { return e != data.source });
+        
+        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 == info.items_n && info.position == 'above') {
+                    nitems.push(data.source);
+                }
+                nitems.push(this.items[i]);
+                if (i == info.items_n && info.position == 'below') {
+                    nitems.push(data.source);
+                }
+            }
+            this.items = nitems;
+            Roo.log(this.items);
+        } else {
+            this.items.push(data.source);
+        }
+        
+        data.source.parentId = this.id;
+        
+        return true;
     },
     
-    headerEl : function()
+    /**    Decide whether to drop above or below a View node. */
+    getDropPoint : function(e, n, dd)
     {
-        if(!this.el || !this.panel.length || !this.header.length){
-            return;
+        if (dd) {
+             return false;
+        }
+        if (n == this.bodyEl.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";
         }
-        
-        return this.el.select('.panel-heading',true).first()
     },
-    
-    bodyEl : function()
-    {
-        if(!this.el || !this.panel.length){
+    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;
         
-        return this.el.select('.panel-body',true).first()
+    
     },
     
-    titleEl : function()
+    onToggleRotate : function(e)
     {
-        if(!this.el || !this.panel.length || !this.header.length){
+        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');
         
-        return this.el.select('.panel-title',true).first();
+        this.rotated = true;
+        this.fireEvent('rotate', this, this.rotated);
+    
     },
     
-    setTitle : function(v)
+    dropPlaceHolder: function (action, info, data)
     {
-        var titleEl = this.titleEl();
-        
-        if(!titleEl){
+        if (this.dropEl === false) {
+            this.dropEl = Roo.DomHelper.append(this.bodyEl, {
+            cls : 'd-none'
+            },true);
+        }
+        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);
         
-        titleEl.dom.innerHTML = v;
-    },
-    
-    getTitle : function()
-    {
-        
-        var titleEl = this.titleEl();
-        
-        if(!titleEl){
-            return '';
+        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.bodyEl.dom.append(this.dropEl.dom);
         }
         
-        return titleEl.dom.innerHTML;
-    },
-    
-    setRightTitle : function(v)
-    {
-        var t = this.el.select('.panel-header-right',true).first();
+        this.dropEl.addClass('d-block roo-card-dropzone');
+        
+        this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
         
-        if(!t){
-            return;
-        }
         
-        t.dom.innerHTML = v;
-    },
     
-    onClick : function(e)
+    
+    
+    },
+    setHeaderText: function(html)
     {
-        e.preventDefault();
-        
-        this.fireEvent('click', this, e);
+        this.headerEl.dom.innerHTML = html;
     }
+
+    
+});
+
+/*
+ * - LGPL
+ *
+ * Card header - holder for the card header elements.
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.CardHeader
+ * @extends Roo.bootstrap.Element
+ * Bootstrap CardHeader class
+ * @constructor
+ * Create a new Card Header - that you can embed children into
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.CardHeader = function(config){
+    Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
+    
+    
+    container_method : 'getCardHeader' 
+    
+     
+    
+    
+   
+});
+
+
+ /*
+ * - LGPL
+ *
+ * Card footer - holder for the card footer elements.
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.CardFooter
+ * @extends Roo.bootstrap.Element
+ * Bootstrap CardFooter class
+ * @constructor
+ * Create a new Card Footer - that you can embed children into
+ * @param {Object} config The config object
+ */
+
+Roo.bootstrap.CardFooter = function(config){
+    Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
+    
+    
+    container_method : 'getCardFooter' 
+    
+     
+    
+    
+   
+});
+
+
+ /*
+ * - LGPL
+ *
+ * Card header - holder for the card header elements.
+ * 
+ */
+
+/**
+ * @class Roo.bootstrap.CardImageTop
+ * @extends Roo.bootstrap.Element
+ * Bootstrap CardImageTop class
+ * @constructor
+ * 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.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
+    
+   
+    container_method : 'getCardImageTop' 
+    
+     
+    
+   
 });
 
+
  /*
  * - LGPL
  *
@@ -2653,8 +3851,10 @@ Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
  * @cfg {Boolean} fitwindow 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) default empty
+ * @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
@@ -2678,7 +3878,15 @@ Roo.bootstrap.Modal = function(config){
          * @param {Roo.bootstrap.Modal} this
          * @param {Roo.EventObject} e
          */
-        "resize" : true
+        "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 || [];
 
@@ -2724,6 +3932,7 @@ Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
     max_height: 0,
     
     fit_content: false,
+    editableTitle  : false,
 
     onRender : function(ct, position)
     {
@@ -2815,9 +4024,8 @@ Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
             html : this.title
         };
 
-        if(this.specificTitle){
+        if(this.specificTitle){ // WTF is this?
             title = this.title;
-
         }
 
         var header = [];
@@ -2831,6 +4039,14 @@ Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
 
         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',
@@ -2913,7 +4129,18 @@ Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
             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.isNavKeyPress()){
+                            this.toggleHeaderInput(false)
+                    }
+                }, this);
+            this.headerEditEl.on('blur', function(e) {
+                this.toggleHeaderInput(false)
+            },this);
+        }
 
     },
   
@@ -3120,6 +4347,7 @@ Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
      */
     setTitle: function(str) {
         this.titleEl.dom.innerHTML = str;
+        this.title = str;
     },
     /**
      * Set the body of the Dialog
@@ -3147,7 +4375,7 @@ Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
             !child_nodes ||
             child_nodes.length == 0
         ) {
-            return;
+            return 0;
         }
         
         var child_height = 0;
@@ -3200,6 +4428,33 @@ Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
         }
         
         return child_height;
+    },
+    toggleHeaderInput : function(is_edit)
+    {
+        
+        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);
+    
+            
+        
     }
 
 });
@@ -3268,6 +4523,7 @@ Roo.apply(Roo.bootstrap.Modal,  {
         
         zIndex : 10001
 });
+
 /*
  * - LGPL
  *
@@ -4225,7 +5481,7 @@ Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
         
         cn.push({
             tag: 'div',
-            cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
+            cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
             cn : []
         });
         
@@ -4275,6 +5531,13 @@ Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
         return this.getChildContainer();
     },
     
+    getChildContainer : function()
+    {
+        
+       return this.el.select('.roo-navbar-collapse',true).first();
+        
+       
+    },
     
     initEvents : function()
     {
@@ -5259,109 +6522,20 @@ Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
  * Create a new Row
  * @param {Object} config The config object
  */
-
-Roo.bootstrap.Row = function(config){
-    Roo.bootstrap.Row.superclass.constructor.call(this, config);
-};
-
-Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
-    
-    getAutoCreate : function(){
-       return {
-            cls: 'row clearfix'
-       };
-    }
-    
-    
-});
-
-
- /*
- * - LGPL
- *
- * element
- * 
- */
-
-/**
- * @class Roo.bootstrap.Element
- * @extends Roo.bootstrap.Component
- * Bootstrap Element class
- * @cfg {String} html contents of the element
- * @cfg {String} tag tag of the element
- * @cfg {String} cls class of the element
- * @cfg {Boolean} preventDefault (true|false) default false
- * @cfg {Boolean} clickable (true|false) default false
- * 
- * @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,
-    
-    getAutoCreate : function(){
-        
-        var cfg = {
-            tag: this.tag,
-            // cls: this.cls, double assign in parent class Component.js :: onRender
-            html: this.html
-        };
-        
-        return cfg;
-    },
-    
-    initEvents: function() 
-    {
-        Roo.bootstrap.Element.superclass.initEvents.call(this);
-        
-        if(this.clickable){
-            this.el.on('click', this.onClick, this);
-        }
-        
-    },
+
+Roo.bootstrap.Row = function(config){
+    Roo.bootstrap.Row.superclass.constructor.call(this, config);
+};
+
+Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
     
-    onClick : function(e)
-    {
-        if(this.preventDefault){
-            e.preventDefault();
-        }
-        
-        this.fireEvent('click', this, e);
-    },
+    getAutoCreate : function(){
+       return {
+            cls: 'row clearfix'
+       };
+    }
     
-    getValue : function()
-    {
-        return this.el.dom.innerHTML;
-    },
     
-    setValue : function(value)
-    {
-        this.el.dom.innerHTML = value;
-    }
-   
 });
 
  
@@ -7320,9 +8494,12 @@ Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
         
         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;
         }
@@ -9008,6 +10185,7 @@ Roo.form.VTypes = function(){
  * @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 {String} align (left|center|right) Default left
  * @cfg {Boolean} forceFeedback (true|false) Default false
@@ -9899,6 +11077,7 @@ Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
             return;
         }
         
+           
         if(this.allowBlank && !this.getRawValue().length){
             return;
         }
@@ -10544,7 +11723,7 @@ Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
                 cls: 'glyphicon form-control-feedback'
             };
             
-            if(this.removable && !this.editable && !this.tickable){
+            if(this.removable && !this.editable  ){
                 inputblock = {
                     cls : 'has-feedback',
                     cn :  [
@@ -10568,7 +11747,7 @@ Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
             }
 
         } else {
-            if(this.removable && !this.editable && !this.tickable){
+            if(this.removable && !this.editable ){
                 inputblock = {
                     cls : 'roo-removable',
                     cn :  [
@@ -11039,7 +12218,329 @@ Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
      */
     onTriggerClick : Roo.emptyFn
 });
- /*
+/*
+* Licence: LGPL
+*/
+
+/**
+ * @class Roo.bootstrap.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.CardUploader = function(config){
+    
+    
+    Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
+    
+    
+    this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
+        return r.data.id
+        });
+    
+    
+};
+
+Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.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',
+                    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.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.CardUploader.ID--,
+            is_uploaded : false,
+            src : url,
+            title : file.name,
+            mimetype : file.type,
+            preview : false,
+            is_deleted : 0
+        })
+        
+    },
+    
+    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-11 text-left',
+                                size: 'sm',
+                                weight: 'link',
+                                fa : 'download',
+                                listeners : {
+                                    click : function() {
+                                        this.downloadCard(data.id)
+                                    }
+                                }
+                            },
+                          
+                            {
+                                xns : Roo.bootstrap,
+                                xtype : 'Button',
+                                
+                                size : 'sm',
+                                weight: 'danger',
+                                cls : 'col-1',
+                                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);
+                    this.imgEl = this.el.select('.card-img-top').first();
+                    if (this.imgEl) {
+                        this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
+                        this.imgEl.set({ 'pointer' : 'cursor' });
+                                  
+                    }
+                    
+                  
+                }
+                
+            }
+        );
+        // dont' really need ot update items.
+        // this.items.push(cn);
+        this.fileCollection.add(cn);
+        this.updateInput();
+        
+    },
+    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);
+        
+    },
+    reset: function()
+    {
+        this.fileCollection.each(function(card) {
+            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.CardUploader.ID = -1;/*
  * Based on:
  * Ext JS Library 1.1.1
  * Copyright(c) 2006-2007, Ext JS, LLC.
@@ -11825,6 +13326,16 @@ Roo.extend(Roo.data.Store, Roo.util.Observable, {
         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.
@@ -12129,14 +13640,16 @@ Roo.extend(Roo.data.Store, Roo.util.Observable, {
  * 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
  * @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)
@@ -12313,6 +13826,9 @@ Roo.data.DataReader = function(meta, recordType){
 };
 
 Roo.data.DataReader.prototype = {
+    
+    
+    readerType : 'Data',
      /**
      * Create an empty record
      * @param {Object} data (optional) - overlay some values
@@ -12333,6 +13849,7 @@ Roo.data.DataReader.prototype = {
         return new this.recordType(Roo.apply(da, d));
     }
     
+    
 };/*
  * Based on:
  * Ext JS Library 1.1.1
@@ -12871,6 +14388,8 @@ Roo.data.JsonReader = function(meta, recordType){
 };
 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.
@@ -13012,6 +14531,14 @@ Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
             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:
@@ -13061,44 +14588,54 @@ var myReader = new Roo.data.ArrayReader({
  * 
  * created using {@link Roo.data.Record#create}.
  */
-Roo.data.ArrayReader = function(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} 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 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;
+       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;
            }
-           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
  * * 
@@ -13244,7 +14781,7 @@ Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
      */
 
      /**
-     * @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>' 
      */
      
      /**
@@ -13594,6 +15131,8 @@ Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
             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',
@@ -20912,10 +22451,10 @@ Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
         
         var cfg = {};
         
-        cfg.cls = 'form-group ' + this.inputType; //input-group
+        cfg.cls = 'form-group form-check ' + this.inputType; //input-group
         
         if(this.inline){
-            cfg.cls += ' ' + this.inputType + '-inline';
+            cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
         }
         
         var input =  {
@@ -22184,7 +23723,7 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
             //<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
@@ -22548,7 +24087,11 @@ Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
     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());
@@ -24493,6 +26036,12 @@ Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
      */
     isLocked : function(){
         return this.locked;
+    },
+    
+    
+    initEvents : function ()
+    {
+        
     }
 });
 /**