2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
22 * Ext JS Library 1.1.1
23 * Copyright(c) 2006-2007, Ext JS, LLC.
25 * Originally Released Under LGPL - original licence link has changed is not relivant.
28 * <script type="text/javascript">
34 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
35 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
36 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
39 * @param {Object} config The config object
41 Roo.Shadow = function(config){
42 Roo.apply(this, config);
43 if(typeof this.mode != "string"){
44 this.mode = this.defaultMode;
46 var o = this.offset, a = {h: 0};
47 var rad = Math.floor(this.offset/2);
48 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
54 a.l -= this.offset + rad;
55 a.t -= this.offset + rad;
66 a.l -= (this.offset - rad);
67 a.t -= this.offset + rad;
69 a.w -= (this.offset - rad)*2;
80 a.l -= (this.offset - rad);
81 a.t -= (this.offset - rad);
83 a.w -= (this.offset + rad + 1);
84 a.h -= (this.offset + rad);
93 Roo.Shadow.prototype = {
96 * The shadow display mode. Supports the following options:<br />
97 * sides: Shadow displays on both sides and bottom only<br />
98 * frame: Shadow displays equally on all four sides<br />
99 * drop: Traditional bottom-right drop shadow (default)
103 * @cfg {String} offset
104 * The number of pixels to offset the shadow from the element (defaults to 4)
112 * Displays the shadow under the target element
113 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
115 show : function(target){
116 target = Roo.get(target);
118 this.el = Roo.Shadow.Pool.pull();
119 if(this.el.dom.nextSibling != target.dom){
120 this.el.insertBefore(target);
123 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
125 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
128 target.getLeft(true),
133 this.el.dom.style.display = "block";
137 * Returns true if the shadow is visible, else false
139 isVisible : function(){
140 return this.el ? true : false;
144 * Direct alignment when values are already available. Show must be called at least once before
145 * calling this method to ensure it is initialized.
146 * @param {Number} left The target element left position
147 * @param {Number} top The target element top position
148 * @param {Number} width The target element width
149 * @param {Number} height The target element height
151 realign : function(l, t, w, h){
155 var a = this.adjusts, d = this.el.dom, s = d.style;
157 s.left = (l+a.l)+"px";
158 s.top = (t+a.t)+"px";
159 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
161 if(s.width != sws || s.height != shs){
165 var cn = d.childNodes;
166 var sww = Math.max(0, (sw-12))+"px";
167 cn[0].childNodes[1].style.width = sww;
168 cn[1].childNodes[1].style.width = sww;
169 cn[2].childNodes[1].style.width = sww;
170 cn[1].style.height = Math.max(0, (sh-12))+"px";
180 this.el.dom.style.display = "none";
181 Roo.Shadow.Pool.push(this.el);
187 * Adjust the z-index of this shadow
188 * @param {Number} zindex The new z-index
190 setZIndex : function(z){
193 this.el.setStyle("z-index", z);
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
201 var markup = Roo.isIE ?
202 '<div class="x-ie-shadow"></div>' :
203 '<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>';
208 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209 sh.autoBoxAdjust = false;
221 * base class for bootstrap elements.
225 Roo.bootstrap = Roo.bootstrap || {};
227 * @class Roo.bootstrap.Component
228 * @extends Roo.Component
230 * @children Roo.bootstrap.Component
231 * Bootstrap Component base class
232 * @cfg {String} cls css class
233 * @cfg {String} style any extra css
234 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
236 * @cfg {string} dataId cutomer id
237 * @cfg {string} name Specifies name attribute
238 * @cfg {string} tooltip Text for the tooltip
239 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
240 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
243 * Do not use directly - it does not do anything..
244 * @param {Object} config The config object
249 Roo.bootstrap.Component = function(config){
250 Roo.bootstrap.Component.superclass.constructor.call(this, config);
254 * @event childrenrendered
255 * Fires when the children have been rendered..
256 * @param {Roo.bootstrap.Component} this
258 "childrenrendered" : true
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
270 allowDomMove : false, // to stop relocations in parent onRender...
280 * Initialize Events for the element
282 initEvents : function() { },
288 can_build_overlaid : true,
290 container_method : false,
297 // returns the parent component..
298 return Roo.ComponentMgr.get(this.parentId)
304 onRender : function(ct, position)
306 // Roo.log("Call onRender: " + this.xtype);
308 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
311 if (this.el.attr('xtype')) {
312 this.el.attr('xtypex', this.el.attr('xtype'));
313 this.el.dom.removeAttribute('xtype');
323 var cfg = Roo.apply({}, this.getAutoCreate());
325 cfg.id = this.id || Roo.id();
327 // fill in the extra attributes
328 if (this.xattr && typeof(this.xattr) =='object') {
329 for (var i in this.xattr) {
330 cfg[i] = this.xattr[i];
335 cfg.dataId = this.dataId;
339 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
342 if (this.style) { // fixme needs to support more complex style data.
343 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347 cfg.name = this.name;
350 this.el = ct.createChild(cfg, position);
353 this.tooltipEl().attr('tooltip', this.tooltip);
356 if(this.tabIndex !== undefined){
357 this.el.dom.setAttribute('tabIndex', this.tabIndex);
364 * Fetch the element to add children to
365 * @return {Roo.Element} defaults to this.el
367 getChildContainer : function()
371 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
373 return Roo.get(document.body);
377 * Fetch the element to display the tooltip on.
378 * @return {Roo.Element} defaults to this.el
380 tooltipEl : function()
385 addxtype : function(tree,cntr)
389 cn = Roo.factory(tree);
390 //Roo.log(['addxtype', cn]);
392 cn.parentType = this.xtype; //??
393 cn.parentId = this.id;
395 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396 if (typeof(cn.container_method) == 'string') {
397 cntr = cn.container_method;
401 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
403 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
405 var build_from_html = Roo.XComponent.build_from_html;
407 var is_body = (tree.xtype == 'Body') ;
409 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
411 var self_cntr_el = Roo.get(this[cntr](false));
413 // do not try and build conditional elements
414 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420 return this.addxtypeChild(tree,cntr, is_body);
423 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
426 return this.addxtypeChild(Roo.apply({}, tree),cntr);
429 Roo.log('skipping render');
435 if (!build_from_html) {
439 // this i think handles overlaying multiple children of the same type
440 // with the sam eelement.. - which might be buggy..
442 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
448 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
459 addxtypeChild : function (tree, cntr, is_body)
461 Roo.debug && Roo.log('addxtypeChild:' + cntr);
463 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
466 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467 (typeof(tree['flexy:foreach']) != 'undefined');
471 skip_children = false;
472 // render the element if it's not BODY.
475 // if parent was disabled, then do not try and create the children..
476 if(!this[cntr](true)){
481 cn = Roo.factory(tree);
483 cn.parentType = this.xtype; //??
484 cn.parentId = this.id;
486 var build_from_html = Roo.XComponent.build_from_html;
489 // does the container contain child eleemnts with 'xtype' attributes.
490 // that match this xtype..
491 // note - when we render we create these as well..
492 // so we should check to see if body has xtype set.
493 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
495 var self_cntr_el = Roo.get(this[cntr](false));
496 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
498 //Roo.log(Roo.XComponent.build_from_html);
499 //Roo.log("got echild:");
502 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503 // and are not displayed -this causes this to use up the wrong element when matching.
504 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
507 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
514 //echild.dom.removeAttribute('xtype');
516 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517 Roo.debug && Roo.log(self_cntr_el);
518 Roo.debug && Roo.log(echild);
519 Roo.debug && Roo.log(cn);
525 // if object has flexy:if - then it may or may not be rendered.
526 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
527 // skip a flexy if element.
528 Roo.debug && Roo.log('skipping render');
529 Roo.debug && Roo.log(tree);
531 Roo.debug && Roo.log('skipping all children');
532 skip_children = true;
537 // actually if flexy:foreach is found, we really want to create
538 // multiple copies here...
540 //Roo.log(this[cntr]());
541 // some elements do not have render methods.. like the layouts...
543 if(this[cntr](true) === false){
548 cn.render && cn.render(this[cntr](true));
551 // then add the element..
558 if (typeof (tree.menu) != 'undefined') {
559 tree.menu.parentType = cn.xtype;
560 tree.menu.triggerEl = cn.el;
561 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
565 if (!tree.items || !tree.items.length) {
567 //Roo.log(["no children", this]);
572 var items = tree.items;
575 //Roo.log(items.length);
577 if (!skip_children) {
578 for(var i =0;i < items.length;i++) {
579 // Roo.log(['add child', items[i]]);
580 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
586 //Roo.log("fire childrenrendered");
588 cn.fireEvent('childrenrendered', this);
594 * Set the element that will be used to show or hide
596 setVisibilityEl : function(el)
598 this.visibilityEl = el;
602 * Get the element that will be used to show or hide
604 getVisibilityEl : function()
606 if (typeof(this.visibilityEl) == 'object') {
607 return this.visibilityEl;
610 if (typeof(this.visibilityEl) == 'string') {
611 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
618 * Show a component - removes 'hidden' class
622 if(!this.getVisibilityEl()){
626 this.getVisibilityEl().removeClass(['hidden','d-none']);
628 this.fireEvent('show', this);
633 * Hide a component - adds 'hidden' class
637 if(!this.getVisibilityEl()){
641 this.getVisibilityEl().addClass(['hidden','d-none']);
643 this.fireEvent('hide', this);
656 * @class Roo.bootstrap.Element
657 * @extends Roo.bootstrap.Component
658 * @children Roo.bootstrap.Component
659 * Bootstrap Element class (basically a DIV used to make random stuff )
661 * @cfg {String} html contents of the element
662 * @cfg {String} tag tag of the element
663 * @cfg {String} cls class of the element
664 * @cfg {Boolean} preventDefault (true|false) default false
665 * @cfg {Boolean} clickable (true|false) default false
666 * @cfg {String} role default blank - set to button to force cursor pointer
670 * Create a new Element
671 * @param {Object} config The config object
674 Roo.bootstrap.Element = function(config){
675 Roo.bootstrap.Element.superclass.constructor.call(this, config);
681 * When a element is chick
682 * @param {Roo.bootstrap.Element} this
683 * @param {Roo.EventObject} e
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
696 preventDefault: false,
701 getAutoCreate : function(){
705 // cls: this.cls, double assign in parent class Component.js :: onRender
708 if (this.role !== false) {
709 cfg.role = this.role;
715 initEvents: function()
717 Roo.bootstrap.Element.superclass.initEvents.call(this);
720 this.el.on('click', this.onClick, this);
726 onClick : function(e)
728 if(this.preventDefault){
732 this.fireEvent('click', this, e); // why was this double click before?
740 getValue : function()
742 return this.el.dom.innerHTML;
745 setValue : function(value)
747 this.el.dom.innerHTML = value;
762 * @class Roo.bootstrap.DropTarget
763 * @extends Roo.bootstrap.Element
764 * Bootstrap DropTarget class
766 * @cfg {string} name dropable name
769 * Create a new Dropable Area
770 * @param {Object} config The config object
773 Roo.bootstrap.DropTarget = function(config){
774 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
780 * When a element is chick
781 * @param {Roo.bootstrap.Element} this
782 * @param {Roo.EventObject} e
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
791 getAutoCreate : function(){
796 initEvents: function()
798 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
802 drop : this.dragDrop.createDelegate(this),
803 enter : this.dragEnter.createDelegate(this),
804 out : this.dragOut.createDelegate(this),
805 over : this.dragOver.createDelegate(this)
809 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
812 dragDrop : function(source,e,data)
814 // user has to decide how to impliment this.
817 //this.fireEvent('drop', this, source, e ,data);
821 dragEnter : function(n, dd, e, data)
823 // probably want to resize the element to match the dropped element..
825 this.originalSize = this.el.getSize();
826 this.el.setSize( n.el.getSize());
827 this.dropZone.DDM.refreshCache(this.name);
828 Roo.log([n, dd, e, data]);
831 dragOut : function(value)
833 // resize back to normal
835 this.el.setSize(this.originalSize);
836 this.dropZone.resetConstraints();
839 dragOver : function()
856 * @class Roo.bootstrap.Body
857 * @extends Roo.bootstrap.Component
858 * @children Roo.bootstrap.Component
859 * @parent none builder
860 * Bootstrap Body class
864 * @param {Object} config The config object
867 Roo.bootstrap.Body = function(config){
869 config = config || {};
871 Roo.bootstrap.Body.superclass.constructor.call(this, config);
872 this.el = Roo.get(config.el ? config.el : document.body );
873 if (this.cls && this.cls.length) {
874 Roo.get(document.body).addClass(this.cls);
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
880 is_body : true,// just to make sure it's constructed?
885 onRender : function(ct, position)
887 /* Roo.log("Roo.bootstrap.Body - onRender");
888 if (this.cls && this.cls.length) {
889 Roo.get(document.body).addClass(this.cls);
908 * @class Roo.bootstrap.ButtonGroup
909 * @extends Roo.bootstrap.Component
910 * Bootstrap ButtonGroup class
911 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913 * @cfg {String} size lg | sm | xs (default empty normal)
914 * @cfg {String} align vertical | justified (default none)
915 * @cfg {String} direction up | down (default down)
916 * @cfg {Boolean} toolbar false | true
917 * @cfg {Boolean} btn true | false
922 * @param {Object} config The config object
925 Roo.bootstrap.ButtonGroup = function(config){
926 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
937 getAutoCreate : function(){
943 cfg.html = this.html || cfg.html;
954 if (['vertical','justified'].indexOf(this.align)!==-1) {
955 cfg.cls = 'btn-group-' + this.align;
957 if (this.align == 'justified') {
958 console.log(this.items);
962 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963 cfg.cls += ' btn-group-' + this.size;
966 if (this.direction == 'up') {
967 cfg.cls += ' dropup' ;
973 * Add a button to the group (similar to NavItem API.)
975 addItem : function(cfg)
977 var cn = new Roo.bootstrap.Button(cfg);
979 cn.parentId = this.id;
980 cn.onRender(this.el, null);
994 * @class Roo.bootstrap.Button
995 * @extends Roo.bootstrap.Component
996 * Bootstrap Button class
997 * @cfg {String} html The button content
998 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001 * @cfg {String} size (lg|sm|xs)
1002 * @cfg {String} tag (a|input|submit)
1003 * @cfg {String} href empty or href
1004 * @cfg {Boolean} disabled default false;
1005 * @cfg {Boolean} isClose default false;
1006 * @cfg {String} glyphicon depricated - use fa
1007 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008 * @cfg {String} badge text for badge
1009 * @cfg {String} theme (default|glow)
1010 * @cfg {Boolean} inverse dark themed version
1011 * @cfg {Boolean} toggle is it a slidy toggle button
1012 * @cfg {Boolean} pressed default null - if the button ahs active state
1013 * @cfg {String} ontext text for on slidy toggle state
1014 * @cfg {String} offtext text for off slidy toggle state
1015 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1016 * @cfg {Boolean} removeClass remove the standard class..
1017 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1018 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1022 * Create a new button
1023 * @param {Object} config The config object
1027 Roo.bootstrap.Button = function(config){
1028 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1034 * When a button is pressed
1035 * @param {Roo.bootstrap.Button} btn
1036 * @param {Roo.EventObject} e
1041 * When a button is double clicked
1042 * @param {Roo.bootstrap.Button} btn
1043 * @param {Roo.EventObject} e
1048 * After the button has been toggles
1049 * @param {Roo.bootstrap.Button} btn
1050 * @param {Roo.EventObject} e
1051 * @param {boolean} pressed (also available as button.pressed)
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1078 preventDefault: true,
1087 getAutoCreate : function(){
1095 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097 this.tag = 'button';
1101 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103 if (this.toggle == true) {
1106 cls: 'slider-frame roo-button',
1110 'data-on-text':'ON',
1111 'data-off-text':'OFF',
1112 cls: 'slider-button',
1117 // why are we validating the weights?
1118 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119 cfg.cls += ' ' + this.weight;
1126 cfg.cls += ' close';
1128 cfg["aria-hidden"] = true;
1130 cfg.html = "×";
1136 if (this.theme==='default') {
1137 cfg.cls = 'btn roo-button';
1139 //if (this.parentType != 'Navbar') {
1140 this.weight = this.weight.length ? this.weight : 'default';
1142 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146 cfg.cls += ' btn-' + outline + weight;
1147 if (this.weight == 'default') {
1149 cfg.cls += ' btn-' + this.weight;
1152 } else if (this.theme==='glow') {
1155 cfg.cls = 'btn-glow roo-button';
1157 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159 cfg.cls += ' ' + this.weight;
1165 this.cls += ' inverse';
1169 if (this.active || this.pressed === true) {
1170 cfg.cls += ' active';
1173 if (this.disabled) {
1174 cfg.disabled = 'disabled';
1178 Roo.log('changing to ul' );
1180 this.glyphicon = 'caret';
1181 if (Roo.bootstrap.version == 4) {
1182 this.fa = 'caret-down';
1187 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189 //gsRoo.log(this.parentType);
1190 if (this.parentType === 'Navbar' && !this.parent().bar) {
1191 Roo.log('changing to li?');
1200 href : this.href || '#'
1203 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1204 cfg.cls += ' dropdown';
1211 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1213 if (this.glyphicon) {
1214 cfg.html = ' ' + cfg.html;
1219 cls: 'glyphicon glyphicon-' + this.glyphicon
1224 cfg.html = ' ' + cfg.html;
1229 cls: 'fa fas fa-' + this.fa
1239 // cfg.cls='btn roo-button';
1243 var value = cfg.html;
1248 cls: 'glyphicon glyphicon-' + this.glyphicon,
1255 cls: 'fa fas fa-' + this.fa,
1260 var bw = this.badge_weight.length ? this.badge_weight :
1261 (this.weight.length ? this.weight : 'secondary');
1262 bw = bw == 'default' ? 'secondary' : bw;
1268 cls: 'badge badge-' + bw,
1277 cfg.cls += ' dropdown';
1278 cfg.html = typeof(cfg.html) != 'undefined' ?
1279 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1282 if (cfg.tag !== 'a' && this.href !== '') {
1283 throw "Tag must be a to set href.";
1284 } else if (this.href.length > 0) {
1285 cfg.href = this.href;
1288 if(this.removeClass){
1293 cfg.target = this.target;
1298 initEvents: function() {
1299 // Roo.log('init events?');
1300 // Roo.log(this.el.dom);
1303 if (typeof (this.menu) != 'undefined') {
1304 this.menu.parentType = this.xtype;
1305 this.menu.triggerEl = this.el;
1306 this.addxtype(Roo.apply({}, this.menu));
1310 if (this.el.hasClass('roo-button')) {
1311 this.el.on('click', this.onClick, this);
1312 this.el.on('dblclick', this.onDblClick, this);
1314 this.el.select('.roo-button').on('click', this.onClick, this);
1315 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1319 if(this.removeClass){
1320 this.el.on('click', this.onClick, this);
1323 if (this.group === true) {
1324 if (this.pressed === false || this.pressed === true) {
1327 this.pressed = false;
1328 this.setActive(this.pressed);
1333 this.el.enableDisplayMode();
1336 onClick : function(e)
1338 if (this.disabled) {
1342 Roo.log('button on click ');
1343 if(this.preventDefault){
1352 this.setActive(true);
1353 var pi = this.parent().items;
1354 for (var i = 0;i < pi.length;i++) {
1355 if (this == pi[i]) {
1358 if (pi[i].el.hasClass('roo-button')) {
1359 pi[i].setActive(false);
1362 this.fireEvent('click', this, e);
1366 if (this.pressed === true || this.pressed === false) {
1367 this.toggleActive(e);
1371 this.fireEvent('click', this, e);
1373 onDblClick: function(e)
1375 if (this.disabled) {
1378 if(this.preventDefault){
1381 this.fireEvent('dblclick', this, e);
1384 * Enables this button
1388 this.disabled = false;
1389 this.el.removeClass('disabled');
1390 this.el.dom.removeAttribute("disabled");
1394 * Disable this button
1396 disable : function()
1398 this.disabled = true;
1399 this.el.addClass('disabled');
1400 this.el.attr("disabled", "disabled")
1403 * sets the active state on/off,
1404 * @param {Boolean} state (optional) Force a particular state
1406 setActive : function(v) {
1408 this.el[v ? 'addClass' : 'removeClass']('active');
1412 * toggles the current active state
1414 toggleActive : function(e)
1416 this.setActive(!this.pressed); // this modifies pressed...
1417 this.fireEvent('toggle', this, e, this.pressed);
1420 * get the current active state
1421 * @return {boolean} true if it's active
1423 isActive : function()
1425 return this.el.hasClass('active');
1428 * set the text of the first selected button
1430 setText : function(str)
1432 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1435 * get the text of the first selected button
1437 getText : function()
1439 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1442 setWeight : function(str)
1444 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447 var outline = this.outline ? 'outline-' : '';
1448 if (str == 'default') {
1449 this.el.addClass('btn-default btn-outline-secondary');
1452 this.el.addClass('btn-' + outline + str);
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459 Roo.bootstrap.Button.weights = [
1479 * @class Roo.bootstrap.Column
1480 * @extends Roo.bootstrap.Component
1481 * @children Roo.bootstrap.Component
1482 * Bootstrap Column class
1483 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1493 * @cfg {Boolean} hidden (true|false) hide the element
1494 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495 * @cfg {String} fa (ban|check|...) font awesome icon
1496 * @cfg {Number} fasize (1|2|....) font awsome size
1498 * @cfg {String} icon (info-sign|check|...) glyphicon name
1500 * @cfg {String} html content of column.
1503 * Create a new Column
1504 * @param {Object} config The config object
1507 Roo.bootstrap.Column = function(config){
1508 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1529 getAutoCreate : function(){
1530 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538 var sizes = ['xs','sm','md','lg'];
1539 sizes.map(function(size ,ix){
1540 //Roo.log( size + ':' + settings[size]);
1542 if (settings[size+'off'] !== false) {
1543 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1546 if (settings[size] === false) {
1550 if (!settings[size]) { // 0 = hidden
1551 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553 for (var i = ix; i > -1; i--) {
1554 cfg.cls += ' d-' + sizes[i] + '-none';
1560 cfg.cls += ' col-' + size + '-' + settings[size] + (
1561 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1567 cfg.cls += ' hidden';
1570 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571 cfg.cls +=' alert alert-' + this.alert;
1575 if (this.html.length) {
1576 cfg.html = this.html;
1580 if (this.fasize > 1) {
1581 fasize = ' fa-' + this.fasize + 'x';
1583 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1588 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1607 * @class Roo.bootstrap.Container
1608 * @extends Roo.bootstrap.Component
1609 * @children Roo.bootstrap.Component
1611 * Bootstrap Container class
1612 * @cfg {Boolean} jumbotron is it a jumbotron element
1613 * @cfg {String} html content of element
1614 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1616 * @cfg {String} header content of header (for panel)
1617 * @cfg {String} footer content of footer (for panel)
1618 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619 * @cfg {String} tag (header|aside|section) type of HTML tag.
1620 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621 * @cfg {String} fa font awesome icon
1622 * @cfg {String} icon (info-sign|check|...) glyphicon name
1623 * @cfg {Boolean} hidden (true|false) hide the element
1624 * @cfg {Boolean} expandable (true|false) default false
1625 * @cfg {Boolean} expanded (true|false) default true
1626 * @cfg {String} rheader contet on the right of header
1627 * @cfg {Boolean} clickable (true|false) default false
1631 * Create a new Container
1632 * @param {Object} config The config object
1635 Roo.bootstrap.Container = function(config){
1636 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1642 * After the panel has been expand
1644 * @param {Roo.bootstrap.Container} this
1649 * After the panel has been collapsed
1651 * @param {Roo.bootstrap.Container} this
1656 * When a element is chick
1657 * @param {Roo.bootstrap.Container} this
1658 * @param {Roo.EventObject} e
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1682 getChildContainer : function() {
1688 if (this.panel.length) {
1689 return this.el.select('.panel-body',true).first();
1696 getAutoCreate : function(){
1699 tag : this.tag || 'div',
1703 if (this.jumbotron) {
1704 cfg.cls = 'jumbotron';
1709 // - this is applied by the parent..
1711 // cfg.cls = this.cls + '';
1714 if (this.sticky.length) {
1716 var bd = Roo.get(document.body);
1717 if (!bd.hasClass('bootstrap-sticky')) {
1718 bd.addClass('bootstrap-sticky');
1719 Roo.select('html',true).setStyle('height', '100%');
1722 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726 if (this.well.length) {
1727 switch (this.well) {
1730 cfg.cls +=' well well-' +this.well;
1739 cfg.cls += ' hidden';
1743 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744 cfg.cls +=' alert alert-' + this.alert;
1749 if (this.panel.length) {
1750 cfg.cls += ' panel panel-' + this.panel;
1752 if (this.header.length) {
1756 if(this.expandable){
1758 cfg.cls = cfg.cls + ' expandable';
1762 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1770 cls : 'panel-title',
1771 html : (this.expandable ? ' ' : '') + this.header
1775 cls: 'panel-header-right',
1781 cls : 'panel-heading',
1782 style : this.expandable ? 'cursor: pointer' : '',
1790 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1795 if (this.footer.length) {
1797 cls : 'panel-footer',
1806 body.html = this.html || cfg.html;
1807 // prefix with the icons..
1809 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1812 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1817 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818 cfg.cls = 'container';
1824 initEvents: function()
1826 if(this.expandable){
1827 var headerEl = this.headerEl();
1830 headerEl.on('click', this.onToggleClick, this);
1835 this.el.on('click', this.onClick, this);
1840 onToggleClick : function()
1842 var headerEl = this.headerEl();
1858 if(this.fireEvent('expand', this)) {
1860 this.expanded = true;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864 this.el.select('.panel-body',true).first().removeClass('hide');
1866 var toggleEl = this.toggleEl();
1872 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1877 collapse : function()
1879 if(this.fireEvent('collapse', this)) {
1881 this.expanded = false;
1883 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884 this.el.select('.panel-body',true).first().addClass('hide');
1886 var toggleEl = this.toggleEl();
1892 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896 toggleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902 return this.el.select('.panel-heading .fa',true).first();
1905 headerEl : function()
1907 if(!this.el || !this.panel.length || !this.header.length){
1911 return this.el.select('.panel-heading',true).first()
1916 if(!this.el || !this.panel.length){
1920 return this.el.select('.panel-body',true).first()
1923 titleEl : function()
1925 if(!this.el || !this.panel.length || !this.header.length){
1929 return this.el.select('.panel-title',true).first();
1932 setTitle : function(v)
1934 var titleEl = this.titleEl();
1940 titleEl.dom.innerHTML = v;
1943 getTitle : function()
1946 var titleEl = this.titleEl();
1952 return titleEl.dom.innerHTML;
1955 setRightTitle : function(v)
1957 var t = this.el.select('.panel-header-right',true).first();
1963 t.dom.innerHTML = v;
1966 onClick : function(e)
1970 this.fireEvent('click', this, e);
1975 * @class Roo.bootstrap.Card
1976 * @extends Roo.bootstrap.Component
1977 * @children Roo.bootstrap.Component
1979 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1982 * possible... may not be implemented..
1983 * @cfg {String} header_image src url of image.
1984 * @cfg {String|Object} header
1985 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1988 * @cfg {String} title
1989 * @cfg {String} subtitle
1990 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991 * @cfg {String} footer
1993 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995 * @cfg {String} margin (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003 * @cfg {String} padding (0|1|2|3|4|5)
2004 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006 * @cfg {String} padding_left (0|1|2|3|4|5)
2007 * @cfg {String} padding_right (0|1|2|3|4|5)
2008 * @cfg {String} padding_x (0|1|2|3|4|5)
2009 * @cfg {String} padding_y (0|1|2|3|4|5)
2011 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @config {Boolean} dragable if this card can be dragged.
2018 * @config {String} drag_group group for drag
2019 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2020 * @config {String} drop_group group for drag
2022 * @config {Boolean} collapsable can the body be collapsed.
2023 * @config {Boolean} collapsed is the body collapsed when rendered...
2024 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025 * @config {Boolean} rotated is the body rotated when rendered...
2028 * Create a new Container
2029 * @param {Object} config The config object
2032 Roo.bootstrap.Card = function(config){
2033 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2039 * When a element a card is dropped
2040 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044 * @param {String} position 'above' or 'below'
2045 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2051 * When a element a card is rotate
2052 * @param {Roo.bootstrap.Card} this
2053 * @param {Roo.Element} n the node being dropped?
2054 * @param {Boolean} rotate status
2059 * When a card element is dragged over ready to drop (return false to block dropable)
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Object} data from dragdrop
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2074 margin: '', /// may be better in component?
2104 collapsable : false,
2113 childContainer : false,
2114 dropEl : false, /// the dom placeholde element that indicates drop location.
2115 containerEl: false, // body container
2116 bodyEl: false, // card-body
2117 headerContainerEl : false, //
2119 header_imageEl : false,
2122 layoutCls : function()
2126 Roo.log(this.margin_bottom.length);
2127 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2133 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2138 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144 // more generic support?
2152 // Roo.log("Call onRender: " + this.xtype);
2153 /* We are looking at something like this.
2155 <img src="..." class="card-img-top" alt="...">
2156 <div class="card-body">
2157 <h5 class="card-title">Card title</h5>
2158 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160 >> this bit is really the body...
2161 <div> << we will ad dthis in hopefully it will not break shit.
2163 ** card text does not actually have any styling...
2165 <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>
2168 <a href="#" class="card-link">Card link</a>
2171 <div class="card-footer">
2172 <small class="text-muted">Last updated 3 mins ago</small>
2176 getAutoCreate : function(){
2184 if (this.weight.length && this.weight != 'light') {
2185 cfg.cls += ' text-white';
2187 cfg.cls += ' text-dark'; // need as it's nested..
2189 if (this.weight.length) {
2190 cfg.cls += ' bg-' + this.weight;
2193 cfg.cls += ' ' + this.layoutCls();
2196 var hdr_ctr = false;
2197 if (this.header.length) {
2199 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2214 if (this.collapsable) {
2217 cls : 'd-block user-select-none',
2221 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226 hdr.cn.push(hdr_ctr);
2231 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236 if (this.header_image.length) {
2239 cls : 'card-img-top',
2240 src: this.header_image // escape?
2245 cls : 'card-img-top d-none'
2251 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2255 if (this.collapsable || this.rotateable) {
2258 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2265 if (this.title.length) {
2269 src: this.title // escape?
2273 if (this.subtitle.length) {
2277 src: this.subtitle // escape?
2283 cls : 'roo-card-body-ctr'
2286 if (this.html.length) {
2292 // fixme ? handle objects?
2294 if (this.footer.length) {
2297 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302 cfg.cn.push({cls : 'card-footer d-none'});
2311 getCardHeader : function()
2313 var ret = this.el.select('.card-header',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardFooter : function()
2322 var ret = this.el.select('.card-footer',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardImageTop : function()
2331 var ret = this.header_imageEl;
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2339 getChildContainer : function()
2345 return this.el.select('.roo-card-body-ctr',true).first();
2348 initEvents: function()
2350 this.bodyEl = this.el.select('.card-body',true).first();
2351 this.containerEl = this.getChildContainer();
2353 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354 containerScroll: true,
2355 ddGroup: this.drag_group || 'default_card_drag_group'
2357 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359 if (this.dropable) {
2360 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361 containerScroll: true,
2362 ddGroup: this.drop_group || 'default_card_drag_group'
2364 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371 if (this.collapsable) {
2372 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374 if (this.rotateable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379 this.footerEl = this.el.select('.card-footer',true).first();
2380 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382 this.headerEl = this.el.select('.card-header',true).first();
2385 this.el.addClass('roo-card-rotated');
2386 this.fireEvent('rotate', this, true);
2388 this.header_imageEl = this.el.select('.card-img-top',true).first();
2389 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392 getDragData : function(e)
2394 var target = this.getEl();
2396 //this.handleSelection(e);
2401 nodes: this.getEl(),
2406 dragData.ddel = target.dom ; // the div element
2407 Roo.log(target.getWidth( ));
2408 dragData.ddel.style.width = target.getWidth() + 'px';
2415 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2416 * whole Element becomes the target, and this causes the drop gesture to append.
2418 * Returns an object:
2421 position : 'below' or 'above'
2422 card : relateive to card OBJECT (or true for no cards listed)
2423 items_n : relative to nth item in list
2424 card_n : relative to nth card in list
2429 getTargetFromEvent : function(e, dragged_card_el)
2431 var target = e.getTarget();
2432 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433 target = target.parentNode;
2444 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445 // see if target is one of the 'cards'...
2448 //Roo.log(this.items.length);
2451 var last_card_n = 0;
2453 for (var i = 0;i< this.items.length;i++) {
2455 if (!this.items[i].el.hasClass('card')) {
2458 pos = this.getDropPoint(e, this.items[i].el.dom);
2460 cards_len = ret.cards.length;
2461 //Roo.log(this.items[i].el.dom.id);
2462 ret.cards.push(this.items[i]);
2464 if (ret.card_n < 0 && pos == 'above') {
2465 ret.position = cards_len > 0 ? 'below' : pos;
2466 ret.items_n = i > 0 ? i - 1 : 0;
2467 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2468 ret.card = ret.cards[ret.card_n];
2471 if (!ret.cards.length) {
2473 ret.position = 'below';
2477 // could not find a card.. stick it at the end..
2478 if (ret.card_n < 0) {
2479 ret.card_n = last_card_n;
2480 ret.card = ret.cards[last_card_n];
2481 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482 ret.position = 'below';
2485 if (this.items[ret.items_n].el == dragged_card_el) {
2489 if (ret.position == 'below') {
2490 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492 if (card_after && card_after.el == dragged_card_el) {
2499 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501 if (card_before && card_before.el == dragged_card_el) {
2508 onNodeEnter : function(n, dd, e, data){
2511 onNodeOver : function(n, dd, e, data)
2514 var target_info = this.getTargetFromEvent(e,data.source.el);
2515 if (target_info === false) {
2516 this.dropPlaceHolder('hide');
2519 Roo.log(['getTargetFromEvent', target_info ]);
2522 if (this.fireEvent('cardover', this, [ data ]) === false) {
2526 this.dropPlaceHolder('show', target_info,data);
2530 onNodeOut : function(n, dd, e, data){
2531 this.dropPlaceHolder('hide');
2534 onNodeDrop : function(n, dd, e, data)
2537 // call drop - return false if
2539 // this could actually fail - if the Network drops..
2540 // we will ignore this at present..- client should probably reload
2541 // the whole set of cards if stuff like that fails.
2544 var info = this.getTargetFromEvent(e,data.source.el);
2545 if (info === false) {
2548 this.dropPlaceHolder('hide');
2552 this.acceptCard(data.source, info.position, info.card, info.items_n);
2556 firstChildCard : function()
2558 for (var i = 0;i< this.items.length;i++) {
2560 if (!this.items[i].el.hasClass('card')) {
2563 return this.items[i];
2565 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2572 acceptCard : function(move_card, position, next_to_card )
2574 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580 move_card.parent().removeCard(move_card);
2583 var dom = move_card.el.dom;
2584 dom.style.width = ''; // clear with - which is set by drag.
2586 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587 var cardel = next_to_card.el.dom;
2589 if (position == 'above' ) {
2590 cardel.parentNode.insertBefore(dom, cardel);
2591 } else if (cardel.nextSibling) {
2592 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594 cardel.parentNode.append(dom);
2597 // card container???
2598 this.containerEl.dom.append(dom);
2601 //FIXME HANDLE card = true
2603 // add this to the correct place in items.
2605 // remove Card from items.
2608 if (this.items.length) {
2610 //Roo.log([info.items_n, info.position, this.items.length]);
2611 for (var i =0; i < this.items.length; i++) {
2612 if (i == to_items_n && position == 'above') {
2613 nitems.push(move_card);
2615 nitems.push(this.items[i]);
2616 if (i == to_items_n && position == 'below') {
2617 nitems.push(move_card);
2620 this.items = nitems;
2621 Roo.log(this.items);
2623 this.items.push(move_card);
2626 move_card.parentId = this.id;
2632 removeCard : function(c)
2634 this.items = this.items.filter(function(e) { return e != c });
2637 dom.parentNode.removeChild(dom);
2638 dom.style.width = ''; // clear with - which is set by drag.
2643 /** Decide whether to drop above or below a View node. */
2644 getDropPoint : function(e, n, dd)
2649 if (n == this.containerEl.dom) {
2652 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653 var c = t + (b - t) / 2;
2654 var y = Roo.lib.Event.getPageY(e);
2661 onToggleCollapse : function(e)
2663 if (this.collapsed) {
2664 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665 this.collapsableEl.addClass('show');
2666 this.collapsed = false;
2669 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670 this.collapsableEl.removeClass('show');
2671 this.collapsed = true;
2676 onToggleRotate : function(e)
2678 this.collapsableEl.removeClass('show');
2679 this.footerEl.removeClass('d-none');
2680 this.el.removeClass('roo-card-rotated');
2681 this.el.removeClass('d-none');
2684 this.collapsableEl.addClass('show');
2685 this.rotated = false;
2686 this.fireEvent('rotate', this, this.rotated);
2689 this.el.addClass('roo-card-rotated');
2690 this.footerEl.addClass('d-none');
2691 this.el.select('.roo-collapsable').removeClass('show');
2693 this.rotated = true;
2694 this.fireEvent('rotate', this, this.rotated);
2698 dropPlaceHolder: function (action, info, data)
2700 if (this.dropEl === false) {
2701 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705 this.dropEl.removeClass(['d-none', 'd-block']);
2706 if (action == 'hide') {
2708 this.dropEl.addClass('d-none');
2711 // FIXME - info.card == true!!!
2712 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714 if (info.card !== true) {
2715 var cardel = info.card.el.dom;
2717 if (info.position == 'above') {
2718 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719 } else if (cardel.nextSibling) {
2720 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722 cardel.parentNode.append(this.dropEl.dom);
2725 // card container???
2726 this.containerEl.dom.append(this.dropEl.dom);
2729 this.dropEl.addClass('d-block roo-card-dropzone');
2731 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2738 setHeaderText: function(html)
2741 if (this.headerContainerEl) {
2742 this.headerContainerEl.dom.innerHTML = html;
2745 onHeaderImageLoad : function(ev, he)
2747 if (!this.header_image_fit_square) {
2751 var hw = he.naturalHeight / he.naturalWidth;
2754 //var w = he.dom.naturalWidth;
2757 he.style.position = 'relative';
2759 var nw = (ww * (1/hw));
2760 Roo.get(he).setSize( ww * (1/hw), ww);
2761 he.style.left = ((ww - nw)/ 2) + 'px';
2762 he.style.position = 'relative';
2773 * Card header - holder for the card header elements.
2778 * @class Roo.bootstrap.CardHeader
2779 * @extends Roo.bootstrap.Element
2780 * @parent Roo.bootstrap.Card
2781 * @children Roo.bootstrap.Component
2782 * Bootstrap CardHeader class
2784 * Create a new Card Header - that you can embed children into
2785 * @param {Object} config The config object
2788 Roo.bootstrap.CardHeader = function(config){
2789 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2795 container_method : 'getCardHeader'
2808 * Card footer - holder for the card footer elements.
2813 * @class Roo.bootstrap.CardFooter
2814 * @extends Roo.bootstrap.Element
2815 * @parent Roo.bootstrap.Card
2816 * @children Roo.bootstrap.Component
2817 * Bootstrap CardFooter class
2820 * Create a new Card Footer - that you can embed children into
2821 * @param {Object} config The config object
2824 Roo.bootstrap.CardFooter = function(config){
2825 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2831 container_method : 'getCardFooter'
2844 * Card header - holder for the card header elements.
2849 * @class Roo.bootstrap.CardImageTop
2850 * @extends Roo.bootstrap.Element
2851 * @parent Roo.bootstrap.Card
2852 * @children Roo.bootstrap.Component
2853 * Bootstrap CardImageTop class
2856 * Create a new Card Image Top container
2857 * @param {Object} config The config object
2860 Roo.bootstrap.CardImageTop = function(config){
2861 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2867 container_method : 'getCardImageTop'
2882 * @class Roo.bootstrap.ButtonUploader
2883 * @extends Roo.bootstrap.Button
2884 * Bootstrap Button Uploader class - it's a button which when you add files to it
2887 * @cfg {Number} errorTimeout default 3000
2888 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2889 * @cfg {Array} html The button text.
2890 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2893 * Create a new CardUploader
2894 * @param {Object} config The config object
2897 Roo.bootstrap.ButtonUploader = function(config){
2901 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2907 * @event beforeselect
2908 * When button is pressed, before show upload files dialog is shown
2909 * @param {Roo.bootstrap.UploaderButton} this
2912 'beforeselect' : true,
2914 * @event fired when files have been selected,
2915 * When a the download link is clicked
2916 * @param {Roo.bootstrap.UploaderButton} this
2917 * @param {Array} Array of files that have been uploaded
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2927 errorTimeout : 3000,
2931 fileCollection : false,
2936 getAutoCreate : function()
2941 cls : 'd-none roo-card-upload-selector'
2944 if (this.multiple) {
2945 im.multiple = 'multiple';
2951 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2961 initEvents : function()
2964 Roo.bootstrap.Button.prototype.initEvents.call(this);
2970 this.urlAPI = (window.createObjectURL && window) ||
2971 (window.URL && URL.revokeObjectURL && URL) ||
2972 (window.webkitURL && webkitURL);
2977 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979 this.selectorEl.on('change', this.onFileSelected, this);
2986 onClick : function(e)
2990 if ( this.fireEvent('beforeselect', this) === false) {
2994 this.selectorEl.dom.click();
2998 onFileSelected : function(e)
3002 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3005 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006 this.selectorEl.dom.value = '';// hopefully reset..
3008 this.fireEvent('uploaded', this, files );
3016 * addCard - add an Attachment to the uploader
3017 * @param data - the data about the image to upload
3021 title : "Title of file",
3022 is_uploaded : false,
3023 src : "http://.....",
3024 srcfile : { the File upload object },
3025 mimetype : file.type,
3028 .. any other data...
3053 * @class Roo.bootstrap.Img
3054 * @extends Roo.bootstrap.Component
3055 * Bootstrap Img class
3056 * @cfg {Boolean} imgResponsive false | true
3057 * @cfg {String} border rounded | circle | thumbnail
3058 * @cfg {String} src image source
3059 * @cfg {String} alt image alternative text
3060 * @cfg {String} href a tag href
3061 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062 * @cfg {String} xsUrl xs image source
3063 * @cfg {String} smUrl sm image source
3064 * @cfg {String} mdUrl md image source
3065 * @cfg {String} lgUrl lg image source
3066 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3069 * Create a new Input
3070 * @param {Object} config The config object
3073 Roo.bootstrap.Img = function(config){
3074 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3080 * The img click event for the img.
3081 * @param {Roo.EventObject} e
3086 * The when any image loads
3087 * @param {Roo.EventObject} e
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3095 imgResponsive: true,
3104 backgroundContain : false,
3106 getAutoCreate : function()
3108 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109 return this.createSingleImg();
3114 cls: 'roo-image-responsive-group',
3119 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121 if(!_this[size + 'Url']){
3127 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128 html: _this.html || cfg.html,
3129 src: _this[size + 'Url']
3132 img.cls += ' roo-image-responsive-' + size;
3134 var s = ['xs', 'sm', 'md', 'lg'];
3136 s.splice(s.indexOf(size), 1);
3138 Roo.each(s, function(ss){
3139 img.cls += ' hidden-' + ss;
3142 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143 cfg.cls += ' img-' + _this.border;
3147 cfg.alt = _this.alt;
3160 a.target = _this.target;
3164 cfg.cn.push((_this.href) ? a : img);
3171 createSingleImg : function()
3175 cls: (this.imgResponsive) ? 'img-responsive' : '',
3177 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3180 if (this.backgroundContain) {
3181 cfg.cls += ' background-contain';
3184 cfg.html = this.html || cfg.html;
3186 if (this.backgroundContain) {
3187 cfg.style="background-image: url(" + this.src + ')';
3189 cfg.src = this.src || cfg.src;
3192 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193 cfg.cls += ' img-' + this.border;
3210 a.target = this.target;
3215 return (this.href) ? a : cfg;
3218 initEvents: function()
3221 this.el.on('click', this.onClick, this);
3223 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224 this.el.on('load', this.onImageLoad, this);
3226 // not sure if this works.. not tested
3227 this.el.select('img', true).on('load', this.onImageLoad, this);
3232 onClick : function(e)
3234 Roo.log('img onclick');
3235 this.fireEvent('click', this, e);
3237 onImageLoad: function(e)
3239 Roo.log('img load');
3240 this.fireEvent('load', this, e);
3244 * Sets the url of the image - used to update it
3245 * @param {String} url the url of the image
3248 setSrc : function(url)
3252 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253 if (this.backgroundContain) {
3254 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3256 this.el.dom.src = url;
3261 this.el.select('img', true).first().dom.src = url;
3277 * @class Roo.bootstrap.Link
3278 * @extends Roo.bootstrap.Component
3279 * @children Roo.bootstrap.Component
3280 * Bootstrap Link Class (eg. '<a href>')
3282 * @cfg {String} alt image alternative text
3283 * @cfg {String} href a tag href
3284 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285 * @cfg {String} html the content of the link.
3286 * @cfg {String} anchor name for the anchor link
3287 * @cfg {String} fa - favicon
3289 * @cfg {Boolean} preventDefault (true | false) default false
3293 * Create a new Input
3294 * @param {Object} config The config object
3297 Roo.bootstrap.Link = function(config){
3298 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3304 * The img click event for the img.
3305 * @param {Roo.EventObject} e
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3315 preventDefault: false,
3321 getAutoCreate : function()
3323 var html = this.html || '';
3325 if (this.fa !== false) {
3326 html = '<i class="fa fa-' + this.fa + '"></i>';
3331 // anchor's do not require html/href...
3332 if (this.anchor === false) {
3334 cfg.href = this.href || '#';
3336 cfg.name = this.anchor;
3337 if (this.html !== false || this.fa !== false) {
3340 if (this.href !== false) {
3341 cfg.href = this.href;
3345 if(this.alt !== false){
3350 if(this.target !== false) {
3351 cfg.target = this.target;
3357 initEvents: function() {
3359 if(!this.href || this.preventDefault){
3360 this.el.on('click', this.onClick, this);
3364 onClick : function(e)
3366 if(this.preventDefault){
3369 //Roo.log('img onclick');
3370 this.fireEvent('click', this, e);
3383 * @class Roo.bootstrap.Header
3384 * @extends Roo.bootstrap.Component
3385 * @children Roo.bootstrap.Component
3386 * Bootstrap Header class
3389 * @cfg {String} html content of header
3390 * @cfg {Number} level (1|2|3|4|5|6) default 1
3393 * Create a new Header
3394 * @param {Object} config The config object
3398 Roo.bootstrap.Header = function(config){
3399 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3410 getAutoCreate : function(){
3415 tag: 'h' + (1 *this.level),
3416 html: this.html || ''
3427 * @class Roo.bootstrap.MenuMgr
3429 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3432 Roo.bootstrap.menu.Manager = function(){
3433 var menus, active, groups = {}, attached = false, lastShow = new Date();
3435 // private - called when first menu is created
3438 active = new Roo.util.MixedCollection();
3439 Roo.get(document).addKeyListener(27, function(){
3440 if(active.length > 0){
3448 if(active && active.length > 0){
3449 var c = active.clone();
3459 if(active.length < 1){
3460 Roo.get(document).un("mouseup", onMouseDown);
3468 var last = active.last();
3469 lastShow = new Date();
3472 Roo.get(document).on("mouseup", onMouseDown);
3477 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478 m.parentMenu.activeChild = m;
3479 }else if(last && last.isVisible()){
3480 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3485 function onBeforeHide(m){
3487 m.activeChild.hide();
3489 if(m.autoHideTimer){
3490 clearTimeout(m.autoHideTimer);
3491 delete m.autoHideTimer;
3496 function onBeforeShow(m){
3497 var pm = m.parentMenu;
3498 if(!pm && !m.allowOtherMenus){
3500 }else if(pm && pm.activeChild && active != m){
3501 pm.activeChild.hide();
3505 // private this should really trigger on mouseup..
3506 function onMouseDown(e){
3507 Roo.log("on Mouse Up");
3509 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510 Roo.log("MenuManager hideAll");
3519 function onBeforeCheck(mi, state){
3521 var g = groups[mi.group];
3522 for(var i = 0, l = g.length; i < l; i++){
3524 g[i].setChecked(false);
3533 * Hides all menus that are currently visible
3535 hideAll : function(){
3540 register : function(menu){
3544 menus[menu.id] = menu;
3545 menu.on("beforehide", onBeforeHide);
3546 menu.on("hide", onHide);
3547 menu.on("beforeshow", onBeforeShow);
3548 menu.on("show", onShow);
3550 if(g && menu.events["checkchange"]){
3554 groups[g].push(menu);
3555 menu.on("checkchange", onCheck);
3560 * Returns a {@link Roo.menu.Menu} object
3561 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562 * be used to generate and return a new Menu instance.
3564 get : function(menu){
3565 if(typeof menu == "string"){ // menu id
3567 }else if(menu.events){ // menu instance
3570 /*else if(typeof menu.length == 'number'){ // array of menu items?
3571 return new Roo.bootstrap.Menu({items:menu});
3572 }else{ // otherwise, must be a config
3573 return new Roo.bootstrap.Menu(menu);
3580 unregister : function(menu){
3581 delete menus[menu.id];
3582 menu.un("beforehide", onBeforeHide);
3583 menu.un("hide", onHide);
3584 menu.un("beforeshow", onBeforeShow);
3585 menu.un("show", onShow);
3587 if(g && menu.events["checkchange"]){
3588 groups[g].remove(menu);
3589 menu.un("checkchange", onCheck);
3594 registerCheckable : function(menuItem){
3595 var g = menuItem.group;
3600 groups[g].push(menuItem);
3601 menuItem.on("beforecheckchange", onBeforeCheck);
3606 unregisterCheckable : function(menuItem){
3607 var g = menuItem.group;
3609 groups[g].remove(menuItem);
3610 menuItem.un("beforecheckchange", onBeforeCheck);
3616 * @class Roo.bootstrap.menu.Menu
3617 * @extends Roo.bootstrap.Component
3619 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3621 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3623 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624 * @cfg {bool} hidden if the menu should be hidden when rendered.
3625 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3626 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3628 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3632 * @param {Object} config The config objectQ
3636 Roo.bootstrap.menu.Menu = function(config){
3638 if (config.type == 'treeview') {
3639 // normally menu's are drawn attached to the document to handle layering etc..
3640 // however treeview (used by the docs menu is drawn into the parent element)
3641 this.container_method = 'getChildContainer';
3644 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645 if (this.registerMenu && this.type != 'treeview') {
3646 Roo.bootstrap.menu.Manager.register(this);
3653 * Fires before this menu is displayed (return false to block)
3654 * @param {Roo.menu.Menu} this
3659 * Fires before this menu is hidden (return false to block)
3660 * @param {Roo.menu.Menu} this
3665 * Fires after this menu is displayed
3666 * @param {Roo.menu.Menu} this
3671 * Fires after this menu is hidden
3672 * @param {Roo.menu.Menu} this
3677 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678 * @param {Roo.menu.Menu} this
3679 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * @param {Roo.EventObject} e
3685 * Fires when the mouse is hovering over this menu
3686 * @param {Roo.menu.Menu} this
3687 * @param {Roo.EventObject} e
3688 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3693 * Fires when the mouse exits this menu
3694 * @param {Roo.menu.Menu} this
3695 * @param {Roo.EventObject} e
3696 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3701 * Fires when a menu item contained in this menu is clicked
3702 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703 * @param {Roo.EventObject} e
3707 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3714 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3717 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719 registerMenu : true,
3721 menuItems :false, // stores the menu items..
3731 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733 hideTrigger : false,
3738 getChildContainer : function() {
3742 getAutoCreate : function(){
3744 //if (['right'].indexOf(this.align)!==-1) {
3745 // cfg.cn[1].cls += ' pull-right'
3750 cls : 'dropdown-menu shadow' ,
3751 style : 'z-index:1000'
3755 if (this.type === 'submenu') {
3756 cfg.cls = 'submenu active';
3758 if (this.type === 'treeview') {
3759 cfg.cls = 'treeview-menu';
3764 initEvents : function() {
3766 // Roo.log("ADD event");
3767 // Roo.log(this.triggerEl.dom);
3768 if (this.triggerEl) {
3770 this.triggerEl.on('click', this.onTriggerClick, this);
3772 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774 if (!this.hideTrigger) {
3775 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776 // dropdown toggle on the 'a' in BS4?
3777 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779 this.triggerEl.addClass('dropdown-toggle');
3785 this.el.on('touchstart' , this.onTouch, this);
3787 this.el.on('click' , this.onClick, this);
3789 this.el.on("mouseover", this.onMouseOver, this);
3790 this.el.on("mouseout", this.onMouseOut, this);
3794 findTargetItem : function(e)
3796 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3800 //Roo.log(t); Roo.log(t.id);
3802 //Roo.log(this.menuitems);
3803 return this.menuitems.get(t.id);
3805 //return this.items.get(t.menuItemId);
3811 onTouch : function(e)
3813 Roo.log("menu.onTouch");
3814 //e.stopEvent(); this make the user popdown broken
3818 onClick : function(e)
3820 Roo.log("menu.onClick");
3822 var t = this.findTargetItem(e);
3823 if(!t || t.isContainer){
3828 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3829 if(t == this.activeItem && t.shouldDeactivate(e)){
3830 this.activeItem.deactivate();
3831 delete this.activeItem;
3835 this.setActiveItem(t, true);
3843 Roo.log('pass click event');
3847 this.fireEvent("click", this, t, e);
3851 if(!t.href.length || t.href == '#'){
3852 (function() { _this.hide(); }).defer(100);
3857 onMouseOver : function(e){
3858 var t = this.findTargetItem(e);
3861 // if(t.canActivate && !t.disabled){
3862 // this.setActiveItem(t, true);
3866 this.fireEvent("mouseover", this, e, t);
3868 isVisible : function(){
3869 return !this.hidden;
3871 onMouseOut : function(e){
3872 var t = this.findTargetItem(e);
3875 // if(t == this.activeItem && t.shouldDeactivate(e)){
3876 // this.activeItem.deactivate();
3877 // delete this.activeItem;
3880 this.fireEvent("mouseout", this, e, t);
3885 * Displays this menu relative to another element
3886 * @param {String/HTMLElement/Roo.Element} element The element to align to
3887 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888 * the element (defaults to this.defaultAlign)
3889 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891 show : function(el, pos, parentMenu)
3893 if (false === this.fireEvent("beforeshow", this)) {
3894 Roo.log("show canceled");
3897 this.parentMenu = parentMenu;
3901 this.el.addClass('show'); // show otherwise we do not know how big we are..
3903 var xy = this.el.getAlignToXY(el, pos);
3905 // bl-tl << left align below
3906 // tl-bl << left align
3908 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909 // if it goes to far to the right.. -> align left.
3910 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3913 // was left align - go right?
3914 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3917 // goes down the bottom
3918 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920 var a = this.align.replace('?', '').split('-');
3921 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3925 this.showAt( xy , parentMenu, false);
3928 * Displays this menu at a specific xy position
3929 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932 showAt : function(xy, parentMenu, /* private: */_e){
3933 this.parentMenu = parentMenu;
3938 this.fireEvent("beforeshow", this);
3939 //xy = this.el.adjustForConstraints(xy);
3943 this.hideMenuItems();
3944 this.hidden = false;
3945 if (this.triggerEl) {
3946 this.triggerEl.addClass('open');
3949 this.el.addClass('show');
3953 // reassign x when hitting right
3955 // reassign y when hitting bottom
3957 // but the list may align on trigger left or trigger top... should it be a properity?
3959 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3964 this.fireEvent("show", this);
3970 this.doFocus.defer(50, this);
3974 doFocus : function(){
3976 this.focusEl.focus();
3981 * Hides this menu and optionally all parent menus
3982 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984 hide : function(deep)
3986 if (false === this.fireEvent("beforehide", this)) {
3987 Roo.log("hide canceled");
3990 this.hideMenuItems();
3991 if(this.el && this.isVisible()){
3993 if(this.activeItem){
3994 this.activeItem.deactivate();
3995 this.activeItem = null;
3997 if (this.triggerEl) {
3998 this.triggerEl.removeClass('open');
4001 this.el.removeClass('show');
4003 this.fireEvent("hide", this);
4005 if(deep === true && this.parentMenu){
4006 this.parentMenu.hide(true);
4010 onTriggerClick : function(e)
4012 Roo.log('trigger click');
4014 var target = e.getTarget();
4016 Roo.log(target.nodeName.toLowerCase());
4018 if(target.nodeName.toLowerCase() === 'i'){
4024 onTriggerPress : function(e)
4026 Roo.log('trigger press');
4027 //Roo.log(e.getTarget());
4028 // Roo.log(this.triggerEl.dom);
4030 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031 var pel = Roo.get(e.getTarget());
4032 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033 Roo.log('is treeview or dropdown?');
4037 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4041 if (this.isVisible()) {
4047 this.show(this.triggerEl, this.align, false);
4050 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4057 hideMenuItems : function()
4059 Roo.log("hide Menu Items");
4064 this.el.select('.open',true).each(function(aa) {
4066 aa.removeClass('open');
4070 addxtypeChild : function (tree, cntr) {
4071 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073 this.menuitems.add(comp);
4085 this.getEl().dom.innerHTML = '';
4086 this.menuitems.clear();
4092 * @class Roo.bootstrap.menu.Item
4093 * @extends Roo.bootstrap.Component
4094 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095 * @parent Roo.bootstrap.menu.Menu
4097 * Bootstrap MenuItem class
4099 * @cfg {String} html the menu label
4100 * @cfg {String} href the link
4101 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103 * @cfg {Boolean} active used on sidebars to highlight active itesm
4104 * @cfg {String} fa favicon to show on left of menu item.
4105 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4109 * Create a new MenuItem
4110 * @param {Object} config The config object
4114 Roo.bootstrap.menu.Item = function(config){
4115 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4120 * The raw click event for the entire grid.
4121 * @param {Roo.bootstrap.menu.Item} this
4122 * @param {Roo.EventObject} e
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4132 preventDefault: false,
4133 isContainer : false,
4137 getAutoCreate : function(){
4139 if(this.isContainer){
4142 cls: 'dropdown-menu-item '
4152 cls : 'dropdown-item',
4157 if (this.fa !== false) {
4160 cls : 'fa fa-' + this.fa
4169 cls: 'dropdown-menu-item',
4172 if (this.parent().type == 'treeview') {
4173 cfg.cls = 'treeview-menu';
4176 cfg.cls += ' active';
4181 anc.href = this.href || cfg.cn[0].href ;
4182 ctag.html = this.html || cfg.cn[0].html ;
4186 initEvents: function()
4188 if (this.parent().type == 'treeview') {
4189 this.el.select('a').on('click', this.onClick, this);
4193 this.menu.parentType = this.xtype;
4194 this.menu.triggerEl = this.el;
4195 this.menu = this.addxtype(Roo.apply({}, this.menu));
4199 onClick : function(e)
4201 //Roo.log('item on click ');
4203 if(this.href === false || this.preventDefault){
4206 //this.parent().hideMenuItems();
4208 this.fireEvent('click', this, e);
4222 * @class Roo.bootstrap.menu.Separator
4223 * @extends Roo.bootstrap.Component
4225 * @parent Roo.bootstrap.menu.Menu
4226 * Bootstrap Separator class
4229 * Create a new Separator
4230 * @param {Object} config The config object
4234 Roo.bootstrap.menu.Separator = function(config){
4235 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4240 getAutoCreate : function(){
4243 cls: 'dropdown-divider divider'
4259 * @class Roo.bootstrap.Modal
4260 * @extends Roo.bootstrap.Component
4261 * @parent none builder
4262 * @children Roo.bootstrap.Component
4263 * Bootstrap Modal class
4264 * @cfg {String} title Title of dialog
4265 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4267 * @cfg {Boolean} specificTitle default false
4268 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270 * @cfg {Boolean} animate default true
4271 * @cfg {Boolean} allow_close default true
4272 * @cfg {Boolean} fitwindow default false
4273 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276 * @cfg {String} size (sm|lg|xl) default empty
4277 * @cfg {Number} max_width set the max width of modal
4278 * @cfg {Boolean} editableTitle can the title be edited
4283 * Create a new Modal Dialog
4284 * @param {Object} config The config object
4287 Roo.bootstrap.Modal = function(config){
4288 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4293 * The raw btnclick event for the button
4294 * @param {Roo.EventObject} e
4299 * Fire when dialog resize
4300 * @param {Roo.bootstrap.Modal} this
4301 * @param {Roo.EventObject} e
4305 * @event titlechanged
4306 * Fire when the editable title has been changed
4307 * @param {Roo.bootstrap.Modal} this
4308 * @param {Roo.EventObject} value
4310 "titlechanged" : true
4313 this.buttons = this.buttons || [];
4316 this.tmpl = Roo.factory(this.tmpl);
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4323 title : 'test dialog',
4333 specificTitle: false,
4335 buttonPosition: 'right',
4357 editableTitle : false,
4359 onRender : function(ct, position)
4361 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364 var cfg = Roo.apply({}, this.getAutoCreate());
4367 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4369 //if (!cfg.name.length) {
4373 cfg.cls += ' ' + this.cls;
4376 cfg.style = this.style;
4378 this.el = Roo.get(document.body).createChild(cfg, position);
4380 //var type = this.el.dom.type;
4383 if(this.tabIndex !== undefined){
4384 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387 this.dialogEl = this.el.select('.modal-dialog',true).first();
4388 this.bodyEl = this.el.select('.modal-body',true).first();
4389 this.closeEl = this.el.select('.modal-header .close', true).first();
4390 this.headerEl = this.el.select('.modal-header',true).first();
4391 this.titleEl = this.el.select('.modal-title',true).first();
4392 this.footerEl = this.el.select('.modal-footer',true).first();
4394 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4396 //this.el.addClass("x-dlg-modal");
4398 if (this.buttons.length) {
4399 Roo.each(this.buttons, function(bb) {
4400 var b = Roo.apply({}, bb);
4401 b.xns = b.xns || Roo.bootstrap;
4402 b.xtype = b.xtype || 'Button';
4403 if (typeof(b.listeners) == 'undefined') {
4404 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4407 var btn = Roo.factory(b);
4409 btn.render(this.getButtonContainer());
4413 // render the children.
4416 if(typeof(this.items) != 'undefined'){
4417 var items = this.items;
4420 for(var i =0;i < items.length;i++) {
4421 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425 this.items = nitems;
4427 // where are these used - they used to be body/close/footer
4431 //this.el.addClass([this.fieldClass, this.cls]);
4435 getAutoCreate : function()
4437 // we will default to modal-body-overflow - might need to remove or make optional later.
4439 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4440 html : this.html || ''
4445 cls : 'modal-title',
4449 if(this.specificTitle){ // WTF is this?
4454 if (this.allow_close && Roo.bootstrap.version == 3) {
4464 if (this.editableTitle) {
4466 cls: 'form-control roo-editable-title d-none',
4472 if (this.allow_close && Roo.bootstrap.version == 4) {
4482 if(this.size.length){
4483 size = 'modal-' + this.size;
4486 var footer = Roo.bootstrap.version == 3 ?
4488 cls : 'modal-footer',
4492 cls: 'btn-' + this.buttonPosition
4497 { // BS4 uses mr-auto on left buttons....
4498 cls : 'modal-footer'
4509 cls: "modal-dialog " + size,
4512 cls : "modal-content",
4515 cls : 'modal-header',
4530 modal.cls += ' fade';
4536 getChildContainer : function() {
4541 getButtonContainer : function() {
4543 return Roo.bootstrap.version == 4 ?
4544 this.el.select('.modal-footer',true).first()
4545 : this.el.select('.modal-footer div',true).first();
4548 initEvents : function()
4550 if (this.allow_close) {
4551 this.closeEl.on('click', this.hide, this);
4553 Roo.EventManager.onWindowResize(this.resize, this, true);
4554 if (this.editableTitle) {
4555 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4556 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557 this.headerEditEl.on('keyup', function(e) {
4558 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559 this.toggleHeaderInput(false)
4562 this.headerEditEl.on('blur', function(e) {
4563 this.toggleHeaderInput(false)
4572 this.maskEl.setSize(
4573 Roo.lib.Dom.getViewWidth(true),
4574 Roo.lib.Dom.getViewHeight(true)
4577 if (this.fitwindow) {
4579 this.dialogEl.setStyle( { 'max-width' : '100%' });
4581 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4587 if(this.max_width !== 0) {
4589 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592 this.setSize(w, this.height);
4596 if(this.max_height) {
4597 this.setSize(w,Math.min(
4599 Roo.lib.Dom.getViewportHeight(true) - 60
4605 if(!this.fit_content) {
4606 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4610 this.setSize(w, Math.min(
4612 this.headerEl.getHeight() +
4613 this.footerEl.getHeight() +
4614 this.getChildHeight(this.bodyEl.dom.childNodes),
4615 Roo.lib.Dom.getViewportHeight(true) - 60)
4621 setSize : function(w,h)
4632 if (!this.rendered) {
4635 this.toggleHeaderInput(false);
4636 //this.el.setStyle('display', 'block');
4637 this.el.removeClass('hideing');
4638 this.el.dom.style.display='block';
4640 Roo.get(document.body).addClass('modal-open');
4642 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4645 this.el.addClass('show');
4646 this.el.addClass('in');
4649 this.el.addClass('show');
4650 this.el.addClass('in');
4653 // not sure how we can show data in here..
4655 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4658 Roo.get(document.body).addClass("x-body-masked");
4660 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4661 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662 this.maskEl.dom.style.display = 'block';
4663 this.maskEl.addClass('show');
4668 this.fireEvent('show', this);
4670 // set zindex here - otherwise it appears to be ignored...
4671 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4674 this.items.forEach( function(e) {
4675 e.layout ? e.layout() : false;
4683 if(this.fireEvent("beforehide", this) !== false){
4685 this.maskEl.removeClass('show');
4687 this.maskEl.dom.style.display = '';
4688 Roo.get(document.body).removeClass("x-body-masked");
4689 this.el.removeClass('in');
4690 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4692 if(this.animate){ // why
4693 this.el.addClass('hideing');
4694 this.el.removeClass('show');
4696 if (!this.el.hasClass('hideing')) {
4697 return; // it's been shown again...
4700 this.el.dom.style.display='';
4702 Roo.get(document.body).removeClass('modal-open');
4703 this.el.removeClass('hideing');
4707 this.el.removeClass('show');
4708 this.el.dom.style.display='';
4709 Roo.get(document.body).removeClass('modal-open');
4712 this.fireEvent('hide', this);
4715 isVisible : function()
4718 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4722 addButton : function(str, cb)
4726 var b = Roo.apply({}, { html : str } );
4727 b.xns = b.xns || Roo.bootstrap;
4728 b.xtype = b.xtype || 'Button';
4729 if (typeof(b.listeners) == 'undefined') {
4730 b.listeners = { click : cb.createDelegate(this) };
4733 var btn = Roo.factory(b);
4735 btn.render(this.getButtonContainer());
4741 setDefaultButton : function(btn)
4743 //this.el.select('.modal-footer').()
4746 resizeTo: function(w,h)
4748 this.dialogEl.setWidth(w);
4750 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4752 this.bodyEl.setHeight(h - diff);
4754 this.fireEvent('resize', this);
4757 setContentSize : function(w, h)
4761 onButtonClick: function(btn,e)
4764 this.fireEvent('btnclick', btn.name, e);
4767 * Set the title of the Dialog
4768 * @param {String} str new Title
4770 setTitle: function(str) {
4771 this.titleEl.dom.innerHTML = str;
4775 * Set the body of the Dialog
4776 * @param {String} str new Title
4778 setBody: function(str) {
4779 this.bodyEl.dom.innerHTML = str;
4782 * Set the body of the Dialog using the template
4783 * @param {Obj} data - apply this data to the template and replace the body contents.
4785 applyBody: function(obj)
4788 Roo.log("Error - using apply Body without a template");
4791 this.tmpl.overwrite(this.bodyEl, obj);
4794 getChildHeight : function(child_nodes)
4798 child_nodes.length == 0
4803 var child_height = 0;
4805 for(var i = 0; i < child_nodes.length; i++) {
4808 * for modal with tabs...
4809 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4811 var layout_childs = child_nodes[i].childNodes;
4813 for(var j = 0; j < layout_childs.length; j++) {
4815 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4817 var layout_body_childs = layout_childs[j].childNodes;
4819 for(var k = 0; k < layout_body_childs.length; k++) {
4821 if(layout_body_childs[k].classList.contains('navbar')) {
4822 child_height += layout_body_childs[k].offsetHeight;
4826 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4828 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4830 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4832 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4848 child_height += child_nodes[i].offsetHeight;
4849 // Roo.log(child_nodes[i].offsetHeight);
4852 return child_height;
4854 toggleHeaderInput : function(is_edit)
4856 if (!this.editableTitle) {
4857 return; // not editable.
4859 if (is_edit && this.is_header_editing) {
4860 return; // already editing..
4864 this.headerEditEl.dom.value = this.title;
4865 this.headerEditEl.removeClass('d-none');
4866 this.headerEditEl.dom.focus();
4867 this.titleEl.addClass('d-none');
4869 this.is_header_editing = true;
4872 // flip back to not editing.
4873 this.title = this.headerEditEl.dom.value;
4874 this.headerEditEl.addClass('d-none');
4875 this.titleEl.removeClass('d-none');
4876 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877 this.is_header_editing = false;
4878 this.fireEvent('titlechanged', this, this.title);
4887 Roo.apply(Roo.bootstrap.Modal, {
4889 * Button config that displays a single OK button
4898 * Button config that displays Yes and No buttons
4914 * Button config that displays OK and Cancel buttons
4929 * Button config that displays Yes, No and Cancel buttons
4954 * messagebox - can be used as a replace
4958 * @class Roo.MessageBox
4959 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4968 // process text value...
4972 // Show a dialog using config options:
4974 title:'Save Changes?',
4975 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976 buttons: Roo.Msg.YESNOCANCEL,
4983 Roo.bootstrap.MessageBox = function(){
4984 var dlg, opt, mask, waitTimer;
4985 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986 var buttons, activeTextEl, bwidth;
4990 var handleButton = function(button){
4992 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4996 var handleHide = function(){
4998 dlg.el.removeClass(opt.cls);
5001 // Roo.TaskMgr.stop(waitTimer);
5002 // waitTimer = null;
5007 var updateButtons = function(b){
5010 buttons["ok"].hide();
5011 buttons["cancel"].hide();
5012 buttons["yes"].hide();
5013 buttons["no"].hide();
5014 dlg.footerEl.hide();
5018 dlg.footerEl.show();
5019 for(var k in buttons){
5020 if(typeof buttons[k] != "function"){
5023 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024 width += buttons[k].el.getWidth()+15;
5034 var handleEsc = function(d, k, e){
5035 if(opt && opt.closable !== false){
5045 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046 * @return {Roo.BasicDialog} The BasicDialog element
5048 getDialog : function(){
5050 dlg = new Roo.bootstrap.Modal( {
5053 //constraintoviewport:false,
5055 //collapsible : false,
5060 //buttonAlign:"center",
5061 closeClick : function(){
5062 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5065 handleButton("cancel");
5070 dlg.on("hide", handleHide);
5072 //dlg.addKeyListener(27, handleEsc);
5074 this.buttons = buttons;
5075 var bt = this.buttonText;
5076 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5081 bodyEl = dlg.bodyEl.createChild({
5083 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084 '<textarea class="roo-mb-textarea"></textarea>' +
5085 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5087 msgEl = bodyEl.dom.firstChild;
5088 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089 textboxEl.enableDisplayMode();
5090 textboxEl.addKeyListener([10,13], function(){
5091 if(dlg.isVisible() && opt && opt.buttons){
5094 }else if(opt.buttons.yes){
5095 handleButton("yes");
5099 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100 textareaEl.enableDisplayMode();
5101 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102 progressEl.enableDisplayMode();
5104 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105 var pf = progressEl.dom.firstChild;
5107 pp = Roo.get(pf.firstChild);
5108 pp.setHeight(pf.offsetHeight);
5116 * Updates the message box body text
5117 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118 * the XHTML-compliant non-breaking space character '&#160;')
5119 * @return {Roo.MessageBox} This message box
5121 updateText : function(text)
5123 if(!dlg.isVisible() && !opt.width){
5124 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5127 msgEl.innerHTML = text || ' ';
5129 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5132 Math.min(opt.width || cw , this.maxWidth),
5133 Math.max(opt.minWidth || this.minWidth, bwidth)
5136 activeTextEl.setWidth(w);
5138 if(dlg.isVisible()){
5139 dlg.fixedcenter = false;
5141 // to big, make it scroll. = But as usual stupid IE does not support
5144 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5148 bodyEl.dom.style.height = '';
5149 bodyEl.dom.style.overflowY = '';
5152 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5154 bodyEl.dom.style.overflowX = '';
5157 dlg.setContentSize(w, bodyEl.getHeight());
5158 if(dlg.isVisible()){
5159 dlg.fixedcenter = true;
5165 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5166 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169 * @return {Roo.MessageBox} This message box
5171 updateProgress : function(value, text){
5173 this.updateText(text);
5176 if (pp) { // weird bug on my firefox - for some reason this is not defined
5177 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5184 * Returns true if the message box is currently displayed
5185 * @return {Boolean} True if the message box is visible, else false
5187 isVisible : function(){
5188 return dlg && dlg.isVisible();
5192 * Hides the message box if it is displayed
5195 if(this.isVisible()){
5201 * Displays a new message box, or reinitializes an existing message box, based on the config options
5202 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203 * The following config object properties are supported:
5205 Property Type Description
5206 ---------- --------------- ------------------------------------------------------------------------------------
5207 animEl String/Element An id or Element from which the message box should animate as it opens and
5208 closes (defaults to undefined)
5209 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable Boolean False to hide the top-right close button (defaults to true). Note that
5212 progress and wait dialogs will ignore this property and always hide the
5213 close button as they can only be closed programmatically.
5214 cls String A custom CSS class to apply to the message box element
5215 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5216 displayed (defaults to 75)
5217 fn Function A callback function to execute after closing the dialog. The arguments to the
5218 function will be btn (the name of the button that was clicked, if applicable,
5219 e.g. "ok"), and text (the value of the active text field, if applicable).
5220 Progress and wait dialogs will ignore this option since they do not respond to
5221 user actions and can only be closed programmatically, so any required function
5222 should be called by the same code after it closes the dialog.
5223 icon String A CSS class that provides a background image to be used as an icon for
5224 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5226 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5227 modal Boolean False to allow user interaction with the page while the message box is
5228 displayed (defaults to true)
5229 msg String A string that will replace the existing message box body text (defaults
5230 to the XHTML-compliant non-breaking space character ' ')
5231 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5232 progress Boolean True to display a progress bar (defaults to false)
5233 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5236 title String The title text
5237 value String The string value to set into the active textbox element if displayed
5238 wait Boolean True to display a progress bar (defaults to false)
5239 width Number The width of the dialog in pixels
5246 msg: 'Please enter your address:',
5248 buttons: Roo.MessageBox.OKCANCEL,
5251 animEl: 'addAddressBtn'
5254 * @param {Object} config Configuration options
5255 * @return {Roo.MessageBox} This message box
5257 show : function(options)
5260 // this causes nightmares if you show one dialog after another
5261 // especially on callbacks..
5263 if(this.isVisible()){
5266 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5268 Roo.log("New Dialog Message:" + options.msg )
5269 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5273 var d = this.getDialog();
5275 d.setTitle(opt.title || " ");
5276 d.closeEl.setDisplayed(opt.closable !== false);
5277 activeTextEl = textboxEl;
5278 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5283 textareaEl.setHeight(typeof opt.multiline == "number" ?
5284 opt.multiline : this.defaultTextHeight);
5285 activeTextEl = textareaEl;
5294 progressEl.setDisplayed(opt.progress === true);
5296 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5298 this.updateProgress(0);
5299 activeTextEl.dom.value = opt.value || "";
5301 dlg.setDefaultButton(activeTextEl);
5303 var bs = opt.buttons;
5307 }else if(bs && bs.yes){
5308 db = buttons["yes"];
5310 dlg.setDefaultButton(db);
5312 bwidth = updateButtons(opt.buttons);
5313 this.updateText(opt.msg);
5315 d.el.addClass(opt.cls);
5317 d.proxyDrag = opt.proxyDrag === true;
5318 d.modal = opt.modal !== false;
5319 d.mask = opt.modal !== false ? mask : false;
5321 // force it to the end of the z-index stack so it gets a cursor in FF
5322 document.body.appendChild(dlg.el.dom);
5323 d.animateTarget = null;
5324 d.show(options.animEl);
5330 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5331 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332 * and closing the message box when the process is complete.
5333 * @param {String} title The title bar text
5334 * @param {String} msg The message box body text
5335 * @return {Roo.MessageBox} This message box
5337 progress : function(title, msg){
5344 minWidth: this.minProgressWidth,
5351 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352 * If a callback function is passed it will be called after the user clicks the button, and the
5353 * id of the button that was clicked will be passed as the only parameter to the callback
5354 * (could also be the top-right close button).
5355 * @param {String} title The title bar text
5356 * @param {String} msg The message box body text
5357 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358 * @param {Object} scope (optional) The scope of the callback function
5359 * @return {Roo.MessageBox} This message box
5361 alert : function(title, msg, fn, scope)
5376 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5377 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378 * You are responsible for closing the message box when the process is complete.
5379 * @param {String} msg The message box body text
5380 * @param {String} title (optional) The title bar text
5381 * @return {Roo.MessageBox} This message box
5383 wait : function(msg, title){
5394 waitTimer = Roo.TaskMgr.start({
5396 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5404 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407 * @param {String} title The title bar text
5408 * @param {String} msg The message box body text
5409 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410 * @param {Object} scope (optional) The scope of the callback function
5411 * @return {Roo.MessageBox} This message box
5413 confirm : function(title, msg, fn, scope){
5417 buttons: this.YESNO,
5426 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5428 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429 * (could also be the top-right close button) and the text that was entered will be passed as the two
5430 * parameters to the callback.
5431 * @param {String} title The title bar text
5432 * @param {String} msg The message box body text
5433 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434 * @param {Object} scope (optional) The scope of the callback function
5435 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437 * @return {Roo.MessageBox} This message box
5439 prompt : function(title, msg, fn, scope, multiline){
5443 buttons: this.OKCANCEL,
5448 multiline: multiline,
5455 * Button config that displays a single OK button
5460 * Button config that displays Yes and No buttons
5463 YESNO : {yes:true, no:true},
5465 * Button config that displays OK and Cancel buttons
5468 OKCANCEL : {ok:true, cancel:true},
5470 * Button config that displays Yes, No and Cancel buttons
5473 YESNOCANCEL : {yes:true, no:true, cancel:true},
5476 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5479 defaultTextHeight : 75,
5481 * The maximum width in pixels of the message box (defaults to 600)
5486 * The minimum width in pixels of the message box (defaults to 100)
5491 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5492 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5495 minProgressWidth : 250,
5497 * An object containing the default button text strings that can be overriden for localized language support.
5498 * Supported properties are: ok, cancel, yes and no.
5499 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5512 * Shorthand for {@link Roo.MessageBox}
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5524 * @class Roo.bootstrap.nav.Bar
5525 * @extends Roo.bootstrap.Component
5527 * Bootstrap Navbar class
5530 * Create a new Navbar
5531 * @param {Object} config The config object
5535 Roo.bootstrap.nav.Bar = function(config){
5536 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5540 * @event beforetoggle
5541 * Fire before toggle the menu
5542 * @param {Roo.EventObject} e
5544 "beforetoggle" : true
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5557 getAutoCreate : function(){
5560 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5564 initEvents :function ()
5566 //Roo.log(this.el.select('.navbar-toggle',true));
5567 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5574 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5576 var size = this.el.getSize();
5577 this.maskEl.setSize(size.width, size.height);
5578 this.maskEl.enableDisplayMode("block");
5587 getChildContainer : function()
5589 if (this.el && this.el.select('.collapse').getCount()) {
5590 return this.el.select('.collapse',true).first();
5605 onToggle : function()
5608 if(this.fireEvent('beforetoggle', this) === false){
5611 var ce = this.el.select('.navbar-collapse',true).first();
5613 if (!ce.hasClass('show')) {
5623 * Expand the navbar pulldown
5625 expand : function ()
5628 var ce = this.el.select('.navbar-collapse',true).first();
5629 if (ce.hasClass('collapsing')) {
5632 ce.dom.style.height = '';
5634 ce.addClass('in'); // old...
5635 ce.removeClass('collapse');
5636 ce.addClass('show');
5637 var h = ce.getHeight();
5639 ce.removeClass('show');
5640 // at this point we should be able to see it..
5641 ce.addClass('collapsing');
5643 ce.setHeight(0); // resize it ...
5644 ce.on('transitionend', function() {
5645 //Roo.log('done transition');
5646 ce.removeClass('collapsing');
5647 ce.addClass('show');
5648 ce.removeClass('collapse');
5650 ce.dom.style.height = '';
5651 }, this, { single: true} );
5653 ce.dom.scrollTop = 0;
5656 * Collapse the navbar pulldown
5658 collapse : function()
5660 var ce = this.el.select('.navbar-collapse',true).first();
5662 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663 // it's collapsed or collapsing..
5666 ce.removeClass('in'); // old...
5667 ce.setHeight(ce.getHeight());
5668 ce.removeClass('show');
5669 ce.addClass('collapsing');
5671 ce.on('transitionend', function() {
5672 ce.dom.style.height = '';
5673 ce.removeClass('collapsing');
5674 ce.addClass('collapse');
5675 }, this, { single: true} );
5695 * @class Roo.bootstrap.nav.Simplebar
5696 * @extends Roo.bootstrap.nav.Bar
5697 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698 * Bootstrap Sidebar class
5700 * @cfg {Boolean} inverse is inverted color
5702 * @cfg {String} type (nav | pills | tabs)
5703 * @cfg {Boolean} arrangement stacked | justified
5704 * @cfg {String} align (left | right) alignment
5706 * @cfg {Boolean} main (true|false) main nav bar? default false
5707 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5709 * @cfg {String} tag (header|footer|nav|div) default is nav
5711 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5715 * Create a new Sidebar
5716 * @param {Object} config The config object
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5740 getAutoCreate : function(){
5744 tag : this.tag || 'div',
5745 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5747 if (['light','white'].indexOf(this.weight) > -1) {
5748 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5750 cfg.cls += ' bg-' + this.weight;
5753 cfg.cls += ' navbar-inverse';
5757 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5759 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5768 cls: 'nav nav-' + this.xtype,
5774 this.type = this.type || 'nav';
5775 if (['tabs','pills'].indexOf(this.type) != -1) {
5776 cfg.cn[0].cls += ' nav-' + this.type
5780 if (this.type!=='nav') {
5781 Roo.log('nav type must be nav/tabs/pills')
5783 cfg.cn[0].cls += ' navbar-nav'
5789 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790 cfg.cn[0].cls += ' nav-' + this.arrangement;
5794 if (this.align === 'right') {
5795 cfg.cn[0].cls += ' navbar-right';
5820 * navbar-expand-md fixed-top
5824 * @class Roo.bootstrap.nav.Headerbar
5825 * @extends Roo.bootstrap.nav.Simplebar
5826 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827 * Bootstrap Sidebar class
5829 * @cfg {String} brand what is brand
5830 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831 * @cfg {String} brand_href href of the brand
5832 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5833 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5838 * Create a new Sidebar
5839 * @param {Object} config The config object
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5855 desktopCenter : false,
5858 getAutoCreate : function(){
5861 tag: this.nav || 'nav',
5862 cls: 'navbar navbar-expand-md',
5868 if (this.desktopCenter) {
5869 cn.push({cls : 'container', cn : []});
5877 cls: 'navbar-toggle navbar-toggler',
5878 'data-toggle': 'collapse',
5883 html: 'Toggle navigation'
5887 cls: 'icon-bar navbar-toggler-icon'
5900 cn.push( Roo.bootstrap.version == 4 ? btn : {
5902 cls: 'navbar-header',
5911 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5915 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5917 if (['light','white'].indexOf(this.weight) > -1) {
5918 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5920 cfg.cls += ' bg-' + this.weight;
5923 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5926 // tag can override this..
5928 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5931 if (this.brand !== '') {
5932 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5935 href: this.brand_href ? this.brand_href : '#',
5936 cls: 'navbar-brand',
5944 cfg.cls += ' main-nav';
5952 getHeaderChildContainer : function()
5954 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955 return this.el.select('.navbar-header',true).first();
5958 return this.getChildContainer();
5961 getChildContainer : function()
5964 return this.el.select('.roo-navbar-collapse',true).first();
5969 initEvents : function()
5971 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5973 if (this.autohide) {
5978 Roo.get(document).on('scroll',function(e) {
5979 var ns = Roo.get(document).getScroll().top;
5980 var os = prevScroll;
5984 ft.removeClass('slideDown');
5985 ft.addClass('slideUp');
5988 ft.removeClass('slideUp');
5989 ft.addClass('slideDown');
6010 * @class Roo.bootstrap.nav.Sidebar
6011 * @extends Roo.bootstrap.nav.Bar
6012 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013 * Bootstrap Sidebar class
6016 * Create a new Sidebar
6017 * @param {Object} config The config object
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6027 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6029 getAutoCreate : function(){
6034 cls: 'sidebar sidebar-nav'
6056 * @class Roo.bootstrap.nav.Group
6057 * @extends Roo.bootstrap.Component
6058 * @children Roo.bootstrap.nav.Item
6059 * Bootstrap NavGroup class
6060 * @cfg {String} align (left|right)
6061 * @cfg {Boolean} inverse
6062 * @cfg {String} type (nav|pills|tab) default nav
6063 * @cfg {String} navId - reference Id for navbar.
6064 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067 * Create a new nav group
6068 * @param {Object} config The config object
6071 Roo.bootstrap.nav.Group = function(config){
6072 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6075 Roo.bootstrap.nav.Group.register(this);
6079 * Fires when the active item changes
6080 * @param {Roo.bootstrap.nav.Group} this
6081 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6101 getAutoCreate : function()
6103 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6109 if (Roo.bootstrap.version == 4) {
6110 if (['tabs','pills'].indexOf(this.type) != -1) {
6111 cfg.cls += ' nav-' + this.type;
6113 // trying to remove so header bar can right align top?
6114 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115 // do not use on header bar...
6116 cfg.cls += ' navbar-nav';
6121 if (['tabs','pills'].indexOf(this.type) != -1) {
6122 cfg.cls += ' nav-' + this.type
6124 if (this.type !== 'nav') {
6125 Roo.log('nav type must be nav/tabs/pills')
6127 cfg.cls += ' navbar-nav'
6131 if (this.parent() && this.parent().sidebar) {
6134 cls: 'dashboard-menu sidebar-menu'
6140 if (this.form === true) {
6143 cls: 'navbar-form form-inline'
6145 //nav navbar-right ml-md-auto
6146 if (this.align === 'right') {
6147 cfg.cls += ' navbar-right ml-md-auto';
6149 cfg.cls += ' navbar-left';
6153 if (this.align === 'right') {
6154 cfg.cls += ' navbar-right ml-md-auto';
6156 cfg.cls += ' mr-auto';
6160 cfg.cls += ' navbar-inverse';
6168 * sets the active Navigation item
6169 * @param {Roo.bootstrap.nav.Item} the new current navitem
6171 setActiveItem : function(item)
6174 Roo.each(this.navItems, function(v){
6179 v.setActive(false, true);
6186 item.setActive(true, true);
6187 this.fireEvent('changed', this, item, prev);
6192 * gets the active Navigation item
6193 * @return {Roo.bootstrap.nav.Item} the current navitem
6195 getActive : function()
6199 Roo.each(this.navItems, function(v){
6210 indexOfNav : function()
6214 Roo.each(this.navItems, function(v,i){
6225 * adds a Navigation item
6226 * @param {Roo.bootstrap.nav.Item} the navitem to add
6228 addItem : function(cfg)
6230 if (this.form && Roo.bootstrap.version == 4) {
6233 var cn = new Roo.bootstrap.nav.Item(cfg);
6235 cn.parentId = this.id;
6236 cn.onRender(this.el, null);
6240 * register a Navigation item
6241 * @param {Roo.bootstrap.nav.Item} the navitem to add
6243 register : function(item)
6245 this.navItems.push( item);
6246 item.navId = this.navId;
6251 * clear all the Navigation item
6254 clearAll : function()
6257 this.el.dom.innerHTML = '';
6260 getNavItem: function(tabId)
6263 Roo.each(this.navItems, function(e) {
6264 if (e.tabId == tabId) {
6274 setActiveNext : function()
6276 var i = this.indexOfNav(this.getActive());
6277 if (i > this.navItems.length) {
6280 this.setActiveItem(this.navItems[i+1]);
6282 setActivePrev : function()
6284 var i = this.indexOfNav(this.getActive());
6288 this.setActiveItem(this.navItems[i-1]);
6290 clearWasActive : function(except) {
6291 Roo.each(this.navItems, function(e) {
6292 if (e.tabId != except.tabId && e.was_active) {
6293 e.was_active = false;
6300 getWasActive : function ()
6303 Roo.each(this.navItems, function(e) {
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6322 * register a Navigation Group
6323 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6325 register : function(navgrp)
6327 this.groups[navgrp.navId] = navgrp;
6331 * fetch a Navigation Group based on the navigation ID
6332 * @param {string} the navgroup to add
6333 * @returns {Roo.bootstrap.nav.Group} the navgroup
6335 get: function(navId) {
6336 if (typeof(this.groups[navId]) == 'undefined') {
6338 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6340 return this.groups[navId] ;
6348 * @class Roo.bootstrap.nav.Item
6349 * @extends Roo.bootstrap.Component
6350 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351 * @parent Roo.bootstrap.nav.Group
6353 * Bootstrap Navbar.NavItem class
6355 * @cfg {String} href link to
6356 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357 * @cfg {Boolean} button_outline show and outlined button
6358 * @cfg {String} html content of button
6359 * @cfg {String} badge text inside badge
6360 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361 * @cfg {String} glyphicon DEPRICATED - use fa
6362 * @cfg {String} icon DEPRICATED - use fa
6363 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364 * @cfg {Boolean} active Is item active
6365 * @cfg {Boolean} disabled Is item disabled
6366 * @cfg {String} linkcls Link Class
6367 * @cfg {Boolean} preventDefault (true | false) default false
6368 * @cfg {String} tabId the tab that this item activates.
6369 * @cfg {String} tagtype (a|span) render as a href or span?
6370 * @cfg {Boolean} animateRef (true|false) link to element default false
6371 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6374 * Create a new Navbar Item
6375 * @param {Object} config The config object
6377 Roo.bootstrap.nav.Item = function(config){
6378 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6383 * The raw click event for the entire grid.
6384 * @param {Roo.EventObject} e
6389 * Fires when the active item active state changes
6390 * @param {Roo.bootstrap.nav.Item} this
6391 * @param {boolean} state the new state
6397 * Fires when scroll to element
6398 * @param {Roo.bootstrap.nav.Item} this
6399 * @param {Object} options
6400 * @param {Roo.EventObject} e
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6417 preventDefault : false,
6425 button_outline : false,
6429 getAutoCreate : function(){
6436 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6439 cfg.cls += ' active' ;
6441 if (this.disabled) {
6442 cfg.cls += ' disabled';
6446 if (this.button_weight.length) {
6447 cfg.tag = this.href ? 'a' : 'button';
6448 cfg.html = this.html || '';
6449 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6451 cfg.href = this.href;
6454 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6456 cfg.cls += " nav-html";
6459 // menu .. should add dropdown-menu class - so no need for carat..
6461 if (this.badge !== '') {
6463 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6468 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6472 href : this.href || "#",
6473 html: this.html || '',
6477 if (this.tagtype == 'a') {
6478 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6482 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483 } else if (this.fa) {
6484 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485 } else if(this.glyphicon) {
6486 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6488 cfg.cn[0].cls += " nav-html";
6492 cfg.cn[0].html += " <span class='caret'></span>";
6496 if (this.badge !== '') {
6497 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6505 onRender : function(ct, position)
6507 // Roo.log("Call onRender: " + this.xtype);
6508 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6512 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513 this.navLink = this.el.select('.nav-link',true).first();
6514 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6519 initEvents: function()
6521 if (typeof (this.menu) != 'undefined') {
6522 this.menu.parentType = this.xtype;
6523 this.menu.triggerEl = this.el;
6524 this.menu = this.addxtype(Roo.apply({}, this.menu));
6527 this.el.on('click', this.onClick, this);
6529 //if(this.tagtype == 'span'){
6530 // this.el.select('span',true).on('click', this.onClick, this);
6533 // at this point parent should be available..
6534 this.parent().register(this);
6537 onClick : function(e)
6539 if (e.getTarget('.dropdown-menu-item')) {
6540 // did you click on a menu itemm.... - then don't trigger onclick..
6545 this.preventDefault ||
6548 Roo.log("NavItem - prevent Default?");
6552 if (this.disabled) {
6556 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6557 if (tg && tg.transition) {
6558 Roo.log("waiting for the transitionend");
6564 //Roo.log("fire event clicked");
6565 if(this.fireEvent('click', this, e) === false){
6569 if(this.tagtype == 'span'){
6573 //Roo.log(this.href);
6574 var ael = this.el.select('a',true).first();
6577 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6578 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6579 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6580 return; // ignore... - it's a 'hash' to another page.
6582 Roo.log("NavItem - prevent Default?");
6584 this.scrollToElement(e);
6588 var p = this.parent();
6590 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6591 if (typeof(p.setActiveItem) !== 'undefined') {
6592 p.setActiveItem(this);
6596 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6597 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6598 // remove the collapsed menu expand...
6599 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6603 isActive: function () {
6606 setActive : function(state, fire, is_was_active)
6608 if (this.active && !state && this.navId) {
6609 this.was_active = true;
6610 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6612 nv.clearWasActive(this);
6616 this.active = state;
6619 this.el.removeClass('active');
6620 this.navLink ? this.navLink.removeClass('active') : false;
6621 } else if (!this.el.hasClass('active')) {
6623 this.el.addClass('active');
6624 if (Roo.bootstrap.version == 4 && this.navLink ) {
6625 this.navLink.addClass('active');
6630 this.fireEvent('changed', this, state);
6633 // show a panel if it's registered and related..
6635 if (!this.navId || !this.tabId || !state || is_was_active) {
6639 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6643 var pan = tg.getPanelByName(this.tabId);
6647 // if we can not flip to new panel - go back to old nav highlight..
6648 if (false == tg.showPanel(pan)) {
6649 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6651 var onav = nv.getWasActive();
6653 onav.setActive(true, false, true);
6662 // this should not be here...
6663 setDisabled : function(state)
6665 this.disabled = state;
6667 this.el.removeClass('disabled');
6668 } else if (!this.el.hasClass('disabled')) {
6669 this.el.addClass('disabled');
6675 * Fetch the element to display the tooltip on.
6676 * @return {Roo.Element} defaults to this.el
6678 tooltipEl : function()
6680 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6683 scrollToElement : function(e)
6685 var c = document.body;
6688 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6690 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6691 c = document.documentElement;
6694 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6700 var o = target.calcOffsetsTo(c);
6707 this.fireEvent('scrollto', this, options, e);
6709 Roo.get(c).scrollTo('top', options.value, true);
6714 * Set the HTML (text content) of the item
6715 * @param {string} html content for the nav item
6717 setHtml : function(html)
6720 this.htmlEl.dom.innerHTML = html;
6732 * <span> icon </span>
6733 * <span> text </span>
6734 * <span>badge </span>
6738 * @class Roo.bootstrap.nav.SidebarItem
6739 * @extends Roo.bootstrap.nav.Item
6740 * Bootstrap Navbar.NavSidebarItem class
6742 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6743 * {Boolean} open is the menu open
6744 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6745 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6746 * {String} buttonSize (sm|md|lg)the extra classes for the button
6747 * {Boolean} showArrow show arrow next to the text (default true)
6749 * Create a new Navbar Button
6750 * @param {Object} config The config object
6752 Roo.bootstrap.nav.SidebarItem = function(config){
6753 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6758 * The raw click event for the entire grid.
6759 * @param {Roo.EventObject} e
6764 * Fires when the active item active state changes
6765 * @param {Roo.bootstrap.nav.SidebarItem} this
6766 * @param {boolean} state the new state
6774 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6776 badgeWeight : 'default',
6782 buttonWeight : 'default',
6788 getAutoCreate : function(){
6793 href : this.href || '#',
6799 if(this.buttonView){
6802 href : this.href || '#',
6803 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6816 cfg.cls += ' active';
6819 if (this.disabled) {
6820 cfg.cls += ' disabled';
6823 cfg.cls += ' open x-open';
6826 if (this.glyphicon || this.icon) {
6827 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6828 a.cn.push({ tag : 'i', cls : c }) ;
6831 if(!this.buttonView){
6834 html : this.html || ''
6841 if (this.badge !== '') {
6842 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6848 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6851 a.cls += ' dropdown-toggle treeview' ;
6857 initEvents : function()
6859 if (typeof (this.menu) != 'undefined') {
6860 this.menu.parentType = this.xtype;
6861 this.menu.triggerEl = this.el;
6862 this.menu = this.addxtype(Roo.apply({}, this.menu));
6865 this.el.on('click', this.onClick, this);
6867 if(this.badge !== ''){
6868 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6873 onClick : function(e)
6880 if(this.preventDefault){
6884 this.fireEvent('click', this, e);
6887 disable : function()
6889 this.setDisabled(true);
6894 this.setDisabled(false);
6897 setDisabled : function(state)
6899 if(this.disabled == state){
6903 this.disabled = state;
6906 this.el.addClass('disabled');
6910 this.el.removeClass('disabled');
6915 setActive : function(state)
6917 if(this.active == state){
6921 this.active = state;
6924 this.el.addClass('active');
6928 this.el.removeClass('active');
6933 isActive: function ()
6938 setBadge : function(str)
6944 this.badgeEl.dom.innerHTML = str;
6961 * @class Roo.bootstrap.nav.ProgressBar
6962 * @extends Roo.bootstrap.Component
6963 * @children Roo.bootstrap.nav.ProgressBarItem
6964 * Bootstrap NavProgressBar class
6967 * Create a new nav progress bar - a bar indicating step along a process
6968 * @param {Object} config The config object
6971 Roo.bootstrap.nav.ProgressBar = function(config){
6972 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6974 this.bullets = this.bullets || [];
6976 // Roo.bootstrap.nav.ProgressBar.register(this);
6980 * Fires when the active item changes
6981 * @param {Roo.bootstrap.nav.ProgressBar} this
6982 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6983 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
6990 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
6992 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6993 * Bullets for the Nav Progress bar for the toolbar
6998 getAutoCreate : function()
7000 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7004 cls : 'roo-navigation-bar-group',
7008 cls : 'roo-navigation-top-bar'
7012 cls : 'roo-navigation-bullets-bar',
7016 cls : 'roo-navigation-bar'
7023 cls : 'roo-navigation-bottom-bar'
7033 initEvents: function()
7038 onRender : function(ct, position)
7040 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7042 if(this.bullets.length){
7043 Roo.each(this.bullets, function(b){
7052 addItem : function(cfg)
7054 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7056 item.parentId = this.id;
7057 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7060 var top = new Roo.bootstrap.Element({
7062 cls : 'roo-navigation-bar-text'
7065 var bottom = new Roo.bootstrap.Element({
7067 cls : 'roo-navigation-bar-text'
7070 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7071 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7073 var topText = new Roo.bootstrap.Element({
7075 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7078 var bottomText = new Roo.bootstrap.Element({
7080 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7083 topText.onRender(top.el, null);
7084 bottomText.onRender(bottom.el, null);
7087 item.bottomEl = bottom;
7090 this.barItems.push(item);
7095 getActive : function()
7099 Roo.each(this.barItems, function(v){
7101 if (!v.isActive()) {
7113 setActiveItem : function(item)
7117 Roo.each(this.barItems, function(v){
7118 if (v.rid == item.rid) {
7128 item.setActive(true);
7130 this.fireEvent('changed', this, item, prev);
7133 getBarItem: function(rid)
7137 Roo.each(this.barItems, function(e) {
7149 indexOfItem : function(item)
7153 Roo.each(this.barItems, function(v, i){
7155 if (v.rid != item.rid) {
7166 setActiveNext : function()
7168 var i = this.indexOfItem(this.getActive());
7170 if (i > this.barItems.length) {
7174 this.setActiveItem(this.barItems[i+1]);
7177 setActivePrev : function()
7179 var i = this.indexOfItem(this.getActive());
7185 this.setActiveItem(this.barItems[i-1]);
7190 if(!this.barItems.length){
7194 var width = 100 / this.barItems.length;
7196 Roo.each(this.barItems, function(i){
7197 i.el.setStyle('width', width + '%');
7198 i.topEl.el.setStyle('width', width + '%');
7199 i.bottomEl.el.setStyle('width', width + '%');
7213 * @class Roo.bootstrap.nav.ProgressBarItem
7214 * @extends Roo.bootstrap.Component
7215 * Bootstrap NavProgressBarItem class
7216 * @cfg {String} rid the reference id
7217 * @cfg {Boolean} active (true|false) Is item active default false
7218 * @cfg {Boolean} disabled (true|false) Is item active default false
7219 * @cfg {String} html
7220 * @cfg {String} position (top|bottom) text position default bottom
7221 * @cfg {String} icon show icon instead of number
7224 * Create a new NavProgressBarItem
7225 * @param {Object} config The config object
7227 Roo.bootstrap.nav.ProgressBarItem = function(config){
7228 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7233 * The raw click event for the entire grid.
7234 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7235 * @param {Roo.EventObject} e
7242 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7248 position : 'bottom',
7251 getAutoCreate : function()
7253 var iconCls = 'roo-navigation-bar-item-icon';
7255 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7259 cls: 'roo-navigation-bar-item',
7269 cfg.cls += ' active';
7272 cfg.cls += ' disabled';
7278 disable : function()
7280 this.setDisabled(true);
7285 this.setDisabled(false);
7288 initEvents: function()
7290 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7292 this.iconEl.on('click', this.onClick, this);
7295 onClick : function(e)
7303 if(this.fireEvent('click', this, e) === false){
7307 this.parent().setActiveItem(this);
7310 isActive: function ()
7315 setActive : function(state)
7317 if(this.active == state){
7321 this.active = state;
7324 this.el.addClass('active');
7328 this.el.removeClass('active');
7333 setDisabled : function(state)
7335 if(this.disabled == state){
7339 this.disabled = state;
7342 this.el.addClass('disabled');
7346 this.el.removeClass('disabled');
7349 tooltipEl : function()
7351 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7362 Roo.namespace('Roo.bootstrap.breadcrumb');
7366 * @class Roo.bootstrap.breadcrumb.Nav
7367 * @extends Roo.bootstrap.Component
7368 * Bootstrap Breadcrumb Nav Class
7370 * @children Roo.bootstrap.breadcrumb.Item
7373 * Create a new breadcrumb.Nav
7374 * @param {Object} config The config object
7378 Roo.bootstrap.breadcrumb.Nav = function(config){
7379 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7384 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7386 getAutoCreate : function()
7403 initEvents: function()
7405 this.olEl = this.el.select('ol',true).first();
7407 getChildContainer : function()
7423 * @class Roo.bootstrap.breadcrumb.Nav
7424 * @extends Roo.bootstrap.Component
7425 * @children Roo.bootstrap.Component
7426 * @parent Roo.bootstrap.breadcrumb.Nav
7427 * Bootstrap Breadcrumb Nav Class
7430 * @cfg {String} html the content of the link.
7431 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7432 * @cfg {Boolean} active is it active
7436 * Create a new breadcrumb.Nav
7437 * @param {Object} config The config object
7440 Roo.bootstrap.breadcrumb.Item = function(config){
7441 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7446 * The img click event for the img.
7447 * @param {Roo.EventObject} e
7454 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7459 getAutoCreate : function()
7464 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7466 if (this.href !== false) {
7473 cfg.html = this.html;
7479 initEvents: function()
7482 this.el.select('a', true).first().on('click',this.onClick, this)
7486 onClick : function(e)
7489 this.fireEvent('click',this, e);
7502 * @class Roo.bootstrap.Row
7503 * @extends Roo.bootstrap.Component
7504 * @children Roo.bootstrap.Component
7505 * Bootstrap Row class (contains columns...)
7509 * @param {Object} config The config object
7512 Roo.bootstrap.Row = function(config){
7513 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7516 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7518 getAutoCreate : function(){
7537 * @class Roo.bootstrap.Pagination
7538 * @extends Roo.bootstrap.Component
7539 * @children Roo.bootstrap.Pagination
7540 * Bootstrap Pagination class
7542 * @cfg {String} size (xs|sm|md|lg|xl)
7543 * @cfg {Boolean} inverse
7546 * Create a new Pagination
7547 * @param {Object} config The config object
7550 Roo.bootstrap.Pagination = function(config){
7551 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7554 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7560 getAutoCreate : function(){
7566 cfg.cls += ' inverse';
7572 cfg.cls += " " + this.cls;
7590 * @class Roo.bootstrap.PaginationItem
7591 * @extends Roo.bootstrap.Component
7592 * Bootstrap PaginationItem class
7593 * @cfg {String} html text
7594 * @cfg {String} href the link
7595 * @cfg {Boolean} preventDefault (true | false) default true
7596 * @cfg {Boolean} active (true | false) default false
7597 * @cfg {Boolean} disabled default false
7601 * Create a new PaginationItem
7602 * @param {Object} config The config object
7606 Roo.bootstrap.PaginationItem = function(config){
7607 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7612 * The raw click event for the entire grid.
7613 * @param {Roo.EventObject} e
7619 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7623 preventDefault: true,
7628 getAutoCreate : function(){
7634 href : this.href ? this.href : '#',
7635 html : this.html ? this.html : ''
7645 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7649 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7655 initEvents: function() {
7657 this.el.on('click', this.onClick, this);
7660 onClick : function(e)
7662 Roo.log('PaginationItem on click ');
7663 if(this.preventDefault){
7671 this.fireEvent('click', this, e);
7687 * @class Roo.bootstrap.Slider
7688 * @extends Roo.bootstrap.Component
7689 * Bootstrap Slider class
7692 * Create a new Slider
7693 * @param {Object} config The config object
7696 Roo.bootstrap.Slider = function(config){
7697 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7700 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7702 getAutoCreate : function(){
7706 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7710 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722 * Ext JS Library 1.1.1
7723 * Copyright(c) 2006-2007, Ext JS, LLC.
7725 * Originally Released Under LGPL - original licence link has changed is not relivant.
7728 * <script type="text/javascript">
7731 * @extends Roo.dd.DDProxy
7732 * @class Roo.grid.SplitDragZone
7733 * Support for Column Header resizing
7735 * @param {Object} config
7738 // This is a support class used internally by the Grid components
7739 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7741 this.view = grid.getView();
7742 this.proxy = this.view.resizeProxy;
7743 Roo.grid.SplitDragZone.superclass.constructor.call(
7746 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7748 dragElId : Roo.id(this.proxy.dom),
7753 this.setHandleElId(Roo.id(hd));
7754 if (hd2 !== false) {
7755 this.setOuterHandleElId(Roo.id(hd2));
7758 this.scroll = false;
7760 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7761 fly: Roo.Element.fly,
7763 b4StartDrag : function(x, y){
7764 this.view.headersDisabled = true;
7765 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7766 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7768 this.proxy.setHeight(h);
7770 // for old system colWidth really stored the actual width?
7771 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7772 // which in reality did not work.. - it worked only for fixed sizes
7773 // for resizable we need to use actual sizes.
7774 var w = this.cm.getColumnWidth(this.cellIndex);
7775 if (!this.view.mainWrap) {
7777 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7782 // this was w-this.grid.minColumnWidth;
7783 // doesnt really make sense? - w = thie curren width or the rendered one?
7784 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7785 this.resetConstraints();
7786 this.setXConstraint(minw, 1000);
7787 this.setYConstraint(0, 0);
7788 this.minX = x - minw;
7789 this.maxX = x + 1000;
7791 if (!this.view.mainWrap) { // this is Bootstrap code..
7792 this.getDragEl().style.display='block';
7795 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7799 handleMouseDown : function(e){
7800 ev = Roo.EventObject.setEvent(e);
7801 var t = this.fly(ev.getTarget());
7802 if(t.hasClass("x-grid-split")){
7803 this.cellIndex = this.view.getCellIndex(t.dom);
7805 this.cm = this.grid.colModel;
7806 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7807 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7812 endDrag : function(e){
7813 this.view.headersDisabled = false;
7814 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7815 var diff = endX - this.startPos;
7817 var w = this.cm.getColumnWidth(this.cellIndex);
7818 if (!this.view.mainWrap) {
7821 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7824 autoOffset : function(){
7829 * Ext JS Library 1.1.1
7830 * Copyright(c) 2006-2007, Ext JS, LLC.
7832 * Originally Released Under LGPL - original licence link has changed is not relivant.
7835 * <script type="text/javascript">
7839 * @class Roo.grid.AbstractSelectionModel
7840 * @extends Roo.util.Observable
7842 * Abstract base class for grid SelectionModels. It provides the interface that should be
7843 * implemented by descendant classes. This class should not be directly instantiated.
7846 Roo.grid.AbstractSelectionModel = function(){
7847 this.locked = false;
7848 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7851 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7852 /** @ignore Called by the grid automatically. Do not call directly. */
7853 init : function(grid){
7859 * Locks the selections.
7866 * Unlocks the selections.
7868 unlock : function(){
7869 this.locked = false;
7873 * Returns true if the selections are locked.
7876 isLocked : function(){
7881 * Ext JS Library 1.1.1
7882 * Copyright(c) 2006-2007, Ext JS, LLC.
7884 * Originally Released Under LGPL - original licence link has changed is not relivant.
7887 * <script type="text/javascript">
7890 * @extends Roo.grid.AbstractSelectionModel
7891 * @class Roo.grid.RowSelectionModel
7892 * The default SelectionModel used by {@link Roo.grid.Grid}.
7893 * It supports multiple selections and keyboard selection/navigation.
7895 * @param {Object} config
7897 Roo.grid.RowSelectionModel = function(config){
7898 Roo.apply(this, config);
7899 this.selections = new Roo.util.MixedCollection(false, function(o){
7904 this.lastActive = false;
7908 * @event selectionchange
7909 * Fires when the selection changes
7910 * @param {SelectionModel} this
7912 "selectionchange" : true,
7914 * @event afterselectionchange
7915 * Fires after the selection changes (eg. by key press or clicking)
7916 * @param {SelectionModel} this
7918 "afterselectionchange" : true,
7920 * @event beforerowselect
7921 * Fires when a row is selected being selected, return false to cancel.
7922 * @param {SelectionModel} this
7923 * @param {Number} rowIndex The selected index
7924 * @param {Boolean} keepExisting False if other selections will be cleared
7926 "beforerowselect" : true,
7929 * Fires when a row is selected.
7930 * @param {SelectionModel} this
7931 * @param {Number} rowIndex The selected index
7932 * @param {Roo.data.Record} r The record
7936 * @event rowdeselect
7937 * Fires when a row is deselected.
7938 * @param {SelectionModel} this
7939 * @param {Number} rowIndex The selected index
7941 "rowdeselect" : true
7943 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7944 this.locked = false;
7947 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7949 * @cfg {Boolean} singleSelect
7950 * True to allow selection of only one row at a time (defaults to false)
7952 singleSelect : false,
7955 initEvents : function(){
7957 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7958 this.grid.on("mousedown", this.handleMouseDown, this);
7959 }else{ // allow click to work like normal
7960 this.grid.on("rowclick", this.handleDragableRowClick, this);
7962 // bootstrap does not have a view..
7963 var view = this.grid.view ? this.grid.view : this.grid;
7964 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7967 this.selectPrevious(e.shiftKey);
7968 }else if(this.last !== false && this.lastActive !== false){
7969 var last = this.last;
7970 this.selectRange(this.last, this.lastActive-1);
7971 view.focusRow(this.lastActive);
7976 this.selectFirstRow();
7978 this.fireEvent("afterselectionchange", this);
7980 "down" : function(e){
7982 this.selectNext(e.shiftKey);
7983 }else if(this.last !== false && this.lastActive !== false){
7984 var last = this.last;
7985 this.selectRange(this.last, this.lastActive+1);
7986 view.focusRow(this.lastActive);
7991 this.selectFirstRow();
7993 this.fireEvent("afterselectionchange", this);
7999 view.on("refresh", this.onRefresh, this);
8000 view.on("rowupdated", this.onRowUpdated, this);
8001 view.on("rowremoved", this.onRemove, this);
8005 onRefresh : function(){
8006 var ds = this.grid.ds, i, v = this.grid.view;
8007 var s = this.selections;
8009 if((i = ds.indexOfId(r.id)) != -1){
8011 s.add(ds.getAt(i)); // updating the selection relate data
8019 onRemove : function(v, index, r){
8020 this.selections.remove(r);
8024 onRowUpdated : function(v, index, r){
8025 if(this.isSelected(r)){
8026 v.onRowSelect(index);
8032 * @param {Array} records The records to select
8033 * @param {Boolean} keepExisting (optional) True to keep existing selections
8035 selectRecords : function(records, keepExisting){
8037 this.clearSelections();
8039 var ds = this.grid.ds;
8040 for(var i = 0, len = records.length; i < len; i++){
8041 this.selectRow(ds.indexOf(records[i]), true);
8046 * Gets the number of selected rows.
8049 getCount : function(){
8050 return this.selections.length;
8054 * Selects the first row in the grid.
8056 selectFirstRow : function(){
8061 * Select the last row.
8062 * @param {Boolean} keepExisting (optional) True to keep existing selections
8064 selectLastRow : function(keepExisting){
8065 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8069 * Selects the row immediately following the last selected row.
8070 * @param {Boolean} keepExisting (optional) True to keep existing selections
8072 selectNext : function(keepExisting){
8073 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8074 this.selectRow(this.last+1, keepExisting);
8075 var view = this.grid.view ? this.grid.view : this.grid;
8076 view.focusRow(this.last);
8081 * Selects the row that precedes the last selected row.
8082 * @param {Boolean} keepExisting (optional) True to keep existing selections
8084 selectPrevious : function(keepExisting){
8086 this.selectRow(this.last-1, keepExisting);
8087 var view = this.grid.view ? this.grid.view : this.grid;
8088 view.focusRow(this.last);
8093 * Returns the selected records
8094 * @return {Array} Array of selected records
8096 getSelections : function(){
8097 return [].concat(this.selections.items);
8101 * Returns the first selected record.
8104 getSelected : function(){
8105 return this.selections.itemAt(0);
8110 * Clears all selections.
8112 clearSelections : function(fast){
8117 var ds = this.grid.ds;
8118 var s = this.selections;
8120 this.deselectRow(ds.indexOfId(r.id));
8124 this.selections.clear();
8133 selectAll : function(){
8137 this.selections.clear();
8138 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8139 this.selectRow(i, true);
8144 * Returns True if there is a selection.
8147 hasSelection : function(){
8148 return this.selections.length > 0;
8152 * Returns True if the specified row is selected.
8153 * @param {Number/Record} record The record or index of the record to check
8156 isSelected : function(index){
8157 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8158 return (r && this.selections.key(r.id) ? true : false);
8162 * Returns True if the specified record id is selected.
8163 * @param {String} id The id of record to check
8166 isIdSelected : function(id){
8167 return (this.selections.key(id) ? true : false);
8171 handleMouseDown : function(e, t)
8173 var view = this.grid.view ? this.grid.view : this.grid;
8175 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8178 if(e.shiftKey && this.last !== false){
8179 var last = this.last;
8180 this.selectRange(last, rowIndex, e.ctrlKey);
8181 this.last = last; // reset the last
8182 view.focusRow(rowIndex);
8184 var isSelected = this.isSelected(rowIndex);
8185 if(e.button !== 0 && isSelected){
8186 view.focusRow(rowIndex);
8187 }else if(e.ctrlKey && isSelected){
8188 this.deselectRow(rowIndex);
8189 }else if(!isSelected){
8190 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8191 view.focusRow(rowIndex);
8194 this.fireEvent("afterselectionchange", this);
8197 handleDragableRowClick : function(grid, rowIndex, e)
8199 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8200 this.selectRow(rowIndex, false);
8201 var view = this.grid.view ? this.grid.view : this.grid;
8202 view.focusRow(rowIndex);
8203 this.fireEvent("afterselectionchange", this);
8208 * Selects multiple rows.
8209 * @param {Array} rows Array of the indexes of the row to select
8210 * @param {Boolean} keepExisting (optional) True to keep existing selections
8212 selectRows : function(rows, keepExisting){
8214 this.clearSelections();
8216 for(var i = 0, len = rows.length; i < len; i++){
8217 this.selectRow(rows[i], true);
8222 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8223 * @param {Number} startRow The index of the first row in the range
8224 * @param {Number} endRow The index of the last row in the range
8225 * @param {Boolean} keepExisting (optional) True to retain existing selections
8227 selectRange : function(startRow, endRow, keepExisting){
8232 this.clearSelections();
8234 if(startRow <= endRow){
8235 for(var i = startRow; i <= endRow; i++){
8236 this.selectRow(i, true);
8239 for(var i = startRow; i >= endRow; i--){
8240 this.selectRow(i, true);
8246 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8247 * @param {Number} startRow The index of the first row in the range
8248 * @param {Number} endRow The index of the last row in the range
8250 deselectRange : function(startRow, endRow, preventViewNotify){
8254 for(var i = startRow; i <= endRow; i++){
8255 this.deselectRow(i, preventViewNotify);
8261 * @param {Number} row The index of the row to select
8262 * @param {Boolean} keepExisting (optional) True to keep existing selections
8264 selectRow : function(index, keepExisting, preventViewNotify){
8265 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8268 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8269 if(!keepExisting || this.singleSelect){
8270 this.clearSelections();
8272 var r = this.grid.ds.getAt(index);
8273 this.selections.add(r);
8274 this.last = this.lastActive = index;
8275 if(!preventViewNotify){
8276 var view = this.grid.view ? this.grid.view : this.grid;
8277 view.onRowSelect(index);
8279 this.fireEvent("rowselect", this, index, r);
8280 this.fireEvent("selectionchange", this);
8286 * @param {Number} row The index of the row to deselect
8288 deselectRow : function(index, preventViewNotify){
8292 if(this.last == index){
8295 if(this.lastActive == index){
8296 this.lastActive = false;
8298 var r = this.grid.ds.getAt(index);
8299 this.selections.remove(r);
8300 if(!preventViewNotify){
8301 var view = this.grid.view ? this.grid.view : this.grid;
8302 view.onRowDeselect(index);
8304 this.fireEvent("rowdeselect", this, index);
8305 this.fireEvent("selectionchange", this);
8309 restoreLast : function(){
8311 this.last = this._last;
8316 acceptsNav : function(row, col, cm){
8317 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8321 onEditorKey : function(field, e){
8322 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8327 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8329 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8331 }else if(k == e.ENTER && !e.ctrlKey){
8335 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8337 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8339 }else if(k == e.ESC){
8343 g.startEditing(newCell[0], newCell[1]);
8348 * Ext JS Library 1.1.1
8349 * Copyright(c) 2006-2007, Ext JS, LLC.
8351 * Originally Released Under LGPL - original licence link has changed is not relivant.
8354 * <script type="text/javascript">
8359 * @class Roo.grid.ColumnModel
8360 * @extends Roo.util.Observable
8361 * This is the default implementation of a ColumnModel used by the Grid. It defines
8362 * the columns in the grid.
8365 var colModel = new Roo.grid.ColumnModel([
8366 {header: "Ticker", width: 60, sortable: true, locked: true},
8367 {header: "Company Name", width: 150, sortable: true},
8368 {header: "Market Cap.", width: 100, sortable: true},
8369 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8370 {header: "Employees", width: 100, sortable: true, resizable: false}
8375 * The config options listed for this class are options which may appear in each
8376 * individual column definition.
8377 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8379 * @param {Object} config An Array of column config objects. See this class's
8380 * config objects for details.
8382 Roo.grid.ColumnModel = function(config){
8384 * The config passed into the constructor
8386 this.config = []; //config;
8389 // if no id, create one
8390 // if the column does not have a dataIndex mapping,
8391 // map it to the order it is in the config
8392 for(var i = 0, len = config.length; i < len; i++){
8393 this.addColumn(config[i]);
8398 * The width of columns which have no width specified (defaults to 100)
8401 this.defaultWidth = 100;
8404 * Default sortable of columns which have no sortable specified (defaults to false)
8407 this.defaultSortable = false;
8411 * @event widthchange
8412 * Fires when the width of a column changes.
8413 * @param {ColumnModel} this
8414 * @param {Number} columnIndex The column index
8415 * @param {Number} newWidth The new width
8417 "widthchange": true,
8419 * @event headerchange
8420 * Fires when the text of a header changes.
8421 * @param {ColumnModel} this
8422 * @param {Number} columnIndex The column index
8423 * @param {Number} newText The new header text
8425 "headerchange": true,
8427 * @event hiddenchange
8428 * Fires when a column is hidden or "unhidden".
8429 * @param {ColumnModel} this
8430 * @param {Number} columnIndex The column index
8431 * @param {Boolean} hidden true if hidden, false otherwise
8433 "hiddenchange": true,
8435 * @event columnmoved
8436 * Fires when a column is moved.
8437 * @param {ColumnModel} this
8438 * @param {Number} oldIndex
8439 * @param {Number} newIndex
8441 "columnmoved" : true,
8443 * @event columlockchange
8444 * Fires when a column's locked state is changed
8445 * @param {ColumnModel} this
8446 * @param {Number} colIndex
8447 * @param {Boolean} locked true if locked
8449 "columnlockchange" : true
8451 Roo.grid.ColumnModel.superclass.constructor.call(this);
8453 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8455 * @cfg {String} header The header text to display in the Grid view.
8458 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8461 * @cfg {String} smHeader Header at Bootsrap Small width
8464 * @cfg {String} mdHeader Header at Bootsrap Medium width
8467 * @cfg {String} lgHeader Header at Bootsrap Large width
8470 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8473 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8474 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8475 * specified, the column's index is used as an index into the Record's data Array.
8478 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8479 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8482 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8483 * Defaults to the value of the {@link #defaultSortable} property.
8484 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8487 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8490 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8493 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8496 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8499 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8500 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8501 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8502 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8505 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8508 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8511 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8514 * @cfg {String} cursor (Optional)
8517 * @cfg {String} tooltip (Optional)
8520 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8523 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8526 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8529 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8532 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8535 * Returns the id of the column at the specified index.
8536 * @param {Number} index The column index
8537 * @return {String} the id
8539 getColumnId : function(index){
8540 return this.config[index].id;
8544 * Returns the column for a specified id.
8545 * @param {String} id The column id
8546 * @return {Object} the column
8548 getColumnById : function(id){
8549 return this.lookup[id];
8554 * Returns the column Object for a specified dataIndex.
8555 * @param {String} dataIndex The column dataIndex
8556 * @return {Object|Boolean} the column or false if not found
8558 getColumnByDataIndex: function(dataIndex){
8559 var index = this.findColumnIndex(dataIndex);
8560 return index > -1 ? this.config[index] : false;
8564 * Returns the index for a specified column id.
8565 * @param {String} id The column id
8566 * @return {Number} the index, or -1 if not found
8568 getIndexById : function(id){
8569 for(var i = 0, len = this.config.length; i < len; i++){
8570 if(this.config[i].id == id){
8578 * Returns the index for a specified column dataIndex.
8579 * @param {String} dataIndex The column dataIndex
8580 * @return {Number} the index, or -1 if not found
8583 findColumnIndex : function(dataIndex){
8584 for(var i = 0, len = this.config.length; i < len; i++){
8585 if(this.config[i].dataIndex == dataIndex){
8593 moveColumn : function(oldIndex, newIndex){
8594 var c = this.config[oldIndex];
8595 this.config.splice(oldIndex, 1);
8596 this.config.splice(newIndex, 0, c);
8597 this.dataMap = null;
8598 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8601 isLocked : function(colIndex){
8602 return this.config[colIndex].locked === true;
8605 setLocked : function(colIndex, value, suppressEvent){
8606 if(this.isLocked(colIndex) == value){
8609 this.config[colIndex].locked = value;
8611 this.fireEvent("columnlockchange", this, colIndex, value);
8615 getTotalLockedWidth : function(){
8617 for(var i = 0; i < this.config.length; i++){
8618 if(this.isLocked(i) && !this.isHidden(i)){
8619 this.totalWidth += this.getColumnWidth(i);
8625 getLockedCount : function(){
8626 for(var i = 0, len = this.config.length; i < len; i++){
8627 if(!this.isLocked(i)){
8632 return this.config.length;
8636 * Returns the number of columns.
8639 getColumnCount : function(visibleOnly){
8640 if(visibleOnly === true){
8642 for(var i = 0, len = this.config.length; i < len; i++){
8643 if(!this.isHidden(i)){
8649 return this.config.length;
8653 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8654 * @param {Function} fn
8655 * @param {Object} scope (optional)
8656 * @return {Array} result
8658 getColumnsBy : function(fn, scope){
8660 for(var i = 0, len = this.config.length; i < len; i++){
8661 var c = this.config[i];
8662 if(fn.call(scope||this, c, i) === true){
8670 * Returns true if the specified column is sortable.
8671 * @param {Number} col The column index
8674 isSortable : function(col){
8675 if(typeof this.config[col].sortable == "undefined"){
8676 return this.defaultSortable;
8678 return this.config[col].sortable;
8682 * Returns the rendering (formatting) function defined for the column.
8683 * @param {Number} col The column index.
8684 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8686 getRenderer : function(col){
8687 if(!this.config[col].renderer){
8688 return Roo.grid.ColumnModel.defaultRenderer;
8690 return this.config[col].renderer;
8694 * Sets the rendering (formatting) function for a column.
8695 * @param {Number} col The column index
8696 * @param {Function} fn The function to use to process the cell's raw data
8697 * to return HTML markup for the grid view. The render function is called with
8698 * the following parameters:<ul>
8699 * <li>Data value.</li>
8700 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8701 * <li>css A CSS style string to apply to the table cell.</li>
8702 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8703 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8704 * <li>Row index</li>
8705 * <li>Column index</li>
8706 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8708 setRenderer : function(col, fn){
8709 this.config[col].renderer = fn;
8713 * Returns the width for the specified column.
8714 * @param {Number} col The column index
8715 * @param (optional) {String} gridSize bootstrap width size.
8718 getColumnWidth : function(col, gridSize)
8720 var cfg = this.config[col];
8722 if (typeof(gridSize) == 'undefined') {
8723 return cfg.width * 1 || this.defaultWidth;
8725 if (gridSize === false) { // if we set it..
8726 return cfg.width || false;
8728 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8730 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8731 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8734 return cfg[ sizes[i] ];
8741 * Sets the width for a column.
8742 * @param {Number} col The column index
8743 * @param {Number} width The new width
8745 setColumnWidth : function(col, width, suppressEvent){
8746 this.config[col].width = width;
8747 this.totalWidth = null;
8749 this.fireEvent("widthchange", this, col, width);
8754 * Returns the total width of all columns.
8755 * @param {Boolean} includeHidden True to include hidden column widths
8758 getTotalWidth : function(includeHidden){
8759 if(!this.totalWidth){
8760 this.totalWidth = 0;
8761 for(var i = 0, len = this.config.length; i < len; i++){
8762 if(includeHidden || !this.isHidden(i)){
8763 this.totalWidth += this.getColumnWidth(i);
8767 return this.totalWidth;
8771 * Returns the header for the specified column.
8772 * @param {Number} col The column index
8775 getColumnHeader : function(col){
8776 return this.config[col].header;
8780 * Sets the header for a column.
8781 * @param {Number} col The column index
8782 * @param {String} header The new header
8784 setColumnHeader : function(col, header){
8785 this.config[col].header = header;
8786 this.fireEvent("headerchange", this, col, header);
8790 * Returns the tooltip for the specified column.
8791 * @param {Number} col The column index
8794 getColumnTooltip : function(col){
8795 return this.config[col].tooltip;
8798 * Sets the tooltip for a column.
8799 * @param {Number} col The column index
8800 * @param {String} tooltip The new tooltip
8802 setColumnTooltip : function(col, tooltip){
8803 this.config[col].tooltip = tooltip;
8807 * Returns the dataIndex for the specified column.
8808 * @param {Number} col The column index
8811 getDataIndex : function(col){
8812 return this.config[col].dataIndex;
8816 * Sets the dataIndex for a column.
8817 * @param {Number} col The column index
8818 * @param {Number} dataIndex The new dataIndex
8820 setDataIndex : function(col, dataIndex){
8821 this.config[col].dataIndex = dataIndex;
8827 * Returns true if the cell is editable.
8828 * @param {Number} colIndex The column index
8829 * @param {Number} rowIndex The row index - this is nto actually used..?
8832 isCellEditable : function(colIndex, rowIndex){
8833 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8837 * Returns the editor defined for the cell/column.
8838 * return false or null to disable editing.
8839 * @param {Number} colIndex The column index
8840 * @param {Number} rowIndex The row index
8843 getCellEditor : function(colIndex, rowIndex){
8844 return this.config[colIndex].editor;
8848 * Sets if a column is editable.
8849 * @param {Number} col The column index
8850 * @param {Boolean} editable True if the column is editable
8852 setEditable : function(col, editable){
8853 this.config[col].editable = editable;
8858 * Returns true if the column is hidden.
8859 * @param {Number} colIndex The column index
8862 isHidden : function(colIndex){
8863 return this.config[colIndex].hidden;
8868 * Returns true if the column width cannot be changed
8870 isFixed : function(colIndex){
8871 return this.config[colIndex].fixed;
8875 * Returns true if the column can be resized
8878 isResizable : function(colIndex){
8879 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8882 * Sets if a column is hidden.
8883 * @param {Number} colIndex The column index
8884 * @param {Boolean} hidden True if the column is hidden
8886 setHidden : function(colIndex, hidden){
8887 this.config[colIndex].hidden = hidden;
8888 this.totalWidth = null;
8889 this.fireEvent("hiddenchange", this, colIndex, hidden);
8893 * Sets the editor for a column.
8894 * @param {Number} col The column index
8895 * @param {Object} editor The editor object
8897 setEditor : function(col, editor){
8898 this.config[col].editor = editor;
8901 * Add a column (experimental...) - defaults to adding to the end..
8902 * @param {Object} config
8904 addColumn : function(c)
8907 var i = this.config.length;
8910 if(typeof c.dataIndex == "undefined"){
8913 if(typeof c.renderer == "string"){
8914 c.renderer = Roo.util.Format[c.renderer];
8916 if(typeof c.id == "undefined"){
8919 if(c.editor && c.editor.xtype){
8920 c.editor = Roo.factory(c.editor, Roo.grid);
8922 if(c.editor && c.editor.isFormField){
8923 c.editor = new Roo.grid.GridEditor(c.editor);
8925 this.lookup[c.id] = c;
8930 Roo.grid.ColumnModel.defaultRenderer = function(value)
8932 if(typeof value == "object") {
8935 if(typeof value == "string" && value.length < 1){
8939 return String.format("{0}", value);
8942 // Alias for backwards compatibility
8943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8946 * Ext JS Library 1.1.1
8947 * Copyright(c) 2006-2007, Ext JS, LLC.
8949 * Originally Released Under LGPL - original licence link has changed is not relivant.
8952 * <script type="text/javascript">
8956 * @class Roo.LoadMask
8957 * A simple utility class for generically masking elements while loading data. If the element being masked has
8958 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8959 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8960 * element's UpdateManager load indicator and will be destroyed after the initial load.
8962 * Create a new LoadMask
8963 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8964 * @param {Object} config The config object
8966 Roo.LoadMask = function(el, config){
8967 this.el = Roo.get(el);
8968 Roo.apply(this, config);
8970 this.store.on('beforeload', this.onBeforeLoad, this);
8971 this.store.on('load', this.onLoad, this);
8972 this.store.on('loadexception', this.onLoadException, this);
8973 this.removeMask = false;
8975 var um = this.el.getUpdateManager();
8976 um.showLoadIndicator = false; // disable the default indicator
8977 um.on('beforeupdate', this.onBeforeLoad, this);
8978 um.on('update', this.onLoad, this);
8979 um.on('failure', this.onLoad, this);
8980 this.removeMask = true;
8984 Roo.LoadMask.prototype = {
8986 * @cfg {Boolean} removeMask
8987 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8988 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8993 * The text to display in a centered loading message box (defaults to 'Loading...')
8997 * @cfg {String} msgCls
8998 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9000 msgCls : 'x-mask-loading',
9003 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9009 * Disables the mask to prevent it from being displayed
9011 disable : function(){
9012 this.disabled = true;
9016 * Enables the mask so that it can be displayed
9018 enable : function(){
9019 this.disabled = false;
9022 onLoadException : function()
9026 if (typeof(arguments[3]) != 'undefined') {
9027 Roo.MessageBox.alert("Error loading",arguments[3]);
9031 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9032 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9039 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9044 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9048 onBeforeLoad : function(){
9050 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9055 destroy : function(){
9057 this.store.un('beforeload', this.onBeforeLoad, this);
9058 this.store.un('load', this.onLoad, this);
9059 this.store.un('loadexception', this.onLoadException, this);
9061 var um = this.el.getUpdateManager();
9062 um.un('beforeupdate', this.onBeforeLoad, this);
9063 um.un('update', this.onLoad, this);
9064 um.un('failure', this.onLoad, this);
9068 * @class Roo.bootstrap.Table
9070 * @extends Roo.bootstrap.Component
9071 * @children Roo.bootstrap.TableBody
9072 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9073 * Similar to Roo.grid.Grid
9075 var table = Roo.factory({
9077 xns : Roo.bootstrap,
9078 autoSizeColumns: true,
9085 sortInfo : { direction : 'ASC', field: 'name' },
9087 xtype : 'HttpProxy',
9090 url : 'https://example.com/some.data.url.json'
9093 xtype : 'JsonReader',
9095 fields : [ 'id', 'name', whatever' ],
9102 xtype : 'ColumnModel',
9106 dataIndex : 'is_in_group',
9109 renderer : function(v, x , r) {
9111 return String.format("{0}", v)
9117 xtype : 'RowSelectionModel',
9118 xns : Roo.bootstrap.Table
9119 // you can add listeners to catch selection change here....
9125 grid.render(Roo.get("some-div"));
9128 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9133 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9134 * @cfg {Roo.data.Store} store The data store to use
9135 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9137 * @cfg {String} cls table class
9140 * @cfg {string} empty_results Text to display for no results
9141 * @cfg {boolean} striped Should the rows be alternative striped
9142 * @cfg {boolean} bordered Add borders to the table
9143 * @cfg {boolean} hover Add hover highlighting
9144 * @cfg {boolean} condensed Format condensed
9145 * @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,
9146 * also adds table-responsive (see bootstrap docs for details)
9147 * @cfg {Boolean} loadMask (true|false) default false
9148 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9149 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9150 * @cfg {Boolean} rowSelection (true|false) default false
9151 * @cfg {Boolean} cellSelection (true|false) default false
9152 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9153 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9154 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9155 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9156 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9159 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9162 * Create a new Table
9163 * @param {Object} config The config object
9166 Roo.bootstrap.Table = function(config)
9168 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9171 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9172 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9173 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9174 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9176 this.view = this; // compat with grid.
9178 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9180 this.sm.grid = this;
9181 this.selModel = Roo.factory(this.sm, Roo.grid);
9182 this.sm = this.selModel;
9183 this.sm.xmodule = this.xmodule || false;
9186 if (this.cm && typeof(this.cm.config) == 'undefined') {
9187 this.colModel = new Roo.grid.ColumnModel(this.cm);
9188 this.cm = this.colModel;
9189 this.cm.xmodule = this.xmodule || false;
9192 this.store= Roo.factory(this.store, Roo.data);
9193 this.ds = this.store;
9194 this.ds.xmodule = this.xmodule || false;
9197 if (this.footer && this.store) {
9198 this.footer.dataSource = this.ds;
9199 this.footer = Roo.factory(this.footer);
9206 * Fires when a cell is clicked
9207 * @param {Roo.bootstrap.Table} this
9208 * @param {Roo.Element} el
9209 * @param {Number} rowIndex
9210 * @param {Number} columnIndex
9211 * @param {Roo.EventObject} e
9215 * @event celldblclick
9216 * Fires when a cell is double clicked
9217 * @param {Roo.bootstrap.Table} this
9218 * @param {Roo.Element} el
9219 * @param {Number} rowIndex
9220 * @param {Number} columnIndex
9221 * @param {Roo.EventObject} e
9223 "celldblclick" : true,
9226 * Fires when a row is clicked
9227 * @param {Roo.bootstrap.Table} this
9228 * @param {Roo.Element} el
9229 * @param {Number} rowIndex
9230 * @param {Roo.EventObject} e
9234 * @event rowdblclick
9235 * Fires when a row is double clicked
9236 * @param {Roo.bootstrap.Table} this
9237 * @param {Roo.Element} el
9238 * @param {Number} rowIndex
9239 * @param {Roo.EventObject} e
9241 "rowdblclick" : true,
9244 * Fires when a mouseover occur
9245 * @param {Roo.bootstrap.Table} this
9246 * @param {Roo.Element} el
9247 * @param {Number} rowIndex
9248 * @param {Number} columnIndex
9249 * @param {Roo.EventObject} e
9254 * Fires when a mouseout occur
9255 * @param {Roo.bootstrap.Table} this
9256 * @param {Roo.Element} el
9257 * @param {Number} rowIndex
9258 * @param {Number} columnIndex
9259 * @param {Roo.EventObject} e
9264 * Fires when a row is rendered, so you can change add a style to it.
9265 * @param {Roo.bootstrap.Table} this
9266 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9270 * @event rowsrendered
9271 * Fires when all the rows have been rendered
9272 * @param {Roo.bootstrap.Table} this
9274 'rowsrendered' : true,
9276 * @event contextmenu
9277 * The raw contextmenu event for the entire grid.
9278 * @param {Roo.EventObject} e
9280 "contextmenu" : true,
9282 * @event rowcontextmenu
9283 * Fires when a row is right clicked
9284 * @param {Roo.bootstrap.Table} this
9285 * @param {Number} rowIndex
9286 * @param {Roo.EventObject} e
9288 "rowcontextmenu" : true,
9290 * @event cellcontextmenu
9291 * Fires when a cell is right clicked
9292 * @param {Roo.bootstrap.Table} this
9293 * @param {Number} rowIndex
9294 * @param {Number} cellIndex
9295 * @param {Roo.EventObject} e
9297 "cellcontextmenu" : true,
9299 * @event headercontextmenu
9300 * Fires when a header is right clicked
9301 * @param {Roo.bootstrap.Table} this
9302 * @param {Number} columnIndex
9303 * @param {Roo.EventObject} e
9305 "headercontextmenu" : true,
9308 * The raw mousedown event for the entire grid.
9309 * @param {Roo.EventObject} e
9316 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9333 enableColumnResize: true,
9335 rowSelection : false,
9336 cellSelection : false,
9339 minColumnWidth : 50,
9341 // Roo.Element - the tbody
9342 bodyEl: false, // <tbody> Roo.Element - thead element
9343 headEl: false, // <thead> Roo.Element - thead element
9344 resizeProxy : false, // proxy element for dragging?
9348 container: false, // used by gridpanel...
9354 auto_hide_footer : false,
9356 view: false, // actually points to this..
9358 getAutoCreate : function()
9360 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9367 // this get's auto added by panel.Grid
9368 if (this.scrollBody) {
9369 cfg.cls += ' table-body-fixed';
9372 cfg.cls += ' table-striped';
9376 cfg.cls += ' table-hover';
9378 if (this.bordered) {
9379 cfg.cls += ' table-bordered';
9381 if (this.condensed) {
9382 cfg.cls += ' table-condensed';
9385 if (this.responsive) {
9386 cfg.cls += ' table-responsive';
9390 cfg.cls+= ' ' +this.cls;
9396 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9399 if(this.store || this.cm){
9400 if(this.headerShow){
9401 cfg.cn.push(this.renderHeader());
9404 cfg.cn.push(this.renderBody());
9406 if(this.footerShow){
9407 cfg.cn.push(this.renderFooter());
9409 // where does this come from?
9410 //cfg.cls+= ' TableGrid';
9413 return { cn : [ cfg ] };
9416 initEvents : function()
9418 if(!this.store || !this.cm){
9421 if (this.selModel) {
9422 this.selModel.initEvents();
9426 //Roo.log('initEvents with ds!!!!');
9428 this.bodyEl = this.el.select('tbody', true).first();
9429 this.headEl = this.el.select('thead', true).first();
9430 this.mainFoot = this.el.select('tfoot', true).first();
9435 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9436 e.on('click', this.sort, this);
9440 // why is this done????? = it breaks dialogs??
9441 //this.parent().el.setStyle('position', 'relative');
9445 this.footer.parentId = this.id;
9446 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9449 this.el.select('tfoot tr td').first().addClass('hide');
9454 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9457 this.store.on('load', this.onLoad, this);
9458 this.store.on('beforeload', this.onBeforeLoad, this);
9459 this.store.on('update', this.onUpdate, this);
9460 this.store.on('add', this.onAdd, this);
9461 this.store.on("clear", this.clear, this);
9463 this.el.on("contextmenu", this.onContextMenu, this);
9466 this.cm.on("headerchange", this.onHeaderChange, this);
9467 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9469 //?? does bodyEl get replaced on render?
9470 this.bodyEl.on("click", this.onClick, this);
9471 this.bodyEl.on("dblclick", this.onDblClick, this);
9472 this.bodyEl.on('scroll', this.onBodyScroll, this);
9474 // guessing mainbody will work - this relays usually caught by selmodel at present.
9475 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9478 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9481 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9482 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9487 // Compatibility with grid - we implement all the view features at present.
9488 getView : function()
9493 initCSS : function()
9497 var cm = this.cm, styles = [];
9498 this.CSS.removeStyleSheet(this.id + '-cssrules');
9499 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9500 // we can honour xs/sm/md/xl as widths...
9501 // we first have to decide what widht we are currently at...
9502 var sz = Roo.getGridSize();
9506 var cols = []; // visable cols.
9508 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9509 var w = cm.getColumnWidth(i, false);
9511 cols.push( { rel : false, abs : 0 });
9515 cols.push( { rel : false, abs : w });
9517 last = i; // not really..
9520 var w = cm.getColumnWidth(i, sz);
9525 cols.push( { rel : w, abs : false });
9528 var avail = this.bodyEl.dom.clientWidth - total_abs;
9530 var unitWidth = Math.floor(avail / total);
9531 var rem = avail - (unitWidth * total);
9533 var hidden, width, pos = 0 , splithide , left;
9534 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9536 hidden = 'display:none;';
9538 width = 'width:0px;';
9540 if(!cm.isHidden(i)){
9544 // we can honour xs/sm/md/xl ?
9545 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9547 hidden = 'display:none;';
9549 // width should return a small number...
9551 w+=rem; // add the remaining with..
9554 left = "left:" + (pos -4) + "px;";
9555 width = "width:" + w+ "px;";
9558 if (this.responsive) {
9561 hidden = cm.isHidden(i) ? 'display:none;' : '';
9562 splithide = 'display: none;';
9565 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9568 splithide = 'display:none;';
9571 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9572 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9573 // this is the popover version..
9574 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9579 //Roo.log(styles.join(''));
9580 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9586 onContextMenu : function(e, t)
9588 this.processEvent("contextmenu", e);
9591 processEvent : function(name, e)
9593 if (name != 'touchstart' ) {
9594 this.fireEvent(name, e);
9597 var t = e.getTarget();
9599 var cell = Roo.get(t);
9605 if(cell.findParent('tfoot', false, true)){
9609 if(cell.findParent('thead', false, true)){
9611 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9612 cell = Roo.get(t).findParent('th', false, true);
9614 Roo.log("failed to find th in thead?");
9615 Roo.log(e.getTarget());
9620 var cellIndex = cell.dom.cellIndex;
9622 var ename = name == 'touchstart' ? 'click' : name;
9623 this.fireEvent("header" + ename, this, cellIndex, e);
9628 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9629 cell = Roo.get(t).findParent('td', false, true);
9631 Roo.log("failed to find th in tbody?");
9632 Roo.log(e.getTarget());
9637 var row = cell.findParent('tr', false, true);
9638 var cellIndex = cell.dom.cellIndex;
9639 var rowIndex = row.dom.rowIndex - 1;
9643 this.fireEvent("row" + name, this, rowIndex, e);
9647 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9653 onMouseover : function(e, el)
9655 var cell = Roo.get(el);
9661 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9662 cell = cell.findParent('td', false, true);
9665 var row = cell.findParent('tr', false, true);
9666 var cellIndex = cell.dom.cellIndex;
9667 var rowIndex = row.dom.rowIndex - 1; // start from 0
9669 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9673 onMouseout : function(e, el)
9675 var cell = Roo.get(el);
9681 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9682 cell = cell.findParent('td', false, true);
9685 var row = cell.findParent('tr', false, true);
9686 var cellIndex = cell.dom.cellIndex;
9687 var rowIndex = row.dom.rowIndex - 1; // start from 0
9689 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9693 onClick : function(e, el)
9695 var cell = Roo.get(el);
9697 if(!cell || (!this.cellSelection && !this.rowSelection)){
9701 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9702 cell = cell.findParent('td', false, true);
9705 if(!cell || typeof(cell) == 'undefined'){
9709 var row = cell.findParent('tr', false, true);
9711 if(!row || typeof(row) == 'undefined'){
9715 var cellIndex = cell.dom.cellIndex;
9716 var rowIndex = this.getRowIndex(row);
9718 // why??? - should these not be based on SelectionModel?
9719 //if(this.cellSelection){
9720 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9723 //if(this.rowSelection){
9724 this.fireEvent('rowclick', this, row, rowIndex, e);
9729 onDblClick : function(e,el)
9731 var cell = Roo.get(el);
9733 if(!cell || (!this.cellSelection && !this.rowSelection)){
9737 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9738 cell = cell.findParent('td', false, true);
9741 if(!cell || typeof(cell) == 'undefined'){
9745 var row = cell.findParent('tr', false, true);
9747 if(!row || typeof(row) == 'undefined'){
9751 var cellIndex = cell.dom.cellIndex;
9752 var rowIndex = this.getRowIndex(row);
9754 if(this.cellSelection){
9755 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9758 if(this.rowSelection){
9759 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9762 findRowIndex : function(el)
9764 var cell = Roo.get(el);
9768 var row = cell.findParent('tr', false, true);
9770 if(!row || typeof(row) == 'undefined'){
9773 return this.getRowIndex(row);
9775 sort : function(e,el)
9777 var col = Roo.get(el);
9779 if(!col.hasClass('sortable')){
9783 var sort = col.attr('sort');
9786 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9790 this.store.sortInfo = {field : sort, direction : dir};
9793 Roo.log("calling footer first");
9794 this.footer.onClick('first');
9797 this.store.load({ params : { start : 0 } });
9801 renderHeader : function()
9809 this.totalWidth = 0;
9811 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9813 var config = cm.config[i];
9817 cls : 'x-hcol-' + i,
9820 html: cm.getColumnHeader(i)
9823 var tooltip = cm.getColumnTooltip(i);
9825 c.tooltip = tooltip;
9831 if(typeof(config.sortable) != 'undefined' && config.sortable){
9832 c.cls += ' sortable';
9833 c.html = '<i class="fa"></i>' + c.html;
9836 // could use BS4 hidden-..-down
9838 if(typeof(config.lgHeader) != 'undefined'){
9839 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9842 if(typeof(config.mdHeader) != 'undefined'){
9843 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9846 if(typeof(config.smHeader) != 'undefined'){
9847 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9850 if(typeof(config.xsHeader) != 'undefined'){
9851 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9858 if(typeof(config.tooltip) != 'undefined'){
9859 c.tooltip = config.tooltip;
9862 if(typeof(config.colspan) != 'undefined'){
9863 c.colspan = config.colspan;
9866 // hidden is handled by CSS now
9868 if(typeof(config.dataIndex) != 'undefined'){
9869 c.sort = config.dataIndex;
9874 if(typeof(config.align) != 'undefined' && config.align.length){
9875 c.style += ' text-align:' + config.align + ';';
9878 /* width is done in CSS
9879 *if(typeof(config.width) != 'undefined'){
9880 c.style += ' width:' + config.width + 'px;';
9881 this.totalWidth += config.width;
9883 this.totalWidth += 100; // assume minimum of 100 per column?
9887 if(typeof(config.cls) != 'undefined'){
9888 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9890 // this is the bit that doesnt reall work at all...
9892 if (this.responsive) {
9895 ['xs','sm','md','lg'].map(function(size){
9897 if(typeof(config[size]) == 'undefined'){
9901 if (!config[size]) { // 0 = hidden
9902 // BS 4 '0' is treated as hide that column and below.
9903 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9907 c.cls += ' col-' + size + '-' + config[size] + (
9908 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9916 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9927 renderBody : function()
9937 colspan : this.cm.getColumnCount()
9947 renderFooter : function()
9957 colspan : this.cm.getColumnCount()
9971 // Roo.log('ds onload');
9976 var ds = this.store;
9978 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9979 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9980 if (_this.store.sortInfo) {
9982 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9983 e.select('i', true).addClass(['fa-arrow-up']);
9986 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9987 e.select('i', true).addClass(['fa-arrow-down']);
9992 var tbody = this.bodyEl;
9994 if(ds.getCount() > 0){
9995 ds.data.each(function(d,rowIndex){
9996 var row = this.renderRow(cm, ds, rowIndex);
9998 tbody.createChild(row);
10002 if(row.cellObjects.length){
10003 Roo.each(row.cellObjects, function(r){
10004 _this.renderCellObject(r);
10009 } else if (this.empty_results.length) {
10010 this.el.mask(this.empty_results, 'no-spinner');
10013 var tfoot = this.el.select('tfoot', true).first();
10015 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10017 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10019 var total = this.ds.getTotalCount();
10021 if(this.footer.pageSize < total){
10022 this.mainFoot.show();
10026 Roo.each(this.el.select('tbody td', true).elements, function(e){
10027 e.on('mouseover', _this.onMouseover, _this);
10030 Roo.each(this.el.select('tbody td', true).elements, function(e){
10031 e.on('mouseout', _this.onMouseout, _this);
10033 this.fireEvent('rowsrendered', this);
10037 this.initCSS(); /// resize cols
10043 onUpdate : function(ds,record)
10045 this.refreshRow(record);
10049 onRemove : function(ds, record, index, isUpdate){
10050 if(isUpdate !== true){
10051 this.fireEvent("beforerowremoved", this, index, record);
10053 var bt = this.bodyEl.dom;
10055 var rows = this.el.select('tbody > tr', true).elements;
10057 if(typeof(rows[index]) != 'undefined'){
10058 bt.removeChild(rows[index].dom);
10061 // if(bt.rows[index]){
10062 // bt.removeChild(bt.rows[index]);
10065 if(isUpdate !== true){
10066 //this.stripeRows(index);
10067 //this.syncRowHeights(index, index);
10069 this.fireEvent("rowremoved", this, index, record);
10073 onAdd : function(ds, records, rowIndex)
10075 //Roo.log('on Add called');
10076 // - note this does not handle multiple adding very well..
10077 var bt = this.bodyEl.dom;
10078 for (var i =0 ; i < records.length;i++) {
10079 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10080 //Roo.log(records[i]);
10081 //Roo.log(this.store.getAt(rowIndex+i));
10082 this.insertRow(this.store, rowIndex + i, false);
10089 refreshRow : function(record){
10090 var ds = this.store, index;
10091 if(typeof record == 'number'){
10093 record = ds.getAt(index);
10095 index = ds.indexOf(record);
10097 return; // should not happen - but seems to
10100 this.insertRow(ds, index, true);
10102 this.onRemove(ds, record, index+1, true);
10104 //this.syncRowHeights(index, index);
10106 this.fireEvent("rowupdated", this, index, record);
10108 // private - called by RowSelection
10109 onRowSelect : function(rowIndex){
10110 var row = this.getRowDom(rowIndex);
10111 row.addClass(['bg-info','info']);
10113 // private - called by RowSelection
10114 onRowDeselect : function(rowIndex)
10116 if (rowIndex < 0) {
10119 var row = this.getRowDom(rowIndex);
10120 row.removeClass(['bg-info','info']);
10123 * Focuses the specified row.
10124 * @param {Number} row The row index
10126 focusRow : function(row)
10128 //Roo.log('GridView.focusRow');
10129 var x = this.bodyEl.dom.scrollLeft;
10130 this.focusCell(row, 0, false);
10131 this.bodyEl.dom.scrollLeft = x;
10135 * Focuses the specified cell.
10136 * @param {Number} row The row index
10137 * @param {Number} col The column index
10138 * @param {Boolean} hscroll false to disable horizontal scrolling
10140 focusCell : function(row, col, hscroll)
10142 //Roo.log('GridView.focusCell');
10143 var el = this.ensureVisible(row, col, hscroll);
10144 // not sure what focusEL achives = it's a <a> pos relative
10145 //this.focusEl.alignTo(el, "tl-tl");
10147 // this.focusEl.focus();
10149 // this.focusEl.focus.defer(1, this.focusEl);
10154 * Scrolls the specified cell into view
10155 * @param {Number} row The row index
10156 * @param {Number} col The column index
10157 * @param {Boolean} hscroll false to disable horizontal scrolling
10159 ensureVisible : function(row, col, hscroll)
10161 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10162 //return null; //disable for testing.
10163 if(typeof row != "number"){
10164 row = row.rowIndex;
10166 if(row < 0 && row >= this.ds.getCount()){
10169 col = (col !== undefined ? col : 0);
10171 while(cm.isHidden(col)){
10175 var el = this.getCellDom(row, col);
10179 var c = this.bodyEl.dom;
10181 var ctop = parseInt(el.offsetTop, 10);
10182 var cleft = parseInt(el.offsetLeft, 10);
10183 var cbot = ctop + el.offsetHeight;
10184 var cright = cleft + el.offsetWidth;
10186 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10187 var ch = 0; //?? header is not withing the area?
10188 var stop = parseInt(c.scrollTop, 10);
10189 var sleft = parseInt(c.scrollLeft, 10);
10190 var sbot = stop + ch;
10191 var sright = sleft + c.clientWidth;
10193 Roo.log('GridView.ensureVisible:' +
10195 ' c.clientHeight:' + c.clientHeight +
10196 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10204 c.scrollTop = ctop;
10205 //Roo.log("set scrolltop to ctop DISABLE?");
10206 }else if(cbot > sbot){
10207 //Roo.log("set scrolltop to cbot-ch");
10208 c.scrollTop = cbot-ch;
10211 if(hscroll !== false){
10213 c.scrollLeft = cleft;
10214 }else if(cright > sright){
10215 c.scrollLeft = cright-c.clientWidth;
10223 insertRow : function(dm, rowIndex, isUpdate){
10226 this.fireEvent("beforerowsinserted", this, rowIndex);
10228 //var s = this.getScrollState();
10229 var row = this.renderRow(this.cm, this.store, rowIndex);
10230 // insert before rowIndex..
10231 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10235 if(row.cellObjects.length){
10236 Roo.each(row.cellObjects, function(r){
10237 _this.renderCellObject(r);
10242 this.fireEvent("rowsinserted", this, rowIndex);
10243 //this.syncRowHeights(firstRow, lastRow);
10244 //this.stripeRows(firstRow);
10251 getRowDom : function(rowIndex)
10253 var rows = this.el.select('tbody > tr', true).elements;
10255 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10258 getCellDom : function(rowIndex, colIndex)
10260 var row = this.getRowDom(rowIndex);
10261 if (row === false) {
10264 var cols = row.select('td', true).elements;
10265 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10269 // returns the object tree for a tr..
10272 renderRow : function(cm, ds, rowIndex)
10274 var d = ds.getAt(rowIndex);
10278 cls : 'x-row-' + rowIndex,
10282 var cellObjects = [];
10284 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10285 var config = cm.config[i];
10287 var renderer = cm.getRenderer(i);
10291 if(typeof(renderer) !== 'undefined'){
10292 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10294 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10295 // and are rendered into the cells after the row is rendered - using the id for the element.
10297 if(typeof(value) === 'object'){
10307 rowIndex : rowIndex,
10312 this.fireEvent('rowclass', this, rowcfg);
10316 // this might end up displaying HTML?
10317 // this is too messy... - better to only do it on columsn you know are going to be too long
10318 //tooltip : (typeof(value) === 'object') ? '' : value,
10319 cls : rowcfg.rowClass + ' x-col-' + i,
10321 html: (typeof(value) === 'object') ? '' : value
10328 if(typeof(config.colspan) != 'undefined'){
10329 td.colspan = config.colspan;
10334 if(typeof(config.align) != 'undefined' && config.align.length){
10335 td.style += ' text-align:' + config.align + ';';
10337 if(typeof(config.valign) != 'undefined' && config.valign.length){
10338 td.style += ' vertical-align:' + config.valign + ';';
10341 if(typeof(config.width) != 'undefined'){
10342 td.style += ' width:' + config.width + 'px;';
10346 if(typeof(config.cursor) != 'undefined'){
10347 td.style += ' cursor:' + config.cursor + ';';
10350 if(typeof(config.cls) != 'undefined'){
10351 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10353 if (this.responsive) {
10354 ['xs','sm','md','lg'].map(function(size){
10356 if(typeof(config[size]) == 'undefined'){
10362 if (!config[size]) { // 0 = hidden
10363 // BS 4 '0' is treated as hide that column and below.
10364 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10368 td.cls += ' col-' + size + '-' + config[size] + (
10369 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10379 row.cellObjects = cellObjects;
10387 onBeforeLoad : function()
10389 this.el.unmask(); // if needed.
10396 this.el.select('tbody', true).first().dom.innerHTML = '';
10399 * Show or hide a row.
10400 * @param {Number} rowIndex to show or hide
10401 * @param {Boolean} state hide
10403 setRowVisibility : function(rowIndex, state)
10405 var bt = this.bodyEl.dom;
10407 var rows = this.el.select('tbody > tr', true).elements;
10409 if(typeof(rows[rowIndex]) == 'undefined'){
10412 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10417 getSelectionModel : function(){
10418 if(!this.selModel){
10419 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10421 return this.selModel;
10424 * Render the Roo.bootstrap object from renderder
10426 renderCellObject : function(r)
10430 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10432 var t = r.cfg.render(r.container);
10435 Roo.each(r.cfg.cn, function(c){
10437 container: t.getChildContainer(),
10440 _this.renderCellObject(child);
10445 * get the Row Index from a dom element.
10446 * @param {Roo.Element} row The row to look for
10447 * @returns {Number} the row
10449 getRowIndex : function(row)
10453 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10464 * get the header TH element for columnIndex
10465 * @param {Number} columnIndex
10466 * @returns {Roo.Element}
10468 getHeaderIndex: function(colIndex)
10470 var cols = this.headEl.select('th', true).elements;
10471 return cols[colIndex];
10474 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10475 * @param {domElement} cell to look for
10476 * @returns {Number} the column
10478 getCellIndex : function(cell)
10480 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10482 return parseInt(id[1], 10);
10487 * Returns the grid's underlying element = used by panel.Grid
10488 * @return {Element} The element
10490 getGridEl : function(){
10494 * Forces a resize - used by panel.Grid
10495 * @return {Element} The element
10497 autoSize : function()
10499 //var ctr = Roo.get(this.container.dom.parentElement);
10500 var ctr = Roo.get(this.el.dom);
10502 var thd = this.getGridEl().select('thead',true).first();
10503 var tbd = this.getGridEl().select('tbody', true).first();
10504 var tfd = this.getGridEl().select('tfoot', true).first();
10506 var cw = ctr.getWidth();
10507 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10511 tbd.setWidth(ctr.getWidth());
10512 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10513 // this needs fixing for various usage - currently only hydra job advers I think..
10515 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10517 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10520 cw = Math.max(cw, this.totalWidth);
10521 this.getGridEl().select('tbody tr',true).setWidth(cw);
10524 // resize 'expandable coloumn?
10526 return; // we doe not have a view in this design..
10529 onBodyScroll: function()
10531 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10533 this.headEl.setStyle({
10534 'position' : 'relative',
10535 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10541 var scrollHeight = this.bodyEl.dom.scrollHeight;
10543 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10545 var height = this.bodyEl.getHeight();
10547 if(scrollHeight - height == scrollTop) {
10549 var total = this.ds.getTotalCount();
10551 if(this.footer.cursor + this.footer.pageSize < total){
10553 this.footer.ds.load({
10555 start : this.footer.cursor + this.footer.pageSize,
10556 limit : this.footer.pageSize
10565 onColumnSplitterMoved : function(i, diff)
10567 this.userResized = true;
10569 var cm = this.colModel;
10571 var w = this.getHeaderIndex(i).getWidth() + diff;
10574 cm.setColumnWidth(i, w, true);
10576 //var cid = cm.getColumnId(i); << not used in this version?
10577 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10579 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10580 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10581 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10583 //this.updateSplitters();
10584 //this.layout(); << ??
10585 this.fireEvent("columnresize", i, w);
10587 onHeaderChange : function()
10589 var header = this.renderHeader();
10590 var table = this.el.select('table', true).first();
10592 this.headEl.remove();
10593 this.headEl = table.createChild(header, this.bodyEl, false);
10595 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10596 e.on('click', this.sort, this);
10599 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10600 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10605 onHiddenChange : function(colModel, colIndex, hidden)
10608 this.cm.setHidden()
10609 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10610 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10612 this.CSS.updateRule(thSelector, "display", "");
10613 this.CSS.updateRule(tdSelector, "display", "");
10616 this.CSS.updateRule(thSelector, "display", "none");
10617 this.CSS.updateRule(tdSelector, "display", "none");
10620 // onload calls initCSS()
10621 this.onHeaderChange();
10625 setColumnWidth: function(col_index, width)
10627 // width = "md-2 xs-2..."
10628 if(!this.colModel.config[col_index]) {
10632 var w = width.split(" ");
10634 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10636 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10639 for(var j = 0; j < w.length; j++) {
10645 var size_cls = w[j].split("-");
10647 if(!Number.isInteger(size_cls[1] * 1)) {
10651 if(!this.colModel.config[col_index][size_cls[0]]) {
10655 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10659 h_row[0].classList.replace(
10660 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10661 "col-"+size_cls[0]+"-"+size_cls[1]
10664 for(var i = 0; i < rows.length; i++) {
10666 var size_cls = w[j].split("-");
10668 if(!Number.isInteger(size_cls[1] * 1)) {
10672 if(!this.colModel.config[col_index][size_cls[0]]) {
10676 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10680 rows[i].classList.replace(
10681 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10682 "col-"+size_cls[0]+"-"+size_cls[1]
10686 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10691 // currently only used to find the split on drag..
10692 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10697 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10698 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10707 * @class Roo.bootstrap.TableCell
10708 * @extends Roo.bootstrap.Component
10709 * @children Roo.bootstrap.Component
10710 * @parent Roo.bootstrap.TableRow
10711 * Bootstrap TableCell class
10713 * @cfg {String} html cell contain text
10714 * @cfg {String} cls cell class
10715 * @cfg {String} tag cell tag (td|th) default td
10716 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10717 * @cfg {String} align Aligns the content in a cell
10718 * @cfg {String} axis Categorizes cells
10719 * @cfg {String} bgcolor Specifies the background color of a cell
10720 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10721 * @cfg {Number} colspan Specifies the number of columns a cell should span
10722 * @cfg {String} headers Specifies one or more header cells a cell is related to
10723 * @cfg {Number} height Sets the height of a cell
10724 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10725 * @cfg {Number} rowspan Sets the number of rows a cell should span
10726 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10727 * @cfg {String} valign Vertical aligns the content in a cell
10728 * @cfg {Number} width Specifies the width of a cell
10731 * Create a new TableCell
10732 * @param {Object} config The config object
10735 Roo.bootstrap.TableCell = function(config){
10736 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10739 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10759 getAutoCreate : function(){
10760 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10767 cfg.tag = this.tag;
10780 cfg.align=this.align
10785 if (this.bgcolor) {
10786 cfg.bgcolor=this.bgcolor
10788 if (this.charoff) {
10789 cfg.charoff=this.charoff
10791 if (this.colspan) {
10792 cfg.colspan=this.colspan
10794 if (this.headers) {
10795 cfg.headers=this.headers
10798 cfg.height=this.height
10801 cfg.nowrap=this.nowrap
10803 if (this.rowspan) {
10804 cfg.rowspan=this.rowspan
10807 cfg.scope=this.scope
10810 cfg.valign=this.valign
10813 cfg.width=this.width
10832 * @class Roo.bootstrap.TableRow
10833 * @extends Roo.bootstrap.Component
10834 * @children Roo.bootstrap.TableCell
10835 * @parent Roo.bootstrap.TableBody
10836 * Bootstrap TableRow class
10837 * @cfg {String} cls row class
10838 * @cfg {String} align Aligns the content in a table row
10839 * @cfg {String} bgcolor Specifies a background color for a table row
10840 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10841 * @cfg {String} valign Vertical aligns the content in a table row
10844 * Create a new TableRow
10845 * @param {Object} config The config object
10848 Roo.bootstrap.TableRow = function(config){
10849 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10852 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10860 getAutoCreate : function(){
10861 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10868 cfg.cls = this.cls;
10871 cfg.align = this.align;
10874 cfg.bgcolor = this.bgcolor;
10877 cfg.charoff = this.charoff;
10880 cfg.valign = this.valign;
10898 * @class Roo.bootstrap.TableBody
10899 * @extends Roo.bootstrap.Component
10900 * @children Roo.bootstrap.TableRow
10901 * @parent Roo.bootstrap.Table
10902 * Bootstrap TableBody class
10903 * @cfg {String} cls element class
10904 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10905 * @cfg {String} align Aligns the content inside the element
10906 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10907 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10910 * Create a new TableBody
10911 * @param {Object} config The config object
10914 Roo.bootstrap.TableBody = function(config){
10915 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10918 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10926 getAutoCreate : function(){
10927 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10937 cfg.tag = this.tag;
10941 cfg.align = this.align;
10944 cfg.charoff = this.charoff;
10947 cfg.valign = this.valign;
10954 // initEvents : function()
10957 // if(!this.store){
10961 // this.store = Roo.factory(this.store, Roo.data);
10962 // this.store.on('load', this.onLoad, this);
10964 // this.store.load();
10968 // onLoad: function ()
10970 // this.fireEvent('load', this);
10980 * Ext JS Library 1.1.1
10981 * Copyright(c) 2006-2007, Ext JS, LLC.
10983 * Originally Released Under LGPL - original licence link has changed is not relivant.
10986 * <script type="text/javascript">
10989 // as we use this in bootstrap.
10990 Roo.namespace('Roo.form');
10992 * @class Roo.form.Action
10993 * Internal Class used to handle form actions
10995 * @param {Roo.form.BasicForm} el The form element or its id
10996 * @param {Object} config Configuration options
11001 // define the action interface
11002 Roo.form.Action = function(form, options){
11004 this.options = options || {};
11007 * Client Validation Failed
11010 Roo.form.Action.CLIENT_INVALID = 'client';
11012 * Server Validation Failed
11015 Roo.form.Action.SERVER_INVALID = 'server';
11017 * Connect to Server Failed
11020 Roo.form.Action.CONNECT_FAILURE = 'connect';
11022 * Reading Data from Server Failed
11025 Roo.form.Action.LOAD_FAILURE = 'load';
11027 Roo.form.Action.prototype = {
11029 failureType : undefined,
11030 response : undefined,
11031 result : undefined,
11033 // interface method
11034 run : function(options){
11038 // interface method
11039 success : function(response){
11043 // interface method
11044 handleResponse : function(response){
11048 // default connection failure
11049 failure : function(response){
11051 this.response = response;
11052 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11053 this.form.afterAction(this, false);
11056 processResponse : function(response){
11057 this.response = response;
11058 if(!response.responseText){
11061 this.result = this.handleResponse(response);
11062 return this.result;
11065 // utility functions used internally
11066 getUrl : function(appendParams){
11067 var url = this.options.url || this.form.url || this.form.el.dom.action;
11069 var p = this.getParams();
11071 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11077 getMethod : function(){
11078 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11081 getParams : function(){
11082 var bp = this.form.baseParams;
11083 var p = this.options.params;
11085 if(typeof p == "object"){
11086 p = Roo.urlEncode(Roo.applyIf(p, bp));
11087 }else if(typeof p == 'string' && bp){
11088 p += '&' + Roo.urlEncode(bp);
11091 p = Roo.urlEncode(bp);
11096 createCallback : function(){
11098 success: this.success,
11099 failure: this.failure,
11101 timeout: (this.form.timeout*1000),
11102 upload: this.form.fileUpload ? this.success : undefined
11107 Roo.form.Action.Submit = function(form, options){
11108 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11111 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11114 haveProgress : false,
11115 uploadComplete : false,
11117 // uploadProgress indicator.
11118 uploadProgress : function()
11120 if (!this.form.progressUrl) {
11124 if (!this.haveProgress) {
11125 Roo.MessageBox.progress("Uploading", "Uploading");
11127 if (this.uploadComplete) {
11128 Roo.MessageBox.hide();
11132 this.haveProgress = true;
11134 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11136 var c = new Roo.data.Connection();
11138 url : this.form.progressUrl,
11143 success : function(req){
11144 //console.log(data);
11148 rdata = Roo.decode(req.responseText)
11150 Roo.log("Invalid data from server..");
11154 if (!rdata || !rdata.success) {
11156 Roo.MessageBox.alert(Roo.encode(rdata));
11159 var data = rdata.data;
11161 if (this.uploadComplete) {
11162 Roo.MessageBox.hide();
11167 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11168 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11171 this.uploadProgress.defer(2000,this);
11174 failure: function(data) {
11175 Roo.log('progress url failed ');
11186 // run get Values on the form, so it syncs any secondary forms.
11187 this.form.getValues();
11189 var o = this.options;
11190 var method = this.getMethod();
11191 var isPost = method == 'POST';
11192 if(o.clientValidation === false || this.form.isValid()){
11194 if (this.form.progressUrl) {
11195 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11196 (new Date() * 1) + '' + Math.random());
11201 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11202 form:this.form.el.dom,
11203 url:this.getUrl(!isPost),
11205 params:isPost ? this.getParams() : null,
11206 isUpload: this.form.fileUpload,
11207 formData : this.form.formData
11210 this.uploadProgress();
11212 }else if (o.clientValidation !== false){ // client validation failed
11213 this.failureType = Roo.form.Action.CLIENT_INVALID;
11214 this.form.afterAction(this, false);
11218 success : function(response)
11220 this.uploadComplete= true;
11221 if (this.haveProgress) {
11222 Roo.MessageBox.hide();
11226 var result = this.processResponse(response);
11227 if(result === true || result.success){
11228 this.form.afterAction(this, true);
11232 this.form.markInvalid(result.errors);
11233 this.failureType = Roo.form.Action.SERVER_INVALID;
11235 this.form.afterAction(this, false);
11237 failure : function(response)
11239 this.uploadComplete= true;
11240 if (this.haveProgress) {
11241 Roo.MessageBox.hide();
11244 this.response = response;
11245 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11246 this.form.afterAction(this, false);
11249 handleResponse : function(response){
11250 if(this.form.errorReader){
11251 var rs = this.form.errorReader.read(response);
11254 for(var i = 0, len = rs.records.length; i < len; i++) {
11255 var r = rs.records[i];
11256 errors[i] = r.data;
11259 if(errors.length < 1){
11263 success : rs.success,
11269 ret = Roo.decode(response.responseText);
11273 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11283 Roo.form.Action.Load = function(form, options){
11284 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11285 this.reader = this.form.reader;
11288 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11293 Roo.Ajax.request(Roo.apply(
11294 this.createCallback(), {
11295 method:this.getMethod(),
11296 url:this.getUrl(false),
11297 params:this.getParams()
11301 success : function(response){
11303 var result = this.processResponse(response);
11304 if(result === true || !result.success || !result.data){
11305 this.failureType = Roo.form.Action.LOAD_FAILURE;
11306 this.form.afterAction(this, false);
11309 this.form.clearInvalid();
11310 this.form.setValues(result.data);
11311 this.form.afterAction(this, true);
11314 handleResponse : function(response){
11315 if(this.form.reader){
11316 var rs = this.form.reader.read(response);
11317 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11319 success : rs.success,
11323 return Roo.decode(response.responseText);
11327 Roo.form.Action.ACTION_TYPES = {
11328 'load' : Roo.form.Action.Load,
11329 'submit' : Roo.form.Action.Submit
11338 * @class Roo.bootstrap.form.Form
11339 * @extends Roo.bootstrap.Component
11340 * @children Roo.bootstrap.Component
11341 * Bootstrap Form class
11342 * @cfg {String} method GET | POST (default POST)
11343 * @cfg {String} labelAlign top | left (default top)
11344 * @cfg {String} align left | right - for navbars
11345 * @cfg {Boolean} loadMask load mask when submit (default true)
11349 * Create a new Form
11350 * @param {Object} config The config object
11354 Roo.bootstrap.form.Form = function(config){
11356 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11358 Roo.bootstrap.form.Form.popover.apply();
11362 * @event clientvalidation
11363 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11364 * @param {Form} this
11365 * @param {Boolean} valid true if the form has passed client-side validation
11367 clientvalidation: true,
11369 * @event beforeaction
11370 * Fires before any action is performed. Return false to cancel the action.
11371 * @param {Form} this
11372 * @param {Action} action The action to be performed
11374 beforeaction: true,
11376 * @event actionfailed
11377 * Fires when an action fails.
11378 * @param {Form} this
11379 * @param {Action} action The action that failed
11381 actionfailed : true,
11383 * @event actioncomplete
11384 * Fires when an action is completed.
11385 * @param {Form} this
11386 * @param {Action} action The action that completed
11388 actioncomplete : true
11392 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11395 * @cfg {String} method
11396 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11400 * @cfg {String} url
11401 * The URL to use for form actions if one isn't supplied in the action options.
11404 * @cfg {Boolean} fileUpload
11405 * Set to true if this form is a file upload.
11409 * @cfg {Object} baseParams
11410 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11414 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11418 * @cfg {Sting} align (left|right) for navbar forms
11423 activeAction : null,
11426 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11427 * element by passing it or its id or mask the form itself by passing in true.
11430 waitMsgTarget : false,
11435 * @cfg {Boolean} errorMask (true|false) default false
11440 * @cfg {Number} maskOffset Default 100
11445 * @cfg {Boolean} maskBody
11449 getAutoCreate : function(){
11453 method : this.method || 'POST',
11454 id : this.id || Roo.id(),
11457 if (this.parent().xtype.match(/^Nav/)) {
11458 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11462 if (this.labelAlign == 'left' ) {
11463 cfg.cls += ' form-horizontal';
11469 initEvents : function()
11471 this.el.on('submit', this.onSubmit, this);
11472 // this was added as random key presses on the form where triggering form submit.
11473 this.el.on('keypress', function(e) {
11474 if (e.getCharCode() != 13) {
11477 // we might need to allow it for textareas.. and some other items.
11478 // check e.getTarget().
11480 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11484 Roo.log("keypress blocked");
11486 e.preventDefault();
11492 onSubmit : function(e){
11497 * Returns true if client-side validation on the form is successful.
11500 isValid : function(){
11501 var items = this.getItems();
11503 var target = false;
11505 items.each(function(f){
11511 Roo.log('invalid field: ' + f.name);
11515 if(!target && f.el.isVisible(true)){
11521 if(this.errorMask && !valid){
11522 Roo.bootstrap.form.Form.popover.mask(this, target);
11529 * Returns true if any fields in this form have changed since their original load.
11532 isDirty : function(){
11534 var items = this.getItems();
11535 items.each(function(f){
11545 * Performs a predefined action (submit or load) or custom actions you define on this form.
11546 * @param {String} actionName The name of the action type
11547 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11548 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11549 * accept other config options):
11551 Property Type Description
11552 ---------------- --------------- ----------------------------------------------------------------------------------
11553 url String The url for the action (defaults to the form's url)
11554 method String The form method to use (defaults to the form's method, or POST if not defined)
11555 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11556 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11557 validate the form on the client (defaults to false)
11559 * @return {BasicForm} this
11561 doAction : function(action, options){
11562 if(typeof action == 'string'){
11563 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11565 if(this.fireEvent('beforeaction', this, action) !== false){
11566 this.beforeAction(action);
11567 action.run.defer(100, action);
11573 beforeAction : function(action){
11574 var o = action.options;
11579 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11581 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11584 // not really supported yet.. ??
11586 //if(this.waitMsgTarget === true){
11587 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11588 //}else if(this.waitMsgTarget){
11589 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11590 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11592 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11598 afterAction : function(action, success){
11599 this.activeAction = null;
11600 var o = action.options;
11605 Roo.get(document.body).unmask();
11611 //if(this.waitMsgTarget === true){
11612 // this.el.unmask();
11613 //}else if(this.waitMsgTarget){
11614 // this.waitMsgTarget.unmask();
11616 // Roo.MessageBox.updateProgress(1);
11617 // Roo.MessageBox.hide();
11624 Roo.callback(o.success, o.scope, [this, action]);
11625 this.fireEvent('actioncomplete', this, action);
11629 // failure condition..
11630 // we have a scenario where updates need confirming.
11631 // eg. if a locking scenario exists..
11632 // we look for { errors : { needs_confirm : true }} in the response.
11634 (typeof(action.result) != 'undefined') &&
11635 (typeof(action.result.errors) != 'undefined') &&
11636 (typeof(action.result.errors.needs_confirm) != 'undefined')
11639 Roo.log("not supported yet");
11642 Roo.MessageBox.confirm(
11643 "Change requires confirmation",
11644 action.result.errorMsg,
11649 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11659 Roo.callback(o.failure, o.scope, [this, action]);
11660 // show an error message if no failed handler is set..
11661 if (!this.hasListener('actionfailed')) {
11662 Roo.log("need to add dialog support");
11664 Roo.MessageBox.alert("Error",
11665 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11666 action.result.errorMsg :
11667 "Saving Failed, please check your entries or try again"
11672 this.fireEvent('actionfailed', this, action);
11677 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11678 * @param {String} id The value to search for
11681 findField : function(id){
11682 var items = this.getItems();
11683 var field = items.get(id);
11685 items.each(function(f){
11686 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11693 return field || null;
11696 * Mark fields in this form invalid in bulk.
11697 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11698 * @return {BasicForm} this
11700 markInvalid : function(errors){
11701 if(errors instanceof Array){
11702 for(var i = 0, len = errors.length; i < len; i++){
11703 var fieldError = errors[i];
11704 var f = this.findField(fieldError.id);
11706 f.markInvalid(fieldError.msg);
11712 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11713 field.markInvalid(errors[id]);
11717 //Roo.each(this.childForms || [], function (f) {
11718 // f.markInvalid(errors);
11725 * Set values for fields in this form in bulk.
11726 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11727 * @return {BasicForm} this
11729 setValues : function(values){
11730 if(values instanceof Array){ // array of objects
11731 for(var i = 0, len = values.length; i < len; i++){
11733 var f = this.findField(v.id);
11735 f.setValue(v.value);
11736 if(this.trackResetOnLoad){
11737 f.originalValue = f.getValue();
11741 }else{ // object hash
11744 if(typeof values[id] != 'function' && (field = this.findField(id))){
11746 if (field.setFromData &&
11747 field.valueField &&
11748 field.displayField &&
11749 // combos' with local stores can
11750 // be queried via setValue()
11751 // to set their value..
11752 (field.store && !field.store.isLocal)
11756 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11757 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11758 field.setFromData(sd);
11760 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11762 field.setFromData(values);
11765 field.setValue(values[id]);
11769 if(this.trackResetOnLoad){
11770 field.originalValue = field.getValue();
11776 //Roo.each(this.childForms || [], function (f) {
11777 // f.setValues(values);
11784 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11785 * they are returned as an array.
11786 * @param {Boolean} asString
11789 getValues : function(asString){
11790 //if (this.childForms) {
11791 // copy values from the child forms
11792 // Roo.each(this.childForms, function (f) {
11793 // this.setValues(f.getValues());
11799 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11800 if(asString === true){
11803 return Roo.urlDecode(fs);
11807 * Returns the fields in this form as an object with key/value pairs.
11808 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11811 getFieldValues : function(with_hidden)
11813 var items = this.getItems();
11815 items.each(function(f){
11817 if (!f.getName()) {
11821 var v = f.getValue();
11823 if (f.inputType =='radio') {
11824 if (typeof(ret[f.getName()]) == 'undefined') {
11825 ret[f.getName()] = ''; // empty..
11828 if (!f.el.dom.checked) {
11832 v = f.el.dom.value;
11836 if(f.xtype == 'MoneyField'){
11837 ret[f.currencyName] = f.getCurrency();
11840 // not sure if this supported any more..
11841 if ((typeof(v) == 'object') && f.getRawValue) {
11842 v = f.getRawValue() ; // dates..
11844 // combo boxes where name != hiddenName...
11845 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11846 ret[f.name] = f.getRawValue();
11848 ret[f.getName()] = v;
11855 * Clears all invalid messages in this form.
11856 * @return {BasicForm} this
11858 clearInvalid : function(){
11859 var items = this.getItems();
11861 items.each(function(f){
11869 * Resets this form.
11870 * @return {BasicForm} this
11872 reset : function(){
11873 var items = this.getItems();
11874 items.each(function(f){
11878 Roo.each(this.childForms || [], function (f) {
11886 getItems : function()
11888 var r=new Roo.util.MixedCollection(false, function(o){
11889 return o.id || (o.id = Roo.id());
11891 var iter = function(el) {
11898 Roo.each(el.items,function(e) {
11907 hideFields : function(items)
11909 Roo.each(items, function(i){
11911 var f = this.findField(i);
11922 showFields : function(items)
11924 Roo.each(items, function(i){
11926 var f = this.findField(i);
11939 Roo.apply(Roo.bootstrap.form.Form, {
11955 intervalID : false,
11961 if(this.isApplied){
11966 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11967 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11968 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11969 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11972 this.maskEl.top.enableDisplayMode("block");
11973 this.maskEl.left.enableDisplayMode("block");
11974 this.maskEl.bottom.enableDisplayMode("block");
11975 this.maskEl.right.enableDisplayMode("block");
11977 this.toolTip = new Roo.bootstrap.Tooltip({
11978 cls : 'roo-form-error-popover',
11980 'left' : ['r-l', [-2,0], 'right'],
11981 'right' : ['l-r', [2,0], 'left'],
11982 'bottom' : ['tl-bl', [0,2], 'top'],
11983 'top' : [ 'bl-tl', [0,-2], 'bottom']
11987 this.toolTip.render(Roo.get(document.body));
11989 this.toolTip.el.enableDisplayMode("block");
11991 Roo.get(document.body).on('click', function(){
11995 Roo.get(document.body).on('touchstart', function(){
11999 this.isApplied = true
12002 mask : function(form, target)
12006 this.target = target;
12008 if(!this.form.errorMask || !target.el){
12012 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12014 Roo.log(scrollable);
12016 var ot = this.target.el.calcOffsetsTo(scrollable);
12018 var scrollTo = ot[1] - this.form.maskOffset;
12020 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12022 scrollable.scrollTo('top', scrollTo);
12024 var box = this.target.el.getBox();
12026 var zIndex = Roo.bootstrap.Modal.zIndex++;
12029 this.maskEl.top.setStyle('position', 'absolute');
12030 this.maskEl.top.setStyle('z-index', zIndex);
12031 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12032 this.maskEl.top.setLeft(0);
12033 this.maskEl.top.setTop(0);
12034 this.maskEl.top.show();
12036 this.maskEl.left.setStyle('position', 'absolute');
12037 this.maskEl.left.setStyle('z-index', zIndex);
12038 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12039 this.maskEl.left.setLeft(0);
12040 this.maskEl.left.setTop(box.y - this.padding);
12041 this.maskEl.left.show();
12043 this.maskEl.bottom.setStyle('position', 'absolute');
12044 this.maskEl.bottom.setStyle('z-index', zIndex);
12045 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12046 this.maskEl.bottom.setLeft(0);
12047 this.maskEl.bottom.setTop(box.bottom + this.padding);
12048 this.maskEl.bottom.show();
12050 this.maskEl.right.setStyle('position', 'absolute');
12051 this.maskEl.right.setStyle('z-index', zIndex);
12052 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12053 this.maskEl.right.setLeft(box.right + this.padding);
12054 this.maskEl.right.setTop(box.y - this.padding);
12055 this.maskEl.right.show();
12057 this.toolTip.bindEl = this.target.el;
12059 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12061 var tip = this.target.blankText;
12063 if(this.target.getValue() !== '' ) {
12065 if (this.target.invalidText.length) {
12066 tip = this.target.invalidText;
12067 } else if (this.target.regexText.length){
12068 tip = this.target.regexText;
12072 this.toolTip.show(tip);
12074 this.intervalID = window.setInterval(function() {
12075 Roo.bootstrap.form.Form.popover.unmask();
12078 window.onwheel = function(){ return false;};
12080 (function(){ this.isMasked = true; }).defer(500, this);
12084 unmask : function()
12086 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12090 this.maskEl.top.setStyle('position', 'absolute');
12091 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12092 this.maskEl.top.hide();
12094 this.maskEl.left.setStyle('position', 'absolute');
12095 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12096 this.maskEl.left.hide();
12098 this.maskEl.bottom.setStyle('position', 'absolute');
12099 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12100 this.maskEl.bottom.hide();
12102 this.maskEl.right.setStyle('position', 'absolute');
12103 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12104 this.maskEl.right.hide();
12106 this.toolTip.hide();
12108 this.toolTip.el.hide();
12110 window.onwheel = function(){ return true;};
12112 if(this.intervalID){
12113 window.clearInterval(this.intervalID);
12114 this.intervalID = false;
12117 this.isMasked = false;
12127 * Ext JS Library 1.1.1
12128 * Copyright(c) 2006-2007, Ext JS, LLC.
12130 * Originally Released Under LGPL - original licence link has changed is not relivant.
12133 * <script type="text/javascript">
12136 * @class Roo.form.VTypes
12137 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12140 Roo.form.VTypes = function(){
12141 // closure these in so they are only created once.
12142 var alpha = /^[a-zA-Z_]+$/;
12143 var alphanum = /^[a-zA-Z0-9_]+$/;
12144 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12145 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12147 // All these messages and functions are configurable
12150 * The function used to validate email addresses
12151 * @param {String} value The email address
12153 'email' : function(v){
12154 return email.test(v);
12157 * The error text to display when the email validation function returns false
12160 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12162 * The keystroke filter mask to be applied on email input
12165 'emailMask' : /[a-z0-9_\.\-@]/i,
12168 * The function used to validate URLs
12169 * @param {String} value The URL
12171 'url' : function(v){
12172 return url.test(v);
12175 * The error text to display when the url validation function returns false
12178 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12181 * The function used to validate alpha values
12182 * @param {String} value The value
12184 'alpha' : function(v){
12185 return alpha.test(v);
12188 * The error text to display when the alpha validation function returns false
12191 'alphaText' : 'This field should only contain letters and _',
12193 * The keystroke filter mask to be applied on alpha input
12196 'alphaMask' : /[a-z_]/i,
12199 * The function used to validate alphanumeric values
12200 * @param {String} value The value
12202 'alphanum' : function(v){
12203 return alphanum.test(v);
12206 * The error text to display when the alphanumeric validation function returns false
12209 'alphanumText' : 'This field should only contain letters, numbers and _',
12211 * The keystroke filter mask to be applied on alphanumeric input
12214 'alphanumMask' : /[a-z0-9_]/i
12224 * @class Roo.bootstrap.form.Input
12225 * @extends Roo.bootstrap.Component
12226 * Bootstrap Input class
12227 * @cfg {Boolean} disabled is it disabled
12228 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12229 * @cfg {String} name name of the input
12230 * @cfg {string} fieldLabel - the label associated
12231 * @cfg {string} placeholder - placeholder to put in text.
12232 * @cfg {string} before - input group add on before
12233 * @cfg {string} after - input group add on after
12234 * @cfg {string} size - (lg|sm) or leave empty..
12235 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12236 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12237 * @cfg {Number} md colspan out of 12 for computer-sized screens
12238 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12239 * @cfg {string} value default value of the input
12240 * @cfg {Number} labelWidth set the width of label
12241 * @cfg {Number} labellg set the width of label (1-12)
12242 * @cfg {Number} labelmd set the width of label (1-12)
12243 * @cfg {Number} labelsm set the width of label (1-12)
12244 * @cfg {Number} labelxs set the width of label (1-12)
12245 * @cfg {String} labelAlign (top|left)
12246 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12247 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12248 * @cfg {String} indicatorpos (left|right) default left
12249 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12250 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12251 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12252 * @cfg {Roo.bootstrap.Button} before Button to show before
12253 * @cfg {Roo.bootstrap.Button} afterButton to show before
12254 * @cfg {String} align (left|center|right) Default left
12255 * @cfg {Boolean} forceFeedback (true|false) Default false
12258 * Create a new Input
12259 * @param {Object} config The config object
12262 Roo.bootstrap.form.Input = function(config){
12264 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12269 * Fires when this field receives input focus.
12270 * @param {Roo.form.Field} this
12275 * Fires when this field loses input focus.
12276 * @param {Roo.form.Field} this
12280 * @event specialkey
12281 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12282 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12283 * @param {Roo.form.Field} this
12284 * @param {Roo.EventObject} e The event object
12289 * Fires just before the field blurs if the field value has changed.
12290 * @param {Roo.form.Field} this
12291 * @param {Mixed} newValue The new value
12292 * @param {Mixed} oldValue The original value
12297 * Fires after the field has been marked as invalid.
12298 * @param {Roo.form.Field} this
12299 * @param {String} msg The validation message
12304 * Fires after the field has been validated with no errors.
12305 * @param {Roo.form.Field} this
12310 * Fires after the key up
12311 * @param {Roo.form.Field} this
12312 * @param {Roo.EventObject} e The event Object
12317 * Fires after the user pastes into input
12318 * @param {Roo.form.Field} this
12319 * @param {Roo.EventObject} e The event Object
12325 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12327 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12328 automatic validation (defaults to "keyup").
12330 validationEvent : "keyup",
12332 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12334 validateOnBlur : true,
12336 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12338 validationDelay : 250,
12340 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12342 focusClass : "x-form-focus", // not needed???
12346 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12348 invalidClass : "has-warning",
12351 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12353 validClass : "has-success",
12356 * @cfg {Boolean} hasFeedback (true|false) default true
12358 hasFeedback : true,
12361 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12363 invalidFeedbackClass : "glyphicon-warning-sign",
12366 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12368 validFeedbackClass : "glyphicon-ok",
12371 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12373 selectOnFocus : false,
12376 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12380 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12385 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12387 disableKeyFilter : false,
12390 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12394 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12398 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12400 blankText : "Please complete this mandatory field",
12403 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12407 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12409 maxLength : Number.MAX_VALUE,
12411 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12413 minLengthText : "The minimum length for this field is {0}",
12415 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12417 maxLengthText : "The maximum length for this field is {0}",
12421 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12422 * If available, this function will be called only after the basic validators all return true, and will be passed the
12423 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12427 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12428 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12429 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12433 * @cfg {String} regexText -- Depricated - use Invalid Text
12438 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12444 autocomplete: false,
12448 inputType : 'text',
12451 placeholder: false,
12456 preventMark: false,
12457 isFormField : true,
12460 labelAlign : false,
12463 formatedValue : false,
12464 forceFeedback : false,
12466 indicatorpos : 'left',
12476 parentLabelAlign : function()
12479 while (parent.parent()) {
12480 parent = parent.parent();
12481 if (typeof(parent.labelAlign) !='undefined') {
12482 return parent.labelAlign;
12489 getAutoCreate : function()
12491 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12497 if(this.inputType != 'hidden'){
12498 cfg.cls = 'form-group' //input-group
12504 type : this.inputType,
12505 value : this.value,
12506 cls : 'form-control',
12507 placeholder : this.placeholder || '',
12508 autocomplete : this.autocomplete || 'new-password'
12510 if (this.inputType == 'file') {
12511 input.style = 'overflow:hidden'; // why not in CSS?
12514 if(this.capture.length){
12515 input.capture = this.capture;
12518 if(this.accept.length){
12519 input.accept = this.accept + "/*";
12523 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12526 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12527 input.maxLength = this.maxLength;
12530 if (this.disabled) {
12531 input.disabled=true;
12534 if (this.readOnly) {
12535 input.readonly=true;
12539 input.name = this.name;
12543 input.cls += ' input-' + this.size;
12547 ['xs','sm','md','lg'].map(function(size){
12548 if (settings[size]) {
12549 cfg.cls += ' col-' + size + '-' + settings[size];
12553 var inputblock = input;
12557 cls: 'glyphicon form-control-feedback'
12560 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12563 cls : 'has-feedback',
12571 if (this.before || this.after) {
12574 cls : 'input-group',
12578 if (this.before && typeof(this.before) == 'string') {
12580 inputblock.cn.push({
12582 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12586 if (this.before && typeof(this.before) == 'object') {
12587 this.before = Roo.factory(this.before);
12589 inputblock.cn.push({
12591 cls : 'roo-input-before input-group-prepend input-group-' +
12592 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12596 inputblock.cn.push(input);
12598 if (this.after && typeof(this.after) == 'string') {
12599 inputblock.cn.push({
12601 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12605 if (this.after && typeof(this.after) == 'object') {
12606 this.after = Roo.factory(this.after);
12608 inputblock.cn.push({
12610 cls : 'roo-input-after input-group-append input-group-' +
12611 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12615 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12616 inputblock.cls += ' has-feedback';
12617 inputblock.cn.push(feedback);
12622 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12623 tooltip : 'This field is required'
12625 if (this.allowBlank ) {
12626 indicator.style = this.allowBlank ? ' display:none' : '';
12628 if (align ==='left' && this.fieldLabel.length) {
12630 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12637 cls : 'control-label col-form-label',
12638 html : this.fieldLabel
12649 var labelCfg = cfg.cn[1];
12650 var contentCfg = cfg.cn[2];
12652 if(this.indicatorpos == 'right'){
12657 cls : 'control-label col-form-label',
12661 html : this.fieldLabel
12675 labelCfg = cfg.cn[0];
12676 contentCfg = cfg.cn[1];
12680 if(this.labelWidth > 12){
12681 labelCfg.style = "width: " + this.labelWidth + 'px';
12684 if(this.labelWidth < 13 && this.labelmd == 0){
12685 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12688 if(this.labellg > 0){
12689 labelCfg.cls += ' col-lg-' + this.labellg;
12690 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12693 if(this.labelmd > 0){
12694 labelCfg.cls += ' col-md-' + this.labelmd;
12695 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12698 if(this.labelsm > 0){
12699 labelCfg.cls += ' col-sm-' + this.labelsm;
12700 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12703 if(this.labelxs > 0){
12704 labelCfg.cls += ' col-xs-' + this.labelxs;
12705 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12709 } else if ( this.fieldLabel.length) {
12716 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12717 tooltip : 'This field is required',
12718 style : this.allowBlank ? ' display:none' : ''
12722 //cls : 'input-group-addon',
12723 html : this.fieldLabel
12731 if(this.indicatorpos == 'right'){
12736 //cls : 'input-group-addon',
12737 html : this.fieldLabel
12742 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12743 tooltip : 'This field is required',
12744 style : this.allowBlank ? ' display:none' : ''
12764 if (this.parentType === 'Navbar' && this.parent().bar) {
12765 cfg.cls += ' navbar-form';
12768 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12769 // on BS4 we do this only if not form
12770 cfg.cls += ' navbar-form';
12778 * return the real input element.
12780 inputEl: function ()
12782 return this.el.select('input.form-control',true).first();
12785 tooltipEl : function()
12787 return this.inputEl();
12790 indicatorEl : function()
12792 if (Roo.bootstrap.version == 4) {
12793 return false; // not enabled in v4 yet.
12796 var indicator = this.el.select('i.roo-required-indicator',true).first();
12806 setDisabled : function(v)
12808 var i = this.inputEl().dom;
12810 i.removeAttribute('disabled');
12814 i.setAttribute('disabled','true');
12816 initEvents : function()
12819 this.inputEl().on("keydown" , this.fireKey, this);
12820 this.inputEl().on("focus", this.onFocus, this);
12821 this.inputEl().on("blur", this.onBlur, this);
12823 this.inputEl().relayEvent('keyup', this);
12824 this.inputEl().relayEvent('paste', this);
12826 this.indicator = this.indicatorEl();
12828 if(this.indicator){
12829 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12832 // reference to original value for reset
12833 this.originalValue = this.getValue();
12834 //Roo.form.TextField.superclass.initEvents.call(this);
12835 if(this.validationEvent == 'keyup'){
12836 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12837 this.inputEl().on('keyup', this.filterValidation, this);
12839 else if(this.validationEvent !== false){
12840 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12843 if(this.selectOnFocus){
12844 this.on("focus", this.preFocus, this);
12847 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12848 this.inputEl().on("keypress", this.filterKeys, this);
12850 this.inputEl().relayEvent('keypress', this);
12853 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12854 this.el.on("click", this.autoSize, this);
12857 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12858 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12861 if (typeof(this.before) == 'object') {
12862 this.before.render(this.el.select('.roo-input-before',true).first());
12864 if (typeof(this.after) == 'object') {
12865 this.after.render(this.el.select('.roo-input-after',true).first());
12868 this.inputEl().on('change', this.onChange, this);
12871 filterValidation : function(e){
12872 if(!e.isNavKeyPress()){
12873 this.validationTask.delay(this.validationDelay);
12877 * Validates the field value
12878 * @return {Boolean} True if the value is valid, else false
12880 validate : function(){
12881 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12882 if(this.disabled || this.validateValue(this.getRawValue())){
12887 this.markInvalid();
12893 * Validates a value according to the field's validation rules and marks the field as invalid
12894 * if the validation fails
12895 * @param {Mixed} value The value to validate
12896 * @return {Boolean} True if the value is valid, else false
12898 validateValue : function(value)
12900 if(this.getVisibilityEl().hasClass('hidden')){
12904 if(value.length < 1) { // if it's blank
12905 if(this.allowBlank){
12911 if(value.length < this.minLength){
12914 if(value.length > this.maxLength){
12918 var vt = Roo.form.VTypes;
12919 if(!vt[this.vtype](value, this)){
12923 if(typeof this.validator == "function"){
12924 var msg = this.validator(value);
12928 if (typeof(msg) == 'string') {
12929 this.invalidText = msg;
12933 if(this.regex && !this.regex.test(value)){
12941 fireKey : function(e){
12942 //Roo.log('field ' + e.getKey());
12943 if(e.isNavKeyPress()){
12944 this.fireEvent("specialkey", this, e);
12947 focus : function (selectText){
12949 this.inputEl().focus();
12950 if(selectText === true){
12951 this.inputEl().dom.select();
12957 onFocus : function(){
12958 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12959 // this.el.addClass(this.focusClass);
12961 if(!this.hasFocus){
12962 this.hasFocus = true;
12963 this.startValue = this.getValue();
12964 this.fireEvent("focus", this);
12968 beforeBlur : Roo.emptyFn,
12972 onBlur : function(){
12974 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12975 //this.el.removeClass(this.focusClass);
12977 this.hasFocus = false;
12978 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12981 var v = this.getValue();
12982 if(String(v) !== String(this.startValue)){
12983 this.fireEvent('change', this, v, this.startValue);
12985 this.fireEvent("blur", this);
12988 onChange : function(e)
12990 var v = this.getValue();
12991 if(String(v) !== String(this.startValue)){
12992 this.fireEvent('change', this, v, this.startValue);
12998 * Resets the current field value to the originally loaded value and clears any validation messages
13000 reset : function(){
13001 this.setValue(this.originalValue);
13005 * Returns the name of the field
13006 * @return {Mixed} name The name field
13008 getName: function(){
13012 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13013 * @return {Mixed} value The field value
13015 getValue : function(){
13017 var v = this.inputEl().getValue();
13022 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13023 * @return {Mixed} value The field value
13025 getRawValue : function(){
13026 var v = this.inputEl().getValue();
13032 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13033 * @param {Mixed} value The value to set
13035 setRawValue : function(v){
13036 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13039 selectText : function(start, end){
13040 var v = this.getRawValue();
13042 start = start === undefined ? 0 : start;
13043 end = end === undefined ? v.length : end;
13044 var d = this.inputEl().dom;
13045 if(d.setSelectionRange){
13046 d.setSelectionRange(start, end);
13047 }else if(d.createTextRange){
13048 var range = d.createTextRange();
13049 range.moveStart("character", start);
13050 range.moveEnd("character", v.length-end);
13057 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13058 * @param {Mixed} value The value to set
13060 setValue : function(v){
13063 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13069 processValue : function(value){
13070 if(this.stripCharsRe){
13071 var newValue = value.replace(this.stripCharsRe, '');
13072 if(newValue !== value){
13073 this.setRawValue(newValue);
13080 preFocus : function(){
13082 if(this.selectOnFocus){
13083 this.inputEl().dom.select();
13086 filterKeys : function(e){
13087 var k = e.getKey();
13088 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13091 var c = e.getCharCode(), cc = String.fromCharCode(c);
13092 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13095 if(!this.maskRe.test(cc)){
13100 * Clear any invalid styles/messages for this field
13102 clearInvalid : function(){
13104 if(!this.el || this.preventMark){ // not rendered
13109 this.el.removeClass([this.invalidClass, 'is-invalid']);
13111 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13113 var feedback = this.el.select('.form-control-feedback', true).first();
13116 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13121 if(this.indicator){
13122 this.indicator.removeClass('visible');
13123 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13126 this.fireEvent('valid', this);
13130 * Mark this field as valid
13132 markValid : function()
13134 if(!this.el || this.preventMark){ // not rendered...
13138 this.el.removeClass([this.invalidClass, this.validClass]);
13139 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13141 var feedback = this.el.select('.form-control-feedback', true).first();
13144 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13147 if(this.indicator){
13148 this.indicator.removeClass('visible');
13149 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13157 if(this.allowBlank && !this.getRawValue().length){
13160 if (Roo.bootstrap.version == 3) {
13161 this.el.addClass(this.validClass);
13163 this.inputEl().addClass('is-valid');
13166 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13168 var feedback = this.el.select('.form-control-feedback', true).first();
13171 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13172 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13177 this.fireEvent('valid', this);
13181 * Mark this field as invalid
13182 * @param {String} msg The validation message
13184 markInvalid : function(msg)
13186 if(!this.el || this.preventMark){ // not rendered
13190 this.el.removeClass([this.invalidClass, this.validClass]);
13191 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13193 var feedback = this.el.select('.form-control-feedback', true).first();
13196 this.el.select('.form-control-feedback', true).first().removeClass(
13197 [this.invalidFeedbackClass, this.validFeedbackClass]);
13204 if(this.allowBlank && !this.getRawValue().length){
13208 if(this.indicator){
13209 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13210 this.indicator.addClass('visible');
13212 if (Roo.bootstrap.version == 3) {
13213 this.el.addClass(this.invalidClass);
13215 this.inputEl().addClass('is-invalid');
13220 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13222 var feedback = this.el.select('.form-control-feedback', true).first();
13225 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13227 if(this.getValue().length || this.forceFeedback){
13228 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13235 this.fireEvent('invalid', this, msg);
13238 SafariOnKeyDown : function(event)
13240 // this is a workaround for a password hang bug on chrome/ webkit.
13241 if (this.inputEl().dom.type != 'password') {
13245 var isSelectAll = false;
13247 if(this.inputEl().dom.selectionEnd > 0){
13248 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13250 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13251 event.preventDefault();
13256 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13258 event.preventDefault();
13259 // this is very hacky as keydown always get's upper case.
13261 var cc = String.fromCharCode(event.getCharCode());
13262 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13266 adjustWidth : function(tag, w){
13267 tag = tag.toLowerCase();
13268 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13269 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13270 if(tag == 'input'){
13273 if(tag == 'textarea'){
13276 }else if(Roo.isOpera){
13277 if(tag == 'input'){
13280 if(tag == 'textarea'){
13288 setFieldLabel : function(v)
13290 if(!this.rendered){
13294 if(this.indicatorEl()){
13295 var ar = this.el.select('label > span',true);
13297 if (ar.elements.length) {
13298 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13299 this.fieldLabel = v;
13303 var br = this.el.select('label',true);
13305 if(br.elements.length) {
13306 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13307 this.fieldLabel = v;
13311 Roo.log('Cannot Found any of label > span || label in input');
13315 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13316 this.fieldLabel = v;
13331 * @class Roo.bootstrap.form.TextArea
13332 * @extends Roo.bootstrap.form.Input
13333 * Bootstrap TextArea class
13334 * @cfg {Number} cols Specifies the visible width of a text area
13335 * @cfg {Number} rows Specifies the visible number of lines in a text area
13336 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13337 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13338 * @cfg {string} html text
13341 * Create a new TextArea
13342 * @param {Object} config The config object
13345 Roo.bootstrap.form.TextArea = function(config){
13346 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13350 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13360 getAutoCreate : function(){
13362 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13368 if(this.inputType != 'hidden'){
13369 cfg.cls = 'form-group' //input-group
13377 value : this.value || '',
13378 html: this.html || '',
13379 cls : 'form-control',
13380 placeholder : this.placeholder || ''
13384 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13385 input.maxLength = this.maxLength;
13389 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13393 input.cols = this.cols;
13396 if (this.readOnly) {
13397 input.readonly = true;
13401 input.name = this.name;
13405 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13409 ['xs','sm','md','lg'].map(function(size){
13410 if (settings[size]) {
13411 cfg.cls += ' col-' + size + '-' + settings[size];
13415 var inputblock = input;
13417 if(this.hasFeedback && !this.allowBlank){
13421 cls: 'glyphicon form-control-feedback'
13425 cls : 'has-feedback',
13434 if (this.before || this.after) {
13437 cls : 'input-group',
13441 inputblock.cn.push({
13443 cls : 'input-group-addon',
13448 inputblock.cn.push(input);
13450 if(this.hasFeedback && !this.allowBlank){
13451 inputblock.cls += ' has-feedback';
13452 inputblock.cn.push(feedback);
13456 inputblock.cn.push({
13458 cls : 'input-group-addon',
13465 if (align ==='left' && this.fieldLabel.length) {
13470 cls : 'control-label',
13471 html : this.fieldLabel
13482 if(this.labelWidth > 12){
13483 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13486 if(this.labelWidth < 13 && this.labelmd == 0){
13487 this.labelmd = this.labelWidth;
13490 if(this.labellg > 0){
13491 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13492 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13495 if(this.labelmd > 0){
13496 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13497 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13500 if(this.labelsm > 0){
13501 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13502 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13505 if(this.labelxs > 0){
13506 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13507 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13510 } else if ( this.fieldLabel.length) {
13515 //cls : 'input-group-addon',
13516 html : this.fieldLabel
13534 if (this.disabled) {
13535 input.disabled=true;
13542 * return the real textarea element.
13544 inputEl: function ()
13546 return this.el.select('textarea.form-control',true).first();
13550 * Clear any invalid styles/messages for this field
13552 clearInvalid : function()
13555 if(!this.el || this.preventMark){ // not rendered
13559 var label = this.el.select('label', true).first();
13560 var icon = this.el.select('i.fa-star', true).first();
13565 this.el.removeClass( this.validClass);
13566 this.inputEl().removeClass('is-invalid');
13568 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13570 var feedback = this.el.select('.form-control-feedback', true).first();
13573 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13578 this.fireEvent('valid', this);
13582 * Mark this field as valid
13584 markValid : function()
13586 if(!this.el || this.preventMark){ // not rendered
13590 this.el.removeClass([this.invalidClass, this.validClass]);
13591 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13593 var feedback = this.el.select('.form-control-feedback', true).first();
13596 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13599 if(this.disabled || this.allowBlank){
13603 var label = this.el.select('label', true).first();
13604 var icon = this.el.select('i.fa-star', true).first();
13609 if (Roo.bootstrap.version == 3) {
13610 this.el.addClass(this.validClass);
13612 this.inputEl().addClass('is-valid');
13616 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13618 var feedback = this.el.select('.form-control-feedback', true).first();
13621 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13622 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13627 this.fireEvent('valid', this);
13631 * Mark this field as invalid
13632 * @param {String} msg The validation message
13634 markInvalid : function(msg)
13636 if(!this.el || this.preventMark){ // not rendered
13640 this.el.removeClass([this.invalidClass, this.validClass]);
13641 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13643 var feedback = this.el.select('.form-control-feedback', true).first();
13646 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13649 if(this.disabled || this.allowBlank){
13653 var label = this.el.select('label', true).first();
13654 var icon = this.el.select('i.fa-star', true).first();
13656 if(!this.getValue().length && label && !icon){
13657 this.el.createChild({
13659 cls : 'text-danger fa fa-lg fa-star',
13660 tooltip : 'This field is required',
13661 style : 'margin-right:5px;'
13665 if (Roo.bootstrap.version == 3) {
13666 this.el.addClass(this.invalidClass);
13668 this.inputEl().addClass('is-invalid');
13671 // fixme ... this may be depricated need to test..
13672 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13674 var feedback = this.el.select('.form-control-feedback', true).first();
13677 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13679 if(this.getValue().length || this.forceFeedback){
13680 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13687 this.fireEvent('invalid', this, msg);
13695 * trigger field - base class for combo..
13700 * @class Roo.bootstrap.form.TriggerField
13701 * @extends Roo.bootstrap.form.Input
13702 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13703 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13704 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13705 * for which you can provide a custom implementation. For example:
13707 var trigger = new Roo.bootstrap.form.TriggerField();
13708 trigger.onTriggerClick = myTriggerFn;
13709 trigger.applyTo('my-field');
13712 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13713 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13714 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13715 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13716 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13719 * Create a new TriggerField.
13720 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13721 * to the base TextField)
13723 Roo.bootstrap.form.TriggerField = function(config){
13724 this.mimicing = false;
13725 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13728 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13730 * @cfg {String} triggerClass A CSS class to apply to the trigger
13733 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13738 * @cfg {Boolean} removable (true|false) special filter default false
13742 /** @cfg {Boolean} grow @hide */
13743 /** @cfg {Number} growMin @hide */
13744 /** @cfg {Number} growMax @hide */
13750 autoSize: Roo.emptyFn,
13754 deferHeight : true,
13757 actionMode : 'wrap',
13762 getAutoCreate : function(){
13764 var align = this.labelAlign || this.parentLabelAlign();
13769 cls: 'form-group' //input-group
13776 type : this.inputType,
13777 cls : 'form-control',
13778 autocomplete: 'new-password',
13779 placeholder : this.placeholder || ''
13783 input.name = this.name;
13786 input.cls += ' input-' + this.size;
13789 if (this.disabled) {
13790 input.disabled=true;
13793 var inputblock = input;
13795 if(this.hasFeedback && !this.allowBlank){
13799 cls: 'glyphicon form-control-feedback'
13802 if(this.removable && !this.editable ){
13804 cls : 'has-feedback',
13810 cls : 'roo-combo-removable-btn close'
13817 cls : 'has-feedback',
13826 if(this.removable && !this.editable ){
13828 cls : 'roo-removable',
13834 cls : 'roo-combo-removable-btn close'
13841 if (this.before || this.after) {
13844 cls : 'input-group',
13848 inputblock.cn.push({
13850 cls : 'input-group-addon input-group-prepend input-group-text',
13855 inputblock.cn.push(input);
13857 if(this.hasFeedback && !this.allowBlank){
13858 inputblock.cls += ' has-feedback';
13859 inputblock.cn.push(feedback);
13863 inputblock.cn.push({
13865 cls : 'input-group-addon input-group-append input-group-text',
13874 var ibwrap = inputblock;
13879 cls: 'roo-select2-choices',
13883 cls: 'roo-select2-search-field',
13895 cls: 'roo-select2-container input-group',
13900 cls: 'form-hidden-field'
13906 if(!this.multiple && this.showToggleBtn){
13912 if (this.caret != false) {
13915 cls: 'fa fa-' + this.caret
13922 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13924 Roo.bootstrap.version == 3 ? caret : '',
13927 cls: 'combobox-clear',
13941 combobox.cls += ' roo-select2-container-multi';
13945 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13946 tooltip : 'This field is required'
13948 if (Roo.bootstrap.version == 4) {
13951 style : 'display:none'
13956 if (align ==='left' && this.fieldLabel.length) {
13958 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13965 cls : 'control-label',
13966 html : this.fieldLabel
13978 var labelCfg = cfg.cn[1];
13979 var contentCfg = cfg.cn[2];
13981 if(this.indicatorpos == 'right'){
13986 cls : 'control-label',
13990 html : this.fieldLabel
14004 labelCfg = cfg.cn[0];
14005 contentCfg = cfg.cn[1];
14008 if(this.labelWidth > 12){
14009 labelCfg.style = "width: " + this.labelWidth + 'px';
14012 if(this.labelWidth < 13 && this.labelmd == 0){
14013 this.labelmd = this.labelWidth;
14016 if(this.labellg > 0){
14017 labelCfg.cls += ' col-lg-' + this.labellg;
14018 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14021 if(this.labelmd > 0){
14022 labelCfg.cls += ' col-md-' + this.labelmd;
14023 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14026 if(this.labelsm > 0){
14027 labelCfg.cls += ' col-sm-' + this.labelsm;
14028 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14031 if(this.labelxs > 0){
14032 labelCfg.cls += ' col-xs-' + this.labelxs;
14033 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14036 } else if ( this.fieldLabel.length) {
14037 // Roo.log(" label");
14042 //cls : 'input-group-addon',
14043 html : this.fieldLabel
14051 if(this.indicatorpos == 'right'){
14059 html : this.fieldLabel
14073 // Roo.log(" no label && no align");
14080 ['xs','sm','md','lg'].map(function(size){
14081 if (settings[size]) {
14082 cfg.cls += ' col-' + size + '-' + settings[size];
14093 onResize : function(w, h){
14094 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14095 // if(typeof w == 'number'){
14096 // var x = w - this.trigger.getWidth();
14097 // this.inputEl().setWidth(this.adjustWidth('input', x));
14098 // this.trigger.setStyle('left', x+'px');
14103 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14106 getResizeEl : function(){
14107 return this.inputEl();
14111 getPositionEl : function(){
14112 return this.inputEl();
14116 alignErrorIcon : function(){
14117 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14121 initEvents : function(){
14125 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14126 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14127 if(!this.multiple && this.showToggleBtn){
14128 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14129 if(this.hideTrigger){
14130 this.trigger.setDisplayed(false);
14132 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14136 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14139 if(this.removable && !this.editable && !this.tickable){
14140 var close = this.closeTriggerEl();
14143 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14144 close.on('click', this.removeBtnClick, this, close);
14148 //this.trigger.addClassOnOver('x-form-trigger-over');
14149 //this.trigger.addClassOnClick('x-form-trigger-click');
14152 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14156 closeTriggerEl : function()
14158 var close = this.el.select('.roo-combo-removable-btn', true).first();
14159 return close ? close : false;
14162 removeBtnClick : function(e, h, el)
14164 e.preventDefault();
14166 if(this.fireEvent("remove", this) !== false){
14168 this.fireEvent("afterremove", this)
14172 createList : function()
14174 this.list = Roo.get(document.body).createChild({
14175 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14176 cls: 'typeahead typeahead-long dropdown-menu shadow',
14177 style: 'display:none'
14180 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14185 initTrigger : function(){
14190 onDestroy : function(){
14192 this.trigger.removeAllListeners();
14193 // this.trigger.remove();
14196 // this.wrap.remove();
14198 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14202 onFocus : function(){
14203 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14205 if(!this.mimicing){
14206 this.wrap.addClass('x-trigger-wrap-focus');
14207 this.mimicing = true;
14208 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14209 if(this.monitorTab){
14210 this.el.on("keydown", this.checkTab, this);
14217 checkTab : function(e){
14218 if(e.getKey() == e.TAB){
14219 this.triggerBlur();
14224 onBlur : function(){
14229 mimicBlur : function(e, t){
14231 if(!this.wrap.contains(t) && this.validateBlur()){
14232 this.triggerBlur();
14238 triggerBlur : function(){
14239 this.mimicing = false;
14240 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14241 if(this.monitorTab){
14242 this.el.un("keydown", this.checkTab, this);
14244 //this.wrap.removeClass('x-trigger-wrap-focus');
14245 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14249 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14250 validateBlur : function(e, t){
14255 onDisable : function(){
14256 this.inputEl().dom.disabled = true;
14257 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14259 // this.wrap.addClass('x-item-disabled');
14264 onEnable : function(){
14265 this.inputEl().dom.disabled = false;
14266 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14268 // this.el.removeClass('x-item-disabled');
14273 onShow : function(){
14274 var ae = this.getActionEl();
14277 ae.dom.style.display = '';
14278 ae.dom.style.visibility = 'visible';
14284 onHide : function(){
14285 var ae = this.getActionEl();
14286 ae.dom.style.display = 'none';
14290 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14291 * by an implementing function.
14293 * @param {EventObject} e
14295 onTriggerClick : Roo.emptyFn
14303 * @class Roo.bootstrap.form.CardUploader
14304 * @extends Roo.bootstrap.Button
14305 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14306 * @cfg {Number} errorTimeout default 3000
14307 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14308 * @cfg {Array} html The button text.
14312 * Create a new CardUploader
14313 * @param {Object} config The config object
14316 Roo.bootstrap.form.CardUploader = function(config){
14320 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14323 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14331 * When a image is clicked on - and needs to display a slideshow or similar..
14332 * @param {Roo.bootstrap.Card} this
14333 * @param {Object} The image information data
14339 * When a the download link is clicked
14340 * @param {Roo.bootstrap.Card} this
14341 * @param {Object} The image information data contains
14348 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14351 errorTimeout : 3000,
14355 fileCollection : false,
14358 getAutoCreate : function()
14362 cls :'form-group' ,
14367 //cls : 'input-group-addon',
14368 html : this.fieldLabel
14376 value : this.value,
14377 cls : 'd-none form-control'
14382 multiple : 'multiple',
14384 cls : 'd-none roo-card-upload-selector'
14388 cls : 'roo-card-uploader-button-container w-100 mb-2'
14391 cls : 'card-columns roo-card-uploader-container'
14401 getChildContainer : function() /// what children are added to.
14403 return this.containerEl;
14406 getButtonContainer : function() /// what children are added to.
14408 return this.el.select(".roo-card-uploader-button-container").first();
14411 initEvents : function()
14414 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14418 xns: Roo.bootstrap,
14421 container_method : 'getButtonContainer' ,
14422 html : this.html, // fix changable?
14425 'click' : function(btn, e) {
14434 this.urlAPI = (window.createObjectURL && window) ||
14435 (window.URL && URL.revokeObjectURL && URL) ||
14436 (window.webkitURL && webkitURL);
14441 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14443 this.selectorEl.on('change', this.onFileSelected, this);
14446 this.images.forEach(function(img) {
14449 this.images = false;
14451 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14457 onClick : function(e)
14459 e.preventDefault();
14461 this.selectorEl.dom.click();
14465 onFileSelected : function(e)
14467 e.preventDefault();
14469 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14473 Roo.each(this.selectorEl.dom.files, function(file){
14474 this.addFile(file);
14483 addFile : function(file)
14486 if(typeof(file) === 'string'){
14487 throw "Add file by name?"; // should not happen
14491 if(!file || !this.urlAPI){
14501 var url = _this.urlAPI.createObjectURL( file);
14504 id : Roo.bootstrap.form.CardUploader.ID--,
14505 is_uploaded : false,
14509 mimetype : file.type,
14517 * addCard - add an Attachment to the uploader
14518 * @param data - the data about the image to upload
14522 title : "Title of file",
14523 is_uploaded : false,
14524 src : "http://.....",
14525 srcfile : { the File upload object },
14526 mimetype : file.type,
14529 .. any other data...
14535 addCard : function (data)
14537 // hidden input element?
14538 // if the file is not an image...
14539 //then we need to use something other that and header_image
14544 xns : Roo.bootstrap,
14545 xtype : 'CardFooter',
14548 xns : Roo.bootstrap,
14554 xns : Roo.bootstrap,
14556 html : String.format("<small>{0}</small>", data.title),
14557 cls : 'col-10 text-left',
14562 click : function() {
14564 t.fireEvent( "download", t, data );
14570 xns : Roo.bootstrap,
14572 style: 'max-height: 28px; ',
14578 click : function() {
14579 t.removeCard(data.id)
14591 var cn = this.addxtype(
14594 xns : Roo.bootstrap,
14597 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14598 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14599 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14604 initEvents : function() {
14605 Roo.bootstrap.Card.prototype.initEvents.call(this);
14607 this.imgEl = this.el.select('.card-img-top').first();
14609 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14610 this.imgEl.set({ 'pointer' : 'cursor' });
14613 this.getCardFooter().addClass('p-1');
14620 // dont' really need ot update items.
14621 // this.items.push(cn);
14622 this.fileCollection.add(cn);
14624 if (!data.srcfile) {
14625 this.updateInput();
14630 var reader = new FileReader();
14631 reader.addEventListener("load", function() {
14632 data.srcdata = reader.result;
14635 reader.readAsDataURL(data.srcfile);
14640 removeCard : function(id)
14643 var card = this.fileCollection.get(id);
14644 card.data.is_deleted = 1;
14645 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14646 //this.fileCollection.remove(card);
14647 //this.items = this.items.filter(function(e) { return e != card });
14648 // dont' really need ot update items.
14649 card.el.dom.parentNode.removeChild(card.el.dom);
14650 this.updateInput();
14656 this.fileCollection.each(function(card) {
14657 if (card.el.dom && card.el.dom.parentNode) {
14658 card.el.dom.parentNode.removeChild(card.el.dom);
14661 this.fileCollection.clear();
14662 this.updateInput();
14665 updateInput : function()
14668 this.fileCollection.each(function(e) {
14672 this.inputEl().dom.value = JSON.stringify(data);
14682 Roo.bootstrap.form.CardUploader.ID = -1;/*
14684 * Ext JS Library 1.1.1
14685 * Copyright(c) 2006-2007, Ext JS, LLC.
14687 * Originally Released Under LGPL - original licence link has changed is not relivant.
14690 * <script type="text/javascript">
14695 * @class Roo.data.SortTypes
14697 * Defines the default sorting (casting?) comparison functions used when sorting data.
14699 Roo.data.SortTypes = {
14701 * Default sort that does nothing
14702 * @param {Mixed} s The value being converted
14703 * @return {Mixed} The comparison value
14705 none : function(s){
14710 * The regular expression used to strip tags
14714 stripTagsRE : /<\/?[^>]+>/gi,
14717 * Strips all HTML tags to sort on text only
14718 * @param {Mixed} s The value being converted
14719 * @return {String} The comparison value
14721 asText : function(s){
14722 return String(s).replace(this.stripTagsRE, "");
14726 * Strips all HTML tags to sort on text only - Case insensitive
14727 * @param {Mixed} s The value being converted
14728 * @return {String} The comparison value
14730 asUCText : function(s){
14731 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14735 * Case insensitive string
14736 * @param {Mixed} s The value being converted
14737 * @return {String} The comparison value
14739 asUCString : function(s) {
14740 return String(s).toUpperCase();
14745 * @param {Mixed} s The value being converted
14746 * @return {Number} The comparison value
14748 asDate : function(s) {
14752 if(s instanceof Date){
14753 return s.getTime();
14755 return Date.parse(String(s));
14760 * @param {Mixed} s The value being converted
14761 * @return {Float} The comparison value
14763 asFloat : function(s) {
14764 var val = parseFloat(String(s).replace(/,/g, ""));
14773 * @param {Mixed} s The value being converted
14774 * @return {Number} The comparison value
14776 asInt : function(s) {
14777 var val = parseInt(String(s).replace(/,/g, ""));
14785 * Ext JS Library 1.1.1
14786 * Copyright(c) 2006-2007, Ext JS, LLC.
14788 * Originally Released Under LGPL - original licence link has changed is not relivant.
14791 * <script type="text/javascript">
14795 * @class Roo.data.Record
14796 * Instances of this class encapsulate both record <em>definition</em> information, and record
14797 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14798 * to access Records cached in an {@link Roo.data.Store} object.<br>
14800 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14801 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14804 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14806 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14807 * {@link #create}. The parameters are the same.
14808 * @param {Array} data An associative Array of data values keyed by the field name.
14809 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14810 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14811 * not specified an integer id is generated.
14813 Roo.data.Record = function(data, id){
14814 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14819 * Generate a constructor for a specific record layout.
14820 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14821 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14822 * Each field definition object may contain the following properties: <ul>
14823 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14824 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14825 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14826 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14827 * is being used, then this is a string containing the javascript expression to reference the data relative to
14828 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14829 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14830 * this may be omitted.</p></li>
14831 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14832 * <ul><li>auto (Default, implies no conversion)</li>
14837 * <li>date</li></ul></p></li>
14838 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14839 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14840 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14841 * by the Reader into an object that will be stored in the Record. It is passed the
14842 * following parameters:<ul>
14843 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14845 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14847 * <br>usage:<br><pre><code>
14848 var TopicRecord = Roo.data.Record.create(
14849 {name: 'title', mapping: 'topic_title'},
14850 {name: 'author', mapping: 'username'},
14851 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14852 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14853 {name: 'lastPoster', mapping: 'user2'},
14854 {name: 'excerpt', mapping: 'post_text'}
14857 var myNewRecord = new TopicRecord({
14858 title: 'Do my job please',
14861 lastPost: new Date(),
14862 lastPoster: 'Animal',
14863 excerpt: 'No way dude!'
14865 myStore.add(myNewRecord);
14870 Roo.data.Record.create = function(o){
14871 var f = function(){
14872 f.superclass.constructor.apply(this, arguments);
14874 Roo.extend(f, Roo.data.Record);
14875 var p = f.prototype;
14876 p.fields = new Roo.util.MixedCollection(false, function(field){
14879 for(var i = 0, len = o.length; i < len; i++){
14880 p.fields.add(new Roo.data.Field(o[i]));
14882 f.getField = function(name){
14883 return p.fields.get(name);
14888 Roo.data.Record.AUTO_ID = 1000;
14889 Roo.data.Record.EDIT = 'edit';
14890 Roo.data.Record.REJECT = 'reject';
14891 Roo.data.Record.COMMIT = 'commit';
14893 Roo.data.Record.prototype = {
14895 * Readonly flag - true if this record has been modified.
14904 join : function(store){
14905 this.store = store;
14909 * Set the named field to the specified value.
14910 * @param {String} name The name of the field to set.
14911 * @param {Object} value The value to set the field to.
14913 set : function(name, value){
14914 if(this.data[name] == value){
14918 if(!this.modified){
14919 this.modified = {};
14921 if(typeof this.modified[name] == 'undefined'){
14922 this.modified[name] = this.data[name];
14924 this.data[name] = value;
14925 if(!this.editing && this.store){
14926 this.store.afterEdit(this);
14931 * Get the value of the named field.
14932 * @param {String} name The name of the field to get the value of.
14933 * @return {Object} The value of the field.
14935 get : function(name){
14936 return this.data[name];
14940 beginEdit : function(){
14941 this.editing = true;
14942 this.modified = {};
14946 cancelEdit : function(){
14947 this.editing = false;
14948 delete this.modified;
14952 endEdit : function(){
14953 this.editing = false;
14954 if(this.dirty && this.store){
14955 this.store.afterEdit(this);
14960 * Usually called by the {@link Roo.data.Store} which owns the Record.
14961 * Rejects all changes made to the Record since either creation, or the last commit operation.
14962 * Modified fields are reverted to their original values.
14964 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14965 * of reject operations.
14967 reject : function(){
14968 var m = this.modified;
14970 if(typeof m[n] != "function"){
14971 this.data[n] = m[n];
14974 this.dirty = false;
14975 delete this.modified;
14976 this.editing = false;
14978 this.store.afterReject(this);
14983 * Usually called by the {@link Roo.data.Store} which owns the Record.
14984 * Commits all changes made to the Record since either creation, or the last commit operation.
14986 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14987 * of commit operations.
14989 commit : function(){
14990 this.dirty = false;
14991 delete this.modified;
14992 this.editing = false;
14994 this.store.afterCommit(this);
14999 hasError : function(){
15000 return this.error != null;
15004 clearError : function(){
15009 * Creates a copy of this record.
15010 * @param {String} id (optional) A new record id if you don't want to use this record's id
15013 copy : function(newId) {
15014 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15018 * Ext JS Library 1.1.1
15019 * Copyright(c) 2006-2007, Ext JS, LLC.
15021 * Originally Released Under LGPL - original licence link has changed is not relivant.
15024 * <script type="text/javascript">
15030 * @class Roo.data.Store
15031 * @extends Roo.util.Observable
15032 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15033 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15035 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15036 * has no knowledge of the format of the data returned by the Proxy.<br>
15038 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15039 * instances from the data object. These records are cached and made available through accessor functions.
15041 * Creates a new Store.
15042 * @param {Object} config A config object containing the objects needed for the Store to access data,
15043 * and read the data into Records.
15045 Roo.data.Store = function(config){
15046 this.data = new Roo.util.MixedCollection(false);
15047 this.data.getKey = function(o){
15050 this.baseParams = {};
15052 this.paramNames = {
15057 "multisort" : "_multisort"
15060 if(config && config.data){
15061 this.inlineData = config.data;
15062 delete config.data;
15065 Roo.apply(this, config);
15067 if(this.reader){ // reader passed
15068 this.reader = Roo.factory(this.reader, Roo.data);
15069 this.reader.xmodule = this.xmodule || false;
15070 if(!this.recordType){
15071 this.recordType = this.reader.recordType;
15073 if(this.reader.onMetaChange){
15074 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15078 if(this.recordType){
15079 this.fields = this.recordType.prototype.fields;
15081 this.modified = [];
15085 * @event datachanged
15086 * Fires when the data cache has changed, and a widget which is using this Store
15087 * as a Record cache should refresh its view.
15088 * @param {Store} this
15090 datachanged : true,
15092 * @event metachange
15093 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15094 * @param {Store} this
15095 * @param {Object} meta The JSON metadata
15100 * Fires when Records have been added to the Store
15101 * @param {Store} this
15102 * @param {Roo.data.Record[]} records The array of Records added
15103 * @param {Number} index The index at which the record(s) were added
15108 * Fires when a Record has been removed from the Store
15109 * @param {Store} this
15110 * @param {Roo.data.Record} record The Record that was removed
15111 * @param {Number} index The index at which the record was removed
15116 * Fires when a Record has been updated
15117 * @param {Store} this
15118 * @param {Roo.data.Record} record The Record that was updated
15119 * @param {String} operation The update operation being performed. Value may be one of:
15121 Roo.data.Record.EDIT
15122 Roo.data.Record.REJECT
15123 Roo.data.Record.COMMIT
15129 * Fires when the data cache has been cleared.
15130 * @param {Store} this
15134 * @event beforeload
15135 * Fires before a request is made for a new data object. If the beforeload handler returns false
15136 * the load action will be canceled.
15137 * @param {Store} this
15138 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15142 * @event beforeloadadd
15143 * Fires after a new set of Records has been loaded.
15144 * @param {Store} this
15145 * @param {Roo.data.Record[]} records The Records that were loaded
15146 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15148 beforeloadadd : true,
15151 * Fires after a new set of Records has been loaded, before they are added to the store.
15152 * @param {Store} this
15153 * @param {Roo.data.Record[]} records The Records that were loaded
15154 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15155 * @params {Object} return from reader
15159 * @event loadexception
15160 * Fires if an exception occurs in the Proxy during loading.
15161 * Called with the signature of the Proxy's "loadexception" event.
15162 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15165 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15166 * @param {Object} load options
15167 * @param {Object} jsonData from your request (normally this contains the Exception)
15169 loadexception : true
15173 this.proxy = Roo.factory(this.proxy, Roo.data);
15174 this.proxy.xmodule = this.xmodule || false;
15175 this.relayEvents(this.proxy, ["loadexception"]);
15177 this.sortToggle = {};
15178 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15180 Roo.data.Store.superclass.constructor.call(this);
15182 if(this.inlineData){
15183 this.loadData(this.inlineData);
15184 delete this.inlineData;
15188 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15190 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15191 * without a remote query - used by combo/forms at present.
15195 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15198 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15201 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15202 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15205 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15206 * on any HTTP request
15209 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15212 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15216 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15217 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15219 remoteSort : false,
15222 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15223 * loaded or when a record is removed. (defaults to false).
15225 pruneModifiedRecords : false,
15228 lastOptions : null,
15231 * Add Records to the Store and fires the add event.
15232 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15234 add : function(records){
15235 records = [].concat(records);
15236 for(var i = 0, len = records.length; i < len; i++){
15237 records[i].join(this);
15239 var index = this.data.length;
15240 this.data.addAll(records);
15241 this.fireEvent("add", this, records, index);
15245 * Remove a Record from the Store and fires the remove event.
15246 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15248 remove : function(record){
15249 var index = this.data.indexOf(record);
15250 this.data.removeAt(index);
15252 if(this.pruneModifiedRecords){
15253 this.modified.remove(record);
15255 this.fireEvent("remove", this, record, index);
15259 * Remove all Records from the Store and fires the clear event.
15261 removeAll : function(){
15263 if(this.pruneModifiedRecords){
15264 this.modified = [];
15266 this.fireEvent("clear", this);
15270 * Inserts Records to the Store at the given index and fires the add event.
15271 * @param {Number} index The start index at which to insert the passed Records.
15272 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15274 insert : function(index, records){
15275 records = [].concat(records);
15276 for(var i = 0, len = records.length; i < len; i++){
15277 this.data.insert(index, records[i]);
15278 records[i].join(this);
15280 this.fireEvent("add", this, records, index);
15284 * Get the index within the cache of the passed Record.
15285 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15286 * @return {Number} The index of the passed Record. Returns -1 if not found.
15288 indexOf : function(record){
15289 return this.data.indexOf(record);
15293 * Get the index within the cache of the Record with the passed id.
15294 * @param {String} id The id of the Record to find.
15295 * @return {Number} The index of the Record. Returns -1 if not found.
15297 indexOfId : function(id){
15298 return this.data.indexOfKey(id);
15302 * Get the Record with the specified id.
15303 * @param {String} id The id of the Record to find.
15304 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15306 getById : function(id){
15307 return this.data.key(id);
15311 * Get the Record at the specified index.
15312 * @param {Number} index The index of the Record to find.
15313 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15315 getAt : function(index){
15316 return this.data.itemAt(index);
15320 * Returns a range of Records between specified indices.
15321 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15322 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15323 * @return {Roo.data.Record[]} An array of Records
15325 getRange : function(start, end){
15326 return this.data.getRange(start, end);
15330 storeOptions : function(o){
15331 o = Roo.apply({}, o);
15334 this.lastOptions = o;
15338 * Loads the Record cache from the configured Proxy using the configured Reader.
15340 * If using remote paging, then the first load call must specify the <em>start</em>
15341 * and <em>limit</em> properties in the options.params property to establish the initial
15342 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15344 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15345 * and this call will return before the new data has been loaded. Perform any post-processing
15346 * in a callback function, or in a "load" event handler.</strong>
15348 * @param {Object} options An object containing properties which control loading options:<ul>
15349 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15350 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15351 * passed the following arguments:<ul>
15352 * <li>r : Roo.data.Record[]</li>
15353 * <li>options: Options object from the load call</li>
15354 * <li>success: Boolean success indicator</li></ul></li>
15355 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15356 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15359 load : function(options){
15360 options = options || {};
15361 if(this.fireEvent("beforeload", this, options) !== false){
15362 this.storeOptions(options);
15363 var p = Roo.apply(options.params || {}, this.baseParams);
15364 // if meta was not loaded from remote source.. try requesting it.
15365 if (!this.reader.metaFromRemote) {
15366 p._requestMeta = 1;
15368 if(this.sortInfo && this.remoteSort){
15369 var pn = this.paramNames;
15370 p[pn["sort"]] = this.sortInfo.field;
15371 p[pn["dir"]] = this.sortInfo.direction;
15373 if (this.multiSort) {
15374 var pn = this.paramNames;
15375 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15378 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15383 * Reloads the Record cache from the configured Proxy using the configured Reader and
15384 * the options from the last load operation performed.
15385 * @param {Object} options (optional) An object containing properties which may override the options
15386 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15387 * the most recently used options are reused).
15389 reload : function(options){
15390 this.load(Roo.applyIf(options||{}, this.lastOptions));
15394 // Called as a callback by the Reader during a load operation.
15395 loadRecords : function(o, options, success){
15398 if(success !== false){
15399 this.fireEvent("load", this, [], options, o);
15401 if(options.callback){
15402 options.callback.call(options.scope || this, [], options, false);
15406 // if data returned failure - throw an exception.
15407 if (o.success === false) {
15408 // show a message if no listener is registered.
15409 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15410 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15412 // loadmask wil be hooked into this..
15413 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15416 var r = o.records, t = o.totalRecords || r.length;
15418 this.fireEvent("beforeloadadd", this, r, options, o);
15420 if(!options || options.add !== true){
15421 if(this.pruneModifiedRecords){
15422 this.modified = [];
15424 for(var i = 0, len = r.length; i < len; i++){
15428 this.data = this.snapshot;
15429 delete this.snapshot;
15432 this.data.addAll(r);
15433 this.totalLength = t;
15435 this.fireEvent("datachanged", this);
15437 this.totalLength = Math.max(t, this.data.length+r.length);
15441 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15443 var e = new Roo.data.Record({});
15445 e.set(this.parent.displayField, this.parent.emptyTitle);
15446 e.set(this.parent.valueField, '');
15451 this.fireEvent("load", this, r, options, o);
15452 if(options.callback){
15453 options.callback.call(options.scope || this, r, options, true);
15459 * Loads data from a passed data block. A Reader which understands the format of the data
15460 * must have been configured in the constructor.
15461 * @param {Object} data The data block from which to read the Records. The format of the data expected
15462 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15463 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15465 loadData : function(o, append){
15466 var r = this.reader.readRecords(o);
15467 this.loadRecords(r, {add: append}, true);
15471 * using 'cn' the nested child reader read the child array into it's child stores.
15472 * @param {Object} rec The record with a 'children array
15474 loadDataFromChildren : function(rec)
15476 this.loadData(this.reader.toLoadData(rec));
15481 * Gets the number of cached records.
15483 * <em>If using paging, this may not be the total size of the dataset. If the data object
15484 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15485 * the data set size</em>
15487 getCount : function(){
15488 return this.data.length || 0;
15492 * Gets the total number of records in the dataset as returned by the server.
15494 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15495 * the dataset size</em>
15497 getTotalCount : function(){
15498 return this.totalLength || 0;
15502 * Returns the sort state of the Store as an object with two properties:
15504 field {String} The name of the field by which the Records are sorted
15505 direction {String} The sort order, "ASC" or "DESC"
15508 getSortState : function(){
15509 return this.sortInfo;
15513 applySort : function(){
15514 if(this.sortInfo && !this.remoteSort){
15515 var s = this.sortInfo, f = s.field;
15516 var st = this.fields.get(f).sortType;
15517 var fn = function(r1, r2){
15518 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15519 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15521 this.data.sort(s.direction, fn);
15522 if(this.snapshot && this.snapshot != this.data){
15523 this.snapshot.sort(s.direction, fn);
15529 * Sets the default sort column and order to be used by the next load operation.
15530 * @param {String} fieldName The name of the field to sort by.
15531 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15533 setDefaultSort : function(field, dir){
15534 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15538 * Sort the Records.
15539 * If remote sorting is used, the sort is performed on the server, and the cache is
15540 * reloaded. If local sorting is used, the cache is sorted internally.
15541 * @param {String} fieldName The name of the field to sort by.
15542 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15544 sort : function(fieldName, dir){
15545 var f = this.fields.get(fieldName);
15547 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15549 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15550 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15555 this.sortToggle[f.name] = dir;
15556 this.sortInfo = {field: f.name, direction: dir};
15557 if(!this.remoteSort){
15559 this.fireEvent("datachanged", this);
15561 this.load(this.lastOptions);
15566 * Calls the specified function for each of the Records in the cache.
15567 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15568 * Returning <em>false</em> aborts and exits the iteration.
15569 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15571 each : function(fn, scope){
15572 this.data.each(fn, scope);
15576 * Gets all records modified since the last commit. Modified records are persisted across load operations
15577 * (e.g., during paging).
15578 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15580 getModifiedRecords : function(){
15581 return this.modified;
15585 createFilterFn : function(property, value, anyMatch){
15586 if(!value.exec){ // not a regex
15587 value = String(value);
15588 if(value.length == 0){
15591 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15593 return function(r){
15594 return value.test(r.data[property]);
15599 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15600 * @param {String} property A field on your records
15601 * @param {Number} start The record index to start at (defaults to 0)
15602 * @param {Number} end The last record index to include (defaults to length - 1)
15603 * @return {Number} The sum
15605 sum : function(property, start, end){
15606 var rs = this.data.items, v = 0;
15607 start = start || 0;
15608 end = (end || end === 0) ? end : rs.length-1;
15610 for(var i = start; i <= end; i++){
15611 v += (rs[i].data[property] || 0);
15617 * Filter the records by a specified property.
15618 * @param {String} field A field on your records
15619 * @param {String/RegExp} value Either a string that the field
15620 * should start with or a RegExp to test against the field
15621 * @param {Boolean} anyMatch True to match any part not just the beginning
15623 filter : function(property, value, anyMatch){
15624 var fn = this.createFilterFn(property, value, anyMatch);
15625 return fn ? this.filterBy(fn) : this.clearFilter();
15629 * Filter by a function. The specified function will be called with each
15630 * record in this data source. If the function returns true the record is included,
15631 * otherwise it is filtered.
15632 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15633 * @param {Object} scope (optional) The scope of the function (defaults to this)
15635 filterBy : function(fn, scope){
15636 this.snapshot = this.snapshot || this.data;
15637 this.data = this.queryBy(fn, scope||this);
15638 this.fireEvent("datachanged", this);
15642 * Query the records by a specified property.
15643 * @param {String} field A field on your records
15644 * @param {String/RegExp} value Either a string that the field
15645 * should start with or a RegExp to test against the field
15646 * @param {Boolean} anyMatch True to match any part not just the beginning
15647 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15649 query : function(property, value, anyMatch){
15650 var fn = this.createFilterFn(property, value, anyMatch);
15651 return fn ? this.queryBy(fn) : this.data.clone();
15655 * Query by a function. The specified function will be called with each
15656 * record in this data source. If the function returns true the record is included
15658 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15659 * @param {Object} scope (optional) The scope of the function (defaults to this)
15660 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15662 queryBy : function(fn, scope){
15663 var data = this.snapshot || this.data;
15664 return data.filterBy(fn, scope||this);
15668 * Collects unique values for a particular dataIndex from this store.
15669 * @param {String} dataIndex The property to collect
15670 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15671 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15672 * @return {Array} An array of the unique values
15674 collect : function(dataIndex, allowNull, bypassFilter){
15675 var d = (bypassFilter === true && this.snapshot) ?
15676 this.snapshot.items : this.data.items;
15677 var v, sv, r = [], l = {};
15678 for(var i = 0, len = d.length; i < len; i++){
15679 v = d[i].data[dataIndex];
15681 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15690 * Revert to a view of the Record cache with no filtering applied.
15691 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15693 clearFilter : function(suppressEvent){
15694 if(this.snapshot && this.snapshot != this.data){
15695 this.data = this.snapshot;
15696 delete this.snapshot;
15697 if(suppressEvent !== true){
15698 this.fireEvent("datachanged", this);
15704 afterEdit : function(record){
15705 if(this.modified.indexOf(record) == -1){
15706 this.modified.push(record);
15708 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15712 afterReject : function(record){
15713 this.modified.remove(record);
15714 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15718 afterCommit : function(record){
15719 this.modified.remove(record);
15720 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15724 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15725 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15727 commitChanges : function(){
15728 var m = this.modified.slice(0);
15729 this.modified = [];
15730 for(var i = 0, len = m.length; i < len; i++){
15736 * Cancel outstanding changes on all changed records.
15738 rejectChanges : function(){
15739 var m = this.modified.slice(0);
15740 this.modified = [];
15741 for(var i = 0, len = m.length; i < len; i++){
15746 onMetaChange : function(meta, rtype, o){
15747 this.recordType = rtype;
15748 this.fields = rtype.prototype.fields;
15749 delete this.snapshot;
15750 this.sortInfo = meta.sortInfo || this.sortInfo;
15751 this.modified = [];
15752 this.fireEvent('metachange', this, this.reader.meta);
15755 moveIndex : function(data, type)
15757 var index = this.indexOf(data);
15759 var newIndex = index + type;
15763 this.insert(newIndex, data);
15768 * Ext JS Library 1.1.1
15769 * Copyright(c) 2006-2007, Ext JS, LLC.
15771 * Originally Released Under LGPL - original licence link has changed is not relivant.
15774 * <script type="text/javascript">
15778 * @class Roo.data.SimpleStore
15779 * @extends Roo.data.Store
15780 * Small helper class to make creating Stores from Array data easier.
15781 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15782 * @cfg {Array} fields An array of field definition objects, or field name strings.
15783 * @cfg {Object} an existing reader (eg. copied from another store)
15784 * @cfg {Array} data The multi-dimensional array of data
15785 * @cfg {Roo.data.DataProxy} proxy [not-required]
15786 * @cfg {Roo.data.Reader} reader [not-required]
15788 * @param {Object} config
15790 Roo.data.SimpleStore = function(config)
15792 Roo.data.SimpleStore.superclass.constructor.call(this, {
15794 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15797 Roo.data.Record.create(config.fields)
15799 proxy : new Roo.data.MemoryProxy(config.data)
15803 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15805 * Ext JS Library 1.1.1
15806 * Copyright(c) 2006-2007, Ext JS, LLC.
15808 * Originally Released Under LGPL - original licence link has changed is not relivant.
15811 * <script type="text/javascript">
15816 * @extends Roo.data.Store
15817 * @class Roo.data.JsonStore
15818 * Small helper class to make creating Stores for JSON data easier. <br/>
15820 var store = new Roo.data.JsonStore({
15821 url: 'get-images.php',
15823 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15826 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15827 * JsonReader and HttpProxy (unless inline data is provided).</b>
15828 * @cfg {Array} fields An array of field definition objects, or field name strings.
15830 * @param {Object} config
15832 Roo.data.JsonStore = function(c){
15833 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15834 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15835 reader: new Roo.data.JsonReader(c, c.fields)
15838 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15840 * Ext JS Library 1.1.1
15841 * Copyright(c) 2006-2007, Ext JS, LLC.
15843 * Originally Released Under LGPL - original licence link has changed is not relivant.
15846 * <script type="text/javascript">
15850 Roo.data.Field = function(config){
15851 if(typeof config == "string"){
15852 config = {name: config};
15854 Roo.apply(this, config);
15857 this.type = "auto";
15860 var st = Roo.data.SortTypes;
15861 // named sortTypes are supported, here we look them up
15862 if(typeof this.sortType == "string"){
15863 this.sortType = st[this.sortType];
15866 // set default sortType for strings and dates
15867 if(!this.sortType){
15870 this.sortType = st.asUCString;
15873 this.sortType = st.asDate;
15876 this.sortType = st.none;
15881 var stripRe = /[\$,%]/g;
15883 // prebuilt conversion function for this field, instead of
15884 // switching every time we're reading a value
15886 var cv, dateFormat = this.dateFormat;
15891 cv = function(v){ return v; };
15894 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15898 return v !== undefined && v !== null && v !== '' ?
15899 parseInt(String(v).replace(stripRe, ""), 10) : '';
15904 return v !== undefined && v !== null && v !== '' ?
15905 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15910 cv = function(v){ return v === true || v === "true" || v == 1; };
15917 if(v instanceof Date){
15921 if(dateFormat == "timestamp"){
15922 return new Date(v*1000);
15924 return Date.parseDate(v, dateFormat);
15926 var parsed = Date.parse(v);
15927 return parsed ? new Date(parsed) : null;
15936 Roo.data.Field.prototype = {
15944 * Ext JS Library 1.1.1
15945 * Copyright(c) 2006-2007, Ext JS, LLC.
15947 * Originally Released Under LGPL - original licence link has changed is not relivant.
15950 * <script type="text/javascript">
15953 // Base class for reading structured data from a data source. This class is intended to be
15954 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15957 * @class Roo.data.DataReader
15959 * Base class for reading structured data from a data source. This class is intended to be
15960 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15963 Roo.data.DataReader = function(meta, recordType){
15967 this.recordType = recordType instanceof Array ?
15968 Roo.data.Record.create(recordType) : recordType;
15971 Roo.data.DataReader.prototype = {
15974 readerType : 'Data',
15976 * Create an empty record
15977 * @param {Object} data (optional) - overlay some values
15978 * @return {Roo.data.Record} record created.
15980 newRow : function(d) {
15982 this.recordType.prototype.fields.each(function(c) {
15984 case 'int' : da[c.name] = 0; break;
15985 case 'date' : da[c.name] = new Date(); break;
15986 case 'float' : da[c.name] = 0.0; break;
15987 case 'boolean' : da[c.name] = false; break;
15988 default : da[c.name] = ""; break;
15992 return new this.recordType(Roo.apply(da, d));
15998 * Ext JS Library 1.1.1
15999 * Copyright(c) 2006-2007, Ext JS, LLC.
16001 * Originally Released Under LGPL - original licence link has changed is not relivant.
16004 * <script type="text/javascript">
16008 * @class Roo.data.DataProxy
16009 * @extends Roo.util.Observable
16011 * This class is an abstract base class for implementations which provide retrieval of
16012 * unformatted data objects.<br>
16014 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16015 * (of the appropriate type which knows how to parse the data object) to provide a block of
16016 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16018 * Custom implementations must implement the load method as described in
16019 * {@link Roo.data.HttpProxy#load}.
16021 Roo.data.DataProxy = function(){
16024 * @event beforeload
16025 * Fires before a network request is made to retrieve a data object.
16026 * @param {Object} This DataProxy object.
16027 * @param {Object} params The params parameter to the load function.
16032 * Fires before the load method's callback is called.
16033 * @param {Object} This DataProxy object.
16034 * @param {Object} o The data object.
16035 * @param {Object} arg The callback argument object passed to the load function.
16039 * @event loadexception
16040 * Fires if an Exception occurs during data retrieval.
16041 * @param {Object} This DataProxy object.
16042 * @param {Object} o The data object.
16043 * @param {Object} arg The callback argument object passed to the load function.
16044 * @param {Object} e The Exception.
16046 loadexception : true
16048 Roo.data.DataProxy.superclass.constructor.call(this);
16051 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16054 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16058 * Ext JS Library 1.1.1
16059 * Copyright(c) 2006-2007, Ext JS, LLC.
16061 * Originally Released Under LGPL - original licence link has changed is not relivant.
16064 * <script type="text/javascript">
16067 * @class Roo.data.MemoryProxy
16068 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16069 * to the Reader when its load method is called.
16071 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16073 Roo.data.MemoryProxy = function(data){
16077 Roo.data.MemoryProxy.superclass.constructor.call(this);
16081 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16084 * Load data from the requested source (in this case an in-memory
16085 * data object passed to the constructor), read the data object into
16086 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16087 * process that block using the passed callback.
16088 * @param {Object} params This parameter is not used by the MemoryProxy class.
16089 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16090 * object into a block of Roo.data.Records.
16091 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16092 * The function must be passed <ul>
16093 * <li>The Record block object</li>
16094 * <li>The "arg" argument from the load function</li>
16095 * <li>A boolean success indicator</li>
16097 * @param {Object} scope The scope in which to call the callback
16098 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16100 load : function(params, reader, callback, scope, arg){
16101 params = params || {};
16104 result = reader.readRecords(params.data ? params.data :this.data);
16106 this.fireEvent("loadexception", this, arg, null, e);
16107 callback.call(scope, null, arg, false);
16110 callback.call(scope, result, arg, true);
16114 update : function(params, records){
16119 * Ext JS Library 1.1.1
16120 * Copyright(c) 2006-2007, Ext JS, LLC.
16122 * Originally Released Under LGPL - original licence link has changed is not relivant.
16125 * <script type="text/javascript">
16128 * @class Roo.data.HttpProxy
16129 * @extends Roo.data.DataProxy
16130 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16131 * configured to reference a certain URL.<br><br>
16133 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16134 * from which the running page was served.<br><br>
16136 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16138 * Be aware that to enable the browser to parse an XML document, the server must set
16139 * the Content-Type header in the HTTP response to "text/xml".
16141 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16142 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16143 * will be used to make the request.
16145 Roo.data.HttpProxy = function(conn){
16146 Roo.data.HttpProxy.superclass.constructor.call(this);
16147 // is conn a conn config or a real conn?
16149 this.useAjax = !conn || !conn.events;
16153 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16154 // thse are take from connection...
16157 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16160 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16161 * extra parameters to each request made by this object. (defaults to undefined)
16164 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16165 * to each request made by this object. (defaults to undefined)
16168 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16171 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16174 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16180 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16184 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16185 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16186 * a finer-grained basis than the DataProxy events.
16188 getConnection : function(){
16189 return this.useAjax ? Roo.Ajax : this.conn;
16193 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16194 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16195 * process that block using the passed callback.
16196 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16197 * for the request to the remote server.
16198 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16199 * object into a block of Roo.data.Records.
16200 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16201 * The function must be passed <ul>
16202 * <li>The Record block object</li>
16203 * <li>The "arg" argument from the load function</li>
16204 * <li>A boolean success indicator</li>
16206 * @param {Object} scope The scope in which to call the callback
16207 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16209 load : function(params, reader, callback, scope, arg){
16210 if(this.fireEvent("beforeload", this, params) !== false){
16212 params : params || {},
16214 callback : callback,
16219 callback : this.loadResponse,
16223 Roo.applyIf(o, this.conn);
16224 if(this.activeRequest){
16225 Roo.Ajax.abort(this.activeRequest);
16227 this.activeRequest = Roo.Ajax.request(o);
16229 this.conn.request(o);
16232 callback.call(scope||this, null, arg, false);
16237 loadResponse : function(o, success, response){
16238 delete this.activeRequest;
16240 this.fireEvent("loadexception", this, o, response);
16241 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16246 result = o.reader.read(response);
16249 o.raw = { errorMsg : response.responseText };
16250 this.fireEvent("loadexception", this, o, response, e);
16251 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16255 this.fireEvent("load", this, o, o.request.arg);
16256 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16260 update : function(dataSet){
16265 updateResponse : function(dataSet){
16270 * Ext JS Library 1.1.1
16271 * Copyright(c) 2006-2007, Ext JS, LLC.
16273 * Originally Released Under LGPL - original licence link has changed is not relivant.
16276 * <script type="text/javascript">
16280 * @class Roo.data.ScriptTagProxy
16281 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16282 * other than the originating domain of the running page.<br><br>
16284 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
16285 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16287 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16288 * source code that is used as the source inside a <script> tag.<br><br>
16290 * In order for the browser to process the returned data, the server must wrap the data object
16291 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16292 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16293 * depending on whether the callback name was passed:
16296 boolean scriptTag = false;
16297 String cb = request.getParameter("callback");
16300 response.setContentType("text/javascript");
16302 response.setContentType("application/x-json");
16304 Writer out = response.getWriter();
16306 out.write(cb + "(");
16308 out.print(dataBlock.toJsonString());
16315 * @param {Object} config A configuration object.
16317 Roo.data.ScriptTagProxy = function(config){
16318 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16319 Roo.apply(this, config);
16320 this.head = document.getElementsByTagName("head")[0];
16323 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16325 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16327 * @cfg {String} url The URL from which to request the data object.
16330 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16334 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16335 * the server the name of the callback function set up by the load call to process the returned data object.
16336 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16337 * javascript output which calls this named function passing the data object as its only parameter.
16339 callbackParam : "callback",
16341 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16342 * name to the request.
16347 * Load data from the configured URL, read the data object into
16348 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16349 * process that block using the passed callback.
16350 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16351 * for the request to the remote server.
16352 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16353 * object into a block of Roo.data.Records.
16354 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16355 * The function must be passed <ul>
16356 * <li>The Record block object</li>
16357 * <li>The "arg" argument from the load function</li>
16358 * <li>A boolean success indicator</li>
16360 * @param {Object} scope The scope in which to call the callback
16361 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16363 load : function(params, reader, callback, scope, arg){
16364 if(this.fireEvent("beforeload", this, params) !== false){
16366 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16368 var url = this.url;
16369 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16371 url += "&_dc=" + (new Date().getTime());
16373 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16376 cb : "stcCallback"+transId,
16377 scriptId : "stcScript"+transId,
16381 callback : callback,
16387 window[trans.cb] = function(o){
16388 conn.handleResponse(o, trans);
16391 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16393 if(this.autoAbort !== false){
16397 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16399 var script = document.createElement("script");
16400 script.setAttribute("src", url);
16401 script.setAttribute("type", "text/javascript");
16402 script.setAttribute("id", trans.scriptId);
16403 this.head.appendChild(script);
16405 this.trans = trans;
16407 callback.call(scope||this, null, arg, false);
16412 isLoading : function(){
16413 return this.trans ? true : false;
16417 * Abort the current server request.
16419 abort : function(){
16420 if(this.isLoading()){
16421 this.destroyTrans(this.trans);
16426 destroyTrans : function(trans, isLoaded){
16427 this.head.removeChild(document.getElementById(trans.scriptId));
16428 clearTimeout(trans.timeoutId);
16430 window[trans.cb] = undefined;
16432 delete window[trans.cb];
16435 // if hasn't been loaded, wait for load to remove it to prevent script error
16436 window[trans.cb] = function(){
16437 window[trans.cb] = undefined;
16439 delete window[trans.cb];
16446 handleResponse : function(o, trans){
16447 this.trans = false;
16448 this.destroyTrans(trans, true);
16451 result = trans.reader.readRecords(o);
16453 this.fireEvent("loadexception", this, o, trans.arg, e);
16454 trans.callback.call(trans.scope||window, null, trans.arg, false);
16457 this.fireEvent("load", this, o, trans.arg);
16458 trans.callback.call(trans.scope||window, result, trans.arg, true);
16462 handleFailure : function(trans){
16463 this.trans = false;
16464 this.destroyTrans(trans, false);
16465 this.fireEvent("loadexception", this, null, trans.arg);
16466 trans.callback.call(trans.scope||window, null, trans.arg, false);
16470 * Ext JS Library 1.1.1
16471 * Copyright(c) 2006-2007, Ext JS, LLC.
16473 * Originally Released Under LGPL - original licence link has changed is not relivant.
16476 * <script type="text/javascript">
16480 * @class Roo.data.JsonReader
16481 * @extends Roo.data.DataReader
16482 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16483 * based on mappings in a provided Roo.data.Record constructor.
16485 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16486 * in the reply previously.
16491 var RecordDef = Roo.data.Record.create([
16492 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16493 {name: 'occupation'} // This field will use "occupation" as the mapping.
16495 var myReader = new Roo.data.JsonReader({
16496 totalProperty: "results", // The property which contains the total dataset size (optional)
16497 root: "rows", // The property which contains an Array of row objects
16498 id: "id" // The property within each row object that provides an ID for the record (optional)
16502 * This would consume a JSON file like this:
16504 { 'results': 2, 'rows': [
16505 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16506 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16509 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16510 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16511 * paged from the remote server.
16512 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16513 * @cfg {String} root name of the property which contains the Array of row objects.
16514 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16515 * @cfg {Array} fields Array of field definition objects
16517 * Create a new JsonReader
16518 * @param {Object} meta Metadata configuration options
16519 * @param {Object} recordType Either an Array of field definition objects,
16520 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16522 Roo.data.JsonReader = function(meta, recordType){
16525 // set some defaults:
16526 Roo.applyIf(meta, {
16527 totalProperty: 'total',
16528 successProperty : 'success',
16533 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16535 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16537 readerType : 'Json',
16540 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16541 * Used by Store query builder to append _requestMeta to params.
16544 metaFromRemote : false,
16546 * This method is only used by a DataProxy which has retrieved data from a remote server.
16547 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16548 * @return {Object} data A data block which is used by an Roo.data.Store object as
16549 * a cache of Roo.data.Records.
16551 read : function(response){
16552 var json = response.responseText;
16554 var o = /* eval:var:o */ eval("("+json+")");
16556 throw {message: "JsonReader.read: Json object not found"};
16562 this.metaFromRemote = true;
16563 this.meta = o.metaData;
16564 this.recordType = Roo.data.Record.create(o.metaData.fields);
16565 this.onMetaChange(this.meta, this.recordType, o);
16567 return this.readRecords(o);
16570 // private function a store will implement
16571 onMetaChange : function(meta, recordType, o){
16578 simpleAccess: function(obj, subsc) {
16585 getJsonAccessor: function(){
16587 return function(expr) {
16589 return(re.test(expr))
16590 ? new Function("obj", "return obj." + expr)
16595 return Roo.emptyFn;
16600 * Create a data block containing Roo.data.Records from an XML document.
16601 * @param {Object} o An object which contains an Array of row objects in the property specified
16602 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16603 * which contains the total size of the dataset.
16604 * @return {Object} data A data block which is used by an Roo.data.Store object as
16605 * a cache of Roo.data.Records.
16607 readRecords : function(o){
16609 * After any data loads, the raw JSON data is available for further custom processing.
16613 var s = this.meta, Record = this.recordType,
16614 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16616 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16618 if(s.totalProperty) {
16619 this.getTotal = this.getJsonAccessor(s.totalProperty);
16621 if(s.successProperty) {
16622 this.getSuccess = this.getJsonAccessor(s.successProperty);
16624 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16626 var g = this.getJsonAccessor(s.id);
16627 this.getId = function(rec) {
16629 return (r === undefined || r === "") ? null : r;
16632 this.getId = function(){return null;};
16635 for(var jj = 0; jj < fl; jj++){
16637 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16638 this.ef[jj] = this.getJsonAccessor(map);
16642 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16643 if(s.totalProperty){
16644 var vt = parseInt(this.getTotal(o), 10);
16649 if(s.successProperty){
16650 var vs = this.getSuccess(o);
16651 if(vs === false || vs === 'false'){
16656 for(var i = 0; i < c; i++){
16659 var id = this.getId(n);
16660 for(var j = 0; j < fl; j++){
16662 var v = this.ef[j](n);
16664 Roo.log('missing convert for ' + f.name);
16668 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16670 var record = new Record(values, id);
16672 records[i] = record;
16678 totalRecords : totalRecords
16681 // used when loading children.. @see loadDataFromChildren
16682 toLoadData: function(rec)
16684 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16685 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16686 return { data : data, total : data.length };
16691 * Ext JS Library 1.1.1
16692 * Copyright(c) 2006-2007, Ext JS, LLC.
16694 * Originally Released Under LGPL - original licence link has changed is not relivant.
16697 * <script type="text/javascript">
16701 * @class Roo.data.ArrayReader
16702 * @extends Roo.data.DataReader
16703 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16704 * Each element of that Array represents a row of data fields. The
16705 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16706 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16710 var RecordDef = Roo.data.Record.create([
16711 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16712 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16714 var myReader = new Roo.data.ArrayReader({
16715 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16719 * This would consume an Array like this:
16721 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16725 * Create a new JsonReader
16726 * @param {Object} meta Metadata configuration options.
16727 * @param {Object|Array} recordType Either an Array of field definition objects
16729 * @cfg {Array} fields Array of field definition objects
16730 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16731 * as specified to {@link Roo.data.Record#create},
16732 * or an {@link Roo.data.Record} object
16735 * created using {@link Roo.data.Record#create}.
16737 Roo.data.ArrayReader = function(meta, recordType)
16739 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16742 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16745 * Create a data block containing Roo.data.Records from an XML document.
16746 * @param {Object} o An Array of row objects which represents the dataset.
16747 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16748 * a cache of Roo.data.Records.
16750 readRecords : function(o)
16752 var sid = this.meta ? this.meta.id : null;
16753 var recordType = this.recordType, fields = recordType.prototype.fields;
16756 for(var i = 0; i < root.length; i++){
16759 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16760 for(var j = 0, jlen = fields.length; j < jlen; j++){
16761 var f = fields.items[j];
16762 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16763 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16765 values[f.name] = v;
16767 var record = new recordType(values, id);
16769 records[records.length] = record;
16773 totalRecords : records.length
16776 // used when loading children.. @see loadDataFromChildren
16777 toLoadData: function(rec)
16779 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16780 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16791 * @class Roo.bootstrap.form.ComboBox
16792 * @extends Roo.bootstrap.form.TriggerField
16793 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16794 * @cfg {Boolean} append (true|false) default false
16795 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16796 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16797 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16798 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16799 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16800 * @cfg {Boolean} animate default true
16801 * @cfg {Boolean} emptyResultText only for touch device
16802 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16803 * @cfg {String} emptyTitle default ''
16804 * @cfg {Number} width fixed with? experimental
16806 * Create a new ComboBox.
16807 * @param {Object} config Configuration options
16809 Roo.bootstrap.form.ComboBox = function(config){
16810 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16814 * Fires when the dropdown list is expanded
16815 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16820 * Fires when the dropdown list is collapsed
16821 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16825 * @event beforeselect
16826 * Fires before a list item is selected. Return false to cancel the selection.
16827 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16828 * @param {Roo.data.Record} record The data record returned from the underlying store
16829 * @param {Number} index The index of the selected item in the dropdown list
16831 'beforeselect' : true,
16834 * Fires when a list item is selected
16835 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16836 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16837 * @param {Number} index The index of the selected item in the dropdown list
16841 * @event beforequery
16842 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16843 * The event object passed has these properties:
16844 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16845 * @param {String} query The query
16846 * @param {Boolean} forceAll true to force "all" query
16847 * @param {Boolean} cancel true to cancel the query
16848 * @param {Object} e The query event object
16850 'beforequery': true,
16853 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16854 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16860 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16861 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16866 * Fires when the remove value from the combobox array
16867 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871 * @event afterremove
16872 * Fires when the remove value from the combobox array
16873 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16875 'afterremove' : true,
16877 * @event specialfilter
16878 * Fires when specialfilter
16879 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881 'specialfilter' : true,
16884 * Fires when tick the element
16885 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16889 * @event touchviewdisplay
16890 * Fires when touch view require special display (default is using displayField)
16891 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892 * @param {Object} cfg set html .
16894 'touchviewdisplay' : true
16899 this.tickItems = [];
16901 this.selectedIndex = -1;
16902 if(this.mode == 'local'){
16903 if(config.queryDelay === undefined){
16904 this.queryDelay = 10;
16906 if(config.minChars === undefined){
16912 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16915 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16916 * rendering into an Roo.Editor, defaults to false)
16919 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16920 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16923 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16926 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16927 * the dropdown list (defaults to undefined, with no header element)
16931 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16935 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16937 listWidth: undefined,
16939 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16940 * mode = 'remote' or 'text' if mode = 'local')
16942 displayField: undefined,
16945 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16946 * mode = 'remote' or 'value' if mode = 'local').
16947 * Note: use of a valueField requires the user make a selection
16948 * in order for a value to be mapped.
16950 valueField: undefined,
16952 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16957 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16958 * field's data value (defaults to the underlying DOM element's name)
16960 hiddenName: undefined,
16962 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16966 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16968 selectedClass: 'active',
16971 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16975 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16976 * anchor positions (defaults to 'tl-bl')
16978 listAlign: 'tl-bl?',
16980 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16984 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16985 * query specified by the allQuery config option (defaults to 'query')
16987 triggerAction: 'query',
16989 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16990 * (defaults to 4, does not apply if editable = false)
16994 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16995 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16999 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17000 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17004 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17005 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17009 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17010 * when editable = true (defaults to false)
17012 selectOnFocus:false,
17014 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17016 queryParam: 'query',
17018 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17019 * when mode = 'remote' (defaults to 'Loading...')
17021 loadingText: 'Loading...',
17023 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17027 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17031 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17032 * traditional select (defaults to true)
17036 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17040 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17044 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17045 * listWidth has a higher value)
17049 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17050 * allow the user to set arbitrary text into the field (defaults to false)
17052 forceSelection:false,
17054 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17055 * if typeAhead = true (defaults to 250)
17057 typeAheadDelay : 250,
17059 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17060 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17062 valueNotFoundText : undefined,
17064 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17066 blockFocus : false,
17069 * @cfg {Boolean} disableClear Disable showing of clear button.
17071 disableClear : false,
17073 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17075 alwaysQuery : false,
17078 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17083 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17085 invalidClass : "has-warning",
17088 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17090 validClass : "has-success",
17093 * @cfg {Boolean} specialFilter (true|false) special filter default false
17095 specialFilter : false,
17098 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17100 mobileTouchView : true,
17103 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17105 useNativeIOS : false,
17108 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17110 mobile_restrict_height : false,
17112 ios_options : false,
17124 btnPosition : 'right',
17125 triggerList : true,
17126 showToggleBtn : true,
17128 emptyResultText: 'Empty',
17129 triggerText : 'Select',
17133 // element that contains real text value.. (when hidden is used..)
17135 getAutoCreate : function()
17140 * Render classic select for iso
17143 if(Roo.isIOS && this.useNativeIOS){
17144 cfg = this.getAutoCreateNativeIOS();
17152 if(Roo.isTouch && this.mobileTouchView){
17153 cfg = this.getAutoCreateTouchView();
17160 if(!this.tickable){
17161 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17166 * ComboBox with tickable selections
17169 var align = this.labelAlign || this.parentLabelAlign();
17172 cls : 'form-group roo-combobox-tickable' //input-group
17175 var btn_text_select = '';
17176 var btn_text_done = '';
17177 var btn_text_cancel = '';
17179 if (this.btn_text_show) {
17180 btn_text_select = 'Select';
17181 btn_text_done = 'Done';
17182 btn_text_cancel = 'Cancel';
17187 cls : 'tickable-buttons',
17192 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17193 //html : this.triggerText
17194 html: btn_text_select
17200 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17202 html: btn_text_done
17208 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17210 html: btn_text_cancel
17216 buttons.cn.unshift({
17218 cls: 'roo-select2-search-field-input'
17224 Roo.each(buttons.cn, function(c){
17226 c.cls += ' btn-' + _this.size;
17229 if (_this.disabled) {
17236 style : 'display: contents',
17241 cls: 'form-hidden-field'
17245 cls: 'roo-select2-choices',
17249 cls: 'roo-select2-search-field',
17260 cls: 'roo-select2-container input-group roo-select2-container-multi',
17266 // cls: 'typeahead typeahead-long dropdown-menu',
17267 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17272 if(this.hasFeedback && !this.allowBlank){
17276 cls: 'glyphicon form-control-feedback'
17279 combobox.cn.push(feedback);
17286 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17287 tooltip : 'This field is required'
17289 if (Roo.bootstrap.version == 4) {
17292 style : 'display:none'
17295 if (align ==='left' && this.fieldLabel.length) {
17297 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17304 cls : 'control-label col-form-label',
17305 html : this.fieldLabel
17317 var labelCfg = cfg.cn[1];
17318 var contentCfg = cfg.cn[2];
17321 if(this.indicatorpos == 'right'){
17327 cls : 'control-label col-form-label',
17331 html : this.fieldLabel
17347 labelCfg = cfg.cn[0];
17348 contentCfg = cfg.cn[1];
17352 if(this.labelWidth > 12){
17353 labelCfg.style = "width: " + this.labelWidth + 'px';
17355 if(this.width * 1 > 0){
17356 contentCfg.style = "width: " + this.width + 'px';
17358 if(this.labelWidth < 13 && this.labelmd == 0){
17359 this.labelmd = this.labelWidth;
17362 if(this.labellg > 0){
17363 labelCfg.cls += ' col-lg-' + this.labellg;
17364 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17367 if(this.labelmd > 0){
17368 labelCfg.cls += ' col-md-' + this.labelmd;
17369 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17372 if(this.labelsm > 0){
17373 labelCfg.cls += ' col-sm-' + this.labelsm;
17374 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17377 if(this.labelxs > 0){
17378 labelCfg.cls += ' col-xs-' + this.labelxs;
17379 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17383 } else if ( this.fieldLabel.length) {
17384 // Roo.log(" label");
17389 //cls : 'input-group-addon',
17390 html : this.fieldLabel
17395 if(this.indicatorpos == 'right'){
17399 //cls : 'input-group-addon',
17400 html : this.fieldLabel
17410 // Roo.log(" no label && no align");
17417 ['xs','sm','md','lg'].map(function(size){
17418 if (settings[size]) {
17419 cfg.cls += ' col-' + size + '-' + settings[size];
17427 _initEventsCalled : false,
17430 initEvents: function()
17432 if (this._initEventsCalled) { // as we call render... prevent looping...
17435 this._initEventsCalled = true;
17438 throw "can not find store for combo";
17441 this.indicator = this.indicatorEl();
17443 this.store = Roo.factory(this.store, Roo.data);
17444 this.store.parent = this;
17446 // if we are building from html. then this element is so complex, that we can not really
17447 // use the rendered HTML.
17448 // so we have to trash and replace the previous code.
17449 if (Roo.XComponent.build_from_html) {
17450 // remove this element....
17451 var e = this.el.dom, k=0;
17452 while (e ) { e = e.previousSibling; ++k;}
17457 this.rendered = false;
17459 this.render(this.parent().getChildContainer(true), k);
17462 if(Roo.isIOS && this.useNativeIOS){
17463 this.initIOSView();
17471 if(Roo.isTouch && this.mobileTouchView){
17472 this.initTouchView();
17477 this.initTickableEvents();
17481 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17483 if(this.hiddenName){
17485 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17487 this.hiddenField.dom.value =
17488 this.hiddenValue !== undefined ? this.hiddenValue :
17489 this.value !== undefined ? this.value : '';
17491 // prevent input submission
17492 this.el.dom.removeAttribute('name');
17493 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17498 // this.el.dom.setAttribute('autocomplete', 'off');
17501 var cls = 'x-combo-list';
17503 //this.list = new Roo.Layer({
17504 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17510 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17511 _this.list.setWidth(lw);
17514 this.list.on('mouseover', this.onViewOver, this);
17515 this.list.on('mousemove', this.onViewMove, this);
17516 this.list.on('scroll', this.onViewScroll, this);
17519 this.list.swallowEvent('mousewheel');
17520 this.assetHeight = 0;
17523 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17524 this.assetHeight += this.header.getHeight();
17527 this.innerList = this.list.createChild({cls:cls+'-inner'});
17528 this.innerList.on('mouseover', this.onViewOver, this);
17529 this.innerList.on('mousemove', this.onViewMove, this);
17530 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17532 if(this.allowBlank && !this.pageSize && !this.disableClear){
17533 this.footer = this.list.createChild({cls:cls+'-ft'});
17534 this.pageTb = new Roo.Toolbar(this.footer);
17538 this.footer = this.list.createChild({cls:cls+'-ft'});
17539 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17540 {pageSize: this.pageSize});
17544 if (this.pageTb && this.allowBlank && !this.disableClear) {
17546 this.pageTb.add(new Roo.Toolbar.Fill(), {
17547 cls: 'x-btn-icon x-btn-clear',
17549 handler: function()
17552 _this.clearValue();
17553 _this.onSelect(false, -1);
17558 this.assetHeight += this.footer.getHeight();
17563 this.tpl = Roo.bootstrap.version == 4 ?
17564 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17565 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17568 this.view = new Roo.View(this.list, this.tpl, {
17569 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17571 //this.view.wrapEl.setDisplayed(false);
17572 this.view.on('click', this.onViewClick, this);
17575 this.store.on('beforeload', this.onBeforeLoad, this);
17576 this.store.on('load', this.onLoad, this);
17577 this.store.on('loadexception', this.onLoadException, this);
17579 if(this.resizable){
17580 this.resizer = new Roo.Resizable(this.list, {
17581 pinned:true, handles:'se'
17583 this.resizer.on('resize', function(r, w, h){
17584 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17585 this.listWidth = w;
17586 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17587 this.restrictHeight();
17589 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17592 if(!this.editable){
17593 this.editable = true;
17594 this.setEditable(false);
17599 if (typeof(this.events.add.listeners) != 'undefined') {
17601 this.addicon = this.wrap.createChild(
17602 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17604 this.addicon.on('click', function(e) {
17605 this.fireEvent('add', this);
17608 if (typeof(this.events.edit.listeners) != 'undefined') {
17610 this.editicon = this.wrap.createChild(
17611 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17612 if (this.addicon) {
17613 this.editicon.setStyle('margin-left', '40px');
17615 this.editicon.on('click', function(e) {
17617 // we fire even if inothing is selected..
17618 this.fireEvent('edit', this, this.lastData );
17624 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17625 "up" : function(e){
17626 this.inKeyMode = true;
17630 "down" : function(e){
17631 if(!this.isExpanded()){
17632 this.onTriggerClick();
17634 this.inKeyMode = true;
17639 "enter" : function(e){
17640 // this.onViewClick();
17644 if(this.fireEvent("specialkey", this, e)){
17645 this.onViewClick(false);
17651 "esc" : function(e){
17655 "tab" : function(e){
17658 if(this.fireEvent("specialkey", this, e)){
17659 this.onViewClick(false);
17667 doRelay : function(foo, bar, hname){
17668 if(hname == 'down' || this.scope.isExpanded()){
17669 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17678 this.queryDelay = Math.max(this.queryDelay || 10,
17679 this.mode == 'local' ? 10 : 250);
17682 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17684 if(this.typeAhead){
17685 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17687 if(this.editable !== false){
17688 this.inputEl().on("keyup", this.onKeyUp, this);
17690 if(this.forceSelection){
17691 this.inputEl().on('blur', this.doForce, this);
17695 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17696 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17700 initTickableEvents: function()
17704 if(this.hiddenName){
17706 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17708 this.hiddenField.dom.value =
17709 this.hiddenValue !== undefined ? this.hiddenValue :
17710 this.value !== undefined ? this.value : '';
17712 // prevent input submission
17713 this.el.dom.removeAttribute('name');
17714 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17719 // this.list = this.el.select('ul.dropdown-menu',true).first();
17721 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17722 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17723 if(this.triggerList){
17724 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17727 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17728 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17730 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17731 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17733 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17734 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17736 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17737 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17738 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17741 this.cancelBtn.hide();
17746 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17747 _this.list.setWidth(lw);
17750 this.list.on('mouseover', this.onViewOver, this);
17751 this.list.on('mousemove', this.onViewMove, this);
17753 this.list.on('scroll', this.onViewScroll, this);
17756 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17757 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17760 this.view = new Roo.View(this.list, this.tpl, {
17765 selectedClass: this.selectedClass
17768 //this.view.wrapEl.setDisplayed(false);
17769 this.view.on('click', this.onViewClick, this);
17773 this.store.on('beforeload', this.onBeforeLoad, this);
17774 this.store.on('load', this.onLoad, this);
17775 this.store.on('loadexception', this.onLoadException, this);
17778 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17779 "up" : function(e){
17780 this.inKeyMode = true;
17784 "down" : function(e){
17785 this.inKeyMode = true;
17789 "enter" : function(e){
17790 if(this.fireEvent("specialkey", this, e)){
17791 this.onViewClick(false);
17797 "esc" : function(e){
17798 this.onTickableFooterButtonClick(e, false, false);
17801 "tab" : function(e){
17802 this.fireEvent("specialkey", this, e);
17804 this.onTickableFooterButtonClick(e, false, false);
17811 doRelay : function(e, fn, key){
17812 if(this.scope.isExpanded()){
17813 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17822 this.queryDelay = Math.max(this.queryDelay || 10,
17823 this.mode == 'local' ? 10 : 250);
17826 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17828 if(this.typeAhead){
17829 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17832 if(this.editable !== false){
17833 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17836 this.indicator = this.indicatorEl();
17838 if(this.indicator){
17839 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17840 this.indicator.hide();
17845 onDestroy : function(){
17847 this.view.setStore(null);
17848 this.view.el.removeAllListeners();
17849 this.view.el.remove();
17850 this.view.purgeListeners();
17853 this.list.dom.innerHTML = '';
17857 this.store.un('beforeload', this.onBeforeLoad, this);
17858 this.store.un('load', this.onLoad, this);
17859 this.store.un('loadexception', this.onLoadException, this);
17861 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17865 fireKey : function(e){
17866 if(e.isNavKeyPress() && !this.list.isVisible()){
17867 this.fireEvent("specialkey", this, e);
17872 onResize: function(w, h)
17876 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17878 // if(typeof w != 'number'){
17879 // // we do not handle it!?!?
17882 // var tw = this.trigger.getWidth();
17883 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17884 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17886 // this.inputEl().setWidth( this.adjustWidth('input', x));
17888 // //this.trigger.setStyle('left', x+'px');
17890 // if(this.list && this.listWidth === undefined){
17891 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17892 // this.list.setWidth(lw);
17893 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17901 * Allow or prevent the user from directly editing the field text. If false is passed,
17902 * the user will only be able to select from the items defined in the dropdown list. This method
17903 * is the runtime equivalent of setting the 'editable' config option at config time.
17904 * @param {Boolean} value True to allow the user to directly edit the field text
17906 setEditable : function(value){
17907 if(value == this.editable){
17910 this.editable = value;
17912 this.inputEl().dom.setAttribute('readOnly', true);
17913 this.inputEl().on('mousedown', this.onTriggerClick, this);
17914 this.inputEl().addClass('x-combo-noedit');
17916 this.inputEl().dom.removeAttribute('readOnly');
17917 this.inputEl().un('mousedown', this.onTriggerClick, this);
17918 this.inputEl().removeClass('x-combo-noedit');
17924 onBeforeLoad : function(combo,opts){
17925 if(!this.hasFocus){
17929 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17931 this.restrictHeight();
17932 this.selectedIndex = -1;
17936 onLoad : function(){
17938 this.hasQuery = false;
17940 if(!this.hasFocus){
17944 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17945 this.loading.hide();
17948 if(this.store.getCount() > 0){
17951 this.restrictHeight();
17952 if(this.lastQuery == this.allQuery){
17953 if(this.editable && !this.tickable){
17954 this.inputEl().dom.select();
17958 !this.selectByValue(this.value, true) &&
17961 !this.store.lastOptions ||
17962 typeof(this.store.lastOptions.add) == 'undefined' ||
17963 this.store.lastOptions.add != true
17966 this.select(0, true);
17969 if(this.autoFocus){
17972 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17973 this.taTask.delay(this.typeAheadDelay);
17977 this.onEmptyResults();
17983 onLoadException : function()
17985 this.hasQuery = false;
17987 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17988 this.loading.hide();
17991 if(this.tickable && this.editable){
17996 // only causes errors at present
17997 //Roo.log(this.store.reader.jsonData);
17998 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18000 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18006 onTypeAhead : function(){
18007 if(this.store.getCount() > 0){
18008 var r = this.store.getAt(0);
18009 var newValue = r.data[this.displayField];
18010 var len = newValue.length;
18011 var selStart = this.getRawValue().length;
18013 if(selStart != len){
18014 this.setRawValue(newValue);
18015 this.selectText(selStart, newValue.length);
18021 onSelect : function(record, index){
18023 if(this.fireEvent('beforeselect', this, record, index) !== false){
18025 this.setFromData(index > -1 ? record.data : false);
18028 this.fireEvent('select', this, record, index);
18033 * Returns the currently selected field value or empty string if no value is set.
18034 * @return {String} value The selected value
18036 getValue : function()
18038 if(Roo.isIOS && this.useNativeIOS){
18039 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18043 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18046 if(this.valueField){
18047 return typeof this.value != 'undefined' ? this.value : '';
18049 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18053 getRawValue : function()
18055 if(Roo.isIOS && this.useNativeIOS){
18056 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18059 var v = this.inputEl().getValue();
18065 * Clears any text/value currently set in the field
18067 clearValue : function(){
18069 if(this.hiddenField){
18070 this.hiddenField.dom.value = '';
18073 this.setRawValue('');
18074 this.lastSelectionText = '';
18075 this.lastData = false;
18077 var close = this.closeTriggerEl();
18088 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18089 * will be displayed in the field. If the value does not match the data value of an existing item,
18090 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18091 * Otherwise the field will be blank (although the value will still be set).
18092 * @param {String} value The value to match
18094 setValue : function(v)
18096 if(Roo.isIOS && this.useNativeIOS){
18097 this.setIOSValue(v);
18107 if(this.valueField){
18108 var r = this.findRecord(this.valueField, v);
18110 text = r.data[this.displayField];
18111 }else if(this.valueNotFoundText !== undefined){
18112 text = this.valueNotFoundText;
18115 this.lastSelectionText = text;
18116 if(this.hiddenField){
18117 this.hiddenField.dom.value = v;
18119 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18122 var close = this.closeTriggerEl();
18125 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18131 * @property {Object} the last set data for the element
18136 * Sets the value of the field based on a object which is related to the record format for the store.
18137 * @param {Object} value the value to set as. or false on reset?
18139 setFromData : function(o){
18146 var dv = ''; // display value
18147 var vv = ''; // value value..
18149 if (this.displayField) {
18150 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18152 // this is an error condition!!!
18153 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18156 if(this.valueField){
18157 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18160 var close = this.closeTriggerEl();
18163 if(dv.length || vv * 1 > 0){
18165 this.blockFocus=true;
18171 if(this.hiddenField){
18172 this.hiddenField.dom.value = vv;
18174 this.lastSelectionText = dv;
18175 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18179 // no hidden field.. - we store the value in 'value', but still display
18180 // display field!!!!
18181 this.lastSelectionText = dv;
18182 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18189 reset : function(){
18190 // overridden so that last data is reset..
18197 this.setValue(this.originalValue);
18198 //this.clearInvalid();
18199 this.lastData = false;
18201 this.view.clearSelections();
18207 findRecord : function(prop, value){
18209 if(this.store.getCount() > 0){
18210 this.store.each(function(r){
18211 if(r.data[prop] == value){
18221 getName: function()
18223 // returns hidden if it's set..
18224 if (!this.rendered) {return ''};
18225 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18229 onViewMove : function(e, t){
18230 this.inKeyMode = false;
18234 onViewOver : function(e, t){
18235 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18238 var item = this.view.findItemFromChild(t);
18241 var index = this.view.indexOf(item);
18242 this.select(index, false);
18247 onViewClick : function(view, doFocus, el, e)
18249 var index = this.view.getSelectedIndexes()[0];
18251 var r = this.store.getAt(index);
18255 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18262 Roo.each(this.tickItems, function(v,k){
18264 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18266 _this.tickItems.splice(k, 1);
18268 if(typeof(e) == 'undefined' && view == false){
18269 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18281 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18282 this.tickItems.push(r.data);
18285 if(typeof(e) == 'undefined' && view == false){
18286 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18293 this.onSelect(r, index);
18295 if(doFocus !== false && !this.blockFocus){
18296 this.inputEl().focus();
18301 restrictHeight : function(){
18302 //this.innerList.dom.style.height = '';
18303 //var inner = this.innerList.dom;
18304 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18305 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18306 //this.list.beginUpdate();
18307 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18308 this.list.alignTo(this.inputEl(), this.listAlign);
18309 this.list.alignTo(this.inputEl(), this.listAlign);
18310 //this.list.endUpdate();
18314 onEmptyResults : function(){
18316 if(this.tickable && this.editable){
18317 this.hasFocus = false;
18318 this.restrictHeight();
18326 * Returns true if the dropdown list is expanded, else false.
18328 isExpanded : function(){
18329 return this.list.isVisible();
18333 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18334 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18335 * @param {String} value The data value of the item to select
18336 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18337 * selected item if it is not currently in view (defaults to true)
18338 * @return {Boolean} True if the value matched an item in the list, else false
18340 selectByValue : function(v, scrollIntoView){
18341 if(v !== undefined && v !== null){
18342 var r = this.findRecord(this.valueField || this.displayField, v);
18344 this.select(this.store.indexOf(r), scrollIntoView);
18352 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18353 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18354 * @param {Number} index The zero-based index of the list item to select
18355 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18356 * selected item if it is not currently in view (defaults to true)
18358 select : function(index, scrollIntoView){
18359 this.selectedIndex = index;
18360 this.view.select(index);
18361 if(scrollIntoView !== false){
18362 var el = this.view.getNode(index);
18364 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18367 this.list.scrollChildIntoView(el, false);
18373 selectNext : function(){
18374 var ct = this.store.getCount();
18376 if(this.selectedIndex == -1){
18378 }else if(this.selectedIndex < ct-1){
18379 this.select(this.selectedIndex+1);
18385 selectPrev : function(){
18386 var ct = this.store.getCount();
18388 if(this.selectedIndex == -1){
18390 }else if(this.selectedIndex != 0){
18391 this.select(this.selectedIndex-1);
18397 onKeyUp : function(e){
18398 if(this.editable !== false && !e.isSpecialKey()){
18399 this.lastKey = e.getKey();
18400 this.dqTask.delay(this.queryDelay);
18405 validateBlur : function(){
18406 return !this.list || !this.list.isVisible();
18410 initQuery : function(){
18412 var v = this.getRawValue();
18414 if(this.tickable && this.editable){
18415 v = this.tickableInputEl().getValue();
18422 doForce : function(){
18423 if(this.inputEl().dom.value.length > 0){
18424 this.inputEl().dom.value =
18425 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18431 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18432 * query allowing the query action to be canceled if needed.
18433 * @param {String} query The SQL query to execute
18434 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18435 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18436 * saved in the current store (defaults to false)
18438 doQuery : function(q, forceAll){
18440 if(q === undefined || q === null){
18445 forceAll: forceAll,
18449 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18454 forceAll = qe.forceAll;
18455 if(forceAll === true || (q.length >= this.minChars)){
18457 this.hasQuery = true;
18459 if(this.lastQuery != q || this.alwaysQuery){
18460 this.lastQuery = q;
18461 if(this.mode == 'local'){
18462 this.selectedIndex = -1;
18464 this.store.clearFilter();
18467 if(this.specialFilter){
18468 this.fireEvent('specialfilter', this);
18473 this.store.filter(this.displayField, q);
18476 this.store.fireEvent("datachanged", this.store);
18483 this.store.baseParams[this.queryParam] = q;
18485 var options = {params : this.getParams(q)};
18488 options.add = true;
18489 options.params.start = this.page * this.pageSize;
18492 this.store.load(options);
18495 * this code will make the page width larger, at the beginning, the list not align correctly,
18496 * we should expand the list on onLoad
18497 * so command out it
18502 this.selectedIndex = -1;
18507 this.loadNext = false;
18511 getParams : function(q){
18513 //p[this.queryParam] = q;
18517 p.limit = this.pageSize;
18523 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18525 collapse : function(){
18526 if(!this.isExpanded()){
18532 this.hasFocus = false;
18536 this.cancelBtn.hide();
18537 this.trigger.show();
18540 this.tickableInputEl().dom.value = '';
18541 this.tickableInputEl().blur();
18546 Roo.get(document).un('mousedown', this.collapseIf, this);
18547 Roo.get(document).un('mousewheel', this.collapseIf, this);
18548 if (!this.editable) {
18549 Roo.get(document).un('keydown', this.listKeyPress, this);
18551 this.fireEvent('collapse', this);
18557 collapseIf : function(e){
18558 var in_combo = e.within(this.el);
18559 var in_list = e.within(this.list);
18560 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18562 if (in_combo || in_list || is_list) {
18563 //e.stopPropagation();
18568 this.onTickableFooterButtonClick(e, false, false);
18576 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18578 expand : function(){
18580 if(this.isExpanded() || !this.hasFocus){
18584 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18585 this.list.setWidth(lw);
18591 this.restrictHeight();
18595 this.tickItems = Roo.apply([], this.item);
18598 this.cancelBtn.show();
18599 this.trigger.hide();
18602 this.tickableInputEl().focus();
18607 Roo.get(document).on('mousedown', this.collapseIf, this);
18608 Roo.get(document).on('mousewheel', this.collapseIf, this);
18609 if (!this.editable) {
18610 Roo.get(document).on('keydown', this.listKeyPress, this);
18613 this.fireEvent('expand', this);
18617 // Implements the default empty TriggerField.onTriggerClick function
18618 onTriggerClick : function(e)
18620 Roo.log('trigger click');
18622 if(this.disabled || !this.triggerList){
18627 this.loadNext = false;
18629 if(this.isExpanded()){
18631 if (!this.blockFocus) {
18632 this.inputEl().focus();
18636 this.hasFocus = true;
18637 if(this.triggerAction == 'all') {
18638 this.doQuery(this.allQuery, true);
18640 this.doQuery(this.getRawValue());
18642 if (!this.blockFocus) {
18643 this.inputEl().focus();
18648 onTickableTriggerClick : function(e)
18655 this.loadNext = false;
18656 this.hasFocus = true;
18658 if(this.triggerAction == 'all') {
18659 this.doQuery(this.allQuery, true);
18661 this.doQuery(this.getRawValue());
18665 onSearchFieldClick : function(e)
18667 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18668 this.onTickableFooterButtonClick(e, false, false);
18672 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18677 this.loadNext = false;
18678 this.hasFocus = true;
18680 if(this.triggerAction == 'all') {
18681 this.doQuery(this.allQuery, true);
18683 this.doQuery(this.getRawValue());
18687 listKeyPress : function(e)
18689 //Roo.log('listkeypress');
18690 // scroll to first matching element based on key pres..
18691 if (e.isSpecialKey()) {
18694 var k = String.fromCharCode(e.getKey()).toUpperCase();
18697 var csel = this.view.getSelectedNodes();
18698 var cselitem = false;
18700 var ix = this.view.indexOf(csel[0]);
18701 cselitem = this.store.getAt(ix);
18702 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18708 this.store.each(function(v) {
18710 // start at existing selection.
18711 if (cselitem.id == v.id) {
18717 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18718 match = this.store.indexOf(v);
18724 if (match === false) {
18725 return true; // no more action?
18728 this.view.select(match);
18729 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18730 sn.scrollIntoView(sn.dom.parentNode, false);
18733 onViewScroll : function(e, t){
18735 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){
18739 this.hasQuery = true;
18741 this.loading = this.list.select('.loading', true).first();
18743 if(this.loading === null){
18744 this.list.createChild({
18746 cls: 'loading roo-select2-more-results roo-select2-active',
18747 html: 'Loading more results...'
18750 this.loading = this.list.select('.loading', true).first();
18752 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18754 this.loading.hide();
18757 this.loading.show();
18762 this.loadNext = true;
18764 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18769 addItem : function(o)
18771 var dv = ''; // display value
18773 if (this.displayField) {
18774 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18776 // this is an error condition!!!
18777 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18784 var choice = this.choices.createChild({
18786 cls: 'roo-select2-search-choice',
18795 cls: 'roo-select2-search-choice-close fa fa-times',
18800 }, this.searchField);
18802 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18804 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18812 this.inputEl().dom.value = '';
18817 onRemoveItem : function(e, _self, o)
18819 e.preventDefault();
18821 this.lastItem = Roo.apply([], this.item);
18823 var index = this.item.indexOf(o.data) * 1;
18826 Roo.log('not this item?!');
18830 this.item.splice(index, 1);
18835 this.fireEvent('remove', this, e);
18841 syncValue : function()
18843 if(!this.item.length){
18850 Roo.each(this.item, function(i){
18851 if(_this.valueField){
18852 value.push(i[_this.valueField]);
18859 this.value = value.join(',');
18861 if(this.hiddenField){
18862 this.hiddenField.dom.value = this.value;
18865 this.store.fireEvent("datachanged", this.store);
18870 clearItem : function()
18872 if(!this.multiple){
18878 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18886 if(this.tickable && !Roo.isTouch){
18887 this.view.refresh();
18891 inputEl: function ()
18893 if(Roo.isIOS && this.useNativeIOS){
18894 return this.el.select('select.roo-ios-select', true).first();
18897 if(Roo.isTouch && this.mobileTouchView){
18898 return this.el.select('input.form-control',true).first();
18902 return this.searchField;
18905 return this.el.select('input.form-control',true).first();
18908 onTickableFooterButtonClick : function(e, btn, el)
18910 e.preventDefault();
18912 this.lastItem = Roo.apply([], this.item);
18914 if(btn && btn.name == 'cancel'){
18915 this.tickItems = Roo.apply([], this.item);
18924 Roo.each(this.tickItems, function(o){
18932 validate : function()
18934 if(this.getVisibilityEl().hasClass('hidden')){
18938 var v = this.getRawValue();
18941 v = this.getValue();
18944 if(this.disabled || this.allowBlank || v.length){
18949 this.markInvalid();
18953 tickableInputEl : function()
18955 if(!this.tickable || !this.editable){
18956 return this.inputEl();
18959 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18963 getAutoCreateTouchView : function()
18968 cls: 'form-group' //input-group
18974 type : this.inputType,
18975 cls : 'form-control x-combo-noedit',
18976 autocomplete: 'new-password',
18977 placeholder : this.placeholder || '',
18982 input.name = this.name;
18986 input.cls += ' input-' + this.size;
18989 if (this.disabled) {
18990 input.disabled = true;
18994 cls : 'roo-combobox-wrap',
19001 inputblock.cls += ' input-group';
19003 inputblock.cn.unshift({
19005 cls : 'input-group-addon input-group-prepend input-group-text',
19010 if(this.removable && !this.multiple){
19011 inputblock.cls += ' roo-removable';
19013 inputblock.cn.push({
19016 cls : 'roo-combo-removable-btn close'
19020 if(this.hasFeedback && !this.allowBlank){
19022 inputblock.cls += ' has-feedback';
19024 inputblock.cn.push({
19026 cls: 'glyphicon form-control-feedback'
19033 inputblock.cls += (this.before) ? '' : ' input-group';
19035 inputblock.cn.push({
19037 cls : 'input-group-addon input-group-append input-group-text',
19043 var ibwrap = inputblock;
19048 cls: 'roo-select2-choices',
19052 cls: 'roo-select2-search-field',
19065 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19070 cls: 'form-hidden-field'
19076 if(!this.multiple && this.showToggleBtn){
19082 if (this.caret != false) {
19085 cls: 'fa fa-' + this.caret
19092 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19094 Roo.bootstrap.version == 3 ? caret : '',
19097 cls: 'combobox-clear',
19111 combobox.cls += ' roo-select2-container-multi';
19114 var required = this.allowBlank ? {
19116 style: 'display: none'
19119 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19120 tooltip : 'This field is required'
19123 var align = this.labelAlign || this.parentLabelAlign();
19125 if (align ==='left' && this.fieldLabel.length) {
19131 cls : 'control-label col-form-label',
19132 html : this.fieldLabel
19136 cls : 'roo-combobox-wrap ',
19143 var labelCfg = cfg.cn[1];
19144 var contentCfg = cfg.cn[2];
19147 if(this.indicatorpos == 'right'){
19152 cls : 'control-label col-form-label',
19156 html : this.fieldLabel
19162 cls : "roo-combobox-wrap ",
19170 labelCfg = cfg.cn[0];
19171 contentCfg = cfg.cn[1];
19176 if(this.labelWidth > 12){
19177 labelCfg.style = "width: " + this.labelWidth + 'px';
19180 if(this.labelWidth < 13 && this.labelmd == 0){
19181 this.labelmd = this.labelWidth;
19184 if(this.labellg > 0){
19185 labelCfg.cls += ' col-lg-' + this.labellg;
19186 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19189 if(this.labelmd > 0){
19190 labelCfg.cls += ' col-md-' + this.labelmd;
19191 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19194 if(this.labelsm > 0){
19195 labelCfg.cls += ' col-sm-' + this.labelsm;
19196 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19199 if(this.labelxs > 0){
19200 labelCfg.cls += ' col-xs-' + this.labelxs;
19201 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19205 } else if ( this.fieldLabel.length) {
19210 cls : 'control-label',
19211 html : this.fieldLabel
19222 if(this.indicatorpos == 'right'){
19226 cls : 'control-label',
19227 html : this.fieldLabel,
19245 var settings = this;
19247 ['xs','sm','md','lg'].map(function(size){
19248 if (settings[size]) {
19249 cfg.cls += ' col-' + size + '-' + settings[size];
19256 initTouchView : function()
19258 this.renderTouchView();
19260 this.touchViewEl.on('scroll', function(){
19261 this.el.dom.scrollTop = 0;
19264 this.originalValue = this.getValue();
19266 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19268 this.inputEl().on("click", this.showTouchView, this);
19269 if (this.triggerEl) {
19270 this.triggerEl.on("click", this.showTouchView, this);
19274 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19275 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19277 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19279 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19280 this.store.on('load', this.onTouchViewLoad, this);
19281 this.store.on('loadexception', this.onTouchViewLoadException, this);
19283 if(this.hiddenName){
19285 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19287 this.hiddenField.dom.value =
19288 this.hiddenValue !== undefined ? this.hiddenValue :
19289 this.value !== undefined ? this.value : '';
19291 this.el.dom.removeAttribute('name');
19292 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19296 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19297 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19300 if(this.removable && !this.multiple){
19301 var close = this.closeTriggerEl();
19303 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19304 close.on('click', this.removeBtnClick, this, close);
19308 * fix the bug in Safari iOS8
19310 this.inputEl().on("focus", function(e){
19311 document.activeElement.blur();
19314 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19321 renderTouchView : function()
19323 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19324 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19326 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19327 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19329 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19330 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19331 this.touchViewBodyEl.setStyle('overflow', 'auto');
19333 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19334 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19336 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19337 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19341 showTouchView : function()
19347 this.touchViewHeaderEl.hide();
19349 if(this.modalTitle.length){
19350 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19351 this.touchViewHeaderEl.show();
19354 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19355 this.touchViewEl.show();
19357 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19359 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19360 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19362 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19364 if(this.modalTitle.length){
19365 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19368 this.touchViewBodyEl.setHeight(bodyHeight);
19372 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19374 this.touchViewEl.addClass(['in','show']);
19377 if(this._touchViewMask){
19378 Roo.get(document.body).addClass("x-body-masked");
19379 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19380 this._touchViewMask.setStyle('z-index', 10000);
19381 this._touchViewMask.addClass('show');
19384 this.doTouchViewQuery();
19388 hideTouchView : function()
19390 this.touchViewEl.removeClass(['in','show']);
19394 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19396 this.touchViewEl.setStyle('display', 'none');
19399 if(this._touchViewMask){
19400 this._touchViewMask.removeClass('show');
19401 Roo.get(document.body).removeClass("x-body-masked");
19405 setTouchViewValue : function()
19412 Roo.each(this.tickItems, function(o){
19417 this.hideTouchView();
19420 doTouchViewQuery : function()
19429 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19433 if(!this.alwaysQuery || this.mode == 'local'){
19434 this.onTouchViewLoad();
19441 onTouchViewBeforeLoad : function(combo,opts)
19447 onTouchViewLoad : function()
19449 if(this.store.getCount() < 1){
19450 this.onTouchViewEmptyResults();
19454 this.clearTouchView();
19456 var rawValue = this.getRawValue();
19458 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19460 this.tickItems = [];
19462 this.store.data.each(function(d, rowIndex){
19463 var row = this.touchViewListGroup.createChild(template);
19465 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19466 row.addClass(d.data.cls);
19469 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19472 html : d.data[this.displayField]
19475 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19476 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19479 row.removeClass('selected');
19480 if(!this.multiple && this.valueField &&
19481 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19484 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19485 row.addClass('selected');
19488 if(this.multiple && this.valueField &&
19489 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19493 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19494 this.tickItems.push(d.data);
19497 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19501 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19503 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19505 if(this.modalTitle.length){
19506 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19509 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19511 if(this.mobile_restrict_height && listHeight < bodyHeight){
19512 this.touchViewBodyEl.setHeight(listHeight);
19517 if(firstChecked && listHeight > bodyHeight){
19518 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19523 onTouchViewLoadException : function()
19525 this.hideTouchView();
19528 onTouchViewEmptyResults : function()
19530 this.clearTouchView();
19532 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19534 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19538 clearTouchView : function()
19540 this.touchViewListGroup.dom.innerHTML = '';
19543 onTouchViewClick : function(e, el, o)
19545 e.preventDefault();
19548 var rowIndex = o.rowIndex;
19550 var r = this.store.getAt(rowIndex);
19552 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19554 if(!this.multiple){
19555 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19556 c.dom.removeAttribute('checked');
19559 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19561 this.setFromData(r.data);
19563 var close = this.closeTriggerEl();
19569 this.hideTouchView();
19571 this.fireEvent('select', this, r, rowIndex);
19576 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19577 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19578 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19582 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19583 this.addItem(r.data);
19584 this.tickItems.push(r.data);
19588 getAutoCreateNativeIOS : function()
19591 cls: 'form-group' //input-group,
19596 cls : 'roo-ios-select'
19600 combobox.name = this.name;
19603 if (this.disabled) {
19604 combobox.disabled = true;
19607 var settings = this;
19609 ['xs','sm','md','lg'].map(function(size){
19610 if (settings[size]) {
19611 cfg.cls += ' col-' + size + '-' + settings[size];
19621 initIOSView : function()
19623 this.store.on('load', this.onIOSViewLoad, this);
19628 onIOSViewLoad : function()
19630 if(this.store.getCount() < 1){
19634 this.clearIOSView();
19636 if(this.allowBlank) {
19638 var default_text = '-- SELECT --';
19640 if(this.placeholder.length){
19641 default_text = this.placeholder;
19644 if(this.emptyTitle.length){
19645 default_text += ' - ' + this.emptyTitle + ' -';
19648 var opt = this.inputEl().createChild({
19651 html : default_text
19655 o[this.valueField] = 0;
19656 o[this.displayField] = default_text;
19658 this.ios_options.push({
19665 this.store.data.each(function(d, rowIndex){
19669 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19670 html = d.data[this.displayField];
19675 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19676 value = d.data[this.valueField];
19685 if(this.value == d.data[this.valueField]){
19686 option['selected'] = true;
19689 var opt = this.inputEl().createChild(option);
19691 this.ios_options.push({
19698 this.inputEl().on('change', function(){
19699 this.fireEvent('select', this);
19704 clearIOSView: function()
19706 this.inputEl().dom.innerHTML = '';
19708 this.ios_options = [];
19711 setIOSValue: function(v)
19715 if(!this.ios_options){
19719 Roo.each(this.ios_options, function(opts){
19721 opts.el.dom.removeAttribute('selected');
19723 if(opts.data[this.valueField] != v){
19727 opts.el.dom.setAttribute('selected', true);
19733 * @cfg {Boolean} grow
19737 * @cfg {Number} growMin
19741 * @cfg {Number} growMax
19750 Roo.apply(Roo.bootstrap.form.ComboBox, {
19754 cls: 'modal-header',
19776 cls: 'list-group-item',
19780 cls: 'roo-combobox-list-group-item-value'
19784 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19798 listItemCheckbox : {
19800 cls: 'list-group-item',
19804 cls: 'roo-combobox-list-group-item-value'
19808 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19824 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19829 cls: 'modal-footer',
19837 cls: 'col-xs-6 text-left',
19840 cls: 'btn btn-danger roo-touch-view-cancel',
19846 cls: 'col-xs-6 text-right',
19849 cls: 'btn btn-success roo-touch-view-ok',
19860 Roo.apply(Roo.bootstrap.form.ComboBox, {
19862 touchViewTemplate : {
19864 cls: 'modal fade roo-combobox-touch-view',
19868 cls: 'modal-dialog',
19869 style : 'position:fixed', // we have to fix position....
19873 cls: 'modal-content',
19875 Roo.bootstrap.form.ComboBox.header,
19876 Roo.bootstrap.form.ComboBox.body,
19877 Roo.bootstrap.form.ComboBox.footer
19886 * Ext JS Library 1.1.1
19887 * Copyright(c) 2006-2007, Ext JS, LLC.
19889 * Originally Released Under LGPL - original licence link has changed is not relivant.
19892 * <script type="text/javascript">
19897 * @extends Roo.util.Observable
19898 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19899 * This class also supports single and multi selection modes. <br>
19900 * Create a data model bound view:
19902 var store = new Roo.data.Store(...);
19904 var view = new Roo.View({
19906 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19908 singleSelect: true,
19909 selectedClass: "ydataview-selected",
19913 // listen for node click?
19914 view.on("click", function(vw, index, node, e){
19915 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19919 dataModel.load("foobar.xml");
19921 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19923 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19924 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19926 * Note: old style constructor is still suported (container, template, config)
19929 * Create a new View
19930 * @param {Object} config The config object
19933 Roo.View = function(config, depreciated_tpl, depreciated_config){
19935 this.parent = false;
19937 if (typeof(depreciated_tpl) == 'undefined') {
19938 // new way.. - universal constructor.
19939 Roo.apply(this, config);
19940 this.el = Roo.get(this.el);
19943 this.el = Roo.get(config);
19944 this.tpl = depreciated_tpl;
19945 Roo.apply(this, depreciated_config);
19947 this.wrapEl = this.el.wrap().wrap();
19948 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19951 if(typeof(this.tpl) == "string"){
19952 this.tpl = new Roo.Template(this.tpl);
19954 // support xtype ctors..
19955 this.tpl = new Roo.factory(this.tpl, Roo);
19959 this.tpl.compile();
19964 * @event beforeclick
19965 * Fires before a click is processed. Returns false to cancel the default action.
19966 * @param {Roo.View} this
19967 * @param {Number} index The index of the target node
19968 * @param {HTMLElement} node The target node
19969 * @param {Roo.EventObject} e The raw event object
19971 "beforeclick" : true,
19974 * Fires when a template node is clicked.
19975 * @param {Roo.View} this
19976 * @param {Number} index The index of the target node
19977 * @param {HTMLElement} node The target node
19978 * @param {Roo.EventObject} e The raw event object
19983 * Fires when a template node is double clicked.
19984 * @param {Roo.View} this
19985 * @param {Number} index The index of the target node
19986 * @param {HTMLElement} node The target node
19987 * @param {Roo.EventObject} e The raw event object
19991 * @event contextmenu
19992 * Fires when a template node is right clicked.
19993 * @param {Roo.View} this
19994 * @param {Number} index The index of the target node
19995 * @param {HTMLElement} node The target node
19996 * @param {Roo.EventObject} e The raw event object
19998 "contextmenu" : true,
20000 * @event selectionchange
20001 * Fires when the selected nodes change.
20002 * @param {Roo.View} this
20003 * @param {Array} selections Array of the selected nodes
20005 "selectionchange" : true,
20008 * @event beforeselect
20009 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20010 * @param {Roo.View} this
20011 * @param {HTMLElement} node The node to be selected
20012 * @param {Array} selections Array of currently selected nodes
20014 "beforeselect" : true,
20016 * @event preparedata
20017 * Fires on every row to render, to allow you to change the data.
20018 * @param {Roo.View} this
20019 * @param {Object} data to be rendered (change this)
20021 "preparedata" : true
20029 "click": this.onClick,
20030 "dblclick": this.onDblClick,
20031 "contextmenu": this.onContextMenu,
20035 this.selections = [];
20037 this.cmp = new Roo.CompositeElementLite([]);
20039 this.store = Roo.factory(this.store, Roo.data);
20040 this.setStore(this.store, true);
20043 if ( this.footer && this.footer.xtype) {
20045 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20047 this.footer.dataSource = this.store;
20048 this.footer.container = fctr;
20049 this.footer = Roo.factory(this.footer, Roo);
20050 fctr.insertFirst(this.el);
20052 // this is a bit insane - as the paging toolbar seems to detach the el..
20053 // dom.parentNode.parentNode.parentNode
20054 // they get detached?
20058 Roo.View.superclass.constructor.call(this);
20063 Roo.extend(Roo.View, Roo.util.Observable, {
20066 * @cfg {Roo.data.Store} store Data store to load data from.
20071 * @cfg {String|Roo.Element} el The container element.
20076 * @cfg {String|Roo.Template} tpl The template used by this View
20080 * @cfg {String} dataName the named area of the template to use as the data area
20081 * Works with domtemplates roo-name="name"
20085 * @cfg {String} selectedClass The css class to add to selected nodes
20087 selectedClass : "x-view-selected",
20089 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20094 * @cfg {String} text to display on mask (default Loading)
20098 * @cfg {Boolean} multiSelect Allow multiple selection
20100 multiSelect : false,
20102 * @cfg {Boolean} singleSelect Allow single selection
20104 singleSelect: false,
20107 * @cfg {Boolean} toggleSelect - selecting
20109 toggleSelect : false,
20112 * @cfg {Boolean} tickable - selecting
20117 * Returns the element this view is bound to.
20118 * @return {Roo.Element}
20120 getEl : function(){
20121 return this.wrapEl;
20127 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20129 refresh : function(){
20130 //Roo.log('refresh');
20133 // if we are using something like 'domtemplate', then
20134 // the what gets used is:
20135 // t.applySubtemplate(NAME, data, wrapping data..)
20136 // the outer template then get' applied with
20137 // the store 'extra data'
20138 // and the body get's added to the
20139 // roo-name="data" node?
20140 // <span class='roo-tpl-{name}'></span> ?????
20144 this.clearSelections();
20145 this.el.update("");
20147 var records = this.store.getRange();
20148 if(records.length < 1) {
20150 // is this valid?? = should it render a template??
20152 this.el.update(this.emptyText);
20156 if (this.dataName) {
20157 this.el.update(t.apply(this.store.meta)); //????
20158 el = this.el.child('.roo-tpl-' + this.dataName);
20161 for(var i = 0, len = records.length; i < len; i++){
20162 var data = this.prepareData(records[i].data, i, records[i]);
20163 this.fireEvent("preparedata", this, data, i, records[i]);
20165 var d = Roo.apply({}, data);
20168 Roo.apply(d, {'roo-id' : Roo.id()});
20172 Roo.each(this.parent.item, function(item){
20173 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20176 Roo.apply(d, {'roo-data-checked' : 'checked'});
20180 html[html.length] = Roo.util.Format.trim(
20182 t.applySubtemplate(this.dataName, d, this.store.meta) :
20189 el.update(html.join(""));
20190 this.nodes = el.dom.childNodes;
20191 this.updateIndexes(0);
20196 * Function to override to reformat the data that is sent to
20197 * the template for each node.
20198 * DEPRICATED - use the preparedata event handler.
20199 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20200 * a JSON object for an UpdateManager bound view).
20202 prepareData : function(data, index, record)
20204 this.fireEvent("preparedata", this, data, index, record);
20208 onUpdate : function(ds, record){
20209 // Roo.log('on update');
20210 this.clearSelections();
20211 var index = this.store.indexOf(record);
20212 var n = this.nodes[index];
20213 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20214 n.parentNode.removeChild(n);
20215 this.updateIndexes(index, index);
20221 onAdd : function(ds, records, index)
20223 //Roo.log(['on Add', ds, records, index] );
20224 this.clearSelections();
20225 if(this.nodes.length == 0){
20229 var n = this.nodes[index];
20230 for(var i = 0, len = records.length; i < len; i++){
20231 var d = this.prepareData(records[i].data, i, records[i]);
20233 this.tpl.insertBefore(n, d);
20236 this.tpl.append(this.el, d);
20239 this.updateIndexes(index);
20242 onRemove : function(ds, record, index){
20243 // Roo.log('onRemove');
20244 this.clearSelections();
20245 var el = this.dataName ?
20246 this.el.child('.roo-tpl-' + this.dataName) :
20249 el.dom.removeChild(this.nodes[index]);
20250 this.updateIndexes(index);
20254 * Refresh an individual node.
20255 * @param {Number} index
20257 refreshNode : function(index){
20258 this.onUpdate(this.store, this.store.getAt(index));
20261 updateIndexes : function(startIndex, endIndex){
20262 var ns = this.nodes;
20263 startIndex = startIndex || 0;
20264 endIndex = endIndex || ns.length - 1;
20265 for(var i = startIndex; i <= endIndex; i++){
20266 ns[i].nodeIndex = i;
20271 * Changes the data store this view uses and refresh the view.
20272 * @param {Store} store
20274 setStore : function(store, initial){
20275 if(!initial && this.store){
20276 this.store.un("datachanged", this.refresh);
20277 this.store.un("add", this.onAdd);
20278 this.store.un("remove", this.onRemove);
20279 this.store.un("update", this.onUpdate);
20280 this.store.un("clear", this.refresh);
20281 this.store.un("beforeload", this.onBeforeLoad);
20282 this.store.un("load", this.onLoad);
20283 this.store.un("loadexception", this.onLoad);
20287 store.on("datachanged", this.refresh, this);
20288 store.on("add", this.onAdd, this);
20289 store.on("remove", this.onRemove, this);
20290 store.on("update", this.onUpdate, this);
20291 store.on("clear", this.refresh, this);
20292 store.on("beforeload", this.onBeforeLoad, this);
20293 store.on("load", this.onLoad, this);
20294 store.on("loadexception", this.onLoad, this);
20302 * onbeforeLoad - masks the loading area.
20305 onBeforeLoad : function(store,opts)
20307 //Roo.log('onBeforeLoad');
20309 this.el.update("");
20311 this.el.mask(this.mask ? this.mask : "Loading" );
20313 onLoad : function ()
20320 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20321 * @param {HTMLElement} node
20322 * @return {HTMLElement} The template node
20324 findItemFromChild : function(node){
20325 var el = this.dataName ?
20326 this.el.child('.roo-tpl-' + this.dataName,true) :
20329 if(!node || node.parentNode == el){
20332 var p = node.parentNode;
20333 while(p && p != el){
20334 if(p.parentNode == el){
20343 onClick : function(e){
20344 var item = this.findItemFromChild(e.getTarget());
20346 var index = this.indexOf(item);
20347 if(this.onItemClick(item, index, e) !== false){
20348 this.fireEvent("click", this, index, item, e);
20351 this.clearSelections();
20356 onContextMenu : function(e){
20357 var item = this.findItemFromChild(e.getTarget());
20359 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20364 onDblClick : function(e){
20365 var item = this.findItemFromChild(e.getTarget());
20367 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20371 onItemClick : function(item, index, e)
20373 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20376 if (this.toggleSelect) {
20377 var m = this.isSelected(item) ? 'unselect' : 'select';
20380 _t[m](item, true, false);
20383 if(this.multiSelect || this.singleSelect){
20384 if(this.multiSelect && e.shiftKey && this.lastSelection){
20385 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20387 this.select(item, this.multiSelect && e.ctrlKey);
20388 this.lastSelection = item;
20391 if(!this.tickable){
20392 e.preventDefault();
20400 * Get the number of selected nodes.
20403 getSelectionCount : function(){
20404 return this.selections.length;
20408 * Get the currently selected nodes.
20409 * @return {Array} An array of HTMLElements
20411 getSelectedNodes : function(){
20412 return this.selections;
20416 * Get the indexes of the selected nodes.
20419 getSelectedIndexes : function(){
20420 var indexes = [], s = this.selections;
20421 for(var i = 0, len = s.length; i < len; i++){
20422 indexes.push(s[i].nodeIndex);
20428 * Clear all selections
20429 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20431 clearSelections : function(suppressEvent){
20432 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20433 this.cmp.elements = this.selections;
20434 this.cmp.removeClass(this.selectedClass);
20435 this.selections = [];
20436 if(!suppressEvent){
20437 this.fireEvent("selectionchange", this, this.selections);
20443 * Returns true if the passed node is selected
20444 * @param {HTMLElement/Number} node The node or node index
20445 * @return {Boolean}
20447 isSelected : function(node){
20448 var s = this.selections;
20452 node = this.getNode(node);
20453 return s.indexOf(node) !== -1;
20458 * @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
20459 * @param {Boolean} keepExisting (optional) true to keep existing selections
20460 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20462 select : function(nodeInfo, keepExisting, suppressEvent){
20463 if(nodeInfo instanceof Array){
20465 this.clearSelections(true);
20467 for(var i = 0, len = nodeInfo.length; i < len; i++){
20468 this.select(nodeInfo[i], true, true);
20472 var node = this.getNode(nodeInfo);
20473 if(!node || this.isSelected(node)){
20474 return; // already selected.
20477 this.clearSelections(true);
20480 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20481 Roo.fly(node).addClass(this.selectedClass);
20482 this.selections.push(node);
20483 if(!suppressEvent){
20484 this.fireEvent("selectionchange", this, this.selections);
20492 * @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
20493 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20494 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20496 unselect : function(nodeInfo, keepExisting, suppressEvent)
20498 if(nodeInfo instanceof Array){
20499 Roo.each(this.selections, function(s) {
20500 this.unselect(s, nodeInfo);
20504 var node = this.getNode(nodeInfo);
20505 if(!node || !this.isSelected(node)){
20506 //Roo.log("not selected");
20507 return; // not selected.
20511 Roo.each(this.selections, function(s) {
20513 Roo.fly(node).removeClass(this.selectedClass);
20520 this.selections= ns;
20521 this.fireEvent("selectionchange", this, this.selections);
20525 * Gets a template node.
20526 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20527 * @return {HTMLElement} The node or null if it wasn't found
20529 getNode : function(nodeInfo){
20530 if(typeof nodeInfo == "string"){
20531 return document.getElementById(nodeInfo);
20532 }else if(typeof nodeInfo == "number"){
20533 return this.nodes[nodeInfo];
20539 * Gets a range template nodes.
20540 * @param {Number} startIndex
20541 * @param {Number} endIndex
20542 * @return {Array} An array of nodes
20544 getNodes : function(start, end){
20545 var ns = this.nodes;
20546 start = start || 0;
20547 end = typeof end == "undefined" ? ns.length - 1 : end;
20550 for(var i = start; i <= end; i++){
20554 for(var i = start; i >= end; i--){
20562 * Finds the index of the passed node
20563 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20564 * @return {Number} The index of the node or -1
20566 indexOf : function(node){
20567 node = this.getNode(node);
20568 if(typeof node.nodeIndex == "number"){
20569 return node.nodeIndex;
20571 var ns = this.nodes;
20572 for(var i = 0, len = ns.length; i < len; i++){
20583 * based on jquery fullcalendar
20587 Roo.bootstrap = Roo.bootstrap || {};
20589 * @class Roo.bootstrap.Calendar
20590 * @extends Roo.bootstrap.Component
20591 * Bootstrap Calendar class
20592 * @cfg {Boolean} loadMask (true|false) default false
20593 * @cfg {Object} header generate the user specific header of the calendar, default false
20596 * Create a new Container
20597 * @param {Object} config The config object
20602 Roo.bootstrap.Calendar = function(config){
20603 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20607 * Fires when a date is selected
20608 * @param {DatePicker} this
20609 * @param {Date} date The selected date
20613 * @event monthchange
20614 * Fires when the displayed month changes
20615 * @param {DatePicker} this
20616 * @param {Date} date The selected month
20618 'monthchange': true,
20620 * @event evententer
20621 * Fires when mouse over an event
20622 * @param {Calendar} this
20623 * @param {event} Event
20625 'evententer': true,
20627 * @event eventleave
20628 * Fires when the mouse leaves an
20629 * @param {Calendar} this
20632 'eventleave': true,
20634 * @event eventclick
20635 * Fires when the mouse click an
20636 * @param {Calendar} this
20645 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20648 * @cfg {Roo.data.Store} store
20649 * The data source for the calendar
20653 * @cfg {Number} startDay
20654 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20662 getAutoCreate : function(){
20665 var fc_button = function(name, corner, style, content ) {
20666 return Roo.apply({},{
20668 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20670 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20673 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20684 style : 'width:100%',
20691 cls : 'fc-header-left',
20693 fc_button('prev', 'left', 'arrow', '‹' ),
20694 fc_button('next', 'right', 'arrow', '›' ),
20695 { tag: 'span', cls: 'fc-header-space' },
20696 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20704 cls : 'fc-header-center',
20708 cls: 'fc-header-title',
20711 html : 'month / year'
20719 cls : 'fc-header-right',
20721 /* fc_button('month', 'left', '', 'month' ),
20722 fc_button('week', '', '', 'week' ),
20723 fc_button('day', 'right', '', 'day' )
20735 header = this.header;
20738 var cal_heads = function() {
20740 // fixme - handle this.
20742 for (var i =0; i < Date.dayNames.length; i++) {
20743 var d = Date.dayNames[i];
20746 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20747 html : d.substring(0,3)
20751 ret[0].cls += ' fc-first';
20752 ret[6].cls += ' fc-last';
20755 var cal_cell = function(n) {
20758 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20763 cls: 'fc-day-number',
20767 cls: 'fc-day-content',
20771 style: 'position: relative;' // height: 17px;
20783 var cal_rows = function() {
20786 for (var r = 0; r < 6; r++) {
20793 for (var i =0; i < Date.dayNames.length; i++) {
20794 var d = Date.dayNames[i];
20795 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20798 row.cn[0].cls+=' fc-first';
20799 row.cn[0].cn[0].style = 'min-height:90px';
20800 row.cn[6].cls+=' fc-last';
20804 ret[0].cls += ' fc-first';
20805 ret[4].cls += ' fc-prev-last';
20806 ret[5].cls += ' fc-last';
20813 cls: 'fc-border-separate',
20814 style : 'width:100%',
20822 cls : 'fc-first fc-last',
20840 cls : 'fc-content',
20841 style : "position: relative;",
20844 cls : 'fc-view fc-view-month fc-grid',
20845 style : 'position: relative',
20846 unselectable : 'on',
20849 cls : 'fc-event-container',
20850 style : 'position:absolute;z-index:8;top:0;left:0;'
20868 initEvents : function()
20871 throw "can not find store for calendar";
20877 style: "text-align:center",
20881 style: "background-color:white;width:50%;margin:250 auto",
20885 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20896 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20898 var size = this.el.select('.fc-content', true).first().getSize();
20899 this.maskEl.setSize(size.width, size.height);
20900 this.maskEl.enableDisplayMode("block");
20901 if(!this.loadMask){
20902 this.maskEl.hide();
20905 this.store = Roo.factory(this.store, Roo.data);
20906 this.store.on('load', this.onLoad, this);
20907 this.store.on('beforeload', this.onBeforeLoad, this);
20911 this.cells = this.el.select('.fc-day',true);
20912 //Roo.log(this.cells);
20913 this.textNodes = this.el.query('.fc-day-number');
20914 this.cells.addClassOnOver('fc-state-hover');
20916 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20917 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20918 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20919 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20921 this.on('monthchange', this.onMonthChange, this);
20923 this.update(new Date().clearTime());
20926 resize : function() {
20927 var sz = this.el.getSize();
20929 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20930 this.el.select('.fc-day-content div',true).setHeight(34);
20935 showPrevMonth : function(e){
20936 this.update(this.activeDate.add("mo", -1));
20938 showToday : function(e){
20939 this.update(new Date().clearTime());
20942 showNextMonth : function(e){
20943 this.update(this.activeDate.add("mo", 1));
20947 showPrevYear : function(){
20948 this.update(this.activeDate.add("y", -1));
20952 showNextYear : function(){
20953 this.update(this.activeDate.add("y", 1));
20958 update : function(date)
20960 var vd = this.activeDate;
20961 this.activeDate = date;
20962 // if(vd && this.el){
20963 // var t = date.getTime();
20964 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20965 // Roo.log('using add remove');
20967 // this.fireEvent('monthchange', this, date);
20969 // this.cells.removeClass("fc-state-highlight");
20970 // this.cells.each(function(c){
20971 // if(c.dateValue == t){
20972 // c.addClass("fc-state-highlight");
20973 // setTimeout(function(){
20974 // try{c.dom.firstChild.focus();}catch(e){}
20984 var days = date.getDaysInMonth();
20986 var firstOfMonth = date.getFirstDateOfMonth();
20987 var startingPos = firstOfMonth.getDay()-this.startDay;
20989 if(startingPos < this.startDay){
20993 var pm = date.add(Date.MONTH, -1);
20994 var prevStart = pm.getDaysInMonth()-startingPos;
20996 this.cells = this.el.select('.fc-day',true);
20997 this.textNodes = this.el.query('.fc-day-number');
20998 this.cells.addClassOnOver('fc-state-hover');
21000 var cells = this.cells.elements;
21001 var textEls = this.textNodes;
21003 Roo.each(cells, function(cell){
21004 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21007 days += startingPos;
21009 // convert everything to numbers so it's fast
21010 var day = 86400000;
21011 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21014 //Roo.log(prevStart);
21016 var today = new Date().clearTime().getTime();
21017 var sel = date.clearTime().getTime();
21018 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21019 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21020 var ddMatch = this.disabledDatesRE;
21021 var ddText = this.disabledDatesText;
21022 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21023 var ddaysText = this.disabledDaysText;
21024 var format = this.format;
21026 var setCellClass = function(cal, cell){
21030 //Roo.log('set Cell Class');
21032 var t = d.getTime();
21036 cell.dateValue = t;
21038 cell.className += " fc-today";
21039 cell.className += " fc-state-highlight";
21040 cell.title = cal.todayText;
21043 // disable highlight in other month..
21044 //cell.className += " fc-state-highlight";
21049 cell.className = " fc-state-disabled";
21050 cell.title = cal.minText;
21054 cell.className = " fc-state-disabled";
21055 cell.title = cal.maxText;
21059 if(ddays.indexOf(d.getDay()) != -1){
21060 cell.title = ddaysText;
21061 cell.className = " fc-state-disabled";
21064 if(ddMatch && format){
21065 var fvalue = d.dateFormat(format);
21066 if(ddMatch.test(fvalue)){
21067 cell.title = ddText.replace("%0", fvalue);
21068 cell.className = " fc-state-disabled";
21072 if (!cell.initialClassName) {
21073 cell.initialClassName = cell.dom.className;
21076 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21081 for(; i < startingPos; i++) {
21082 textEls[i].innerHTML = (++prevStart);
21083 d.setDate(d.getDate()+1);
21085 cells[i].className = "fc-past fc-other-month";
21086 setCellClass(this, cells[i]);
21091 for(; i < days; i++){
21092 intDay = i - startingPos + 1;
21093 textEls[i].innerHTML = (intDay);
21094 d.setDate(d.getDate()+1);
21096 cells[i].className = ''; // "x-date-active";
21097 setCellClass(this, cells[i]);
21101 for(; i < 42; i++) {
21102 textEls[i].innerHTML = (++extraDays);
21103 d.setDate(d.getDate()+1);
21105 cells[i].className = "fc-future fc-other-month";
21106 setCellClass(this, cells[i]);
21109 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21111 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21113 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21114 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21116 if(totalRows != 6){
21117 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21118 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21121 this.fireEvent('monthchange', this, date);
21125 if(!this.internalRender){
21126 var main = this.el.dom.firstChild;
21127 var w = main.offsetWidth;
21128 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21129 Roo.fly(main).setWidth(w);
21130 this.internalRender = true;
21131 // opera does not respect the auto grow header center column
21132 // then, after it gets a width opera refuses to recalculate
21133 // without a second pass
21134 if(Roo.isOpera && !this.secondPass){
21135 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21136 this.secondPass = true;
21137 this.update.defer(10, this, [date]);
21144 findCell : function(dt) {
21145 dt = dt.clearTime().getTime();
21147 this.cells.each(function(c){
21148 //Roo.log("check " +c.dateValue + '?=' + dt);
21149 if(c.dateValue == dt){
21159 findCells : function(ev) {
21160 var s = ev.start.clone().clearTime().getTime();
21162 var e= ev.end.clone().clearTime().getTime();
21165 this.cells.each(function(c){
21166 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21168 if(c.dateValue > e){
21171 if(c.dateValue < s){
21180 // findBestRow: function(cells)
21184 // for (var i =0 ; i < cells.length;i++) {
21185 // ret = Math.max(cells[i].rows || 0,ret);
21192 addItem : function(ev)
21194 // look for vertical location slot in
21195 var cells = this.findCells(ev);
21197 // ev.row = this.findBestRow(cells);
21199 // work out the location.
21203 for(var i =0; i < cells.length; i++) {
21205 cells[i].row = cells[0].row;
21208 cells[i].row = cells[i].row + 1;
21218 if (crow.start.getY() == cells[i].getY()) {
21220 crow.end = cells[i];
21237 cells[0].events.push(ev);
21239 this.calevents.push(ev);
21242 clearEvents: function() {
21244 if(!this.calevents){
21248 Roo.each(this.cells.elements, function(c){
21254 Roo.each(this.calevents, function(e) {
21255 Roo.each(e.els, function(el) {
21256 el.un('mouseenter' ,this.onEventEnter, this);
21257 el.un('mouseleave' ,this.onEventLeave, this);
21262 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21268 renderEvents: function()
21272 this.cells.each(function(c) {
21281 if(c.row != c.events.length){
21282 r = 4 - (4 - (c.row - c.events.length));
21285 c.events = ev.slice(0, r);
21286 c.more = ev.slice(r);
21288 if(c.more.length && c.more.length == 1){
21289 c.events.push(c.more.pop());
21292 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21296 this.cells.each(function(c) {
21298 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21301 for (var e = 0; e < c.events.length; e++){
21302 var ev = c.events[e];
21303 var rows = ev.rows;
21305 for(var i = 0; i < rows.length; i++) {
21307 // how many rows should it span..
21310 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21311 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21313 unselectable : "on",
21316 cls: 'fc-event-inner',
21320 // cls: 'fc-event-time',
21321 // html : cells.length > 1 ? '' : ev.time
21325 cls: 'fc-event-title',
21326 html : String.format('{0}', ev.title)
21333 cls: 'ui-resizable-handle ui-resizable-e',
21334 html : '  '
21341 cfg.cls += ' fc-event-start';
21343 if ((i+1) == rows.length) {
21344 cfg.cls += ' fc-event-end';
21347 var ctr = _this.el.select('.fc-event-container',true).first();
21348 var cg = ctr.createChild(cfg);
21350 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21351 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21353 var r = (c.more.length) ? 1 : 0;
21354 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21355 cg.setWidth(ebox.right - sbox.x -2);
21357 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21358 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21359 cg.on('click', _this.onEventClick, _this, ev);
21370 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21371 style : 'position: absolute',
21372 unselectable : "on",
21375 cls: 'fc-event-inner',
21379 cls: 'fc-event-title',
21387 cls: 'ui-resizable-handle ui-resizable-e',
21388 html : '  '
21394 var ctr = _this.el.select('.fc-event-container',true).first();
21395 var cg = ctr.createChild(cfg);
21397 var sbox = c.select('.fc-day-content',true).first().getBox();
21398 var ebox = c.select('.fc-day-content',true).first().getBox();
21400 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21401 cg.setWidth(ebox.right - sbox.x -2);
21403 cg.on('click', _this.onMoreEventClick, _this, c.more);
21413 onEventEnter: function (e, el,event,d) {
21414 this.fireEvent('evententer', this, el, event);
21417 onEventLeave: function (e, el,event,d) {
21418 this.fireEvent('eventleave', this, el, event);
21421 onEventClick: function (e, el,event,d) {
21422 this.fireEvent('eventclick', this, el, event);
21425 onMonthChange: function () {
21429 onMoreEventClick: function(e, el, more)
21433 this.calpopover.placement = 'right';
21434 this.calpopover.setTitle('More');
21436 this.calpopover.setContent('');
21438 var ctr = this.calpopover.el.select('.popover-content', true).first();
21440 Roo.each(more, function(m){
21442 cls : 'fc-event-hori fc-event-draggable',
21445 var cg = ctr.createChild(cfg);
21447 cg.on('click', _this.onEventClick, _this, m);
21450 this.calpopover.show(el);
21455 onLoad: function ()
21457 this.calevents = [];
21460 if(this.store.getCount() > 0){
21461 this.store.data.each(function(d){
21464 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21465 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21466 time : d.data.start_time,
21467 title : d.data.title,
21468 description : d.data.description,
21469 venue : d.data.venue
21474 this.renderEvents();
21476 if(this.calevents.length && this.loadMask){
21477 this.maskEl.hide();
21481 onBeforeLoad: function()
21483 this.clearEvents();
21485 this.maskEl.show();
21499 * @class Roo.bootstrap.Popover
21500 * @extends Roo.bootstrap.Component
21501 * @parent none builder
21502 * @children Roo.bootstrap.Component
21503 * Bootstrap Popover class
21504 * @cfg {String} html contents of the popover (or false to use children..)
21505 * @cfg {String} title of popover (or false to hide)
21506 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21507 * @cfg {String} trigger click || hover (or false to trigger manually)
21508 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21509 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21510 * - if false and it has a 'parent' then it will be automatically added to that element
21511 * - if string - Roo.get will be called
21512 * @cfg {Number} delay - delay before showing
21515 * Create a new Popover
21516 * @param {Object} config The config object
21519 Roo.bootstrap.Popover = function(config){
21520 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21526 * After the popover show
21528 * @param {Roo.bootstrap.Popover} this
21533 * After the popover hide
21535 * @param {Roo.bootstrap.Popover} this
21541 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21546 placement : 'right',
21547 trigger : 'hover', // hover
21553 can_build_overlaid : false,
21555 maskEl : false, // the mask element
21558 alignEl : false, // when show is called with an element - this get's stored.
21560 getChildContainer : function()
21562 return this.contentEl;
21565 getPopoverHeader : function()
21567 this.title = true; // flag not to hide it..
21568 this.headerEl.addClass('p-0');
21569 return this.headerEl
21573 getAutoCreate : function(){
21576 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21577 style: 'display:block',
21583 cls : 'popover-inner ',
21587 cls: 'popover-title popover-header',
21588 html : this.title === false ? '' : this.title
21591 cls : 'popover-content popover-body ' + (this.cls || ''),
21592 html : this.html || ''
21603 * @param {string} the title
21605 setTitle: function(str)
21609 this.headerEl.dom.innerHTML = str;
21614 * @param {string} the body content
21616 setContent: function(str)
21619 if (this.contentEl) {
21620 this.contentEl.dom.innerHTML = str;
21624 // as it get's added to the bottom of the page.
21625 onRender : function(ct, position)
21627 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21632 var cfg = Roo.apply({}, this.getAutoCreate());
21636 cfg.cls += ' ' + this.cls;
21639 cfg.style = this.style;
21641 //Roo.log("adding to ");
21642 this.el = Roo.get(document.body).createChild(cfg, position);
21643 // Roo.log(this.el);
21646 this.contentEl = this.el.select('.popover-content',true).first();
21647 this.headerEl = this.el.select('.popover-title',true).first();
21650 if(typeof(this.items) != 'undefined'){
21651 var items = this.items;
21654 for(var i =0;i < items.length;i++) {
21655 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21659 this.items = nitems;
21661 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21662 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21669 resizeMask : function()
21671 this.maskEl.setSize(
21672 Roo.lib.Dom.getViewWidth(true),
21673 Roo.lib.Dom.getViewHeight(true)
21677 initEvents : function()
21681 Roo.bootstrap.Popover.register(this);
21684 this.arrowEl = this.el.select('.arrow',true).first();
21685 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21686 this.el.enableDisplayMode('block');
21690 if (this.over === false && !this.parent()) {
21693 if (this.triggers === false) {
21698 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21699 var triggers = this.trigger ? this.trigger.split(' ') : [];
21700 Roo.each(triggers, function(trigger) {
21702 if (trigger == 'click') {
21703 on_el.on('click', this.toggle, this);
21704 } else if (trigger != 'manual') {
21705 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21706 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21708 on_el.on(eventIn ,this.enter, this);
21709 on_el.on(eventOut, this.leave, this);
21719 toggle : function () {
21720 this.hoverState == 'in' ? this.leave() : this.enter();
21723 enter : function () {
21725 clearTimeout(this.timeout);
21727 this.hoverState = 'in';
21729 if (!this.delay || !this.delay.show) {
21734 this.timeout = setTimeout(function () {
21735 if (_t.hoverState == 'in') {
21738 }, this.delay.show)
21741 leave : function() {
21742 clearTimeout(this.timeout);
21744 this.hoverState = 'out';
21746 if (!this.delay || !this.delay.hide) {
21751 this.timeout = setTimeout(function () {
21752 if (_t.hoverState == 'out') {
21755 }, this.delay.hide)
21759 * update the position of the dialog
21760 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21765 doAlign : function()
21768 if (this.alignEl) {
21769 this.updatePosition(this.placement, true);
21772 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21773 var es = this.el.getSize();
21774 var x = Roo.lib.Dom.getViewWidth()/2;
21775 var y = Roo.lib.Dom.getViewHeight()/2;
21776 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21788 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21789 * @param {string} (left|right|top|bottom) position
21791 show : function (on_el, placement)
21793 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21794 on_el = on_el || false; // default to false
21797 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21798 on_el = this.parent().el;
21799 } else if (this.over) {
21800 on_el = Roo.get(this.over);
21805 this.alignEl = Roo.get( on_el );
21808 this.render(document.body);
21814 if (this.title === false) {
21815 this.headerEl.hide();
21820 this.el.dom.style.display = 'block';
21824 //var arrow = this.el.select('.arrow',true).first();
21825 //arrow.set(align[2],
21827 this.el.addClass('in');
21831 this.hoverState = 'in';
21834 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21835 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21836 this.maskEl.dom.style.display = 'block';
21837 this.maskEl.addClass('show');
21839 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21841 this.fireEvent('show', this);
21845 * fire this manually after loading a grid in the table for example
21846 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21847 * @param {Boolean} try and move it if we cant get right position.
21849 updatePosition : function(placement, try_move)
21851 // allow for calling with no parameters
21852 placement = placement ? placement : this.placement;
21853 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21855 this.el.removeClass([
21856 'fade','top','bottom', 'left', 'right','in',
21857 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21859 this.el.addClass(placement + ' bs-popover-' + placement);
21861 if (!this.alignEl ) {
21865 switch (placement) {
21867 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21868 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21869 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21870 //normal display... or moved up/down.
21871 this.el.setXY(offset);
21872 var xy = this.alignEl.getAnchorXY('tr', false);
21874 this.arrowEl.setXY(xy);
21877 // continue through...
21878 return this.updatePosition('left', false);
21882 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21883 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21884 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21885 //normal display... or moved up/down.
21886 this.el.setXY(offset);
21887 var xy = this.alignEl.getAnchorXY('tl', false);
21888 xy[0]-=10;xy[1]+=5; // << fix me
21889 this.arrowEl.setXY(xy);
21893 return this.updatePosition('right', false);
21896 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21897 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21898 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21899 //normal display... or moved up/down.
21900 this.el.setXY(offset);
21901 var xy = this.alignEl.getAnchorXY('t', false);
21902 xy[1]-=10; // << fix me
21903 this.arrowEl.setXY(xy);
21907 return this.updatePosition('bottom', false);
21910 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21911 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21912 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21913 //normal display... or moved up/down.
21914 this.el.setXY(offset);
21915 var xy = this.alignEl.getAnchorXY('b', false);
21916 xy[1]+=2; // << fix me
21917 this.arrowEl.setXY(xy);
21921 return this.updatePosition('top', false);
21932 this.el.setXY([0,0]);
21933 this.el.removeClass('in');
21935 this.hoverState = null;
21936 this.maskEl.hide(); // always..
21937 this.fireEvent('hide', this);
21943 Roo.apply(Roo.bootstrap.Popover, {
21946 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21947 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21948 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21949 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21954 clickHander : false,
21958 onMouseDown : function(e)
21960 if (this.popups.length && !e.getTarget(".roo-popover")) {
21961 /// what is nothing is showing..
21970 register : function(popup)
21972 if (!Roo.bootstrap.Popover.clickHandler) {
21973 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21975 // hide other popups.
21976 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21977 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21978 this.hideAll(); //<< why?
21979 //this.popups.push(popup);
21981 hideAll : function()
21983 this.popups.forEach(function(p) {
21987 onShow : function() {
21988 Roo.bootstrap.Popover.popups.push(this);
21990 onHide : function() {
21991 Roo.bootstrap.Popover.popups.remove(this);
21996 * @class Roo.bootstrap.PopoverNav
21997 * @extends Roo.bootstrap.nav.Simplebar
21998 * @parent Roo.bootstrap.Popover
21999 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22001 * Bootstrap Popover header navigation class
22002 * FIXME? should this go under nav?
22006 * Create a new Popover Header Navigation
22007 * @param {Object} config The config object
22010 Roo.bootstrap.PopoverNav = function(config){
22011 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22014 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22017 container_method : 'getPopoverHeader'
22035 * @class Roo.bootstrap.Progress
22036 * @extends Roo.bootstrap.Component
22037 * @children Roo.bootstrap.ProgressBar
22038 * Bootstrap Progress class
22039 * @cfg {Boolean} striped striped of the progress bar
22040 * @cfg {Boolean} active animated of the progress bar
22044 * Create a new Progress
22045 * @param {Object} config The config object
22048 Roo.bootstrap.Progress = function(config){
22049 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22052 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22057 getAutoCreate : function(){
22065 cfg.cls += ' progress-striped';
22069 cfg.cls += ' active';
22088 * @class Roo.bootstrap.ProgressBar
22089 * @extends Roo.bootstrap.Component
22090 * Bootstrap ProgressBar class
22091 * @cfg {Number} aria_valuenow aria-value now
22092 * @cfg {Number} aria_valuemin aria-value min
22093 * @cfg {Number} aria_valuemax aria-value max
22094 * @cfg {String} label label for the progress bar
22095 * @cfg {String} panel (success | info | warning | danger )
22096 * @cfg {String} role role of the progress bar
22097 * @cfg {String} sr_only text
22101 * Create a new ProgressBar
22102 * @param {Object} config The config object
22105 Roo.bootstrap.ProgressBar = function(config){
22106 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22109 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22113 aria_valuemax : 100,
22119 getAutoCreate : function()
22124 cls: 'progress-bar',
22125 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22137 cfg.role = this.role;
22140 if(this.aria_valuenow){
22141 cfg['aria-valuenow'] = this.aria_valuenow;
22144 if(this.aria_valuemin){
22145 cfg['aria-valuemin'] = this.aria_valuemin;
22148 if(this.aria_valuemax){
22149 cfg['aria-valuemax'] = this.aria_valuemax;
22152 if(this.label && !this.sr_only){
22153 cfg.html = this.label;
22157 cfg.cls += ' progress-bar-' + this.panel;
22163 update : function(aria_valuenow)
22165 this.aria_valuenow = aria_valuenow;
22167 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22175 * @class Roo.bootstrap.TabGroup
22176 * @extends Roo.bootstrap.Column
22177 * @children Roo.bootstrap.TabPanel
22178 * Bootstrap Column class
22179 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22180 * @cfg {Boolean} carousel true to make the group behave like a carousel
22181 * @cfg {Boolean} bullets show bullets for the panels
22182 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22183 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22184 * @cfg {Boolean} showarrow (true|false) show arrow default true
22187 * Create a new TabGroup
22188 * @param {Object} config The config object
22191 Roo.bootstrap.TabGroup = function(config){
22192 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22194 this.navId = Roo.id();
22197 Roo.bootstrap.TabGroup.register(this);
22201 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22204 transition : false,
22209 slideOnTouch : false,
22212 getAutoCreate : function()
22214 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22216 cfg.cls += ' tab-content';
22218 if (this.carousel) {
22219 cfg.cls += ' carousel slide';
22222 cls : 'carousel-inner',
22226 if(this.bullets && !Roo.isTouch){
22229 cls : 'carousel-bullets',
22233 if(this.bullets_cls){
22234 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22241 cfg.cn[0].cn.push(bullets);
22244 if(this.showarrow){
22245 cfg.cn[0].cn.push({
22247 class : 'carousel-arrow',
22251 class : 'carousel-prev',
22255 class : 'fa fa-chevron-left'
22261 class : 'carousel-next',
22265 class : 'fa fa-chevron-right'
22278 initEvents: function()
22280 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22281 // this.el.on("touchstart", this.onTouchStart, this);
22284 if(this.autoslide){
22287 this.slideFn = window.setInterval(function() {
22288 _this.showPanelNext();
22292 if(this.showarrow){
22293 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22294 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22300 // onTouchStart : function(e, el, o)
22302 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22306 // this.showPanelNext();
22310 getChildContainer : function()
22312 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22316 * register a Navigation item
22317 * @param {Roo.bootstrap.nav.Item} the navitem to add
22319 register : function(item)
22321 this.tabs.push( item);
22322 item.navId = this.navId; // not really needed..
22327 getActivePanel : function()
22330 Roo.each(this.tabs, function(t) {
22340 getPanelByName : function(n)
22343 Roo.each(this.tabs, function(t) {
22344 if (t.tabId == n) {
22352 indexOfPanel : function(p)
22355 Roo.each(this.tabs, function(t,i) {
22356 if (t.tabId == p.tabId) {
22365 * show a specific panel
22366 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22367 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22369 showPanel : function (pan)
22371 if(this.transition || typeof(pan) == 'undefined'){
22372 Roo.log("waiting for the transitionend");
22376 if (typeof(pan) == 'number') {
22377 pan = this.tabs[pan];
22380 if (typeof(pan) == 'string') {
22381 pan = this.getPanelByName(pan);
22384 var cur = this.getActivePanel();
22387 Roo.log('pan or acitve pan is undefined');
22391 if (pan.tabId == this.getActivePanel().tabId) {
22395 if (false === cur.fireEvent('beforedeactivate')) {
22399 if(this.bullets > 0 && !Roo.isTouch){
22400 this.setActiveBullet(this.indexOfPanel(pan));
22403 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22405 //class="carousel-item carousel-item-next carousel-item-left"
22407 this.transition = true;
22408 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22409 var lr = dir == 'next' ? 'left' : 'right';
22410 pan.el.addClass(dir); // or prev
22411 pan.el.addClass('carousel-item-' + dir); // or prev
22412 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22413 cur.el.addClass(lr); // or right
22414 pan.el.addClass(lr);
22415 cur.el.addClass('carousel-item-' +lr); // or right
22416 pan.el.addClass('carousel-item-' +lr);
22420 cur.el.on('transitionend', function() {
22421 Roo.log("trans end?");
22423 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22424 pan.setActive(true);
22426 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22427 cur.setActive(false);
22429 _this.transition = false;
22431 }, this, { single: true } );
22436 cur.setActive(false);
22437 pan.setActive(true);
22442 showPanelNext : function()
22444 var i = this.indexOfPanel(this.getActivePanel());
22446 if (i >= this.tabs.length - 1 && !this.autoslide) {
22450 if (i >= this.tabs.length - 1 && this.autoslide) {
22454 this.showPanel(this.tabs[i+1]);
22457 showPanelPrev : function()
22459 var i = this.indexOfPanel(this.getActivePanel());
22461 if (i < 1 && !this.autoslide) {
22465 if (i < 1 && this.autoslide) {
22466 i = this.tabs.length;
22469 this.showPanel(this.tabs[i-1]);
22473 addBullet: function()
22475 if(!this.bullets || Roo.isTouch){
22478 var ctr = this.el.select('.carousel-bullets',true).first();
22479 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22480 var bullet = ctr.createChild({
22481 cls : 'bullet bullet-' + i
22482 },ctr.dom.lastChild);
22487 bullet.on('click', (function(e, el, o, ii, t){
22489 e.preventDefault();
22491 this.showPanel(ii);
22493 if(this.autoslide && this.slideFn){
22494 clearInterval(this.slideFn);
22495 this.slideFn = window.setInterval(function() {
22496 _this.showPanelNext();
22500 }).createDelegate(this, [i, bullet], true));
22505 setActiveBullet : function(i)
22511 Roo.each(this.el.select('.bullet', true).elements, function(el){
22512 el.removeClass('selected');
22515 var bullet = this.el.select('.bullet-' + i, true).first();
22521 bullet.addClass('selected');
22532 Roo.apply(Roo.bootstrap.TabGroup, {
22536 * register a Navigation Group
22537 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22539 register : function(navgrp)
22541 this.groups[navgrp.navId] = navgrp;
22545 * fetch a Navigation Group based on the navigation ID
22546 * if one does not exist , it will get created.
22547 * @param {string} the navgroup to add
22548 * @returns {Roo.bootstrap.nav.Group} the navgroup
22550 get: function(navId) {
22551 if (typeof(this.groups[navId]) == 'undefined') {
22552 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22554 return this.groups[navId] ;
22569 * @class Roo.bootstrap.TabPanel
22570 * @extends Roo.bootstrap.Component
22571 * @children Roo.bootstrap.Component
22572 * Bootstrap TabPanel class
22573 * @cfg {Boolean} active panel active
22574 * @cfg {String} html panel content
22575 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22576 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22577 * @cfg {String} href click to link..
22578 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22582 * Create a new TabPanel
22583 * @param {Object} config The config object
22586 Roo.bootstrap.TabPanel = function(config){
22587 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22591 * Fires when the active status changes
22592 * @param {Roo.bootstrap.TabPanel} this
22593 * @param {Boolean} state the new state
22598 * @event beforedeactivate
22599 * Fires before a tab is de-activated - can be used to do validation on a form.
22600 * @param {Roo.bootstrap.TabPanel} this
22601 * @return {Boolean} false if there is an error
22604 'beforedeactivate': true
22607 this.tabId = this.tabId || Roo.id();
22611 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22618 touchSlide : false,
22619 getAutoCreate : function(){
22624 // item is needed for carousel - not sure if it has any effect otherwise
22625 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22626 html: this.html || ''
22630 cfg.cls += ' active';
22634 cfg.tabId = this.tabId;
22642 initEvents: function()
22644 var p = this.parent();
22646 this.navId = this.navId || p.navId;
22648 if (typeof(this.navId) != 'undefined') {
22649 // not really needed.. but just in case.. parent should be a NavGroup.
22650 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22654 var i = tg.tabs.length - 1;
22656 if(this.active && tg.bullets > 0 && i < tg.bullets){
22657 tg.setActiveBullet(i);
22661 this.el.on('click', this.onClick, this);
22663 if(Roo.isTouch && this.touchSlide){
22664 this.el.on("touchstart", this.onTouchStart, this);
22665 this.el.on("touchmove", this.onTouchMove, this);
22666 this.el.on("touchend", this.onTouchEnd, this);
22671 onRender : function(ct, position)
22673 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22676 setActive : function(state)
22678 Roo.log("panel - set active " + this.tabId + "=" + state);
22680 this.active = state;
22682 this.el.removeClass('active');
22684 } else if (!this.el.hasClass('active')) {
22685 this.el.addClass('active');
22688 this.fireEvent('changed', this, state);
22691 onClick : function(e)
22693 e.preventDefault();
22695 if(!this.href.length){
22699 window.location.href = this.href;
22708 onTouchStart : function(e)
22710 this.swiping = false;
22712 this.startX = e.browserEvent.touches[0].clientX;
22713 this.startY = e.browserEvent.touches[0].clientY;
22716 onTouchMove : function(e)
22718 this.swiping = true;
22720 this.endX = e.browserEvent.touches[0].clientX;
22721 this.endY = e.browserEvent.touches[0].clientY;
22724 onTouchEnd : function(e)
22731 var tabGroup = this.parent();
22733 if(this.endX > this.startX){ // swiping right
22734 tabGroup.showPanelPrev();
22738 if(this.startX > this.endX){ // swiping left
22739 tabGroup.showPanelNext();
22758 * @class Roo.bootstrap.form.DateField
22759 * @extends Roo.bootstrap.form.Input
22760 * Bootstrap DateField class
22761 * @cfg {Number} weekStart default 0
22762 * @cfg {String} viewMode default empty, (months|years)
22763 * @cfg {String} minViewMode default empty, (months|years)
22764 * @cfg {Number} startDate default -Infinity
22765 * @cfg {Number} endDate default Infinity
22766 * @cfg {Boolean} todayHighlight default false
22767 * @cfg {Boolean} todayBtn default false
22768 * @cfg {Boolean} calendarWeeks default false
22769 * @cfg {Object} daysOfWeekDisabled default empty
22770 * @cfg {Boolean} singleMode default false (true | false)
22772 * @cfg {Boolean} keyboardNavigation default true
22773 * @cfg {String} language default en
22776 * Create a new DateField
22777 * @param {Object} config The config object
22780 Roo.bootstrap.form.DateField = function(config){
22781 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22785 * Fires when this field show.
22786 * @param {Roo.bootstrap.form.DateField} this
22787 * @param {Mixed} date The date value
22792 * Fires when this field hide.
22793 * @param {Roo.bootstrap.form.DateField} this
22794 * @param {Mixed} date The date value
22799 * Fires when select a date.
22800 * @param {Roo.bootstrap.form.DateField} this
22801 * @param {Mixed} date The date value
22805 * @event beforeselect
22806 * Fires when before select a date.
22807 * @param {Roo.bootstrap.form.DateField} this
22808 * @param {Mixed} date The date value
22810 beforeselect : true
22814 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22817 * @cfg {String} format
22818 * The default date format string which can be overriden for localization support. The format must be
22819 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22823 * @cfg {String} altFormats
22824 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22825 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22827 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22835 todayHighlight : false,
22841 keyboardNavigation: true,
22843 calendarWeeks: false,
22845 startDate: -Infinity,
22849 daysOfWeekDisabled: [],
22853 singleMode : false,
22855 UTCDate: function()
22857 return new Date(Date.UTC.apply(Date, arguments));
22860 UTCToday: function()
22862 var today = new Date();
22863 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22866 getDate: function() {
22867 var d = this.getUTCDate();
22868 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22871 getUTCDate: function() {
22875 setDate: function(d) {
22876 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22879 setUTCDate: function(d) {
22881 this.setValue(this.formatDate(this.date));
22884 onRender: function(ct, position)
22887 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22889 this.language = this.language || 'en';
22890 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22891 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22893 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22894 this.format = this.format || 'm/d/y';
22895 this.isInline = false;
22896 this.isInput = true;
22897 this.component = this.el.select('.add-on', true).first() || false;
22898 this.component = (this.component && this.component.length === 0) ? false : this.component;
22899 this.hasInput = this.component && this.inputEl().length;
22901 if (typeof(this.minViewMode === 'string')) {
22902 switch (this.minViewMode) {
22904 this.minViewMode = 1;
22907 this.minViewMode = 2;
22910 this.minViewMode = 0;
22915 if (typeof(this.viewMode === 'string')) {
22916 switch (this.viewMode) {
22929 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22931 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22933 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22935 this.picker().on('mousedown', this.onMousedown, this);
22936 this.picker().on('click', this.onClick, this);
22938 this.picker().addClass('datepicker-dropdown');
22940 this.startViewMode = this.viewMode;
22942 if(this.singleMode){
22943 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22944 v.setVisibilityMode(Roo.Element.DISPLAY);
22948 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22949 v.setStyle('width', '189px');
22953 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22954 if(!this.calendarWeeks){
22959 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22960 v.attr('colspan', function(i, val){
22961 return parseInt(val) + 1;
22966 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22968 this.setStartDate(this.startDate);
22969 this.setEndDate(this.endDate);
22971 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22978 if(this.isInline) {
22983 picker : function()
22985 return this.pickerEl;
22986 // return this.el.select('.datepicker', true).first();
22989 fillDow: function()
22991 var dowCnt = this.weekStart;
23000 if(this.calendarWeeks){
23008 while (dowCnt < this.weekStart + 7) {
23012 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23016 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23019 fillMonths: function()
23022 var months = this.picker().select('>.datepicker-months td', true).first();
23024 months.dom.innerHTML = '';
23030 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23033 months.createChild(month);
23040 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;
23042 if (this.date < this.startDate) {
23043 this.viewDate = new Date(this.startDate);
23044 } else if (this.date > this.endDate) {
23045 this.viewDate = new Date(this.endDate);
23047 this.viewDate = new Date(this.date);
23055 var d = new Date(this.viewDate),
23056 year = d.getUTCFullYear(),
23057 month = d.getUTCMonth(),
23058 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23059 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23060 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23061 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23062 currentDate = this.date && this.date.valueOf(),
23063 today = this.UTCToday();
23065 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23067 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23069 // this.picker.select('>tfoot th.today').
23070 // .text(dates[this.language].today)
23071 // .toggle(this.todayBtn !== false);
23073 this.updateNavArrows();
23076 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23078 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23080 prevMonth.setUTCDate(day);
23082 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23084 var nextMonth = new Date(prevMonth);
23086 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23088 nextMonth = nextMonth.valueOf();
23090 var fillMonths = false;
23092 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23094 while(prevMonth.valueOf() <= nextMonth) {
23097 if (prevMonth.getUTCDay() === this.weekStart) {
23099 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23107 if(this.calendarWeeks){
23108 // ISO 8601: First week contains first thursday.
23109 // ISO also states week starts on Monday, but we can be more abstract here.
23111 // Start of current week: based on weekstart/current date
23112 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23113 // Thursday of this week
23114 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23115 // First Thursday of year, year from thursday
23116 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23117 // Calendar week: ms between thursdays, div ms per day, div 7 days
23118 calWeek = (th - yth) / 864e5 / 7 + 1;
23120 fillMonths.cn.push({
23128 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23130 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23133 if (this.todayHighlight &&
23134 prevMonth.getUTCFullYear() == today.getFullYear() &&
23135 prevMonth.getUTCMonth() == today.getMonth() &&
23136 prevMonth.getUTCDate() == today.getDate()) {
23137 clsName += ' today';
23140 if (currentDate && prevMonth.valueOf() === currentDate) {
23141 clsName += ' active';
23144 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23145 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23146 clsName += ' disabled';
23149 fillMonths.cn.push({
23151 cls: 'day ' + clsName,
23152 html: prevMonth.getDate()
23155 prevMonth.setDate(prevMonth.getDate()+1);
23158 var currentYear = this.date && this.date.getUTCFullYear();
23159 var currentMonth = this.date && this.date.getUTCMonth();
23161 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23163 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23164 v.removeClass('active');
23166 if(currentYear === year && k === currentMonth){
23167 v.addClass('active');
23170 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23171 v.addClass('disabled');
23177 year = parseInt(year/10, 10) * 10;
23179 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23181 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23184 for (var i = -1; i < 11; i++) {
23185 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23187 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23195 showMode: function(dir)
23198 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23201 Roo.each(this.picker().select('>div',true).elements, function(v){
23202 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23205 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23210 if(this.isInline) {
23214 this.picker().removeClass(['bottom', 'top']);
23216 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23218 * place to the top of element!
23222 this.picker().addClass('top');
23223 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23228 this.picker().addClass('bottom');
23230 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23233 parseDate : function(value)
23235 if(!value || value instanceof Date){
23238 var v = Date.parseDate(value, this.format);
23239 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23240 v = Date.parseDate(value, 'Y-m-d');
23242 if(!v && this.altFormats){
23243 if(!this.altFormatsArray){
23244 this.altFormatsArray = this.altFormats.split("|");
23246 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23247 v = Date.parseDate(value, this.altFormatsArray[i]);
23253 formatDate : function(date, fmt)
23255 return (!date || !(date instanceof Date)) ?
23256 date : date.dateFormat(fmt || this.format);
23259 onFocus : function()
23261 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23265 onBlur : function()
23267 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23269 var d = this.inputEl().getValue();
23276 showPopup : function()
23278 this.picker().show();
23282 this.fireEvent('showpopup', this, this.date);
23285 hidePopup : function()
23287 if(this.isInline) {
23290 this.picker().hide();
23291 this.viewMode = this.startViewMode;
23294 this.fireEvent('hidepopup', this, this.date);
23298 onMousedown: function(e)
23300 e.stopPropagation();
23301 e.preventDefault();
23306 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23310 setValue: function(v)
23312 if(this.fireEvent('beforeselect', this, v) !== false){
23313 var d = new Date(this.parseDate(v) ).clearTime();
23315 if(isNaN(d.getTime())){
23316 this.date = this.viewDate = '';
23317 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23321 v = this.formatDate(d);
23323 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23325 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23329 this.fireEvent('select', this, this.date);
23333 getValue: function()
23335 return this.formatDate(this.date);
23338 fireKey: function(e)
23340 if (!this.picker().isVisible()){
23341 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23347 var dateChanged = false,
23349 newDate, newViewDate;
23354 e.preventDefault();
23358 if (!this.keyboardNavigation) {
23361 dir = e.keyCode == 37 ? -1 : 1;
23364 newDate = this.moveYear(this.date, dir);
23365 newViewDate = this.moveYear(this.viewDate, dir);
23366 } else if (e.shiftKey){
23367 newDate = this.moveMonth(this.date, dir);
23368 newViewDate = this.moveMonth(this.viewDate, dir);
23370 newDate = new Date(this.date);
23371 newDate.setUTCDate(this.date.getUTCDate() + dir);
23372 newViewDate = new Date(this.viewDate);
23373 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23375 if (this.dateWithinRange(newDate)){
23376 this.date = newDate;
23377 this.viewDate = newViewDate;
23378 this.setValue(this.formatDate(this.date));
23380 e.preventDefault();
23381 dateChanged = true;
23386 if (!this.keyboardNavigation) {
23389 dir = e.keyCode == 38 ? -1 : 1;
23391 newDate = this.moveYear(this.date, dir);
23392 newViewDate = this.moveYear(this.viewDate, dir);
23393 } else if (e.shiftKey){
23394 newDate = this.moveMonth(this.date, dir);
23395 newViewDate = this.moveMonth(this.viewDate, dir);
23397 newDate = new Date(this.date);
23398 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23399 newViewDate = new Date(this.viewDate);
23400 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23402 if (this.dateWithinRange(newDate)){
23403 this.date = newDate;
23404 this.viewDate = newViewDate;
23405 this.setValue(this.formatDate(this.date));
23407 e.preventDefault();
23408 dateChanged = true;
23412 this.setValue(this.formatDate(this.date));
23414 e.preventDefault();
23417 this.setValue(this.formatDate(this.date));
23431 onClick: function(e)
23433 e.stopPropagation();
23434 e.preventDefault();
23436 var target = e.getTarget();
23438 if(target.nodeName.toLowerCase() === 'i'){
23439 target = Roo.get(target).dom.parentNode;
23442 var nodeName = target.nodeName;
23443 var className = target.className;
23444 var html = target.innerHTML;
23445 //Roo.log(nodeName);
23447 switch(nodeName.toLowerCase()) {
23449 switch(className) {
23455 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23456 switch(this.viewMode){
23458 this.viewDate = this.moveMonth(this.viewDate, dir);
23462 this.viewDate = this.moveYear(this.viewDate, dir);
23468 var date = new Date();
23469 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23471 this.setValue(this.formatDate(this.date));
23478 if (className.indexOf('disabled') < 0) {
23479 if (!this.viewDate) {
23480 this.viewDate = new Date();
23482 this.viewDate.setUTCDate(1);
23483 if (className.indexOf('month') > -1) {
23484 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23486 var year = parseInt(html, 10) || 0;
23487 this.viewDate.setUTCFullYear(year);
23491 if(this.singleMode){
23492 this.setValue(this.formatDate(this.viewDate));
23503 //Roo.log(className);
23504 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23505 var day = parseInt(html, 10) || 1;
23506 var year = (this.viewDate || new Date()).getUTCFullYear(),
23507 month = (this.viewDate || new Date()).getUTCMonth();
23509 if (className.indexOf('old') > -1) {
23516 } else if (className.indexOf('new') > -1) {
23524 //Roo.log([year,month,day]);
23525 this.date = this.UTCDate(year, month, day,0,0,0,0);
23526 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23528 //Roo.log(this.formatDate(this.date));
23529 this.setValue(this.formatDate(this.date));
23536 setStartDate: function(startDate)
23538 this.startDate = startDate || -Infinity;
23539 if (this.startDate !== -Infinity) {
23540 this.startDate = this.parseDate(this.startDate);
23543 this.updateNavArrows();
23546 setEndDate: function(endDate)
23548 this.endDate = endDate || Infinity;
23549 if (this.endDate !== Infinity) {
23550 this.endDate = this.parseDate(this.endDate);
23553 this.updateNavArrows();
23556 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23558 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23559 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23560 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23562 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23563 return parseInt(d, 10);
23566 this.updateNavArrows();
23569 updateNavArrows: function()
23571 if(this.singleMode){
23575 var d = new Date(this.viewDate),
23576 year = d.getUTCFullYear(),
23577 month = d.getUTCMonth();
23579 Roo.each(this.picker().select('.prev', true).elements, function(v){
23581 switch (this.viewMode) {
23584 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23590 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23597 Roo.each(this.picker().select('.next', true).elements, function(v){
23599 switch (this.viewMode) {
23602 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23608 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23616 moveMonth: function(date, dir)
23621 var new_date = new Date(date.valueOf()),
23622 day = new_date.getUTCDate(),
23623 month = new_date.getUTCMonth(),
23624 mag = Math.abs(dir),
23626 dir = dir > 0 ? 1 : -1;
23629 // If going back one month, make sure month is not current month
23630 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23632 return new_date.getUTCMonth() == month;
23634 // If going forward one month, make sure month is as expected
23635 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23637 return new_date.getUTCMonth() != new_month;
23639 new_month = month + dir;
23640 new_date.setUTCMonth(new_month);
23641 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23642 if (new_month < 0 || new_month > 11) {
23643 new_month = (new_month + 12) % 12;
23646 // For magnitudes >1, move one month at a time...
23647 for (var i=0; i<mag; i++) {
23648 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23649 new_date = this.moveMonth(new_date, dir);
23651 // ...then reset the day, keeping it in the new month
23652 new_month = new_date.getUTCMonth();
23653 new_date.setUTCDate(day);
23655 return new_month != new_date.getUTCMonth();
23658 // Common date-resetting loop -- if date is beyond end of month, make it
23661 new_date.setUTCDate(--day);
23662 new_date.setUTCMonth(new_month);
23667 moveYear: function(date, dir)
23669 return this.moveMonth(date, dir*12);
23672 dateWithinRange: function(date)
23674 return date >= this.startDate && date <= this.endDate;
23680 this.picker().remove();
23683 validateValue : function(value)
23685 if(this.getVisibilityEl().hasClass('hidden')){
23689 if(value.length < 1) {
23690 if(this.allowBlank){
23696 if(value.length < this.minLength){
23699 if(value.length > this.maxLength){
23703 var vt = Roo.form.VTypes;
23704 if(!vt[this.vtype](value, this)){
23708 if(typeof this.validator == "function"){
23709 var msg = this.validator(value);
23715 if(this.regex && !this.regex.test(value)){
23719 if(typeof(this.parseDate(value)) == 'undefined'){
23723 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23727 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23737 this.date = this.viewDate = '';
23739 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23744 Roo.apply(Roo.bootstrap.form.DateField, {
23755 html: '<i class="fa fa-arrow-left"/>'
23765 html: '<i class="fa fa-arrow-right"/>'
23807 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23808 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23809 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23810 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23811 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23824 navFnc: 'FullYear',
23829 navFnc: 'FullYear',
23834 Roo.apply(Roo.bootstrap.form.DateField, {
23838 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23842 cls: 'datepicker-days',
23846 cls: 'table-condensed',
23848 Roo.bootstrap.form.DateField.head,
23852 Roo.bootstrap.form.DateField.footer
23859 cls: 'datepicker-months',
23863 cls: 'table-condensed',
23865 Roo.bootstrap.form.DateField.head,
23866 Roo.bootstrap.form.DateField.content,
23867 Roo.bootstrap.form.DateField.footer
23874 cls: 'datepicker-years',
23878 cls: 'table-condensed',
23880 Roo.bootstrap.form.DateField.head,
23881 Roo.bootstrap.form.DateField.content,
23882 Roo.bootstrap.form.DateField.footer
23901 * @class Roo.bootstrap.form.TimeField
23902 * @extends Roo.bootstrap.form.Input
23903 * Bootstrap DateField class
23907 * Create a new TimeField
23908 * @param {Object} config The config object
23911 Roo.bootstrap.form.TimeField = function(config){
23912 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23916 * Fires when this field show.
23917 * @param {Roo.bootstrap.form.DateField} thisthis
23918 * @param {Mixed} date The date value
23923 * Fires when this field hide.
23924 * @param {Roo.bootstrap.form.DateField} this
23925 * @param {Mixed} date The date value
23930 * Fires when select a date.
23931 * @param {Roo.bootstrap.form.DateField} this
23932 * @param {Mixed} date The date value
23938 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23941 * @cfg {String} format
23942 * The default time format string which can be overriden for localization support. The format must be
23943 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23947 getAutoCreate : function()
23949 this.after = '<i class="fa far fa-clock"></i>';
23950 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23954 onRender: function(ct, position)
23957 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23959 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23961 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23963 this.pop = this.picker().select('>.datepicker-time',true).first();
23964 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23966 this.picker().on('mousedown', this.onMousedown, this);
23967 this.picker().on('click', this.onClick, this);
23969 this.picker().addClass('datepicker-dropdown');
23974 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23975 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23976 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23977 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23978 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23979 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23983 fireKey: function(e){
23984 if (!this.picker().isVisible()){
23985 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23991 e.preventDefault();
23999 this.onTogglePeriod();
24002 this.onIncrementMinutes();
24005 this.onDecrementMinutes();
24014 onClick: function(e) {
24015 e.stopPropagation();
24016 e.preventDefault();
24019 picker : function()
24021 return this.pickerEl;
24024 fillTime: function()
24026 var time = this.pop.select('tbody', true).first();
24028 time.dom.innerHTML = '';
24043 cls: 'hours-up fa fas fa-chevron-up'
24063 cls: 'minutes-up fa fas fa-chevron-up'
24084 cls: 'timepicker-hour',
24099 cls: 'timepicker-minute',
24114 cls: 'btn btn-primary period',
24136 cls: 'hours-down fa fas fa-chevron-down'
24156 cls: 'minutes-down fa fas fa-chevron-down'
24174 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24181 var hours = this.time.getHours();
24182 var minutes = this.time.getMinutes();
24195 hours = hours - 12;
24199 hours = '0' + hours;
24203 minutes = '0' + minutes;
24206 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24207 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24208 this.pop.select('button', true).first().dom.innerHTML = period;
24214 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24216 var cls = ['bottom'];
24218 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24225 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24229 //this.picker().setXY(20000,20000);
24230 this.picker().addClass(cls.join('-'));
24234 Roo.each(cls, function(c){
24239 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24240 //_this.picker().setTop(_this.inputEl().getHeight());
24244 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24246 //_this.picker().setTop(0 - _this.picker().getHeight());
24251 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24255 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24263 onFocus : function()
24265 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24269 onBlur : function()
24271 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24277 this.picker().show();
24282 this.fireEvent('show', this, this.date);
24287 this.picker().hide();
24290 this.fireEvent('hide', this, this.date);
24293 setTime : function()
24296 this.setValue(this.time.format(this.format));
24298 this.fireEvent('select', this, this.date);
24303 onMousedown: function(e){
24304 e.stopPropagation();
24305 e.preventDefault();
24308 onIncrementHours: function()
24310 Roo.log('onIncrementHours');
24311 this.time = this.time.add(Date.HOUR, 1);
24316 onDecrementHours: function()
24318 Roo.log('onDecrementHours');
24319 this.time = this.time.add(Date.HOUR, -1);
24323 onIncrementMinutes: function()
24325 Roo.log('onIncrementMinutes');
24326 this.time = this.time.add(Date.MINUTE, 1);
24330 onDecrementMinutes: function()
24332 Roo.log('onDecrementMinutes');
24333 this.time = this.time.add(Date.MINUTE, -1);
24337 onTogglePeriod: function()
24339 Roo.log('onTogglePeriod');
24340 this.time = this.time.add(Date.HOUR, 12);
24348 Roo.apply(Roo.bootstrap.form.TimeField, {
24352 cls: 'datepicker dropdown-menu',
24356 cls: 'datepicker-time',
24360 cls: 'table-condensed',
24389 cls: 'btn btn-info ok',
24417 * @class Roo.bootstrap.form.MonthField
24418 * @extends Roo.bootstrap.form.Input
24419 * Bootstrap MonthField class
24421 * @cfg {String} language default en
24424 * Create a new MonthField
24425 * @param {Object} config The config object
24428 Roo.bootstrap.form.MonthField = function(config){
24429 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24434 * Fires when this field show.
24435 * @param {Roo.bootstrap.form.MonthField} this
24436 * @param {Mixed} date The date value
24441 * Fires when this field hide.
24442 * @param {Roo.bootstrap.form.MonthField} this
24443 * @param {Mixed} date The date value
24448 * Fires when select a date.
24449 * @param {Roo.bootstrap.form.MonthField} this
24450 * @param {String} oldvalue The old value
24451 * @param {String} newvalue The new value
24457 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24459 onRender: function(ct, position)
24462 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24464 this.language = this.language || 'en';
24465 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24466 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24468 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24469 this.isInline = false;
24470 this.isInput = true;
24471 this.component = this.el.select('.add-on', true).first() || false;
24472 this.component = (this.component && this.component.length === 0) ? false : this.component;
24473 this.hasInput = this.component && this.inputEL().length;
24475 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24477 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24479 this.picker().on('mousedown', this.onMousedown, this);
24480 this.picker().on('click', this.onClick, this);
24482 this.picker().addClass('datepicker-dropdown');
24484 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24485 v.setStyle('width', '189px');
24492 if(this.isInline) {
24498 setValue: function(v, suppressEvent)
24500 var o = this.getValue();
24502 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24506 if(suppressEvent !== true){
24507 this.fireEvent('select', this, o, v);
24512 getValue: function()
24517 onClick: function(e)
24519 e.stopPropagation();
24520 e.preventDefault();
24522 var target = e.getTarget();
24524 if(target.nodeName.toLowerCase() === 'i'){
24525 target = Roo.get(target).dom.parentNode;
24528 var nodeName = target.nodeName;
24529 var className = target.className;
24530 var html = target.innerHTML;
24532 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24536 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24538 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24544 picker : function()
24546 return this.pickerEl;
24549 fillMonths: function()
24552 var months = this.picker().select('>.datepicker-months td', true).first();
24554 months.dom.innerHTML = '';
24560 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24563 months.createChild(month);
24572 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24573 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24576 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24577 e.removeClass('active');
24579 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24580 e.addClass('active');
24587 if(this.isInline) {
24591 this.picker().removeClass(['bottom', 'top']);
24593 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24595 * place to the top of element!
24599 this.picker().addClass('top');
24600 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24605 this.picker().addClass('bottom');
24607 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24610 onFocus : function()
24612 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24616 onBlur : function()
24618 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24620 var d = this.inputEl().getValue();
24629 this.picker().show();
24630 this.picker().select('>.datepicker-months', true).first().show();
24634 this.fireEvent('show', this, this.date);
24639 if(this.isInline) {
24642 this.picker().hide();
24643 this.fireEvent('hide', this, this.date);
24647 onMousedown: function(e)
24649 e.stopPropagation();
24650 e.preventDefault();
24655 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24659 fireKey: function(e)
24661 if (!this.picker().isVisible()){
24662 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24673 e.preventDefault();
24677 dir = e.keyCode == 37 ? -1 : 1;
24679 this.vIndex = this.vIndex + dir;
24681 if(this.vIndex < 0){
24685 if(this.vIndex > 11){
24689 if(isNaN(this.vIndex)){
24693 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24699 dir = e.keyCode == 38 ? -1 : 1;
24701 this.vIndex = this.vIndex + dir * 4;
24703 if(this.vIndex < 0){
24707 if(this.vIndex > 11){
24711 if(isNaN(this.vIndex)){
24715 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24720 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24721 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24725 e.preventDefault();
24728 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24729 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24745 this.picker().remove();
24750 Roo.apply(Roo.bootstrap.form.MonthField, {
24769 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24770 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24775 Roo.apply(Roo.bootstrap.form.MonthField, {
24779 cls: 'datepicker dropdown-menu roo-dynamic',
24783 cls: 'datepicker-months',
24787 cls: 'table-condensed',
24789 Roo.bootstrap.form.DateField.content
24809 * @class Roo.bootstrap.form.CheckBox
24810 * @extends Roo.bootstrap.form.Input
24811 * Bootstrap CheckBox class
24813 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24814 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24815 * @cfg {String} boxLabel The text that appears beside the checkbox
24816 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24817 * @cfg {Boolean} checked initnal the element
24818 * @cfg {Boolean} inline inline the element (default false)
24819 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24820 * @cfg {String} tooltip label tooltip
24823 * Create a new CheckBox
24824 * @param {Object} config The config object
24827 Roo.bootstrap.form.CheckBox = function(config){
24828 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24833 * Fires when the element is checked or unchecked.
24834 * @param {Roo.bootstrap.form.CheckBox} this This input
24835 * @param {Boolean} checked The new checked value
24840 * Fires when the element is click.
24841 * @param {Roo.bootstrap.form.CheckBox} this This input
24848 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24850 inputType: 'checkbox',
24859 // checkbox success does not make any sense really..
24864 getAutoCreate : function()
24866 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24872 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24875 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24881 type : this.inputType,
24882 value : this.inputValue,
24883 cls : 'roo-' + this.inputType, //'form-box',
24884 placeholder : this.placeholder || ''
24888 if(this.inputType != 'radio'){
24892 cls : 'roo-hidden-value',
24893 value : this.checked ? this.inputValue : this.valueOff
24898 if (this.weight) { // Validity check?
24899 cfg.cls += " " + this.inputType + "-" + this.weight;
24902 if (this.disabled) {
24903 input.disabled=true;
24907 input.checked = this.checked;
24912 input.name = this.name;
24914 if(this.inputType != 'radio'){
24915 hidden.name = this.name;
24916 input.name = '_hidden_' + this.name;
24921 input.cls += ' input-' + this.size;
24926 ['xs','sm','md','lg'].map(function(size){
24927 if (settings[size]) {
24928 cfg.cls += ' col-' + size + '-' + settings[size];
24932 var inputblock = input;
24934 if (this.before || this.after) {
24937 cls : 'input-group',
24942 inputblock.cn.push({
24944 cls : 'input-group-addon',
24949 inputblock.cn.push(input);
24951 if(this.inputType != 'radio'){
24952 inputblock.cn.push(hidden);
24956 inputblock.cn.push({
24958 cls : 'input-group-addon',
24964 var boxLabelCfg = false;
24970 //'for': id, // box label is handled by onclick - so no for...
24972 html: this.boxLabel
24975 boxLabelCfg.tooltip = this.tooltip;
24981 if (align ==='left' && this.fieldLabel.length) {
24982 // Roo.log("left and has label");
24987 cls : 'control-label',
24988 html : this.fieldLabel
24999 cfg.cn[1].cn.push(boxLabelCfg);
25002 if(this.labelWidth > 12){
25003 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25006 if(this.labelWidth < 13 && this.labelmd == 0){
25007 this.labelmd = this.labelWidth;
25010 if(this.labellg > 0){
25011 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25012 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25015 if(this.labelmd > 0){
25016 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25017 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25020 if(this.labelsm > 0){
25021 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25022 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25025 if(this.labelxs > 0){
25026 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25027 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25030 } else if ( this.fieldLabel.length) {
25031 // Roo.log(" label");
25035 tag: this.boxLabel ? 'span' : 'label',
25037 cls: 'control-label box-input-label',
25038 //cls : 'input-group-addon',
25039 html : this.fieldLabel
25046 cfg.cn.push(boxLabelCfg);
25051 // Roo.log(" no label && no align");
25052 cfg.cn = [ inputblock ] ;
25054 cfg.cn.push(boxLabelCfg);
25062 if(this.inputType != 'radio'){
25063 cfg.cn.push(hidden);
25071 * return the real input element.
25073 inputEl: function ()
25075 return this.el.select('input.roo-' + this.inputType,true).first();
25077 hiddenEl: function ()
25079 return this.el.select('input.roo-hidden-value',true).first();
25082 labelEl: function()
25084 return this.el.select('label.control-label',true).first();
25086 /* depricated... */
25090 return this.labelEl();
25093 boxLabelEl: function()
25095 return this.el.select('label.box-label',true).first();
25098 initEvents : function()
25100 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25102 this.inputEl().on('click', this.onClick, this);
25104 if (this.boxLabel) {
25105 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25108 this.startValue = this.getValue();
25111 Roo.bootstrap.form.CheckBox.register(this);
25115 onClick : function(e)
25117 if(this.fireEvent('click', this, e) !== false){
25118 this.setChecked(!this.checked);
25123 setChecked : function(state,suppressEvent)
25125 this.startValue = this.getValue();
25127 if(this.inputType == 'radio'){
25129 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25130 e.dom.checked = false;
25133 this.inputEl().dom.checked = true;
25135 this.inputEl().dom.value = this.inputValue;
25137 if(suppressEvent !== true){
25138 this.fireEvent('check', this, true);
25146 this.checked = state;
25148 this.inputEl().dom.checked = state;
25151 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25153 if(suppressEvent !== true){
25154 this.fireEvent('check', this, state);
25160 getValue : function()
25162 if(this.inputType == 'radio'){
25163 return this.getGroupValue();
25166 return this.hiddenEl().dom.value;
25170 getGroupValue : function()
25172 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25176 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25179 setValue : function(v,suppressEvent)
25181 if(this.inputType == 'radio'){
25182 this.setGroupValue(v, suppressEvent);
25186 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25191 setGroupValue : function(v, suppressEvent)
25193 this.startValue = this.getValue();
25195 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25196 e.dom.checked = false;
25198 if(e.dom.value == v){
25199 e.dom.checked = true;
25203 if(suppressEvent !== true){
25204 this.fireEvent('check', this, true);
25212 validate : function()
25214 if(this.getVisibilityEl().hasClass('hidden')){
25220 (this.inputType == 'radio' && this.validateRadio()) ||
25221 (this.inputType == 'checkbox' && this.validateCheckbox())
25227 this.markInvalid();
25231 validateRadio : function()
25233 if(this.getVisibilityEl().hasClass('hidden')){
25237 if(this.allowBlank){
25243 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25244 if(!e.dom.checked){
25256 validateCheckbox : function()
25259 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25260 //return (this.getValue() == this.inputValue) ? true : false;
25263 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25271 for(var i in group){
25272 if(group[i].el.isVisible(true)){
25280 for(var i in group){
25285 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25292 * Mark this field as valid
25294 markValid : function()
25298 this.fireEvent('valid', this);
25300 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25303 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25310 if(this.inputType == 'radio'){
25311 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25312 var fg = e.findParent('.form-group', false, true);
25313 if (Roo.bootstrap.version == 3) {
25314 fg.removeClass([_this.invalidClass, _this.validClass]);
25315 fg.addClass(_this.validClass);
25317 fg.removeClass(['is-valid', 'is-invalid']);
25318 fg.addClass('is-valid');
25326 var fg = this.el.findParent('.form-group', false, true);
25327 if (Roo.bootstrap.version == 3) {
25328 fg.removeClass([this.invalidClass, this.validClass]);
25329 fg.addClass(this.validClass);
25331 fg.removeClass(['is-valid', 'is-invalid']);
25332 fg.addClass('is-valid');
25337 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25343 for(var i in group){
25344 var fg = group[i].el.findParent('.form-group', false, true);
25345 if (Roo.bootstrap.version == 3) {
25346 fg.removeClass([this.invalidClass, this.validClass]);
25347 fg.addClass(this.validClass);
25349 fg.removeClass(['is-valid', 'is-invalid']);
25350 fg.addClass('is-valid');
25356 * Mark this field as invalid
25357 * @param {String} msg The validation message
25359 markInvalid : function(msg)
25361 if(this.allowBlank){
25367 this.fireEvent('invalid', this, msg);
25369 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25372 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25376 label.markInvalid();
25379 if(this.inputType == 'radio'){
25381 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25382 var fg = e.findParent('.form-group', false, true);
25383 if (Roo.bootstrap.version == 3) {
25384 fg.removeClass([_this.invalidClass, _this.validClass]);
25385 fg.addClass(_this.invalidClass);
25387 fg.removeClass(['is-invalid', 'is-valid']);
25388 fg.addClass('is-invalid');
25396 var fg = this.el.findParent('.form-group', false, true);
25397 if (Roo.bootstrap.version == 3) {
25398 fg.removeClass([_this.invalidClass, _this.validClass]);
25399 fg.addClass(_this.invalidClass);
25401 fg.removeClass(['is-invalid', 'is-valid']);
25402 fg.addClass('is-invalid');
25407 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25413 for(var i in group){
25414 var fg = group[i].el.findParent('.form-group', false, true);
25415 if (Roo.bootstrap.version == 3) {
25416 fg.removeClass([_this.invalidClass, _this.validClass]);
25417 fg.addClass(_this.invalidClass);
25419 fg.removeClass(['is-invalid', 'is-valid']);
25420 fg.addClass('is-invalid');
25426 clearInvalid : function()
25428 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25430 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25432 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25434 if (label && label.iconEl) {
25435 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25436 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25440 disable : function()
25442 if(this.inputType != 'radio'){
25443 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25450 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25451 _this.getActionEl().addClass(this.disabledClass);
25452 e.dom.disabled = true;
25456 this.disabled = true;
25457 this.fireEvent("disable", this);
25461 enable : function()
25463 if(this.inputType != 'radio'){
25464 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25471 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25472 _this.getActionEl().removeClass(this.disabledClass);
25473 e.dom.disabled = false;
25477 this.disabled = false;
25478 this.fireEvent("enable", this);
25482 setBoxLabel : function(v)
25487 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25493 Roo.apply(Roo.bootstrap.form.CheckBox, {
25498 * register a CheckBox Group
25499 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25501 register : function(checkbox)
25503 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25504 this.groups[checkbox.groupId] = {};
25507 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25511 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25515 * fetch a CheckBox Group based on the group ID
25516 * @param {string} the group ID
25517 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25519 get: function(groupId) {
25520 if (typeof(this.groups[groupId]) == 'undefined') {
25524 return this.groups[groupId] ;
25537 * @class Roo.bootstrap.form.Radio
25538 * @extends Roo.bootstrap.Component
25539 * Bootstrap Radio class
25540 * @cfg {String} boxLabel - the label associated
25541 * @cfg {String} value - the value of radio
25544 * Create a new Radio
25545 * @param {Object} config The config object
25547 Roo.bootstrap.form.Radio = function(config){
25548 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25552 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25558 getAutoCreate : function()
25562 cls : 'form-group radio',
25567 html : this.boxLabel
25575 initEvents : function()
25577 this.parent().register(this);
25579 this.el.on('click', this.onClick, this);
25583 onClick : function(e)
25585 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25586 this.setChecked(true);
25590 setChecked : function(state, suppressEvent)
25592 this.parent().setValue(this.value, suppressEvent);
25596 setBoxLabel : function(v)
25601 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25616 * @class Roo.bootstrap.form.SecurePass
25617 * @extends Roo.bootstrap.form.Input
25618 * Bootstrap SecurePass class
25622 * Create a new SecurePass
25623 * @param {Object} config The config object
25626 Roo.bootstrap.form.SecurePass = function (config) {
25627 // these go here, so the translation tool can replace them..
25629 PwdEmpty: "Please type a password, and then retype it to confirm.",
25630 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25631 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25632 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25633 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25634 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25635 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25636 TooWeak: "Your password is Too Weak."
25638 this.meterLabel = "Password strength:";
25639 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25640 this.meterClass = [
25641 "roo-password-meter-tooweak",
25642 "roo-password-meter-weak",
25643 "roo-password-meter-medium",
25644 "roo-password-meter-strong",
25645 "roo-password-meter-grey"
25650 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25653 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25655 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25657 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25658 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25659 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25660 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25661 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25662 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25663 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25673 * @cfg {String/Object} Label for the strength meter (defaults to
25674 * 'Password strength:')
25679 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25680 * ['Weak', 'Medium', 'Strong'])
25683 pwdStrengths: false,
25696 initEvents: function ()
25698 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25700 if (this.el.is('input[type=password]') && Roo.isSafari) {
25701 this.el.on('keydown', this.SafariOnKeyDown, this);
25704 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25707 onRender: function (ct, position)
25709 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25710 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25711 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25713 this.trigger.createChild({
25718 cls: 'roo-password-meter-grey col-xs-12',
25721 //width: this.meterWidth + 'px'
25725 cls: 'roo-password-meter-text'
25731 if (this.hideTrigger) {
25732 this.trigger.setDisplayed(false);
25734 this.setSize(this.width || '', this.height || '');
25737 onDestroy: function ()
25739 if (this.trigger) {
25740 this.trigger.removeAllListeners();
25741 this.trigger.remove();
25744 this.wrap.remove();
25746 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25749 checkStrength: function ()
25751 var pwd = this.inputEl().getValue();
25752 if (pwd == this._lastPwd) {
25757 if (this.ClientSideStrongPassword(pwd)) {
25759 } else if (this.ClientSideMediumPassword(pwd)) {
25761 } else if (this.ClientSideWeakPassword(pwd)) {
25767 Roo.log('strength1: ' + strength);
25769 //var pm = this.trigger.child('div/div/div').dom;
25770 var pm = this.trigger.child('div/div');
25771 pm.removeClass(this.meterClass);
25772 pm.addClass(this.meterClass[strength]);
25775 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25777 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25779 this._lastPwd = pwd;
25783 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25785 this._lastPwd = '';
25787 var pm = this.trigger.child('div/div');
25788 pm.removeClass(this.meterClass);
25789 pm.addClass('roo-password-meter-grey');
25792 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25795 this.inputEl().dom.type='password';
25798 validateValue: function (value)
25800 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25803 if (value.length == 0) {
25804 if (this.allowBlank) {
25805 this.clearInvalid();
25809 this.markInvalid(this.errors.PwdEmpty);
25810 this.errorMsg = this.errors.PwdEmpty;
25818 if (!value.match(/[\x21-\x7e]+/)) {
25819 this.markInvalid(this.errors.PwdBadChar);
25820 this.errorMsg = this.errors.PwdBadChar;
25823 if (value.length < 6) {
25824 this.markInvalid(this.errors.PwdShort);
25825 this.errorMsg = this.errors.PwdShort;
25828 if (value.length > 16) {
25829 this.markInvalid(this.errors.PwdLong);
25830 this.errorMsg = this.errors.PwdLong;
25834 if (this.ClientSideStrongPassword(value)) {
25836 } else if (this.ClientSideMediumPassword(value)) {
25838 } else if (this.ClientSideWeakPassword(value)) {
25845 if (strength < 2) {
25846 //this.markInvalid(this.errors.TooWeak);
25847 this.errorMsg = this.errors.TooWeak;
25852 console.log('strength2: ' + strength);
25854 //var pm = this.trigger.child('div/div/div').dom;
25856 var pm = this.trigger.child('div/div');
25857 pm.removeClass(this.meterClass);
25858 pm.addClass(this.meterClass[strength]);
25860 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25862 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25864 this.errorMsg = '';
25868 CharacterSetChecks: function (type)
25871 this.fResult = false;
25874 isctype: function (character, type)
25877 case this.kCapitalLetter:
25878 if (character >= 'A' && character <= 'Z') {
25883 case this.kSmallLetter:
25884 if (character >= 'a' && character <= 'z') {
25890 if (character >= '0' && character <= '9') {
25895 case this.kPunctuation:
25896 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25907 IsLongEnough: function (pwd, size)
25909 return !(pwd == null || isNaN(size) || pwd.length < size);
25912 SpansEnoughCharacterSets: function (word, nb)
25914 if (!this.IsLongEnough(word, nb))
25919 var characterSetChecks = new Array(
25920 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25921 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25924 for (var index = 0; index < word.length; ++index) {
25925 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25926 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25927 characterSetChecks[nCharSet].fResult = true;
25934 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25935 if (characterSetChecks[nCharSet].fResult) {
25940 if (nCharSets < nb) {
25946 ClientSideStrongPassword: function (pwd)
25948 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25951 ClientSideMediumPassword: function (pwd)
25953 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25956 ClientSideWeakPassword: function (pwd)
25958 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25961 })//<script type="text/javascript">
25964 * Based Ext JS Library 1.1.1
25965 * Copyright(c) 2006-2007, Ext JS, LLC.
25971 * @class Roo.HtmlEditorCore
25972 * @extends Roo.Component
25973 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25975 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25978 Roo.HtmlEditorCore = function(config){
25981 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25986 * @event initialize
25987 * Fires when the editor is fully initialized (including the iframe)
25988 * @param {Roo.HtmlEditorCore} this
25993 * Fires when the editor is first receives the focus. Any insertion must wait
25994 * until after this event.
25995 * @param {Roo.HtmlEditorCore} this
25999 * @event beforesync
26000 * Fires before the textarea is updated with content from the editor iframe. Return false
26001 * to cancel the sync.
26002 * @param {Roo.HtmlEditorCore} this
26003 * @param {String} html
26007 * @event beforepush
26008 * Fires before the iframe editor is updated with content from the textarea. Return false
26009 * to cancel the push.
26010 * @param {Roo.HtmlEditorCore} this
26011 * @param {String} html
26016 * Fires when the textarea is updated with content from the editor iframe.
26017 * @param {Roo.HtmlEditorCore} this
26018 * @param {String} html
26023 * Fires when the iframe editor is updated with content from the textarea.
26024 * @param {Roo.HtmlEditorCore} this
26025 * @param {String} html
26030 * @event editorevent
26031 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26032 * @param {Roo.HtmlEditorCore} this
26038 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26040 // defaults : white / black...
26041 this.applyBlacklists();
26048 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26052 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26058 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26063 * @cfg {Number} height (in pixels)
26067 * @cfg {Number} width (in pixels)
26072 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26075 stylesheets: false,
26078 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26080 allowComments: false,
26084 // private properties
26085 validationEvent : false,
26087 initialized : false,
26089 sourceEditMode : false,
26090 onFocus : Roo.emptyFn,
26092 hideMode:'offsets',
26096 // blacklist + whitelisted elements..
26103 * Protected method that will not generally be called directly. It
26104 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26105 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26107 getDocMarkup : function(){
26111 // inherit styels from page...??
26112 if (this.stylesheets === false) {
26114 Roo.get(document.head).select('style').each(function(node) {
26115 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26118 Roo.get(document.head).select('link').each(function(node) {
26119 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26122 } else if (!this.stylesheets.length) {
26124 st = '<style type="text/css">' +
26125 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26128 for (var i in this.stylesheets) {
26129 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26134 st += '<style type="text/css">' +
26135 'IMG { cursor: pointer } ' +
26138 var cls = 'roo-htmleditor-body';
26140 if(this.bodyCls.length){
26141 cls += ' ' + this.bodyCls;
26144 return '<html><head>' + st +
26145 //<style type="text/css">' +
26146 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26148 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26152 onRender : function(ct, position)
26155 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26156 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26159 this.el.dom.style.border = '0 none';
26160 this.el.dom.setAttribute('tabIndex', -1);
26161 this.el.addClass('x-hidden hide');
26165 if(Roo.isIE){ // fix IE 1px bogus margin
26166 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26170 this.frameId = Roo.id();
26174 var iframe = this.owner.wrap.createChild({
26176 cls: 'form-control', // bootstrap..
26178 name: this.frameId,
26179 frameBorder : 'no',
26180 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26185 this.iframe = iframe.dom;
26187 this.assignDocWin();
26189 this.doc.designMode = 'on';
26192 this.doc.write(this.getDocMarkup());
26196 var task = { // must defer to wait for browser to be ready
26198 //console.log("run task?" + this.doc.readyState);
26199 this.assignDocWin();
26200 if(this.doc.body || this.doc.readyState == 'complete'){
26202 this.doc.designMode="on";
26206 Roo.TaskMgr.stop(task);
26207 this.initEditor.defer(10, this);
26214 Roo.TaskMgr.start(task);
26219 onResize : function(w, h)
26221 Roo.log('resize: ' +w + ',' + h );
26222 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26226 if(typeof w == 'number'){
26228 this.iframe.style.width = w + 'px';
26230 if(typeof h == 'number'){
26232 this.iframe.style.height = h + 'px';
26234 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26241 * Toggles the editor between standard and source edit mode.
26242 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26244 toggleSourceEdit : function(sourceEditMode){
26246 this.sourceEditMode = sourceEditMode === true;
26248 if(this.sourceEditMode){
26250 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26253 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26254 //this.iframe.className = '';
26257 //this.setSize(this.owner.wrap.getSize());
26258 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26265 * Protected method that will not generally be called directly. If you need/want
26266 * custom HTML cleanup, this is the method you should override.
26267 * @param {String} html The HTML to be cleaned
26268 * return {String} The cleaned HTML
26270 cleanHtml : function(html){
26271 html = String(html);
26272 if(html.length > 5){
26273 if(Roo.isSafari){ // strip safari nonsense
26274 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26277 if(html == ' '){
26284 * HTML Editor -> Textarea
26285 * Protected method that will not generally be called directly. Syncs the contents
26286 * of the editor iframe with the textarea.
26288 syncValue : function(){
26289 if(this.initialized){
26290 var bd = (this.doc.body || this.doc.documentElement);
26291 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26292 var html = bd.innerHTML;
26294 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26295 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26297 html = '<div style="'+m[0]+'">' + html + '</div>';
26300 html = this.cleanHtml(html);
26301 // fix up the special chars.. normaly like back quotes in word...
26302 // however we do not want to do this with chinese..
26303 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26305 var cc = match.charCodeAt();
26307 // Get the character value, handling surrogate pairs
26308 if (match.length == 2) {
26309 // It's a surrogate pair, calculate the Unicode code point
26310 var high = match.charCodeAt(0) - 0xD800;
26311 var low = match.charCodeAt(1) - 0xDC00;
26312 cc = (high * 0x400) + low + 0x10000;
26314 (cc >= 0x4E00 && cc < 0xA000 ) ||
26315 (cc >= 0x3400 && cc < 0x4E00 ) ||
26316 (cc >= 0xf900 && cc < 0xfb00 )
26321 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26322 return "&#" + cc + ";";
26329 if(this.owner.fireEvent('beforesync', this, html) !== false){
26330 this.el.dom.value = html;
26331 this.owner.fireEvent('sync', this, html);
26337 * Protected method that will not generally be called directly. Pushes the value of the textarea
26338 * into the iframe editor.
26340 pushValue : function(){
26341 if(this.initialized){
26342 var v = this.el.dom.value.trim();
26344 // if(v.length < 1){
26348 if(this.owner.fireEvent('beforepush', this, v) !== false){
26349 var d = (this.doc.body || this.doc.documentElement);
26351 this.cleanUpPaste();
26352 this.el.dom.value = d.innerHTML;
26353 this.owner.fireEvent('push', this, v);
26359 deferFocus : function(){
26360 this.focus.defer(10, this);
26364 focus : function(){
26365 if(this.win && !this.sourceEditMode){
26372 assignDocWin: function()
26374 var iframe = this.iframe;
26377 this.doc = iframe.contentWindow.document;
26378 this.win = iframe.contentWindow;
26380 // if (!Roo.get(this.frameId)) {
26383 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26384 // this.win = Roo.get(this.frameId).dom.contentWindow;
26386 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26390 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26391 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26396 initEditor : function(){
26397 //console.log("INIT EDITOR");
26398 this.assignDocWin();
26402 this.doc.designMode="on";
26404 this.doc.write(this.getDocMarkup());
26407 var dbody = (this.doc.body || this.doc.documentElement);
26408 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26409 // this copies styles from the containing element into thsi one..
26410 // not sure why we need all of this..
26411 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26413 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26414 //ss['background-attachment'] = 'fixed'; // w3c
26415 dbody.bgProperties = 'fixed'; // ie
26416 //Roo.DomHelper.applyStyles(dbody, ss);
26417 Roo.EventManager.on(this.doc, {
26418 //'mousedown': this.onEditorEvent,
26419 'mouseup': this.onEditorEvent,
26420 'dblclick': this.onEditorEvent,
26421 'click': this.onEditorEvent,
26422 'keyup': this.onEditorEvent,
26427 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26429 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26430 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26432 this.initialized = true;
26434 this.owner.fireEvent('initialize', this);
26439 onDestroy : function(){
26445 //for (var i =0; i < this.toolbars.length;i++) {
26446 // // fixme - ask toolbars for heights?
26447 // this.toolbars[i].onDestroy();
26450 //this.wrap.dom.innerHTML = '';
26451 //this.wrap.remove();
26456 onFirstFocus : function(){
26458 this.assignDocWin();
26461 this.activated = true;
26464 if(Roo.isGecko){ // prevent silly gecko errors
26466 var s = this.win.getSelection();
26467 if(!s.focusNode || s.focusNode.nodeType != 3){
26468 var r = s.getRangeAt(0);
26469 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26474 this.execCmd('useCSS', true);
26475 this.execCmd('styleWithCSS', false);
26478 this.owner.fireEvent('activate', this);
26482 adjustFont: function(btn){
26483 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26484 //if(Roo.isSafari){ // safari
26487 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26488 if(Roo.isSafari){ // safari
26489 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26490 v = (v < 10) ? 10 : v;
26491 v = (v > 48) ? 48 : v;
26492 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26497 v = Math.max(1, v+adjust);
26499 this.execCmd('FontSize', v );
26502 onEditorEvent : function(e)
26504 this.owner.fireEvent('editorevent', this, e);
26505 // this.updateToolbar();
26506 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26509 insertTag : function(tg)
26511 // could be a bit smarter... -> wrap the current selected tRoo..
26512 if (tg.toLowerCase() == 'span' ||
26513 tg.toLowerCase() == 'code' ||
26514 tg.toLowerCase() == 'sup' ||
26515 tg.toLowerCase() == 'sub'
26518 range = this.createRange(this.getSelection());
26519 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26520 wrappingNode.appendChild(range.extractContents());
26521 range.insertNode(wrappingNode);
26528 this.execCmd("formatblock", tg);
26532 insertText : function(txt)
26536 var range = this.createRange();
26537 range.deleteContents();
26538 //alert(Sender.getAttribute('label'));
26540 range.insertNode(this.doc.createTextNode(txt));
26546 * Executes a Midas editor command on the editor document and performs necessary focus and
26547 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26548 * @param {String} cmd The Midas command
26549 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26551 relayCmd : function(cmd, value){
26553 this.execCmd(cmd, value);
26554 this.owner.fireEvent('editorevent', this);
26555 //this.updateToolbar();
26556 this.owner.deferFocus();
26560 * Executes a Midas editor command directly on the editor document.
26561 * For visual commands, you should use {@link #relayCmd} instead.
26562 * <b>This should only be called after the editor is initialized.</b>
26563 * @param {String} cmd The Midas command
26564 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26566 execCmd : function(cmd, value){
26567 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26574 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26576 * @param {String} text | dom node..
26578 insertAtCursor : function(text)
26581 if(!this.activated){
26587 var r = this.doc.selection.createRange();
26598 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26602 // from jquery ui (MIT licenced)
26604 var win = this.win;
26606 if (win.getSelection && win.getSelection().getRangeAt) {
26607 range = win.getSelection().getRangeAt(0);
26608 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26609 range.insertNode(node);
26610 } else if (win.document.selection && win.document.selection.createRange) {
26611 // no firefox support
26612 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26613 win.document.selection.createRange().pasteHTML(txt);
26615 // no firefox support
26616 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26617 this.execCmd('InsertHTML', txt);
26626 mozKeyPress : function(e){
26628 var c = e.getCharCode(), cmd;
26631 c = String.fromCharCode(c).toLowerCase();
26645 this.cleanUpPaste.defer(100, this);
26653 e.preventDefault();
26661 fixKeys : function(){ // load time branching for fastest keydown performance
26663 return function(e){
26664 var k = e.getKey(), r;
26667 r = this.doc.selection.createRange();
26670 r.pasteHTML('    ');
26677 r = this.doc.selection.createRange();
26679 var target = r.parentElement();
26680 if(!target || target.tagName.toLowerCase() != 'li'){
26682 r.pasteHTML('<br />');
26688 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26689 this.cleanUpPaste.defer(100, this);
26695 }else if(Roo.isOpera){
26696 return function(e){
26697 var k = e.getKey();
26701 this.execCmd('InsertHTML','    ');
26704 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26705 this.cleanUpPaste.defer(100, this);
26710 }else if(Roo.isSafari){
26711 return function(e){
26712 var k = e.getKey();
26716 this.execCmd('InsertText','\t');
26720 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26721 this.cleanUpPaste.defer(100, this);
26729 getAllAncestors: function()
26731 var p = this.getSelectedNode();
26734 a.push(p); // push blank onto stack..
26735 p = this.getParentElement();
26739 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26743 a.push(this.doc.body);
26747 lastSelNode : false,
26750 getSelection : function()
26752 this.assignDocWin();
26753 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26756 getSelectedNode: function()
26758 // this may only work on Gecko!!!
26760 // should we cache this!!!!
26765 var range = this.createRange(this.getSelection()).cloneRange();
26768 var parent = range.parentElement();
26770 var testRange = range.duplicate();
26771 testRange.moveToElementText(parent);
26772 if (testRange.inRange(range)) {
26775 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26778 parent = parent.parentElement;
26783 // is ancestor a text element.
26784 var ac = range.commonAncestorContainer;
26785 if (ac.nodeType == 3) {
26786 ac = ac.parentNode;
26789 var ar = ac.childNodes;
26792 var other_nodes = [];
26793 var has_other_nodes = false;
26794 for (var i=0;i<ar.length;i++) {
26795 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26798 // fullly contained node.
26800 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26805 // probably selected..
26806 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26807 other_nodes.push(ar[i]);
26811 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26816 has_other_nodes = true;
26818 if (!nodes.length && other_nodes.length) {
26819 nodes= other_nodes;
26821 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26827 createRange: function(sel)
26829 // this has strange effects when using with
26830 // top toolbar - not sure if it's a great idea.
26831 //this.editor.contentWindow.focus();
26832 if (typeof sel != "undefined") {
26834 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26836 return this.doc.createRange();
26839 return this.doc.createRange();
26842 getParentElement: function()
26845 this.assignDocWin();
26846 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26848 var range = this.createRange(sel);
26851 var p = range.commonAncestorContainer;
26852 while (p.nodeType == 3) { // text node
26863 * Range intersection.. the hard stuff...
26867 * [ -- selected range --- ]
26871 * if end is before start or hits it. fail.
26872 * if start is after end or hits it fail.
26874 * if either hits (but other is outside. - then it's not
26880 // @see http://www.thismuchiknow.co.uk/?p=64.
26881 rangeIntersectsNode : function(range, node)
26883 var nodeRange = node.ownerDocument.createRange();
26885 nodeRange.selectNode(node);
26887 nodeRange.selectNodeContents(node);
26890 var rangeStartRange = range.cloneRange();
26891 rangeStartRange.collapse(true);
26893 var rangeEndRange = range.cloneRange();
26894 rangeEndRange.collapse(false);
26896 var nodeStartRange = nodeRange.cloneRange();
26897 nodeStartRange.collapse(true);
26899 var nodeEndRange = nodeRange.cloneRange();
26900 nodeEndRange.collapse(false);
26902 return rangeStartRange.compareBoundaryPoints(
26903 Range.START_TO_START, nodeEndRange) == -1 &&
26904 rangeEndRange.compareBoundaryPoints(
26905 Range.START_TO_START, nodeStartRange) == 1;
26909 rangeCompareNode : function(range, node)
26911 var nodeRange = node.ownerDocument.createRange();
26913 nodeRange.selectNode(node);
26915 nodeRange.selectNodeContents(node);
26919 range.collapse(true);
26921 nodeRange.collapse(true);
26923 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26924 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26926 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26928 var nodeIsBefore = ss == 1;
26929 var nodeIsAfter = ee == -1;
26931 if (nodeIsBefore && nodeIsAfter) {
26934 if (!nodeIsBefore && nodeIsAfter) {
26935 return 1; //right trailed.
26938 if (nodeIsBefore && !nodeIsAfter) {
26939 return 2; // left trailed.
26945 // private? - in a new class?
26946 cleanUpPaste : function()
26948 // cleans up the whole document..
26949 Roo.log('cleanuppaste');
26951 this.cleanUpChildren(this.doc.body);
26952 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26953 if (clean != this.doc.body.innerHTML) {
26954 this.doc.body.innerHTML = clean;
26959 cleanWordChars : function(input) {// change the chars to hex code
26960 var he = Roo.HtmlEditorCore;
26962 var output = input;
26963 Roo.each(he.swapCodes, function(sw) {
26964 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26966 output = output.replace(swapper, sw[1]);
26973 cleanUpChildren : function (n)
26975 if (!n.childNodes.length) {
26978 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26979 this.cleanUpChild(n.childNodes[i]);
26986 cleanUpChild : function (node)
26989 //console.log(node);
26990 if (node.nodeName == "#text") {
26991 // clean up silly Windows -- stuff?
26994 if (node.nodeName == "#comment") {
26995 if (!this.allowComments) {
26996 node.parentNode.removeChild(node);
26998 // clean up silly Windows -- stuff?
27001 var lcname = node.tagName.toLowerCase();
27002 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27003 // whitelist of tags..
27005 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27007 node.parentNode.removeChild(node);
27012 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27014 // spans with no attributes - just remove them..
27015 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27016 remove_keep_children = true;
27019 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27020 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27022 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27023 // remove_keep_children = true;
27026 if (remove_keep_children) {
27027 this.cleanUpChildren(node);
27028 // inserts everything just before this node...
27029 while (node.childNodes.length) {
27030 var cn = node.childNodes[0];
27031 node.removeChild(cn);
27032 node.parentNode.insertBefore(cn, node);
27034 node.parentNode.removeChild(node);
27038 if (!node.attributes || !node.attributes.length) {
27043 this.cleanUpChildren(node);
27047 function cleanAttr(n,v)
27050 if (v.match(/^\./) || v.match(/^\//)) {
27053 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27056 if (v.match(/^#/)) {
27059 if (v.match(/^\{/)) { // allow template editing.
27062 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27063 node.removeAttribute(n);
27067 var cwhite = this.cwhite;
27068 var cblack = this.cblack;
27070 function cleanStyle(n,v)
27072 if (v.match(/expression/)) { //XSS?? should we even bother..
27073 node.removeAttribute(n);
27077 var parts = v.split(/;/);
27080 Roo.each(parts, function(p) {
27081 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27085 var l = p.split(':').shift().replace(/\s+/g,'');
27086 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27088 if ( cwhite.length && cblack.indexOf(l) > -1) {
27089 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27090 //node.removeAttribute(n);
27094 // only allow 'c whitelisted system attributes'
27095 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27096 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27097 //node.removeAttribute(n);
27107 if (clean.length) {
27108 node.setAttribute(n, clean.join(';'));
27110 node.removeAttribute(n);
27116 for (var i = node.attributes.length-1; i > -1 ; i--) {
27117 var a = node.attributes[i];
27120 if (a.name.toLowerCase().substr(0,2)=='on') {
27121 node.removeAttribute(a.name);
27124 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27125 node.removeAttribute(a.name);
27128 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27129 cleanAttr(a.name,a.value); // fixme..
27132 if (a.name == 'style') {
27133 cleanStyle(a.name,a.value);
27136 /// clean up MS crap..
27137 // tecnically this should be a list of valid class'es..
27140 if (a.name == 'class') {
27141 if (a.value.match(/^Mso/)) {
27142 node.removeAttribute('class');
27145 if (a.value.match(/^body$/)) {
27146 node.removeAttribute('class');
27157 this.cleanUpChildren(node);
27163 * Clean up MS wordisms...
27165 cleanWord : function(node)
27168 this.cleanWord(this.doc.body);
27173 node.nodeName == 'SPAN' &&
27174 !node.hasAttributes() &&
27175 node.childNodes.length == 1 &&
27176 node.firstChild.nodeName == "#text"
27178 var textNode = node.firstChild;
27179 node.removeChild(textNode);
27180 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27181 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27183 node.parentNode.insertBefore(textNode, node);
27184 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27185 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27187 node.parentNode.removeChild(node);
27190 if (node.nodeName == "#text") {
27191 // clean up silly Windows -- stuff?
27194 if (node.nodeName == "#comment") {
27195 node.parentNode.removeChild(node);
27196 // clean up silly Windows -- stuff?
27200 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27201 node.parentNode.removeChild(node);
27204 //Roo.log(node.tagName);
27205 // remove - but keep children..
27206 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27207 //Roo.log('-- removed');
27208 while (node.childNodes.length) {
27209 var cn = node.childNodes[0];
27210 node.removeChild(cn);
27211 node.parentNode.insertBefore(cn, node);
27212 // move node to parent - and clean it..
27213 this.cleanWord(cn);
27215 node.parentNode.removeChild(node);
27216 /// no need to iterate chidlren = it's got none..
27217 //this.iterateChildren(node, this.cleanWord);
27221 if (node.className.length) {
27223 var cn = node.className.split(/\W+/);
27225 Roo.each(cn, function(cls) {
27226 if (cls.match(/Mso[a-zA-Z]+/)) {
27231 node.className = cna.length ? cna.join(' ') : '';
27233 node.removeAttribute("class");
27237 if (node.hasAttribute("lang")) {
27238 node.removeAttribute("lang");
27241 if (node.hasAttribute("style")) {
27243 var styles = node.getAttribute("style").split(";");
27245 Roo.each(styles, function(s) {
27246 if (!s.match(/:/)) {
27249 var kv = s.split(":");
27250 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27253 // what ever is left... we allow.
27256 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27257 if (!nstyle.length) {
27258 node.removeAttribute('style');
27261 this.iterateChildren(node, this.cleanWord);
27267 * iterateChildren of a Node, calling fn each time, using this as the scole..
27268 * @param {DomNode} node node to iterate children of.
27269 * @param {Function} fn method of this class to call on each item.
27271 iterateChildren : function(node, fn)
27273 if (!node.childNodes.length) {
27276 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27277 fn.call(this, node.childNodes[i])
27283 * cleanTableWidths.
27285 * Quite often pasting from word etc.. results in tables with column and widths.
27286 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27289 cleanTableWidths : function(node)
27294 this.cleanTableWidths(this.doc.body);
27299 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27302 Roo.log(node.tagName);
27303 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27304 this.iterateChildren(node, this.cleanTableWidths);
27307 if (node.hasAttribute('width')) {
27308 node.removeAttribute('width');
27312 if (node.hasAttribute("style")) {
27315 var styles = node.getAttribute("style").split(";");
27317 Roo.each(styles, function(s) {
27318 if (!s.match(/:/)) {
27321 var kv = s.split(":");
27322 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27325 // what ever is left... we allow.
27328 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27329 if (!nstyle.length) {
27330 node.removeAttribute('style');
27334 this.iterateChildren(node, this.cleanTableWidths);
27342 domToHTML : function(currentElement, depth, nopadtext) {
27344 depth = depth || 0;
27345 nopadtext = nopadtext || false;
27347 if (!currentElement) {
27348 return this.domToHTML(this.doc.body);
27351 //Roo.log(currentElement);
27353 var allText = false;
27354 var nodeName = currentElement.nodeName;
27355 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27357 if (nodeName == '#text') {
27359 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27364 if (nodeName != 'BODY') {
27367 // Prints the node tagName, such as <A>, <IMG>, etc
27370 for(i = 0; i < currentElement.attributes.length;i++) {
27372 var aname = currentElement.attributes.item(i).name;
27373 if (!currentElement.attributes.item(i).value.length) {
27376 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27379 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27388 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27391 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27396 // Traverse the tree
27398 var currentElementChild = currentElement.childNodes.item(i);
27399 var allText = true;
27400 var innerHTML = '';
27402 while (currentElementChild) {
27403 // Formatting code (indent the tree so it looks nice on the screen)
27404 var nopad = nopadtext;
27405 if (lastnode == 'SPAN') {
27409 if (currentElementChild.nodeName == '#text') {
27410 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27411 toadd = nopadtext ? toadd : toadd.trim();
27412 if (!nopad && toadd.length > 80) {
27413 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27415 innerHTML += toadd;
27418 currentElementChild = currentElement.childNodes.item(i);
27424 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27426 // Recursively traverse the tree structure of the child node
27427 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27428 lastnode = currentElementChild.nodeName;
27430 currentElementChild=currentElement.childNodes.item(i);
27436 // The remaining code is mostly for formatting the tree
27437 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27442 ret+= "</"+tagName+">";
27448 applyBlacklists : function()
27450 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27451 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27455 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27456 if (b.indexOf(tag) > -1) {
27459 this.white.push(tag);
27463 Roo.each(w, function(tag) {
27464 if (b.indexOf(tag) > -1) {
27467 if (this.white.indexOf(tag) > -1) {
27470 this.white.push(tag);
27475 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27476 if (w.indexOf(tag) > -1) {
27479 this.black.push(tag);
27483 Roo.each(b, function(tag) {
27484 if (w.indexOf(tag) > -1) {
27487 if (this.black.indexOf(tag) > -1) {
27490 this.black.push(tag);
27495 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27496 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27500 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27501 if (b.indexOf(tag) > -1) {
27504 this.cwhite.push(tag);
27508 Roo.each(w, function(tag) {
27509 if (b.indexOf(tag) > -1) {
27512 if (this.cwhite.indexOf(tag) > -1) {
27515 this.cwhite.push(tag);
27520 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27521 if (w.indexOf(tag) > -1) {
27524 this.cblack.push(tag);
27528 Roo.each(b, function(tag) {
27529 if (w.indexOf(tag) > -1) {
27532 if (this.cblack.indexOf(tag) > -1) {
27535 this.cblack.push(tag);
27540 setStylesheets : function(stylesheets)
27542 if(typeof(stylesheets) == 'string'){
27543 Roo.get(this.iframe.contentDocument.head).createChild({
27545 rel : 'stylesheet',
27554 Roo.each(stylesheets, function(s) {
27559 Roo.get(_this.iframe.contentDocument.head).createChild({
27561 rel : 'stylesheet',
27570 removeStylesheets : function()
27574 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27579 setStyle : function(style)
27581 Roo.get(this.iframe.contentDocument.head).createChild({
27590 // hide stuff that is not compatible
27604 * @event specialkey
27608 * @cfg {String} fieldClass @hide
27611 * @cfg {String} focusClass @hide
27614 * @cfg {String} autoCreate @hide
27617 * @cfg {String} inputType @hide
27620 * @cfg {String} invalidClass @hide
27623 * @cfg {String} invalidText @hide
27626 * @cfg {String} msgFx @hide
27629 * @cfg {String} validateOnBlur @hide
27633 Roo.HtmlEditorCore.white = [
27634 'area', 'br', 'img', 'input', 'hr', 'wbr',
27636 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27637 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27638 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27639 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27640 'table', 'ul', 'xmp',
27642 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27645 'dir', 'menu', 'ol', 'ul', 'dl',
27651 Roo.HtmlEditorCore.black = [
27652 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27654 'base', 'basefont', 'bgsound', 'blink', 'body',
27655 'frame', 'frameset', 'head', 'html', 'ilayer',
27656 'iframe', 'layer', 'link', 'meta', 'object',
27657 'script', 'style' ,'title', 'xml' // clean later..
27659 Roo.HtmlEditorCore.clean = [
27660 'script', 'style', 'title', 'xml'
27662 Roo.HtmlEditorCore.remove = [
27667 Roo.HtmlEditorCore.ablack = [
27671 Roo.HtmlEditorCore.aclean = [
27672 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27676 Roo.HtmlEditorCore.pwhite= [
27677 'http', 'https', 'mailto'
27680 // white listed style attributes.
27681 Roo.HtmlEditorCore.cwhite= [
27682 // 'text-align', /// default is to allow most things..
27688 // black listed style attributes.
27689 Roo.HtmlEditorCore.cblack= [
27690 // 'font-size' -- this can be set by the project
27694 Roo.HtmlEditorCore.swapCodes =[
27695 [ 8211, "–" ],
27696 [ 8212, "—" ],
27713 * @class Roo.bootstrap.form.HtmlEditor
27714 * @extends Roo.bootstrap.form.TextArea
27715 * Bootstrap HtmlEditor class
27718 * Create a new HtmlEditor
27719 * @param {Object} config The config object
27722 Roo.bootstrap.form.HtmlEditor = function(config){
27723 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27724 if (!this.toolbars) {
27725 this.toolbars = [];
27728 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27731 * @event initialize
27732 * Fires when the editor is fully initialized (including the iframe)
27733 * @param {HtmlEditor} this
27738 * Fires when the editor is first receives the focus. Any insertion must wait
27739 * until after this event.
27740 * @param {HtmlEditor} this
27744 * @event beforesync
27745 * Fires before the textarea is updated with content from the editor iframe. Return false
27746 * to cancel the sync.
27747 * @param {HtmlEditor} this
27748 * @param {String} html
27752 * @event beforepush
27753 * Fires before the iframe editor is updated with content from the textarea. Return false
27754 * to cancel the push.
27755 * @param {HtmlEditor} this
27756 * @param {String} html
27761 * Fires when the textarea is updated with content from the editor iframe.
27762 * @param {HtmlEditor} this
27763 * @param {String} html
27768 * Fires when the iframe editor is updated with content from the textarea.
27769 * @param {HtmlEditor} this
27770 * @param {String} html
27774 * @event editmodechange
27775 * Fires when the editor switches edit modes
27776 * @param {HtmlEditor} this
27777 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27779 editmodechange: true,
27781 * @event editorevent
27782 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27783 * @param {HtmlEditor} this
27787 * @event firstfocus
27788 * Fires when on first focus - needed by toolbars..
27789 * @param {HtmlEditor} this
27794 * Auto save the htmlEditor value as a file into Events
27795 * @param {HtmlEditor} this
27799 * @event savedpreview
27800 * preview the saved version of htmlEditor
27801 * @param {HtmlEditor} this
27808 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27812 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27817 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27822 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27827 * @cfg {Number} height (in pixels)
27831 * @cfg {Number} width (in pixels)
27836 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27839 stylesheets: false,
27844 // private properties
27845 validationEvent : false,
27847 initialized : false,
27850 onFocus : Roo.emptyFn,
27852 hideMode:'offsets',
27854 tbContainer : false,
27858 toolbarContainer :function() {
27859 return this.wrap.select('.x-html-editor-tb',true).first();
27863 * Protected method that will not generally be called directly. It
27864 * is called when the editor creates its toolbar. Override this method if you need to
27865 * add custom toolbar buttons.
27866 * @param {HtmlEditor} editor
27868 createToolbar : function(){
27869 Roo.log('renewing');
27870 Roo.log("create toolbars");
27872 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27873 this.toolbars[0].render(this.toolbarContainer());
27877 // if (!editor.toolbars || !editor.toolbars.length) {
27878 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27881 // for (var i =0 ; i < editor.toolbars.length;i++) {
27882 // editor.toolbars[i] = Roo.factory(
27883 // typeof(editor.toolbars[i]) == 'string' ?
27884 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27885 // Roo.bootstrap.form.HtmlEditor);
27886 // editor.toolbars[i].init(editor);
27892 onRender : function(ct, position)
27894 // Roo.log("Call onRender: " + this.xtype);
27896 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27898 this.wrap = this.inputEl().wrap({
27899 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27902 this.editorcore.onRender(ct, position);
27904 if (this.resizable) {
27905 this.resizeEl = new Roo.Resizable(this.wrap, {
27909 minHeight : this.height,
27910 height: this.height,
27911 handles : this.resizable,
27914 resize : function(r, w, h) {
27915 _t.onResize(w,h); // -something
27921 this.createToolbar(this);
27924 if(!this.width && this.resizable){
27925 this.setSize(this.wrap.getSize());
27927 if (this.resizeEl) {
27928 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27929 // should trigger onReize..
27935 onResize : function(w, h)
27937 Roo.log('resize: ' +w + ',' + h );
27938 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27942 if(this.inputEl() ){
27943 if(typeof w == 'number'){
27944 var aw = w - this.wrap.getFrameWidth('lr');
27945 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27948 if(typeof h == 'number'){
27949 var tbh = -11; // fixme it needs to tool bar size!
27950 for (var i =0; i < this.toolbars.length;i++) {
27951 // fixme - ask toolbars for heights?
27952 tbh += this.toolbars[i].el.getHeight();
27953 //if (this.toolbars[i].footer) {
27954 // tbh += this.toolbars[i].footer.el.getHeight();
27962 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27963 ah -= 5; // knock a few pixes off for look..
27964 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27968 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27969 this.editorcore.onResize(ew,eh);
27974 * Toggles the editor between standard and source edit mode.
27975 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27977 toggleSourceEdit : function(sourceEditMode)
27979 this.editorcore.toggleSourceEdit(sourceEditMode);
27981 if(this.editorcore.sourceEditMode){
27982 Roo.log('editor - showing textarea');
27985 // Roo.log(this.syncValue());
27987 this.inputEl().removeClass(['hide', 'x-hidden']);
27988 this.inputEl().dom.removeAttribute('tabIndex');
27989 this.inputEl().focus();
27991 Roo.log('editor - hiding textarea');
27993 // Roo.log(this.pushValue());
27996 this.inputEl().addClass(['hide', 'x-hidden']);
27997 this.inputEl().dom.setAttribute('tabIndex', -1);
27998 //this.deferFocus();
28001 if(this.resizable){
28002 this.setSize(this.wrap.getSize());
28005 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28008 // private (for BoxComponent)
28009 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28011 // private (for BoxComponent)
28012 getResizeEl : function(){
28016 // private (for BoxComponent)
28017 getPositionEl : function(){
28022 initEvents : function(){
28023 this.originalValue = this.getValue();
28027 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28030 // markInvalid : Roo.emptyFn,
28032 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28035 // clearInvalid : Roo.emptyFn,
28037 setValue : function(v){
28038 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28039 this.editorcore.pushValue();
28044 deferFocus : function(){
28045 this.focus.defer(10, this);
28049 focus : function(){
28050 this.editorcore.focus();
28056 onDestroy : function(){
28062 for (var i =0; i < this.toolbars.length;i++) {
28063 // fixme - ask toolbars for heights?
28064 this.toolbars[i].onDestroy();
28067 this.wrap.dom.innerHTML = '';
28068 this.wrap.remove();
28073 onFirstFocus : function(){
28074 //Roo.log("onFirstFocus");
28075 this.editorcore.onFirstFocus();
28076 for (var i =0; i < this.toolbars.length;i++) {
28077 this.toolbars[i].onFirstFocus();
28083 syncValue : function()
28085 this.editorcore.syncValue();
28088 pushValue : function()
28090 this.editorcore.pushValue();
28094 // hide stuff that is not compatible
28108 * @event specialkey
28112 * @cfg {String} fieldClass @hide
28115 * @cfg {String} focusClass @hide
28118 * @cfg {String} autoCreate @hide
28121 * @cfg {String} inputType @hide
28125 * @cfg {String} invalidText @hide
28128 * @cfg {String} msgFx @hide
28131 * @cfg {String} validateOnBlur @hide
28140 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28142 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28143 * @parent Roo.bootstrap.form.HtmlEditor
28144 * @extends Roo.bootstrap.nav.Simplebar
28150 new Roo.bootstrap.form.HtmlEditor({
28153 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28154 disable : { fonts: 1 , format: 1, ..., ... , ...],
28160 * @cfg {Object} disable List of elements to disable..
28161 * @cfg {Array} btns List of additional buttons.
28165 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28168 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28171 Roo.apply(this, config);
28173 // default disabled, based on 'good practice'..
28174 this.disable = this.disable || {};
28175 Roo.applyIf(this.disable, {
28178 specialElements : true
28180 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28182 this.editor = config.editor;
28183 this.editorcore = config.editor.editorcore;
28185 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28187 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28188 // dont call parent... till later.
28190 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28195 editorcore : false,
28200 "h1","h2","h3","h4","h5","h6",
28202 "abbr", "acronym", "address", "cite", "samp", "var",
28206 onRender : function(ct, position)
28208 // Roo.log("Call onRender: " + this.xtype);
28210 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28212 this.el.dom.style.marginBottom = '0';
28214 var editorcore = this.editorcore;
28215 var editor= this.editor;
28218 var btn = function(id,cmd , toggle, handler, html){
28220 var event = toggle ? 'toggle' : 'click';
28225 xns: Roo.bootstrap,
28229 enableToggle:toggle !== false,
28231 pressed : toggle ? false : null,
28234 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28235 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28241 // var cb_box = function...
28246 xns: Roo.bootstrap,
28251 xns: Roo.bootstrap,
28255 Roo.each(this.formats, function(f) {
28256 style.menu.items.push({
28258 xns: Roo.bootstrap,
28259 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28264 editorcore.insertTag(this.tagname);
28271 children.push(style);
28273 btn('bold',false,true);
28274 btn('italic',false,true);
28275 btn('align-left', 'justifyleft',true);
28276 btn('align-center', 'justifycenter',true);
28277 btn('align-right' , 'justifyright',true);
28278 btn('link', false, false, function(btn) {
28279 //Roo.log("create link?");
28280 var url = prompt(this.createLinkText, this.defaultLinkValue);
28281 if(url && url != 'http:/'+'/'){
28282 this.editorcore.relayCmd('createlink', url);
28285 btn('list','insertunorderedlist',true);
28286 btn('pencil', false,true, function(btn){
28288 this.toggleSourceEdit(btn.pressed);
28291 if (this.editor.btns.length > 0) {
28292 for (var i = 0; i<this.editor.btns.length; i++) {
28293 children.push(this.editor.btns[i]);
28301 xns: Roo.bootstrap,
28306 xns: Roo.bootstrap,
28311 cog.menu.items.push({
28313 xns: Roo.bootstrap,
28314 html : Clean styles,
28319 editorcore.insertTag(this.tagname);
28328 this.xtype = 'NavSimplebar';
28330 for(var i=0;i< children.length;i++) {
28332 this.buttons.add(this.addxtypeChild(children[i]));
28336 editor.on('editorevent', this.updateToolbar, this);
28338 onBtnClick : function(id)
28340 this.editorcore.relayCmd(id);
28341 this.editorcore.focus();
28345 * Protected method that will not generally be called directly. It triggers
28346 * a toolbar update by reading the markup state of the current selection in the editor.
28348 updateToolbar: function(){
28350 if(!this.editorcore.activated){
28351 this.editor.onFirstFocus(); // is this neeed?
28355 var btns = this.buttons;
28356 var doc = this.editorcore.doc;
28357 btns.get('bold').setActive(doc.queryCommandState('bold'));
28358 btns.get('italic').setActive(doc.queryCommandState('italic'));
28359 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28361 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28362 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28363 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28365 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28366 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28369 var ans = this.editorcore.getAllAncestors();
28370 if (this.formatCombo) {
28373 var store = this.formatCombo.store;
28374 this.formatCombo.setValue("");
28375 for (var i =0; i < ans.length;i++) {
28376 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28378 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28386 // hides menus... - so this cant be on a menu...
28387 Roo.bootstrap.MenuMgr.hideAll();
28389 Roo.bootstrap.menu.Manager.hideAll();
28390 //this.editorsyncValue();
28392 onFirstFocus: function() {
28393 this.buttons.each(function(item){
28397 toggleSourceEdit : function(sourceEditMode){
28400 if(sourceEditMode){
28401 Roo.log("disabling buttons");
28402 this.buttons.each( function(item){
28403 if(item.cmd != 'pencil'){
28409 Roo.log("enabling buttons");
28410 if(this.editorcore.initialized){
28411 this.buttons.each( function(item){
28417 Roo.log("calling toggole on editor");
28418 // tell the editor that it's been pressed..
28419 this.editor.toggleSourceEdit(sourceEditMode);
28433 * @class Roo.bootstrap.form.Markdown
28434 * @extends Roo.bootstrap.form.TextArea
28435 * Bootstrap Showdown editable area
28436 * @cfg {string} content
28439 * Create a new Showdown
28442 Roo.bootstrap.form.Markdown = function(config){
28443 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28447 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28451 initEvents : function()
28454 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28455 this.markdownEl = this.el.createChild({
28456 cls : 'roo-markdown-area'
28458 this.inputEl().addClass('d-none');
28459 if (this.getValue() == '') {
28460 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28463 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28465 this.markdownEl.on('click', this.toggleTextEdit, this);
28466 this.on('blur', this.toggleTextEdit, this);
28467 this.on('specialkey', this.resizeTextArea, this);
28470 toggleTextEdit : function()
28472 var sh = this.markdownEl.getHeight();
28473 this.inputEl().addClass('d-none');
28474 this.markdownEl.addClass('d-none');
28475 if (!this.editing) {
28477 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28478 this.inputEl().removeClass('d-none');
28479 this.inputEl().focus();
28480 this.editing = true;
28483 // show showdown...
28484 this.updateMarkdown();
28485 this.markdownEl.removeClass('d-none');
28486 this.editing = false;
28489 updateMarkdown : function()
28491 if (this.getValue() == '') {
28492 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28496 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28499 resizeTextArea: function () {
28502 Roo.log([sh, this.getValue().split("\n").length * 30]);
28503 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28505 setValue : function(val)
28507 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28508 if (!this.editing) {
28509 this.updateMarkdown();
28515 if (!this.editing) {
28516 this.toggleTextEdit();
28524 * Ext JS Library 1.1.1
28525 * Copyright(c) 2006-2007, Ext JS, LLC.
28527 * Originally Released Under LGPL - original licence link has changed is not relivant.
28530 * <script type="text/javascript">
28534 * @class Roo.bootstrap.PagingToolbar
28535 * @extends Roo.bootstrap.nav.Simplebar
28536 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28538 * Create a new PagingToolbar
28539 * @param {Object} config The config object
28540 * @param {Roo.data.Store} store
28542 Roo.bootstrap.PagingToolbar = function(config)
28544 // old args format still supported... - xtype is prefered..
28545 // created from xtype...
28547 this.ds = config.dataSource;
28549 if (config.store && !this.ds) {
28550 this.store= Roo.factory(config.store, Roo.data);
28551 this.ds = this.store;
28552 this.ds.xmodule = this.xmodule || false;
28555 this.toolbarItems = [];
28556 if (config.items) {
28557 this.toolbarItems = config.items;
28560 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28565 this.bind(this.ds);
28568 if (Roo.bootstrap.version == 4) {
28569 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28571 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28576 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28578 * @cfg {Roo.bootstrap.Button} buttons[]
28579 * Buttons for the toolbar
28582 * @cfg {Roo.data.Store} store
28583 * The underlying data store providing the paged data
28586 * @cfg {String/HTMLElement/Element} container
28587 * container The id or element that will contain the toolbar
28590 * @cfg {Boolean} displayInfo
28591 * True to display the displayMsg (defaults to false)
28594 * @cfg {Number} pageSize
28595 * The number of records to display per page (defaults to 20)
28599 * @cfg {String} displayMsg
28600 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28602 displayMsg : 'Displaying {0} - {1} of {2}',
28604 * @cfg {String} emptyMsg
28605 * The message to display when no records are found (defaults to "No data to display")
28607 emptyMsg : 'No data to display',
28609 * Customizable piece of the default paging text (defaults to "Page")
28612 beforePageText : "Page",
28614 * Customizable piece of the default paging text (defaults to "of %0")
28617 afterPageText : "of {0}",
28619 * Customizable piece of the default paging text (defaults to "First Page")
28622 firstText : "First Page",
28624 * Customizable piece of the default paging text (defaults to "Previous Page")
28627 prevText : "Previous Page",
28629 * Customizable piece of the default paging text (defaults to "Next Page")
28632 nextText : "Next Page",
28634 * Customizable piece of the default paging text (defaults to "Last Page")
28637 lastText : "Last Page",
28639 * Customizable piece of the default paging text (defaults to "Refresh")
28642 refreshText : "Refresh",
28646 onRender : function(ct, position)
28648 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28649 this.navgroup.parentId = this.id;
28650 this.navgroup.onRender(this.el, null);
28651 // add the buttons to the navgroup
28653 if(this.displayInfo){
28654 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28655 this.displayEl = this.el.select('.x-paging-info', true).first();
28656 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28657 // this.displayEl = navel.el.select('span',true).first();
28663 Roo.each(_this.buttons, function(e){ // this might need to use render????
28664 Roo.factory(e).render(_this.el);
28668 Roo.each(_this.toolbarItems, function(e) {
28669 _this.navgroup.addItem(e);
28673 this.first = this.navgroup.addItem({
28674 tooltip: this.firstText,
28675 cls: "prev btn-outline-secondary",
28676 html : ' <i class="fa fa-step-backward"></i>',
28678 preventDefault: true,
28679 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28682 this.prev = this.navgroup.addItem({
28683 tooltip: this.prevText,
28684 cls: "prev btn-outline-secondary",
28685 html : ' <i class="fa fa-backward"></i>',
28687 preventDefault: true,
28688 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28690 //this.addSeparator();
28693 var field = this.navgroup.addItem( {
28695 cls : 'x-paging-position btn-outline-secondary',
28697 html : this.beforePageText +
28698 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28699 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28702 this.field = field.el.select('input', true).first();
28703 this.field.on("keydown", this.onPagingKeydown, this);
28704 this.field.on("focus", function(){this.dom.select();});
28707 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28708 //this.field.setHeight(18);
28709 //this.addSeparator();
28710 this.next = this.navgroup.addItem({
28711 tooltip: this.nextText,
28712 cls: "next btn-outline-secondary",
28713 html : ' <i class="fa fa-forward"></i>',
28715 preventDefault: true,
28716 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28718 this.last = this.navgroup.addItem({
28719 tooltip: this.lastText,
28720 html : ' <i class="fa fa-step-forward"></i>',
28721 cls: "next btn-outline-secondary",
28723 preventDefault: true,
28724 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28726 //this.addSeparator();
28727 this.loading = this.navgroup.addItem({
28728 tooltip: this.refreshText,
28729 cls: "btn-outline-secondary",
28730 html : ' <i class="fa fa-refresh"></i>',
28731 preventDefault: true,
28732 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28738 updateInfo : function(){
28739 if(this.displayEl){
28740 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28741 var msg = count == 0 ?
28745 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28747 this.displayEl.update(msg);
28752 onLoad : function(ds, r, o)
28754 this.cursor = o.params && o.params.start ? o.params.start : 0;
28756 var d = this.getPageData(),
28761 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28762 this.field.dom.value = ap;
28763 this.first.setDisabled(ap == 1);
28764 this.prev.setDisabled(ap == 1);
28765 this.next.setDisabled(ap == ps);
28766 this.last.setDisabled(ap == ps);
28767 this.loading.enable();
28772 getPageData : function(){
28773 var total = this.ds.getTotalCount();
28776 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28777 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28782 onLoadError : function(proxy, o){
28783 this.loading.enable();
28784 if (this.ds.events.loadexception.listeners.length < 2) {
28785 // nothing has been assigned to loadexception except this...
28787 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28793 onPagingKeydown : function(e){
28794 var k = e.getKey();
28795 var d = this.getPageData();
28797 var v = this.field.dom.value, pageNum;
28798 if(!v || isNaN(pageNum = parseInt(v, 10))){
28799 this.field.dom.value = d.activePage;
28802 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28803 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28806 else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
28808 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28809 this.field.dom.value = pageNum;
28810 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28813 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28815 var v = this.field.dom.value, pageNum;
28816 var increment = (e.shiftKey) ? 10 : 1;
28817 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28820 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28821 this.field.dom.value = d.activePage;
28824 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28826 this.field.dom.value = parseInt(v, 10) + increment;
28827 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28828 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28835 beforeLoad : function(){
28837 this.loading.disable();
28842 onClick : function(which){
28851 ds.load({params:{start: 0, limit: this.pageSize}});
28854 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28857 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28860 var total = ds.getTotalCount();
28861 var extra = total % this.pageSize;
28862 var lastStart = extra ? (total - extra) : total-this.pageSize;
28863 ds.load({params:{start: lastStart, limit: this.pageSize}});
28866 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28872 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28873 * @param {Roo.data.Store} store The data store to unbind
28875 unbind : function(ds){
28876 ds.un("beforeload", this.beforeLoad, this);
28877 ds.un("load", this.onLoad, this);
28878 ds.un("loadexception", this.onLoadError, this);
28879 ds.un("remove", this.updateInfo, this);
28880 ds.un("add", this.updateInfo, this);
28881 this.ds = undefined;
28885 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28886 * @param {Roo.data.Store} store The data store to bind
28888 bind : function(ds){
28889 ds.on("beforeload", this.beforeLoad, this);
28890 ds.on("load", this.onLoad, this);
28891 ds.on("loadexception", this.onLoadError, this);
28892 ds.on("remove", this.updateInfo, this);
28893 ds.on("add", this.updateInfo, this);
28904 * @class Roo.bootstrap.MessageBar
28905 * @extends Roo.bootstrap.Component
28906 * Bootstrap MessageBar class
28907 * @cfg {String} html contents of the MessageBar
28908 * @cfg {String} weight (info | success | warning | danger) default info
28909 * @cfg {String} beforeClass insert the bar before the given class
28910 * @cfg {Boolean} closable (true | false) default false
28911 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28914 * Create a new Element
28915 * @param {Object} config The config object
28918 Roo.bootstrap.MessageBar = function(config){
28919 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28922 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28928 beforeClass: 'bootstrap-sticky-wrap',
28930 getAutoCreate : function(){
28934 cls: 'alert alert-dismissable alert-' + this.weight,
28939 html: this.html || ''
28945 cfg.cls += ' alert-messages-fixed';
28959 onRender : function(ct, position)
28961 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28964 var cfg = Roo.apply({}, this.getAutoCreate());
28968 cfg.cls += ' ' + this.cls;
28971 cfg.style = this.style;
28973 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28975 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28978 this.el.select('>button.close').on('click', this.hide, this);
28984 if (!this.rendered) {
28990 this.fireEvent('show', this);
28996 if (!this.rendered) {
29002 this.fireEvent('hide', this);
29005 update : function()
29007 // var e = this.el.dom.firstChild;
29009 // if(this.closable){
29010 // e = e.nextSibling;
29013 // e.data = this.html || '';
29015 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29031 * @class Roo.bootstrap.Graph
29032 * @extends Roo.bootstrap.Component
29033 * Bootstrap Graph class
29037 @cfg {String} graphtype bar | vbar | pie
29038 @cfg {number} g_x coodinator | centre x (pie)
29039 @cfg {number} g_y coodinator | centre y (pie)
29040 @cfg {number} g_r radius (pie)
29041 @cfg {number} g_height height of the chart (respected by all elements in the set)
29042 @cfg {number} g_width width of the chart (respected by all elements in the set)
29043 @cfg {Object} title The title of the chart
29046 -opts (object) options for the chart
29048 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29049 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29051 o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
29052 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29054 o stretch (boolean)
29056 -opts (object) options for the pie
29059 o startAngle (number)
29060 o endAngle (number)
29064 * Create a new Input
29065 * @param {Object} config The config object
29068 Roo.bootstrap.Graph = function(config){
29069 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29075 * The img click event for the img.
29076 * @param {Roo.EventObject} e
29082 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29093 //g_colors: this.colors,
29100 getAutoCreate : function(){
29111 onRender : function(ct,position){
29114 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29116 if (typeof(Raphael) == 'undefined') {
29117 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29121 this.raphael = Raphael(this.el.dom);
29123 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29124 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29125 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29126 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29128 r.text(160, 10, "Single Series Chart").attr(txtattr);
29129 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29130 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29131 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29133 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29134 r.barchart(330, 10, 300, 220, data1);
29135 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29136 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29139 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29140 // r.barchart(30, 30, 560, 250, xdata, {
29141 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29142 // axis : "0 0 1 1",
29143 // axisxlabels : xdata
29144 // //yvalues : cols,
29147 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29149 // this.load(null,xdata,{
29150 // axis : "0 0 1 1",
29151 // axisxlabels : xdata
29156 load : function(graphtype,xdata,opts)
29158 this.raphael.clear();
29160 graphtype = this.graphtype;
29165 var r = this.raphael,
29166 fin = function () {
29167 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29169 fout = function () {
29170 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29172 pfin = function() {
29173 this.sector.stop();
29174 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29177 this.label[0].stop();
29178 this.label[0].attr({ r: 7.5 });
29179 this.label[1].attr({ "font-weight": 800 });
29182 pfout = function() {
29183 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29186 this.label[0].animate({ r: 5 }, 500, "bounce");
29187 this.label[1].attr({ "font-weight": 400 });
29193 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29196 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29199 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29200 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29202 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29209 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29214 setTitle: function(o)
29219 initEvents: function() {
29222 this.el.on('click', this.onClick, this);
29226 onClick : function(e)
29228 Roo.log('img onclick');
29229 this.fireEvent('click', this, e);
29241 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29244 * @class Roo.bootstrap.dash.NumberBox
29245 * @extends Roo.bootstrap.Component
29246 * Bootstrap NumberBox class
29247 * @cfg {String} headline Box headline
29248 * @cfg {String} content Box content
29249 * @cfg {String} icon Box icon
29250 * @cfg {String} footer Footer text
29251 * @cfg {String} fhref Footer href
29254 * Create a new NumberBox
29255 * @param {Object} config The config object
29259 Roo.bootstrap.dash.NumberBox = function(config){
29260 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29264 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29273 getAutoCreate : function(){
29277 cls : 'small-box ',
29285 cls : 'roo-headline',
29286 html : this.headline
29290 cls : 'roo-content',
29291 html : this.content
29305 cls : 'ion ' + this.icon
29314 cls : 'small-box-footer',
29315 href : this.fhref || '#',
29319 cfg.cn.push(footer);
29326 onRender : function(ct,position){
29327 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29334 setHeadline: function (value)
29336 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29339 setFooter: function (value, href)
29341 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29344 this.el.select('a.small-box-footer',true).first().attr('href', href);
29349 setContent: function (value)
29351 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29354 initEvents: function()
29368 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29371 * @class Roo.bootstrap.dash.TabBox
29372 * @extends Roo.bootstrap.Component
29373 * @children Roo.bootstrap.dash.TabPane
29374 * Bootstrap TabBox class
29375 * @cfg {String} title Title of the TabBox
29376 * @cfg {String} icon Icon of the TabBox
29377 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29378 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29381 * Create a new TabBox
29382 * @param {Object} config The config object
29386 Roo.bootstrap.dash.TabBox = function(config){
29387 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29392 * When a pane is added
29393 * @param {Roo.bootstrap.dash.TabPane} pane
29397 * @event activatepane
29398 * When a pane is activated
29399 * @param {Roo.bootstrap.dash.TabPane} pane
29401 "activatepane" : true
29409 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29414 tabScrollable : false,
29416 getChildContainer : function()
29418 return this.el.select('.tab-content', true).first();
29421 getAutoCreate : function(){
29425 cls: 'pull-left header',
29433 cls: 'fa ' + this.icon
29439 cls: 'nav nav-tabs pull-right',
29445 if(this.tabScrollable){
29452 cls: 'nav nav-tabs pull-right',
29463 cls: 'nav-tabs-custom',
29468 cls: 'tab-content no-padding',
29476 initEvents : function()
29478 //Roo.log('add add pane handler');
29479 this.on('addpane', this.onAddPane, this);
29482 * Updates the box title
29483 * @param {String} html to set the title to.
29485 setTitle : function(value)
29487 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29489 onAddPane : function(pane)
29491 this.panes.push(pane);
29492 //Roo.log('addpane');
29494 // tabs are rendere left to right..
29495 if(!this.showtabs){
29499 var ctr = this.el.select('.nav-tabs', true).first();
29502 var existing = ctr.select('.nav-tab',true);
29503 var qty = existing.getCount();;
29506 var tab = ctr.createChild({
29508 cls : 'nav-tab' + (qty ? '' : ' active'),
29516 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29519 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29521 pane.el.addClass('active');
29526 onTabClick : function(ev,un,ob,pane)
29528 //Roo.log('tab - prev default');
29529 ev.preventDefault();
29532 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29533 pane.tab.addClass('active');
29534 //Roo.log(pane.title);
29535 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29536 // technically we should have a deactivate event.. but maybe add later.
29537 // and it should not de-activate the selected tab...
29538 this.fireEvent('activatepane', pane);
29539 pane.el.addClass('active');
29540 pane.fireEvent('activate');
29545 getActivePane : function()
29548 Roo.each(this.panes, function(p) {
29549 if(p.el.hasClass('active')){
29570 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29572 * @class Roo.bootstrap.TabPane
29573 * @extends Roo.bootstrap.Component
29574 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29575 * Bootstrap TabPane class
29576 * @cfg {Boolean} active (false | true) Default false
29577 * @cfg {String} title title of panel
29581 * Create a new TabPane
29582 * @param {Object} config The config object
29585 Roo.bootstrap.dash.TabPane = function(config){
29586 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29592 * When a pane is activated
29593 * @param {Roo.bootstrap.dash.TabPane} pane
29600 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29605 // the tabBox that this is attached to.
29608 getAutoCreate : function()
29616 cfg.cls += ' active';
29621 initEvents : function()
29623 //Roo.log('trigger add pane handler');
29624 this.parent().fireEvent('addpane', this)
29628 * Updates the tab title
29629 * @param {String} html to set the title to.
29631 setTitle: function(str)
29637 this.tab.select('a', true).first().dom.innerHTML = str;
29656 * @class Roo.bootstrap.Tooltip
29657 * Bootstrap Tooltip class
29658 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29659 * to determine which dom element triggers the tooltip.
29661 * It needs to add support for additional attributes like tooltip-position
29664 * Create a new Toolti
29665 * @param {Object} config The config object
29668 Roo.bootstrap.Tooltip = function(config){
29669 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29671 this.alignment = Roo.bootstrap.Tooltip.alignment;
29673 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29674 this.alignment = config.alignment;
29679 Roo.apply(Roo.bootstrap.Tooltip, {
29681 * @function init initialize tooltip monitoring.
29685 currentTip : false,
29686 currentRegion : false,
29692 Roo.get(document).on('mouseover', this.enter ,this);
29693 Roo.get(document).on('mouseout', this.leave, this);
29696 this.currentTip = new Roo.bootstrap.Tooltip();
29699 enter : function(ev)
29701 var dom = ev.getTarget();
29703 //Roo.log(['enter',dom]);
29704 var el = Roo.fly(dom);
29705 if (this.currentEl) {
29707 //Roo.log(this.currentEl);
29708 //Roo.log(this.currentEl.contains(dom));
29709 if (this.currentEl == el) {
29712 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29718 if (this.currentTip.el) {
29719 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29723 if(!el || el.dom == document){
29729 if (!el.attr('tooltip')) {
29730 pel = el.findParent("[tooltip]");
29732 bindEl = Roo.get(pel);
29738 // you can not look for children, as if el is the body.. then everythign is the child..
29739 if (!pel && !el.attr('tooltip')) { //
29740 if (!el.select("[tooltip]").elements.length) {
29743 // is the mouse over this child...?
29744 bindEl = el.select("[tooltip]").first();
29745 var xy = ev.getXY();
29746 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29747 //Roo.log("not in region.");
29750 //Roo.log("child element over..");
29753 this.currentEl = el;
29754 this.currentTip.bind(bindEl);
29755 this.currentRegion = Roo.lib.Region.getRegion(dom);
29756 this.currentTip.enter();
29759 leave : function(ev)
29761 var dom = ev.getTarget();
29762 //Roo.log(['leave',dom]);
29763 if (!this.currentEl) {
29768 if (dom != this.currentEl.dom) {
29771 var xy = ev.getXY();
29772 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29775 // only activate leave if mouse cursor is outside... bounding box..
29780 if (this.currentTip) {
29781 this.currentTip.leave();
29783 //Roo.log('clear currentEl');
29784 this.currentEl = false;
29789 'left' : ['r-l', [-2,0], 'right'],
29790 'right' : ['l-r', [2,0], 'left'],
29791 'bottom' : ['t-b', [0,2], 'top'],
29792 'top' : [ 'b-t', [0,-2], 'bottom']
29798 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29803 delay : null, // can be { show : 300 , hide: 500}
29807 hoverState : null, //???
29809 placement : 'bottom',
29813 getAutoCreate : function(){
29820 cls : 'tooltip-arrow arrow'
29823 cls : 'tooltip-inner'
29830 bind : function(el)
29835 initEvents : function()
29837 this.arrowEl = this.el.select('.arrow', true).first();
29838 this.innerEl = this.el.select('.tooltip-inner', true).first();
29841 enter : function () {
29843 if (this.timeout != null) {
29844 clearTimeout(this.timeout);
29847 this.hoverState = 'in';
29848 //Roo.log("enter - show");
29849 if (!this.delay || !this.delay.show) {
29854 this.timeout = setTimeout(function () {
29855 if (_t.hoverState == 'in') {
29858 }, this.delay.show);
29862 clearTimeout(this.timeout);
29864 this.hoverState = 'out';
29865 if (!this.delay || !this.delay.hide) {
29871 this.timeout = setTimeout(function () {
29872 //Roo.log("leave - timeout");
29874 if (_t.hoverState == 'out') {
29876 Roo.bootstrap.Tooltip.currentEl = false;
29881 show : function (msg)
29884 this.render(document.body);
29887 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29889 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29891 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29893 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29894 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29896 var placement = typeof this.placement == 'function' ?
29897 this.placement.call(this, this.el, on_el) :
29900 var autoToken = /\s?auto?\s?/i;
29901 var autoPlace = autoToken.test(placement);
29903 placement = placement.replace(autoToken, '') || 'top';
29907 //this.el.setXY([0,0]);
29909 //this.el.dom.style.display='block';
29911 //this.el.appendTo(on_el);
29913 var p = this.getPosition();
29914 var box = this.el.getBox();
29920 var align = this.alignment[placement];
29922 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29924 if(placement == 'top' || placement == 'bottom'){
29926 placement = 'right';
29929 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29930 placement = 'left';
29933 var scroll = Roo.select('body', true).first().getScroll();
29935 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29939 align = this.alignment[placement];
29941 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29945 var elems = document.getElementsByTagName('div');
29946 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29947 for (var i = 0; i < elems.length; i++) {
29948 var zindex = Number.parseInt(
29949 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29952 if (zindex > highest) {
29959 this.el.dom.style.zIndex = highest;
29961 this.el.alignTo(this.bindEl, align[0],align[1]);
29962 //var arrow = this.el.select('.arrow',true).first();
29963 //arrow.set(align[2],
29965 this.el.addClass(placement);
29966 this.el.addClass("bs-tooltip-"+ placement);
29968 this.el.addClass('in fade show');
29970 this.hoverState = null;
29972 if (this.el.hasClass('fade')) {
29987 //this.el.setXY([0,0]);
29988 this.el.removeClass(['show', 'in']);
30004 * @class Roo.bootstrap.LocationPicker
30005 * @extends Roo.bootstrap.Component
30006 * Bootstrap LocationPicker class
30007 * @cfg {Number} latitude Position when init default 0
30008 * @cfg {Number} longitude Position when init default 0
30009 * @cfg {Number} zoom default 15
30010 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30011 * @cfg {Boolean} mapTypeControl default false
30012 * @cfg {Boolean} disableDoubleClickZoom default false
30013 * @cfg {Boolean} scrollwheel default true
30014 * @cfg {Boolean} streetViewControl default false
30015 * @cfg {Number} radius default 0
30016 * @cfg {String} locationName
30017 * @cfg {Boolean} draggable default true
30018 * @cfg {Boolean} enableAutocomplete default false
30019 * @cfg {Boolean} enableReverseGeocode default true
30020 * @cfg {String} markerTitle
30023 * Create a new LocationPicker
30024 * @param {Object} config The config object
30028 Roo.bootstrap.LocationPicker = function(config){
30030 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30035 * Fires when the picker initialized.
30036 * @param {Roo.bootstrap.LocationPicker} this
30037 * @param {Google Location} location
30041 * @event positionchanged
30042 * Fires when the picker position changed.
30043 * @param {Roo.bootstrap.LocationPicker} this
30044 * @param {Google Location} location
30046 positionchanged : true,
30049 * Fires when the map resize.
30050 * @param {Roo.bootstrap.LocationPicker} this
30055 * Fires when the map show.
30056 * @param {Roo.bootstrap.LocationPicker} this
30061 * Fires when the map hide.
30062 * @param {Roo.bootstrap.LocationPicker} this
30067 * Fires when click the map.
30068 * @param {Roo.bootstrap.LocationPicker} this
30069 * @param {Map event} e
30073 * @event mapRightClick
30074 * Fires when right click the map.
30075 * @param {Roo.bootstrap.LocationPicker} this
30076 * @param {Map event} e
30078 mapRightClick : true,
30080 * @event markerClick
30081 * Fires when click the marker.
30082 * @param {Roo.bootstrap.LocationPicker} this
30083 * @param {Map event} e
30085 markerClick : true,
30087 * @event markerRightClick
30088 * Fires when right click the marker.
30089 * @param {Roo.bootstrap.LocationPicker} this
30090 * @param {Map event} e
30092 markerRightClick : true,
30094 * @event OverlayViewDraw
30095 * Fires when OverlayView Draw
30096 * @param {Roo.bootstrap.LocationPicker} this
30098 OverlayViewDraw : true,
30100 * @event OverlayViewOnAdd
30101 * Fires when OverlayView Draw
30102 * @param {Roo.bootstrap.LocationPicker} this
30104 OverlayViewOnAdd : true,
30106 * @event OverlayViewOnRemove
30107 * Fires when OverlayView Draw
30108 * @param {Roo.bootstrap.LocationPicker} this
30110 OverlayViewOnRemove : true,
30112 * @event OverlayViewShow
30113 * Fires when OverlayView Draw
30114 * @param {Roo.bootstrap.LocationPicker} this
30115 * @param {Pixel} cpx
30117 OverlayViewShow : true,
30119 * @event OverlayViewHide
30120 * Fires when OverlayView Draw
30121 * @param {Roo.bootstrap.LocationPicker} this
30123 OverlayViewHide : true,
30125 * @event loadexception
30126 * Fires when load google lib failed.
30127 * @param {Roo.bootstrap.LocationPicker} this
30129 loadexception : true
30134 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30136 gMapContext: false,
30142 mapTypeControl: false,
30143 disableDoubleClickZoom: false,
30145 streetViewControl: false,
30149 enableAutocomplete: false,
30150 enableReverseGeocode: true,
30153 getAutoCreate: function()
30158 cls: 'roo-location-picker'
30164 initEvents: function(ct, position)
30166 if(!this.el.getWidth() || this.isApplied()){
30170 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30175 initial: function()
30177 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30178 this.fireEvent('loadexception', this);
30182 if(!this.mapTypeId){
30183 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30186 this.gMapContext = this.GMapContext();
30188 this.initOverlayView();
30190 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30194 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30195 _this.setPosition(_this.gMapContext.marker.position);
30198 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30199 _this.fireEvent('mapClick', this, event);
30203 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30204 _this.fireEvent('mapRightClick', this, event);
30208 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30209 _this.fireEvent('markerClick', this, event);
30213 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30214 _this.fireEvent('markerRightClick', this, event);
30218 this.setPosition(this.gMapContext.location);
30220 this.fireEvent('initial', this, this.gMapContext.location);
30223 initOverlayView: function()
30227 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30231 _this.fireEvent('OverlayViewDraw', _this);
30236 _this.fireEvent('OverlayViewOnAdd', _this);
30239 onRemove: function()
30241 _this.fireEvent('OverlayViewOnRemove', _this);
30244 show: function(cpx)
30246 _this.fireEvent('OverlayViewShow', _this, cpx);
30251 _this.fireEvent('OverlayViewHide', _this);
30257 fromLatLngToContainerPixel: function(event)
30259 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30262 isApplied: function()
30264 return this.getGmapContext() == false ? false : true;
30267 getGmapContext: function()
30269 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30272 GMapContext: function()
30274 var position = new google.maps.LatLng(this.latitude, this.longitude);
30276 var _map = new google.maps.Map(this.el.dom, {
30279 mapTypeId: this.mapTypeId,
30280 mapTypeControl: this.mapTypeControl,
30281 disableDoubleClickZoom: this.disableDoubleClickZoom,
30282 scrollwheel: this.scrollwheel,
30283 streetViewControl: this.streetViewControl,
30284 locationName: this.locationName,
30285 draggable: this.draggable,
30286 enableAutocomplete: this.enableAutocomplete,
30287 enableReverseGeocode: this.enableReverseGeocode
30290 var _marker = new google.maps.Marker({
30291 position: position,
30293 title: this.markerTitle,
30294 draggable: this.draggable
30301 location: position,
30302 radius: this.radius,
30303 locationName: this.locationName,
30304 addressComponents: {
30305 formatted_address: null,
30306 addressLine1: null,
30307 addressLine2: null,
30309 streetNumber: null,
30313 stateOrProvince: null
30316 domContainer: this.el.dom,
30317 geodecoder: new google.maps.Geocoder()
30321 drawCircle: function(center, radius, options)
30323 if (this.gMapContext.circle != null) {
30324 this.gMapContext.circle.setMap(null);
30328 options = Roo.apply({}, options, {
30329 strokeColor: "#0000FF",
30330 strokeOpacity: .35,
30332 fillColor: "#0000FF",
30336 options.map = this.gMapContext.map;
30337 options.radius = radius;
30338 options.center = center;
30339 this.gMapContext.circle = new google.maps.Circle(options);
30340 return this.gMapContext.circle;
30346 setPosition: function(location)
30348 this.gMapContext.location = location;
30349 this.gMapContext.marker.setPosition(location);
30350 this.gMapContext.map.panTo(location);
30351 this.drawCircle(location, this.gMapContext.radius, {});
30355 if (this.gMapContext.settings.enableReverseGeocode) {
30356 this.gMapContext.geodecoder.geocode({
30357 latLng: this.gMapContext.location
30358 }, function(results, status) {
30360 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30361 _this.gMapContext.locationName = results[0].formatted_address;
30362 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30364 _this.fireEvent('positionchanged', this, location);
30371 this.fireEvent('positionchanged', this, location);
30376 google.maps.event.trigger(this.gMapContext.map, "resize");
30378 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30380 this.fireEvent('resize', this);
30383 setPositionByLatLng: function(latitude, longitude)
30385 this.setPosition(new google.maps.LatLng(latitude, longitude));
30388 getCurrentPosition: function()
30391 latitude: this.gMapContext.location.lat(),
30392 longitude: this.gMapContext.location.lng()
30396 getAddressName: function()
30398 return this.gMapContext.locationName;
30401 getAddressComponents: function()
30403 return this.gMapContext.addressComponents;
30406 address_component_from_google_geocode: function(address_components)
30410 for (var i = 0; i < address_components.length; i++) {
30411 var component = address_components[i];
30412 if (component.types.indexOf("postal_code") >= 0) {
30413 result.postalCode = component.short_name;
30414 } else if (component.types.indexOf("street_number") >= 0) {
30415 result.streetNumber = component.short_name;
30416 } else if (component.types.indexOf("route") >= 0) {
30417 result.streetName = component.short_name;
30418 } else if (component.types.indexOf("neighborhood") >= 0) {
30419 result.city = component.short_name;
30420 } else if (component.types.indexOf("locality") >= 0) {
30421 result.city = component.short_name;
30422 } else if (component.types.indexOf("sublocality") >= 0) {
30423 result.district = component.short_name;
30424 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30425 result.stateOrProvince = component.short_name;
30426 } else if (component.types.indexOf("country") >= 0) {
30427 result.country = component.short_name;
30431 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30432 result.addressLine2 = "";
30436 setZoomLevel: function(zoom)
30438 this.gMapContext.map.setZoom(zoom);
30451 this.fireEvent('show', this);
30462 this.fireEvent('hide', this);
30467 Roo.apply(Roo.bootstrap.LocationPicker, {
30469 OverlayView : function(map, options)
30471 options = options || {};
30478 * @class Roo.bootstrap.Alert
30479 * @extends Roo.bootstrap.Component
30480 * Bootstrap Alert class - shows an alert area box
30482 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30483 Enter a valid email address
30486 * @cfg {String} title The title of alert
30487 * @cfg {String} html The content of alert
30488 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30489 * @cfg {String} fa font-awesomeicon
30490 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30491 * @cfg {Boolean} close true to show a x closer
30495 * Create a new alert
30496 * @param {Object} config The config object
30500 Roo.bootstrap.Alert = function(config){
30501 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30505 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30511 faicon: false, // BC
30515 getAutoCreate : function()
30527 style : this.close ? '' : 'display:none'
30531 cls : 'roo-alert-icon'
30536 cls : 'roo-alert-title',
30541 cls : 'roo-alert-text',
30548 cfg.cn[0].cls += ' fa ' + this.faicon;
30551 cfg.cn[0].cls += ' fa ' + this.fa;
30555 cfg.cls += ' alert-' + this.weight;
30561 initEvents: function()
30563 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30564 this.titleEl = this.el.select('.roo-alert-title',true).first();
30565 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30566 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30567 if (this.seconds > 0) {
30568 this.hide.defer(this.seconds, this);
30572 * Set the Title Message HTML
30573 * @param {String} html
30575 setTitle : function(str)
30577 this.titleEl.dom.innerHTML = str;
30581 * Set the Body Message HTML
30582 * @param {String} html
30584 setHtml : function(str)
30586 this.htmlEl.dom.innerHTML = str;
30589 * Set the Weight of the alert
30590 * @param {String} (success|info|warning|danger) weight
30593 setWeight : function(weight)
30596 this.el.removeClass('alert-' + this.weight);
30599 this.weight = weight;
30601 this.el.addClass('alert-' + this.weight);
30604 * Set the Icon of the alert
30605 * @param {String} see fontawsome names (name without the 'fa-' bit)
30607 setIcon : function(icon)
30610 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30613 this.faicon = icon;
30615 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30640 * @class Roo.bootstrap.UploadCropbox
30641 * @extends Roo.bootstrap.Component
30642 * Bootstrap UploadCropbox class
30643 * @cfg {String} emptyText show when image has been loaded
30644 * @cfg {String} rotateNotify show when image too small to rotate
30645 * @cfg {Number} errorTimeout default 3000
30646 * @cfg {Number} minWidth default 300
30647 * @cfg {Number} minHeight default 300
30648 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30649 * @cfg {Boolean} isDocument (true|false) default false
30650 * @cfg {String} url action url
30651 * @cfg {String} paramName default 'imageUpload'
30652 * @cfg {String} method default POST
30653 * @cfg {Boolean} loadMask (true|false) default true
30654 * @cfg {Boolean} loadingText default 'Loading...'
30657 * Create a new UploadCropbox
30658 * @param {Object} config The config object
30661 Roo.bootstrap.UploadCropbox = function(config){
30662 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30666 * @event beforeselectfile
30667 * Fire before select file
30668 * @param {Roo.bootstrap.UploadCropbox} this
30670 "beforeselectfile" : true,
30673 * Fire after initEvent
30674 * @param {Roo.bootstrap.UploadCropbox} this
30679 * Fire after initEvent
30680 * @param {Roo.bootstrap.UploadCropbox} this
30681 * @param {String} data
30686 * Fire when preparing the file data
30687 * @param {Roo.bootstrap.UploadCropbox} this
30688 * @param {Object} file
30693 * Fire when get exception
30694 * @param {Roo.bootstrap.UploadCropbox} this
30695 * @param {XMLHttpRequest} xhr
30697 "exception" : true,
30699 * @event beforeloadcanvas
30700 * Fire before load the canvas
30701 * @param {Roo.bootstrap.UploadCropbox} this
30702 * @param {String} src
30704 "beforeloadcanvas" : true,
30707 * Fire when trash image
30708 * @param {Roo.bootstrap.UploadCropbox} this
30713 * Fire when download the image
30714 * @param {Roo.bootstrap.UploadCropbox} this
30718 * @event footerbuttonclick
30719 * Fire when footerbuttonclick
30720 * @param {Roo.bootstrap.UploadCropbox} this
30721 * @param {String} type
30723 "footerbuttonclick" : true,
30727 * @param {Roo.bootstrap.UploadCropbox} this
30732 * Fire when rotate the image
30733 * @param {Roo.bootstrap.UploadCropbox} this
30734 * @param {String} pos
30739 * Fire when inspect the file
30740 * @param {Roo.bootstrap.UploadCropbox} this
30741 * @param {Object} file
30746 * Fire when xhr upload the file
30747 * @param {Roo.bootstrap.UploadCropbox} this
30748 * @param {Object} data
30753 * Fire when arrange the file data
30754 * @param {Roo.bootstrap.UploadCropbox} this
30755 * @param {Object} formData
30760 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30763 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30765 emptyText : 'Click to upload image',
30766 rotateNotify : 'Image is too small to rotate',
30767 errorTimeout : 3000,
30781 cropType : 'image/jpeg',
30783 canvasLoaded : false,
30784 isDocument : false,
30786 paramName : 'imageUpload',
30788 loadingText : 'Loading...',
30791 getAutoCreate : function()
30795 cls : 'roo-upload-cropbox',
30799 cls : 'roo-upload-cropbox-selector',
30804 cls : 'roo-upload-cropbox-body',
30805 style : 'cursor:pointer',
30809 cls : 'roo-upload-cropbox-preview'
30813 cls : 'roo-upload-cropbox-thumb'
30817 cls : 'roo-upload-cropbox-empty-notify',
30818 html : this.emptyText
30822 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30823 html : this.rotateNotify
30829 cls : 'roo-upload-cropbox-footer',
30832 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30842 onRender : function(ct, position)
30844 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30846 if (this.buttons.length) {
30848 Roo.each(this.buttons, function(bb) {
30850 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30852 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30858 this.maskEl = this.el;
30862 initEvents : function()
30864 this.urlAPI = (window.createObjectURL && window) ||
30865 (window.URL && URL.revokeObjectURL && URL) ||
30866 (window.webkitURL && webkitURL);
30868 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30869 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30871 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30872 this.selectorEl.hide();
30874 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30875 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30877 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30878 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30879 this.thumbEl.hide();
30881 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30882 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30884 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30885 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30886 this.errorEl.hide();
30888 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30889 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30890 this.footerEl.hide();
30892 this.setThumbBoxSize();
30898 this.fireEvent('initial', this);
30905 window.addEventListener("resize", function() { _this.resize(); } );
30907 this.bodyEl.on('click', this.beforeSelectFile, this);
30910 this.bodyEl.on('touchstart', this.onTouchStart, this);
30911 this.bodyEl.on('touchmove', this.onTouchMove, this);
30912 this.bodyEl.on('touchend', this.onTouchEnd, this);
30916 this.bodyEl.on('mousedown', this.onMouseDown, this);
30917 this.bodyEl.on('mousemove', this.onMouseMove, this);
30918 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30919 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30920 Roo.get(document).on('mouseup', this.onMouseUp, this);
30923 this.selectorEl.on('change', this.onFileSelected, this);
30929 this.baseScale = 1;
30931 this.baseRotate = 1;
30932 this.dragable = false;
30933 this.pinching = false;
30936 this.cropData = false;
30937 this.notifyEl.dom.innerHTML = this.emptyText;
30939 this.selectorEl.dom.value = '';
30943 resize : function()
30945 if(this.fireEvent('resize', this) != false){
30946 this.setThumbBoxPosition();
30947 this.setCanvasPosition();
30951 onFooterButtonClick : function(e, el, o, type)
30954 case 'rotate-left' :
30955 this.onRotateLeft(e);
30957 case 'rotate-right' :
30958 this.onRotateRight(e);
30961 this.beforeSelectFile(e);
30976 this.fireEvent('footerbuttonclick', this, type);
30979 beforeSelectFile : function(e)
30981 e.preventDefault();
30983 if(this.fireEvent('beforeselectfile', this) != false){
30984 this.selectorEl.dom.click();
30988 onFileSelected : function(e)
30990 e.preventDefault();
30992 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30996 var file = this.selectorEl.dom.files[0];
30998 if(this.fireEvent('inspect', this, file) != false){
30999 this.prepare(file);
31004 trash : function(e)
31006 this.fireEvent('trash', this);
31009 download : function(e)
31011 this.fireEvent('download', this);
31014 loadCanvas : function(src)
31016 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31020 this.imageEl = document.createElement('img');
31024 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31026 this.imageEl.src = src;
31030 onLoadCanvas : function()
31032 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31033 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31035 this.bodyEl.un('click', this.beforeSelectFile, this);
31037 this.notifyEl.hide();
31038 this.thumbEl.show();
31039 this.footerEl.show();
31041 this.baseRotateLevel();
31043 if(this.isDocument){
31044 this.setThumbBoxSize();
31047 this.setThumbBoxPosition();
31049 this.baseScaleLevel();
31055 this.canvasLoaded = true;
31058 this.maskEl.unmask();
31063 setCanvasPosition : function()
31065 if(!this.canvasEl){
31069 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31070 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31072 this.previewEl.setLeft(pw);
31073 this.previewEl.setTop(ph);
31077 onMouseDown : function(e)
31081 this.dragable = true;
31082 this.pinching = false;
31084 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31085 this.dragable = false;
31089 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31090 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31094 onMouseMove : function(e)
31098 if(!this.canvasLoaded){
31102 if (!this.dragable){
31106 var minX = Math.ceil(this.thumbEl.getLeft(true));
31107 var minY = Math.ceil(this.thumbEl.getTop(true));
31109 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31110 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31112 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31113 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31115 x = x - this.mouseX;
31116 y = y - this.mouseY;
31118 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31119 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31121 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31122 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31124 this.previewEl.setLeft(bgX);
31125 this.previewEl.setTop(bgY);
31127 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31128 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31131 onMouseUp : function(e)
31135 this.dragable = false;
31138 onMouseWheel : function(e)
31142 this.startScale = this.scale;
31144 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31146 if(!this.zoomable()){
31147 this.scale = this.startScale;
31156 zoomable : function()
31158 var minScale = this.thumbEl.getWidth() / this.minWidth;
31160 if(this.minWidth < this.minHeight){
31161 minScale = this.thumbEl.getHeight() / this.minHeight;
31164 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31165 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31169 (this.rotate == 0 || this.rotate == 180) &&
31171 width > this.imageEl.OriginWidth ||
31172 height > this.imageEl.OriginHeight ||
31173 (width < this.minWidth && height < this.minHeight)
31181 (this.rotate == 90 || this.rotate == 270) &&
31183 width > this.imageEl.OriginWidth ||
31184 height > this.imageEl.OriginHeight ||
31185 (width < this.minHeight && height < this.minWidth)
31192 !this.isDocument &&
31193 (this.rotate == 0 || this.rotate == 180) &&
31195 width < this.minWidth ||
31196 width > this.imageEl.OriginWidth ||
31197 height < this.minHeight ||
31198 height > this.imageEl.OriginHeight
31205 !this.isDocument &&
31206 (this.rotate == 90 || this.rotate == 270) &&
31208 width < this.minHeight ||
31209 width > this.imageEl.OriginWidth ||
31210 height < this.minWidth ||
31211 height > this.imageEl.OriginHeight
31221 onRotateLeft : function(e)
31223 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31225 var minScale = this.thumbEl.getWidth() / this.minWidth;
31227 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31228 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31230 this.startScale = this.scale;
31232 while (this.getScaleLevel() < minScale){
31234 this.scale = this.scale + 1;
31236 if(!this.zoomable()){
31241 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31242 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31247 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31254 this.scale = this.startScale;
31256 this.onRotateFail();
31261 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31263 if(this.isDocument){
31264 this.setThumbBoxSize();
31265 this.setThumbBoxPosition();
31266 this.setCanvasPosition();
31271 this.fireEvent('rotate', this, 'left');
31275 onRotateRight : function(e)
31277 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31279 var minScale = this.thumbEl.getWidth() / this.minWidth;
31281 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31282 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31284 this.startScale = this.scale;
31286 while (this.getScaleLevel() < minScale){
31288 this.scale = this.scale + 1;
31290 if(!this.zoomable()){
31295 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31296 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31301 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31308 this.scale = this.startScale;
31310 this.onRotateFail();
31315 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31317 if(this.isDocument){
31318 this.setThumbBoxSize();
31319 this.setThumbBoxPosition();
31320 this.setCanvasPosition();
31325 this.fireEvent('rotate', this, 'right');
31328 onRotateFail : function()
31330 this.errorEl.show(true);
31334 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31339 this.previewEl.dom.innerHTML = '';
31341 var canvasEl = document.createElement("canvas");
31343 var contextEl = canvasEl.getContext("2d");
31345 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31346 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31347 var center = this.imageEl.OriginWidth / 2;
31349 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31350 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31351 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31352 center = this.imageEl.OriginHeight / 2;
31355 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31357 contextEl.translate(center, center);
31358 contextEl.rotate(this.rotate * Math.PI / 180);
31360 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31362 this.canvasEl = document.createElement("canvas");
31364 this.contextEl = this.canvasEl.getContext("2d");
31366 switch (this.rotate) {
31369 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31370 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31372 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31377 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31378 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31380 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31381 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31385 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31390 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31391 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31393 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31394 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31398 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31403 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31404 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31406 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31407 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31411 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31418 this.previewEl.appendChild(this.canvasEl);
31420 this.setCanvasPosition();
31425 if(!this.canvasLoaded){
31429 var imageCanvas = document.createElement("canvas");
31431 var imageContext = imageCanvas.getContext("2d");
31433 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31434 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31436 var center = imageCanvas.width / 2;
31438 imageContext.translate(center, center);
31440 imageContext.rotate(this.rotate * Math.PI / 180);
31442 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31444 var canvas = document.createElement("canvas");
31446 var context = canvas.getContext("2d");
31448 canvas.width = this.minWidth;
31449 canvas.height = this.minHeight;
31451 switch (this.rotate) {
31454 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31455 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31457 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31458 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31460 var targetWidth = this.minWidth - 2 * x;
31461 var targetHeight = this.minHeight - 2 * y;
31465 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31466 scale = targetWidth / width;
31469 if(x > 0 && y == 0){
31470 scale = targetHeight / height;
31473 if(x > 0 && y > 0){
31474 scale = targetWidth / width;
31476 if(width < height){
31477 scale = targetHeight / height;
31481 context.scale(scale, scale);
31483 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31484 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31486 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31487 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31489 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31494 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31495 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31497 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31498 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31500 var targetWidth = this.minWidth - 2 * x;
31501 var targetHeight = this.minHeight - 2 * y;
31505 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31506 scale = targetWidth / width;
31509 if(x > 0 && y == 0){
31510 scale = targetHeight / height;
31513 if(x > 0 && y > 0){
31514 scale = targetWidth / width;
31516 if(width < height){
31517 scale = targetHeight / height;
31521 context.scale(scale, scale);
31523 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31524 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31526 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31527 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31529 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31531 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31536 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31537 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31539 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31540 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31542 var targetWidth = this.minWidth - 2 * x;
31543 var targetHeight = this.minHeight - 2 * y;
31547 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31548 scale = targetWidth / width;
31551 if(x > 0 && y == 0){
31552 scale = targetHeight / height;
31555 if(x > 0 && y > 0){
31556 scale = targetWidth / width;
31558 if(width < height){
31559 scale = targetHeight / height;
31563 context.scale(scale, scale);
31565 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31566 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31568 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31569 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31571 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31572 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31574 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31579 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31580 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31582 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31583 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31585 var targetWidth = this.minWidth - 2 * x;
31586 var targetHeight = this.minHeight - 2 * y;
31590 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31591 scale = targetWidth / width;
31594 if(x > 0 && y == 0){
31595 scale = targetHeight / height;
31598 if(x > 0 && y > 0){
31599 scale = targetWidth / width;
31601 if(width < height){
31602 scale = targetHeight / height;
31606 context.scale(scale, scale);
31608 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31609 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31611 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31612 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31614 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31616 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31623 this.cropData = canvas.toDataURL(this.cropType);
31625 if(this.fireEvent('crop', this, this.cropData) !== false){
31626 this.process(this.file, this.cropData);
31633 setThumbBoxSize : function()
31637 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31638 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31639 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31641 this.minWidth = width;
31642 this.minHeight = height;
31644 if(this.rotate == 90 || this.rotate == 270){
31645 this.minWidth = height;
31646 this.minHeight = width;
31651 width = Math.ceil(this.minWidth * height / this.minHeight);
31653 if(this.minWidth > this.minHeight){
31655 height = Math.ceil(this.minHeight * width / this.minWidth);
31658 this.thumbEl.setStyle({
31659 width : width + 'px',
31660 height : height + 'px'
31667 setThumbBoxPosition : function()
31669 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31670 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31672 this.thumbEl.setLeft(x);
31673 this.thumbEl.setTop(y);
31677 baseRotateLevel : function()
31679 this.baseRotate = 1;
31682 typeof(this.exif) != 'undefined' &&
31683 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31684 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31686 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31689 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31693 baseScaleLevel : function()
31697 if(this.isDocument){
31699 if(this.baseRotate == 6 || this.baseRotate == 8){
31701 height = this.thumbEl.getHeight();
31702 this.baseScale = height / this.imageEl.OriginWidth;
31704 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31705 width = this.thumbEl.getWidth();
31706 this.baseScale = width / this.imageEl.OriginHeight;
31712 height = this.thumbEl.getHeight();
31713 this.baseScale = height / this.imageEl.OriginHeight;
31715 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31716 width = this.thumbEl.getWidth();
31717 this.baseScale = width / this.imageEl.OriginWidth;
31723 if(this.baseRotate == 6 || this.baseRotate == 8){
31725 width = this.thumbEl.getHeight();
31726 this.baseScale = width / this.imageEl.OriginHeight;
31728 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31729 height = this.thumbEl.getWidth();
31730 this.baseScale = height / this.imageEl.OriginHeight;
31733 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31734 height = this.thumbEl.getWidth();
31735 this.baseScale = height / this.imageEl.OriginHeight;
31737 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31738 width = this.thumbEl.getHeight();
31739 this.baseScale = width / this.imageEl.OriginWidth;
31746 width = this.thumbEl.getWidth();
31747 this.baseScale = width / this.imageEl.OriginWidth;
31749 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31750 height = this.thumbEl.getHeight();
31751 this.baseScale = height / this.imageEl.OriginHeight;
31754 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31756 height = this.thumbEl.getHeight();
31757 this.baseScale = height / this.imageEl.OriginHeight;
31759 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31760 width = this.thumbEl.getWidth();
31761 this.baseScale = width / this.imageEl.OriginWidth;
31769 getScaleLevel : function()
31771 return this.baseScale * Math.pow(1.1, this.scale);
31774 onTouchStart : function(e)
31776 if(!this.canvasLoaded){
31777 this.beforeSelectFile(e);
31781 var touches = e.browserEvent.touches;
31787 if(touches.length == 1){
31788 this.onMouseDown(e);
31792 if(touches.length != 2){
31798 for(var i = 0, finger; finger = touches[i]; i++){
31799 coords.push(finger.pageX, finger.pageY);
31802 var x = Math.pow(coords[0] - coords[2], 2);
31803 var y = Math.pow(coords[1] - coords[3], 2);
31805 this.startDistance = Math.sqrt(x + y);
31807 this.startScale = this.scale;
31809 this.pinching = true;
31810 this.dragable = false;
31814 onTouchMove : function(e)
31816 if(!this.pinching && !this.dragable){
31820 var touches = e.browserEvent.touches;
31827 this.onMouseMove(e);
31833 for(var i = 0, finger; finger = touches[i]; i++){
31834 coords.push(finger.pageX, finger.pageY);
31837 var x = Math.pow(coords[0] - coords[2], 2);
31838 var y = Math.pow(coords[1] - coords[3], 2);
31840 this.endDistance = Math.sqrt(x + y);
31842 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31844 if(!this.zoomable()){
31845 this.scale = this.startScale;
31853 onTouchEnd : function(e)
31855 this.pinching = false;
31856 this.dragable = false;
31860 process : function(file, crop)
31863 this.maskEl.mask(this.loadingText);
31866 this.xhr = new XMLHttpRequest();
31868 file.xhr = this.xhr;
31870 this.xhr.open(this.method, this.url, true);
31873 "Accept": "application/json",
31874 "Cache-Control": "no-cache",
31875 "X-Requested-With": "XMLHttpRequest"
31878 for (var headerName in headers) {
31879 var headerValue = headers[headerName];
31881 this.xhr.setRequestHeader(headerName, headerValue);
31887 this.xhr.onload = function()
31889 _this.xhrOnLoad(_this.xhr);
31892 this.xhr.onerror = function()
31894 _this.xhrOnError(_this.xhr);
31897 var formData = new FormData();
31899 formData.append('returnHTML', 'NO');
31902 formData.append('crop', crop);
31905 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31906 formData.append(this.paramName, file, file.name);
31909 if(typeof(file.filename) != 'undefined'){
31910 formData.append('filename', file.filename);
31913 if(typeof(file.mimetype) != 'undefined'){
31914 formData.append('mimetype', file.mimetype);
31917 if(this.fireEvent('arrange', this, formData) != false){
31918 this.xhr.send(formData);
31922 xhrOnLoad : function(xhr)
31925 this.maskEl.unmask();
31928 if (xhr.readyState !== 4) {
31929 this.fireEvent('exception', this, xhr);
31933 var response = Roo.decode(xhr.responseText);
31935 if(!response.success){
31936 this.fireEvent('exception', this, xhr);
31940 var response = Roo.decode(xhr.responseText);
31942 this.fireEvent('upload', this, response);
31946 xhrOnError : function()
31949 this.maskEl.unmask();
31952 Roo.log('xhr on error');
31954 var response = Roo.decode(xhr.responseText);
31960 prepare : function(file)
31963 this.maskEl.mask(this.loadingText);
31969 if(typeof(file) === 'string'){
31970 this.loadCanvas(file);
31974 if(!file || !this.urlAPI){
31979 this.cropType = file.type;
31983 if(this.fireEvent('prepare', this, this.file) != false){
31985 var reader = new FileReader();
31987 reader.onload = function (e) {
31988 if (e.target.error) {
31989 Roo.log(e.target.error);
31993 var buffer = e.target.result,
31994 dataView = new DataView(buffer),
31996 maxOffset = dataView.byteLength - 4,
32000 if (dataView.getUint16(0) === 0xffd8) {
32001 while (offset < maxOffset) {
32002 markerBytes = dataView.getUint16(offset);
32004 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32005 markerLength = dataView.getUint16(offset + 2) + 2;
32006 if (offset + markerLength > dataView.byteLength) {
32007 Roo.log('Invalid meta data: Invalid segment size.');
32011 if(markerBytes == 0xffe1){
32012 _this.parseExifData(
32019 offset += markerLength;
32029 var url = _this.urlAPI.createObjectURL(_this.file);
32031 _this.loadCanvas(url);
32036 reader.readAsArrayBuffer(this.file);
32042 parseExifData : function(dataView, offset, length)
32044 var tiffOffset = offset + 10,
32048 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32049 // No Exif data, might be XMP data instead
32053 // Check for the ASCII code for "Exif" (0x45786966):
32054 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32055 // No Exif data, might be XMP data instead
32058 if (tiffOffset + 8 > dataView.byteLength) {
32059 Roo.log('Invalid Exif data: Invalid segment size.');
32062 // Check for the two null bytes:
32063 if (dataView.getUint16(offset + 8) !== 0x0000) {
32064 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32067 // Check the byte alignment:
32068 switch (dataView.getUint16(tiffOffset)) {
32070 littleEndian = true;
32073 littleEndian = false;
32076 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32079 // Check for the TIFF tag marker (0x002A):
32080 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32081 Roo.log('Invalid Exif data: Missing TIFF marker.');
32084 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32085 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32087 this.parseExifTags(
32090 tiffOffset + dirOffset,
32095 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32100 if (dirOffset + 6 > dataView.byteLength) {
32101 Roo.log('Invalid Exif data: Invalid directory offset.');
32104 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32105 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32106 if (dirEndOffset + 4 > dataView.byteLength) {
32107 Roo.log('Invalid Exif data: Invalid directory size.');
32110 for (i = 0; i < tagsNumber; i += 1) {
32114 dirOffset + 2 + 12 * i, // tag offset
32118 // Return the offset to the next directory:
32119 return dataView.getUint32(dirEndOffset, littleEndian);
32122 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32124 var tag = dataView.getUint16(offset, littleEndian);
32126 this.exif[tag] = this.getExifValue(
32130 dataView.getUint16(offset + 2, littleEndian), // tag type
32131 dataView.getUint32(offset + 4, littleEndian), // tag length
32136 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32138 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32147 Roo.log('Invalid Exif data: Invalid tag type.');
32151 tagSize = tagType.size * length;
32152 // Determine if the value is contained in the dataOffset bytes,
32153 // or if the value at the dataOffset is a pointer to the actual data:
32154 dataOffset = tagSize > 4 ?
32155 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32156 if (dataOffset + tagSize > dataView.byteLength) {
32157 Roo.log('Invalid Exif data: Invalid data offset.');
32160 if (length === 1) {
32161 return tagType.getValue(dataView, dataOffset, littleEndian);
32164 for (i = 0; i < length; i += 1) {
32165 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32168 if (tagType.ascii) {
32170 // Concatenate the chars:
32171 for (i = 0; i < values.length; i += 1) {
32173 // Ignore the terminating NULL byte(s):
32174 if (c === '\u0000') {
32186 Roo.apply(Roo.bootstrap.UploadCropbox, {
32188 'Orientation': 0x0112
32192 1: 0, //'top-left',
32194 3: 180, //'bottom-right',
32195 // 4: 'bottom-left',
32197 6: 90, //'right-top',
32198 // 7: 'right-bottom',
32199 8: 270 //'left-bottom'
32203 // byte, 8-bit unsigned int:
32205 getValue: function (dataView, dataOffset) {
32206 return dataView.getUint8(dataOffset);
32210 // ascii, 8-bit byte:
32212 getValue: function (dataView, dataOffset) {
32213 return String.fromCharCode(dataView.getUint8(dataOffset));
32218 // short, 16 bit int:
32220 getValue: function (dataView, dataOffset, littleEndian) {
32221 return dataView.getUint16(dataOffset, littleEndian);
32225 // long, 32 bit int:
32227 getValue: function (dataView, dataOffset, littleEndian) {
32228 return dataView.getUint32(dataOffset, littleEndian);
32232 // rational = two long values, first is numerator, second is denominator:
32234 getValue: function (dataView, dataOffset, littleEndian) {
32235 return dataView.getUint32(dataOffset, littleEndian) /
32236 dataView.getUint32(dataOffset + 4, littleEndian);
32240 // slong, 32 bit signed int:
32242 getValue: function (dataView, dataOffset, littleEndian) {
32243 return dataView.getInt32(dataOffset, littleEndian);
32247 // srational, two slongs, first is numerator, second is denominator:
32249 getValue: function (dataView, dataOffset, littleEndian) {
32250 return dataView.getInt32(dataOffset, littleEndian) /
32251 dataView.getInt32(dataOffset + 4, littleEndian);
32261 cls : 'btn-group roo-upload-cropbox-rotate-left',
32262 action : 'rotate-left',
32266 cls : 'btn btn-default',
32267 html : '<i class="fa fa-undo"></i>'
32273 cls : 'btn-group roo-upload-cropbox-picture',
32274 action : 'picture',
32278 cls : 'btn btn-default',
32279 html : '<i class="fa fa-picture-o"></i>'
32285 cls : 'btn-group roo-upload-cropbox-rotate-right',
32286 action : 'rotate-right',
32290 cls : 'btn btn-default',
32291 html : '<i class="fa fa-repeat"></i>'
32299 cls : 'btn-group roo-upload-cropbox-rotate-left',
32300 action : 'rotate-left',
32304 cls : 'btn btn-default',
32305 html : '<i class="fa fa-undo"></i>'
32311 cls : 'btn-group roo-upload-cropbox-download',
32312 action : 'download',
32316 cls : 'btn btn-default',
32317 html : '<i class="fa fa-download"></i>'
32323 cls : 'btn-group roo-upload-cropbox-crop',
32328 cls : 'btn btn-default',
32329 html : '<i class="fa fa-crop"></i>'
32335 cls : 'btn-group roo-upload-cropbox-trash',
32340 cls : 'btn btn-default',
32341 html : '<i class="fa fa-trash"></i>'
32347 cls : 'btn-group roo-upload-cropbox-rotate-right',
32348 action : 'rotate-right',
32352 cls : 'btn btn-default',
32353 html : '<i class="fa fa-repeat"></i>'
32361 cls : 'btn-group roo-upload-cropbox-rotate-left',
32362 action : 'rotate-left',
32366 cls : 'btn btn-default',
32367 html : '<i class="fa fa-undo"></i>'
32373 cls : 'btn-group roo-upload-cropbox-rotate-right',
32374 action : 'rotate-right',
32378 cls : 'btn btn-default',
32379 html : '<i class="fa fa-repeat"></i>'
32392 * @class Roo.bootstrap.DocumentManager
32393 * @extends Roo.bootstrap.Component
32394 * Bootstrap DocumentManager class
32395 * @cfg {String} paramName default 'imageUpload'
32396 * @cfg {String} toolTipName default 'filename'
32397 * @cfg {String} method default POST
32398 * @cfg {String} url action url
32399 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32400 * @cfg {Boolean} multiple multiple upload default true
32401 * @cfg {Number} thumbSize default 300
32402 * @cfg {String} fieldLabel
32403 * @cfg {Number} labelWidth default 4
32404 * @cfg {String} labelAlign (left|top) default left
32405 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32406 * @cfg {Number} labellg set the width of label (1-12)
32407 * @cfg {Number} labelmd set the width of label (1-12)
32408 * @cfg {Number} labelsm set the width of label (1-12)
32409 * @cfg {Number} labelxs set the width of label (1-12)
32412 * Create a new DocumentManager
32413 * @param {Object} config The config object
32416 Roo.bootstrap.DocumentManager = function(config){
32417 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32420 this.delegates = [];
32425 * Fire when initial the DocumentManager
32426 * @param {Roo.bootstrap.DocumentManager} this
32431 * inspect selected file
32432 * @param {Roo.bootstrap.DocumentManager} this
32433 * @param {File} file
32438 * Fire when xhr load exception
32439 * @param {Roo.bootstrap.DocumentManager} this
32440 * @param {XMLHttpRequest} xhr
32442 "exception" : true,
32444 * @event afterupload
32445 * Fire when xhr load exception
32446 * @param {Roo.bootstrap.DocumentManager} this
32447 * @param {XMLHttpRequest} xhr
32449 "afterupload" : true,
32452 * prepare the form data
32453 * @param {Roo.bootstrap.DocumentManager} this
32454 * @param {Object} formData
32459 * Fire when remove the file
32460 * @param {Roo.bootstrap.DocumentManager} this
32461 * @param {Object} file
32466 * Fire after refresh the file
32467 * @param {Roo.bootstrap.DocumentManager} this
32472 * Fire after click the image
32473 * @param {Roo.bootstrap.DocumentManager} this
32474 * @param {Object} file
32479 * Fire when upload a image and editable set to true
32480 * @param {Roo.bootstrap.DocumentManager} this
32481 * @param {Object} file
32485 * @event beforeselectfile
32486 * Fire before select file
32487 * @param {Roo.bootstrap.DocumentManager} this
32489 "beforeselectfile" : true,
32492 * Fire before process file
32493 * @param {Roo.bootstrap.DocumentManager} this
32494 * @param {Object} file
32498 * @event previewrendered
32499 * Fire when preview rendered
32500 * @param {Roo.bootstrap.DocumentManager} this
32501 * @param {Object} file
32503 "previewrendered" : true,
32506 "previewResize" : true
32511 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32520 paramName : 'imageUpload',
32521 toolTipName : 'filename',
32524 labelAlign : 'left',
32534 getAutoCreate : function()
32536 var managerWidget = {
32538 cls : 'roo-document-manager',
32542 cls : 'roo-document-manager-selector',
32547 cls : 'roo-document-manager-uploader',
32551 cls : 'roo-document-manager-upload-btn',
32552 html : '<i class="fa fa-plus"></i>'
32563 cls : 'column col-md-12',
32568 if(this.fieldLabel.length){
32573 cls : 'column col-md-12',
32574 html : this.fieldLabel
32578 cls : 'column col-md-12',
32583 if(this.labelAlign == 'left'){
32588 html : this.fieldLabel
32597 if(this.labelWidth > 12){
32598 content[0].style = "width: " + this.labelWidth + 'px';
32601 if(this.labelWidth < 13 && this.labelmd == 0){
32602 this.labelmd = this.labelWidth;
32605 if(this.labellg > 0){
32606 content[0].cls += ' col-lg-' + this.labellg;
32607 content[1].cls += ' col-lg-' + (12 - this.labellg);
32610 if(this.labelmd > 0){
32611 content[0].cls += ' col-md-' + this.labelmd;
32612 content[1].cls += ' col-md-' + (12 - this.labelmd);
32615 if(this.labelsm > 0){
32616 content[0].cls += ' col-sm-' + this.labelsm;
32617 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32620 if(this.labelxs > 0){
32621 content[0].cls += ' col-xs-' + this.labelxs;
32622 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32630 cls : 'row clearfix',
32638 initEvents : function()
32640 this.managerEl = this.el.select('.roo-document-manager', true).first();
32641 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32643 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32644 this.selectorEl.hide();
32647 this.selectorEl.attr('multiple', 'multiple');
32650 this.selectorEl.on('change', this.onFileSelected, this);
32652 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32653 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32655 this.uploader.on('click', this.onUploaderClick, this);
32657 this.renderProgressDialog();
32661 window.addEventListener("resize", function() { _this.refresh(); } );
32663 this.fireEvent('initial', this);
32666 renderProgressDialog : function()
32670 this.progressDialog = new Roo.bootstrap.Modal({
32671 cls : 'roo-document-manager-progress-dialog',
32672 allow_close : false,
32683 btnclick : function() {
32684 _this.uploadCancel();
32690 this.progressDialog.render(Roo.get(document.body));
32692 this.progress = new Roo.bootstrap.Progress({
32693 cls : 'roo-document-manager-progress',
32698 this.progress.render(this.progressDialog.getChildContainer());
32700 this.progressBar = new Roo.bootstrap.ProgressBar({
32701 cls : 'roo-document-manager-progress-bar',
32704 aria_valuemax : 12,
32708 this.progressBar.render(this.progress.getChildContainer());
32711 onUploaderClick : function(e)
32713 e.preventDefault();
32715 if(this.fireEvent('beforeselectfile', this) != false){
32716 this.selectorEl.dom.click();
32721 onFileSelected : function(e)
32723 e.preventDefault();
32725 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32729 Roo.each(this.selectorEl.dom.files, function(file){
32730 if(this.fireEvent('inspect', this, file) != false){
32731 this.files.push(file);
32741 this.selectorEl.dom.value = '';
32743 if(!this.files || !this.files.length){
32747 if(this.boxes > 0 && this.files.length > this.boxes){
32748 this.files = this.files.slice(0, this.boxes);
32751 this.uploader.show();
32753 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32754 this.uploader.hide();
32763 Roo.each(this.files, function(file){
32765 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32766 var f = this.renderPreview(file);
32771 if(file.type.indexOf('image') != -1){
32772 this.delegates.push(
32774 _this.process(file);
32775 }).createDelegate(this)
32783 _this.process(file);
32784 }).createDelegate(this)
32789 this.files = files;
32791 this.delegates = this.delegates.concat(docs);
32793 if(!this.delegates.length){
32798 this.progressBar.aria_valuemax = this.delegates.length;
32805 arrange : function()
32807 if(!this.delegates.length){
32808 this.progressDialog.hide();
32813 var delegate = this.delegates.shift();
32815 this.progressDialog.show();
32817 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32819 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32824 refresh : function()
32826 this.uploader.show();
32828 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32829 this.uploader.hide();
32832 Roo.isTouch ? this.closable(false) : this.closable(true);
32834 this.fireEvent('refresh', this);
32837 onRemove : function(e, el, o)
32839 e.preventDefault();
32841 this.fireEvent('remove', this, o);
32845 remove : function(o)
32849 Roo.each(this.files, function(file){
32850 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32859 this.files = files;
32866 Roo.each(this.files, function(file){
32871 file.target.remove();
32880 onClick : function(e, el, o)
32882 e.preventDefault();
32884 this.fireEvent('click', this, o);
32888 closable : function(closable)
32890 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32892 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32904 xhrOnLoad : function(xhr)
32906 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32910 if (xhr.readyState !== 4) {
32912 this.fireEvent('exception', this, xhr);
32916 var response = Roo.decode(xhr.responseText);
32918 if(!response.success){
32920 this.fireEvent('exception', this, xhr);
32924 var file = this.renderPreview(response.data);
32926 this.files.push(file);
32930 this.fireEvent('afterupload', this, xhr);
32934 xhrOnError : function(xhr)
32936 Roo.log('xhr on error');
32938 var response = Roo.decode(xhr.responseText);
32945 process : function(file)
32947 if(this.fireEvent('process', this, file) !== false){
32948 if(this.editable && file.type.indexOf('image') != -1){
32949 this.fireEvent('edit', this, file);
32953 this.uploadStart(file, false);
32960 uploadStart : function(file, crop)
32962 this.xhr = new XMLHttpRequest();
32964 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32969 file.xhr = this.xhr;
32971 this.managerEl.createChild({
32973 cls : 'roo-document-manager-loading',
32977 tooltip : file.name,
32978 cls : 'roo-document-manager-thumb',
32979 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32985 this.xhr.open(this.method, this.url, true);
32988 "Accept": "application/json",
32989 "Cache-Control": "no-cache",
32990 "X-Requested-With": "XMLHttpRequest"
32993 for (var headerName in headers) {
32994 var headerValue = headers[headerName];
32996 this.xhr.setRequestHeader(headerName, headerValue);
33002 this.xhr.onload = function()
33004 _this.xhrOnLoad(_this.xhr);
33007 this.xhr.onerror = function()
33009 _this.xhrOnError(_this.xhr);
33012 var formData = new FormData();
33014 formData.append('returnHTML', 'NO');
33017 formData.append('crop', crop);
33020 formData.append(this.paramName, file, file.name);
33027 if(this.fireEvent('prepare', this, formData, options) != false){
33029 if(options.manually){
33033 this.xhr.send(formData);
33037 this.uploadCancel();
33040 uploadCancel : function()
33046 this.delegates = [];
33048 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33055 renderPreview : function(file)
33057 if(typeof(file.target) != 'undefined' && file.target){
33061 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33063 var previewEl = this.managerEl.createChild({
33065 cls : 'roo-document-manager-preview',
33069 tooltip : file[this.toolTipName],
33070 cls : 'roo-document-manager-thumb',
33071 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33076 html : '<i class="fa fa-times-circle"></i>'
33081 var close = previewEl.select('button.close', true).first();
33083 close.on('click', this.onRemove, this, file);
33085 file.target = previewEl;
33087 var image = previewEl.select('img', true).first();
33091 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33093 image.on('click', this.onClick, this, file);
33095 this.fireEvent('previewrendered', this, file);
33101 onPreviewLoad : function(file, image)
33103 if(typeof(file.target) == 'undefined' || !file.target){
33107 var width = image.dom.naturalWidth || image.dom.width;
33108 var height = image.dom.naturalHeight || image.dom.height;
33110 if(!this.previewResize) {
33114 if(width > height){
33115 file.target.addClass('wide');
33119 file.target.addClass('tall');
33124 uploadFromSource : function(file, crop)
33126 this.xhr = new XMLHttpRequest();
33128 this.managerEl.createChild({
33130 cls : 'roo-document-manager-loading',
33134 tooltip : file.name,
33135 cls : 'roo-document-manager-thumb',
33136 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33142 this.xhr.open(this.method, this.url, true);
33145 "Accept": "application/json",
33146 "Cache-Control": "no-cache",
33147 "X-Requested-With": "XMLHttpRequest"
33150 for (var headerName in headers) {
33151 var headerValue = headers[headerName];
33153 this.xhr.setRequestHeader(headerName, headerValue);
33159 this.xhr.onload = function()
33161 _this.xhrOnLoad(_this.xhr);
33164 this.xhr.onerror = function()
33166 _this.xhrOnError(_this.xhr);
33169 var formData = new FormData();
33171 formData.append('returnHTML', 'NO');
33173 formData.append('crop', crop);
33175 if(typeof(file.filename) != 'undefined'){
33176 formData.append('filename', file.filename);
33179 if(typeof(file.mimetype) != 'undefined'){
33180 formData.append('mimetype', file.mimetype);
33185 if(this.fireEvent('prepare', this, formData) != false){
33186 this.xhr.send(formData);
33196 * @class Roo.bootstrap.DocumentViewer
33197 * @extends Roo.bootstrap.Component
33198 * Bootstrap DocumentViewer class
33199 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33200 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33203 * Create a new DocumentViewer
33204 * @param {Object} config The config object
33207 Roo.bootstrap.DocumentViewer = function(config){
33208 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33213 * Fire after initEvent
33214 * @param {Roo.bootstrap.DocumentViewer} this
33220 * @param {Roo.bootstrap.DocumentViewer} this
33225 * Fire after download button
33226 * @param {Roo.bootstrap.DocumentViewer} this
33231 * Fire after trash button
33232 * @param {Roo.bootstrap.DocumentViewer} this
33239 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33241 showDownload : true,
33245 getAutoCreate : function()
33249 cls : 'roo-document-viewer',
33253 cls : 'roo-document-viewer-body',
33257 cls : 'roo-document-viewer-thumb',
33261 cls : 'roo-document-viewer-image'
33269 cls : 'roo-document-viewer-footer',
33272 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33276 cls : 'btn-group roo-document-viewer-download',
33280 cls : 'btn btn-default',
33281 html : '<i class="fa fa-download"></i>'
33287 cls : 'btn-group roo-document-viewer-trash',
33291 cls : 'btn btn-default',
33292 html : '<i class="fa fa-trash"></i>'
33305 initEvents : function()
33307 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33308 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33310 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33311 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33313 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33314 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33316 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33317 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33319 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33320 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33322 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33323 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33325 this.bodyEl.on('click', this.onClick, this);
33326 this.downloadBtn.on('click', this.onDownload, this);
33327 this.trashBtn.on('click', this.onTrash, this);
33329 this.downloadBtn.hide();
33330 this.trashBtn.hide();
33332 if(this.showDownload){
33333 this.downloadBtn.show();
33336 if(this.showTrash){
33337 this.trashBtn.show();
33340 if(!this.showDownload && !this.showTrash) {
33341 this.footerEl.hide();
33346 initial : function()
33348 this.fireEvent('initial', this);
33352 onClick : function(e)
33354 e.preventDefault();
33356 this.fireEvent('click', this);
33359 onDownload : function(e)
33361 e.preventDefault();
33363 this.fireEvent('download', this);
33366 onTrash : function(e)
33368 e.preventDefault();
33370 this.fireEvent('trash', this);
33382 * @class Roo.bootstrap.form.FieldLabel
33383 * @extends Roo.bootstrap.Component
33384 * Bootstrap FieldLabel class
33385 * @cfg {String} html contents of the element
33386 * @cfg {String} tag tag of the element default label
33387 * @cfg {String} cls class of the element
33388 * @cfg {String} target label target
33389 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33390 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33391 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33392 * @cfg {String} iconTooltip default "This field is required"
33393 * @cfg {String} indicatorpos (left|right) default left
33396 * Create a new FieldLabel
33397 * @param {Object} config The config object
33400 Roo.bootstrap.form.FieldLabel = function(config){
33401 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33406 * Fires after the field has been marked as invalid.
33407 * @param {Roo.form.FieldLabel} this
33408 * @param {String} msg The validation message
33413 * Fires after the field has been validated with no errors.
33414 * @param {Roo.form.FieldLabel} this
33420 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33427 invalidClass : 'has-warning',
33428 validClass : 'has-success',
33429 iconTooltip : 'This field is required',
33430 indicatorpos : 'left',
33432 getAutoCreate : function(){
33435 if (!this.allowBlank) {
33441 cls : 'roo-bootstrap-field-label ' + this.cls,
33446 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33447 tooltip : this.iconTooltip
33456 if(this.indicatorpos == 'right'){
33459 cls : 'roo-bootstrap-field-label ' + this.cls,
33468 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33469 tooltip : this.iconTooltip
33478 initEvents: function()
33480 Roo.bootstrap.Element.superclass.initEvents.call(this);
33482 this.indicator = this.indicatorEl();
33484 if(this.indicator){
33485 this.indicator.removeClass('visible');
33486 this.indicator.addClass('invisible');
33489 Roo.bootstrap.form.FieldLabel.register(this);
33492 indicatorEl : function()
33494 var indicator = this.el.select('i.roo-required-indicator',true).first();
33505 * Mark this field as valid
33507 markValid : function()
33509 if(this.indicator){
33510 this.indicator.removeClass('visible');
33511 this.indicator.addClass('invisible');
33513 if (Roo.bootstrap.version == 3) {
33514 this.el.removeClass(this.invalidClass);
33515 this.el.addClass(this.validClass);
33517 this.el.removeClass('is-invalid');
33518 this.el.addClass('is-valid');
33522 this.fireEvent('valid', this);
33526 * Mark this field as invalid
33527 * @param {String} msg The validation message
33529 markInvalid : function(msg)
33531 if(this.indicator){
33532 this.indicator.removeClass('invisible');
33533 this.indicator.addClass('visible');
33535 if (Roo.bootstrap.version == 3) {
33536 this.el.removeClass(this.validClass);
33537 this.el.addClass(this.invalidClass);
33539 this.el.removeClass('is-valid');
33540 this.el.addClass('is-invalid');
33544 this.fireEvent('invalid', this, msg);
33550 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33555 * register a FieldLabel Group
33556 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33558 register : function(label)
33560 if(this.groups.hasOwnProperty(label.target)){
33564 this.groups[label.target] = label;
33568 * fetch a FieldLabel Group based on the target
33569 * @param {string} target
33570 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33572 get: function(target) {
33573 if (typeof(this.groups[target]) == 'undefined') {
33577 return this.groups[target] ;
33586 * page DateSplitField.
33592 * @class Roo.bootstrap.form.DateSplitField
33593 * @extends Roo.bootstrap.Component
33594 * Bootstrap DateSplitField class
33595 * @cfg {string} fieldLabel - the label associated
33596 * @cfg {Number} labelWidth set the width of label (0-12)
33597 * @cfg {String} labelAlign (top|left)
33598 * @cfg {Boolean} dayAllowBlank (true|false) default false
33599 * @cfg {Boolean} monthAllowBlank (true|false) default false
33600 * @cfg {Boolean} yearAllowBlank (true|false) default false
33601 * @cfg {string} dayPlaceholder
33602 * @cfg {string} monthPlaceholder
33603 * @cfg {string} yearPlaceholder
33604 * @cfg {string} dayFormat default 'd'
33605 * @cfg {string} monthFormat default 'm'
33606 * @cfg {string} yearFormat default 'Y'
33607 * @cfg {Number} labellg set the width of label (1-12)
33608 * @cfg {Number} labelmd set the width of label (1-12)
33609 * @cfg {Number} labelsm set the width of label (1-12)
33610 * @cfg {Number} labelxs set the width of label (1-12)
33614 * Create a new DateSplitField
33615 * @param {Object} config The config object
33618 Roo.bootstrap.form.DateSplitField = function(config){
33619 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33625 * getting the data of years
33626 * @param {Roo.bootstrap.form.DateSplitField} this
33627 * @param {Object} years
33632 * getting the data of days
33633 * @param {Roo.bootstrap.form.DateSplitField} this
33634 * @param {Object} days
33639 * Fires after the field has been marked as invalid.
33640 * @param {Roo.form.Field} this
33641 * @param {String} msg The validation message
33646 * Fires after the field has been validated with no errors.
33647 * @param {Roo.form.Field} this
33653 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33656 labelAlign : 'top',
33658 dayAllowBlank : false,
33659 monthAllowBlank : false,
33660 yearAllowBlank : false,
33661 dayPlaceholder : '',
33662 monthPlaceholder : '',
33663 yearPlaceholder : '',
33667 isFormField : true,
33673 getAutoCreate : function()
33677 cls : 'row roo-date-split-field-group',
33682 cls : 'form-hidden-field roo-date-split-field-group-value',
33688 var labelCls = 'col-md-12';
33689 var contentCls = 'col-md-4';
33691 if(this.fieldLabel){
33695 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33699 html : this.fieldLabel
33704 if(this.labelAlign == 'left'){
33706 if(this.labelWidth > 12){
33707 label.style = "width: " + this.labelWidth + 'px';
33710 if(this.labelWidth < 13 && this.labelmd == 0){
33711 this.labelmd = this.labelWidth;
33714 if(this.labellg > 0){
33715 labelCls = ' col-lg-' + this.labellg;
33716 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33719 if(this.labelmd > 0){
33720 labelCls = ' col-md-' + this.labelmd;
33721 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33724 if(this.labelsm > 0){
33725 labelCls = ' col-sm-' + this.labelsm;
33726 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33729 if(this.labelxs > 0){
33730 labelCls = ' col-xs-' + this.labelxs;
33731 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33735 label.cls += ' ' + labelCls;
33737 cfg.cn.push(label);
33740 Roo.each(['day', 'month', 'year'], function(t){
33743 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33750 inputEl: function ()
33752 return this.el.select('.roo-date-split-field-group-value', true).first();
33755 onRender : function(ct, position)
33759 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33761 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33763 this.dayField = new Roo.bootstrap.form.ComboBox({
33764 allowBlank : this.dayAllowBlank,
33765 alwaysQuery : true,
33766 displayField : 'value',
33769 forceSelection : true,
33771 placeholder : this.dayPlaceholder,
33772 selectOnFocus : true,
33773 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33774 triggerAction : 'all',
33776 valueField : 'value',
33777 store : new Roo.data.SimpleStore({
33778 data : (function() {
33780 _this.fireEvent('days', _this, days);
33783 fields : [ 'value' ]
33786 select : function (_self, record, index)
33788 _this.setValue(_this.getValue());
33793 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33795 this.monthField = new Roo.bootstrap.form.MonthField({
33796 after : '<i class=\"fa fa-calendar\"></i>',
33797 allowBlank : this.monthAllowBlank,
33798 placeholder : this.monthPlaceholder,
33801 render : function (_self)
33803 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33804 e.preventDefault();
33808 select : function (_self, oldvalue, newvalue)
33810 _this.setValue(_this.getValue());
33815 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33817 this.yearField = new Roo.bootstrap.form.ComboBox({
33818 allowBlank : this.yearAllowBlank,
33819 alwaysQuery : true,
33820 displayField : 'value',
33823 forceSelection : true,
33825 placeholder : this.yearPlaceholder,
33826 selectOnFocus : true,
33827 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33828 triggerAction : 'all',
33830 valueField : 'value',
33831 store : new Roo.data.SimpleStore({
33832 data : (function() {
33834 _this.fireEvent('years', _this, years);
33837 fields : [ 'value' ]
33840 select : function (_self, record, index)
33842 _this.setValue(_this.getValue());
33847 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33850 setValue : function(v, format)
33852 this.inputEl.dom.value = v;
33854 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33856 var d = Date.parseDate(v, f);
33863 this.setDay(d.format(this.dayFormat));
33864 this.setMonth(d.format(this.monthFormat));
33865 this.setYear(d.format(this.yearFormat));
33872 setDay : function(v)
33874 this.dayField.setValue(v);
33875 this.inputEl.dom.value = this.getValue();
33880 setMonth : function(v)
33882 this.monthField.setValue(v, true);
33883 this.inputEl.dom.value = this.getValue();
33888 setYear : function(v)
33890 this.yearField.setValue(v);
33891 this.inputEl.dom.value = this.getValue();
33896 getDay : function()
33898 return this.dayField.getValue();
33901 getMonth : function()
33903 return this.monthField.getValue();
33906 getYear : function()
33908 return this.yearField.getValue();
33911 getValue : function()
33913 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33915 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33925 this.inputEl.dom.value = '';
33930 validate : function()
33932 var d = this.dayField.validate();
33933 var m = this.monthField.validate();
33934 var y = this.yearField.validate();
33939 (!this.dayAllowBlank && !d) ||
33940 (!this.monthAllowBlank && !m) ||
33941 (!this.yearAllowBlank && !y)
33946 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33955 this.markInvalid();
33960 markValid : function()
33963 var label = this.el.select('label', true).first();
33964 var icon = this.el.select('i.fa-star', true).first();
33970 this.fireEvent('valid', this);
33974 * Mark this field as invalid
33975 * @param {String} msg The validation message
33977 markInvalid : function(msg)
33980 var label = this.el.select('label', true).first();
33981 var icon = this.el.select('i.fa-star', true).first();
33983 if(label && !icon){
33984 this.el.select('.roo-date-split-field-label', true).createChild({
33986 cls : 'text-danger fa fa-lg fa-star',
33987 tooltip : 'This field is required',
33988 style : 'margin-right:5px;'
33992 this.fireEvent('invalid', this, msg);
33995 clearInvalid : function()
33997 var label = this.el.select('label', true).first();
33998 var icon = this.el.select('i.fa-star', true).first();
34004 this.fireEvent('valid', this);
34007 getName: function()
34017 * @class Roo.bootstrap.LayoutMasonry
34018 * @extends Roo.bootstrap.Component
34019 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34020 * Bootstrap Layout Masonry class
34023 * http://masonry.desandro.com
34025 * The idea is to render all the bricks based on vertical width...
34027 * The original code extends 'outlayer' - we might need to use that....
34030 * Create a new Element
34031 * @param {Object} config The config object
34034 Roo.bootstrap.LayoutMasonry = function(config){
34036 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34040 Roo.bootstrap.LayoutMasonry.register(this);
34046 * Fire after layout the items
34047 * @param {Roo.bootstrap.LayoutMasonry} this
34048 * @param {Roo.EventObject} e
34055 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34058 * @cfg {Boolean} isLayoutInstant = no animation?
34060 isLayoutInstant : false, // needed?
34063 * @cfg {Number} boxWidth width of the columns
34068 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34073 * @cfg {Number} padWidth padding below box..
34078 * @cfg {Number} gutter gutter width..
34083 * @cfg {Number} maxCols maximum number of columns
34089 * @cfg {Boolean} isAutoInitial defalut true
34091 isAutoInitial : true,
34096 * @cfg {Boolean} isHorizontal defalut false
34098 isHorizontal : false,
34100 currentSize : null,
34106 bricks: null, //CompositeElement
34110 _isLayoutInited : false,
34112 // isAlternative : false, // only use for vertical layout...
34115 * @cfg {Number} alternativePadWidth padding below box..
34117 alternativePadWidth : 50,
34119 selectedBrick : [],
34121 getAutoCreate : function(){
34123 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34127 cls: 'blog-masonary-wrapper ' + this.cls,
34129 cls : 'mas-boxes masonary'
34136 getChildContainer: function( )
34138 if (this.boxesEl) {
34139 return this.boxesEl;
34142 this.boxesEl = this.el.select('.mas-boxes').first();
34144 return this.boxesEl;
34148 initEvents : function()
34152 if(this.isAutoInitial){
34153 Roo.log('hook children rendered');
34154 this.on('childrenrendered', function() {
34155 Roo.log('children rendered');
34161 initial : function()
34163 this.selectedBrick = [];
34165 this.currentSize = this.el.getBox(true);
34167 Roo.EventManager.onWindowResize(this.resize, this);
34169 if(!this.isAutoInitial){
34177 //this.layout.defer(500,this);
34181 resize : function()
34183 var cs = this.el.getBox(true);
34186 this.currentSize.width == cs.width &&
34187 this.currentSize.x == cs.x &&
34188 this.currentSize.height == cs.height &&
34189 this.currentSize.y == cs.y
34191 Roo.log("no change in with or X or Y");
34195 this.currentSize = cs;
34201 layout : function()
34203 this._resetLayout();
34205 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34207 this.layoutItems( isInstant );
34209 this._isLayoutInited = true;
34211 this.fireEvent('layout', this);
34215 _resetLayout : function()
34217 if(this.isHorizontal){
34218 this.horizontalMeasureColumns();
34222 this.verticalMeasureColumns();
34226 verticalMeasureColumns : function()
34228 this.getContainerWidth();
34230 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34231 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34235 var boxWidth = this.boxWidth + this.padWidth;
34237 if(this.containerWidth < this.boxWidth){
34238 boxWidth = this.containerWidth
34241 var containerWidth = this.containerWidth;
34243 var cols = Math.floor(containerWidth / boxWidth);
34245 this.cols = Math.max( cols, 1 );
34247 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34249 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34251 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34253 this.colWidth = boxWidth + avail - this.padWidth;
34255 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34256 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34259 horizontalMeasureColumns : function()
34261 this.getContainerWidth();
34263 var boxWidth = this.boxWidth;
34265 if(this.containerWidth < boxWidth){
34266 boxWidth = this.containerWidth;
34269 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34271 this.el.setHeight(boxWidth);
34275 getContainerWidth : function()
34277 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34280 layoutItems : function( isInstant )
34282 Roo.log(this.bricks);
34284 var items = Roo.apply([], this.bricks);
34286 if(this.isHorizontal){
34287 this._horizontalLayoutItems( items , isInstant );
34291 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34292 // this._verticalAlternativeLayoutItems( items , isInstant );
34296 this._verticalLayoutItems( items , isInstant );
34300 _verticalLayoutItems : function ( items , isInstant)
34302 if ( !items || !items.length ) {
34307 ['xs', 'xs', 'xs', 'tall'],
34308 ['xs', 'xs', 'tall'],
34309 ['xs', 'xs', 'sm'],
34310 ['xs', 'xs', 'xs'],
34316 ['sm', 'xs', 'xs'],
34320 ['tall', 'xs', 'xs', 'xs'],
34321 ['tall', 'xs', 'xs'],
34333 Roo.each(items, function(item, k){
34335 switch (item.size) {
34336 // these layouts take up a full box,
34347 boxes.push([item]);
34370 var filterPattern = function(box, length)
34378 var pattern = box.slice(0, length);
34382 Roo.each(pattern, function(i){
34383 format.push(i.size);
34386 Roo.each(standard, function(s){
34388 if(String(s) != String(format)){
34397 if(!match && length == 1){
34402 filterPattern(box, length - 1);
34406 queue.push(pattern);
34408 box = box.slice(length, box.length);
34410 filterPattern(box, 4);
34416 Roo.each(boxes, function(box, k){
34422 if(box.length == 1){
34427 filterPattern(box, 4);
34431 this._processVerticalLayoutQueue( queue, isInstant );
34435 // _verticalAlternativeLayoutItems : function( items , isInstant )
34437 // if ( !items || !items.length ) {
34441 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34445 _horizontalLayoutItems : function ( items , isInstant)
34447 if ( !items || !items.length || items.length < 3) {
34453 var eItems = items.slice(0, 3);
34455 items = items.slice(3, items.length);
34458 ['xs', 'xs', 'xs', 'wide'],
34459 ['xs', 'xs', 'wide'],
34460 ['xs', 'xs', 'sm'],
34461 ['xs', 'xs', 'xs'],
34467 ['sm', 'xs', 'xs'],
34471 ['wide', 'xs', 'xs', 'xs'],
34472 ['wide', 'xs', 'xs'],
34485 Roo.each(items, function(item, k){
34487 switch (item.size) {
34498 boxes.push([item]);
34522 var filterPattern = function(box, length)
34530 var pattern = box.slice(0, length);
34534 Roo.each(pattern, function(i){
34535 format.push(i.size);
34538 Roo.each(standard, function(s){
34540 if(String(s) != String(format)){
34549 if(!match && length == 1){
34554 filterPattern(box, length - 1);
34558 queue.push(pattern);
34560 box = box.slice(length, box.length);
34562 filterPattern(box, 4);
34568 Roo.each(boxes, function(box, k){
34574 if(box.length == 1){
34579 filterPattern(box, 4);
34586 var pos = this.el.getBox(true);
34590 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34592 var hit_end = false;
34594 Roo.each(queue, function(box){
34598 Roo.each(box, function(b){
34600 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34610 Roo.each(box, function(b){
34612 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34615 mx = Math.max(mx, b.x);
34619 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34623 Roo.each(box, function(b){
34625 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34639 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34642 /** Sets position of item in DOM
34643 * @param {Element} item
34644 * @param {Number} x - horizontal position
34645 * @param {Number} y - vertical position
34646 * @param {Boolean} isInstant - disables transitions
34648 _processVerticalLayoutQueue : function( queue, isInstant )
34650 var pos = this.el.getBox(true);
34655 for (var i = 0; i < this.cols; i++){
34659 Roo.each(queue, function(box, k){
34661 var col = k % this.cols;
34663 Roo.each(box, function(b,kk){
34665 b.el.position('absolute');
34667 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34668 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34670 if(b.size == 'md-left' || b.size == 'md-right'){
34671 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34672 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34675 b.el.setWidth(width);
34676 b.el.setHeight(height);
34678 b.el.select('iframe',true).setSize(width,height);
34682 for (var i = 0; i < this.cols; i++){
34684 if(maxY[i] < maxY[col]){
34689 col = Math.min(col, i);
34693 x = pos.x + col * (this.colWidth + this.padWidth);
34697 var positions = [];
34699 switch (box.length){
34701 positions = this.getVerticalOneBoxColPositions(x, y, box);
34704 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34707 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34710 positions = this.getVerticalFourBoxColPositions(x, y, box);
34716 Roo.each(box, function(b,kk){
34718 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34720 var sz = b.el.getSize();
34722 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34730 for (var i = 0; i < this.cols; i++){
34731 mY = Math.max(mY, maxY[i]);
34734 this.el.setHeight(mY - pos.y);
34738 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34740 // var pos = this.el.getBox(true);
34743 // var maxX = pos.right;
34745 // var maxHeight = 0;
34747 // Roo.each(items, function(item, k){
34751 // item.el.position('absolute');
34753 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34755 // item.el.setWidth(width);
34757 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34759 // item.el.setHeight(height);
34762 // item.el.setXY([x, y], isInstant ? false : true);
34764 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34767 // y = y + height + this.alternativePadWidth;
34769 // maxHeight = maxHeight + height + this.alternativePadWidth;
34773 // this.el.setHeight(maxHeight);
34777 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34779 var pos = this.el.getBox(true);
34784 var maxX = pos.right;
34786 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34788 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34790 Roo.each(queue, function(box, k){
34792 Roo.each(box, function(b, kk){
34794 b.el.position('absolute');
34796 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34797 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34799 if(b.size == 'md-left' || b.size == 'md-right'){
34800 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34801 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34804 b.el.setWidth(width);
34805 b.el.setHeight(height);
34813 var positions = [];
34815 switch (box.length){
34817 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34820 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34823 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34826 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34832 Roo.each(box, function(b,kk){
34834 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34836 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34844 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34846 Roo.each(eItems, function(b,k){
34848 b.size = (k == 0) ? 'sm' : 'xs';
34849 b.x = (k == 0) ? 2 : 1;
34850 b.y = (k == 0) ? 2 : 1;
34852 b.el.position('absolute');
34854 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34856 b.el.setWidth(width);
34858 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34860 b.el.setHeight(height);
34864 var positions = [];
34867 x : maxX - this.unitWidth * 2 - this.gutter,
34872 x : maxX - this.unitWidth,
34873 y : minY + (this.unitWidth + this.gutter) * 2
34877 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34881 Roo.each(eItems, function(b,k){
34883 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34889 getVerticalOneBoxColPositions : function(x, y, box)
34893 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34895 if(box[0].size == 'md-left'){
34899 if(box[0].size == 'md-right'){
34904 x : x + (this.unitWidth + this.gutter) * rand,
34911 getVerticalTwoBoxColPositions : function(x, y, box)
34915 if(box[0].size == 'xs'){
34919 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34923 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34937 x : x + (this.unitWidth + this.gutter) * 2,
34938 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34945 getVerticalThreeBoxColPositions : function(x, y, box)
34949 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34957 x : x + (this.unitWidth + this.gutter) * 1,
34962 x : x + (this.unitWidth + this.gutter) * 2,
34970 if(box[0].size == 'xs' && box[1].size == 'xs'){
34979 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34983 x : x + (this.unitWidth + this.gutter) * 1,
34997 x : x + (this.unitWidth + this.gutter) * 2,
35002 x : x + (this.unitWidth + this.gutter) * 2,
35003 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35010 getVerticalFourBoxColPositions : function(x, y, box)
35014 if(box[0].size == 'xs'){
35023 y : y + (this.unitHeight + this.gutter) * 1
35028 y : y + (this.unitHeight + this.gutter) * 2
35032 x : x + (this.unitWidth + this.gutter) * 1,
35046 x : x + (this.unitWidth + this.gutter) * 2,
35051 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35052 y : y + (this.unitHeight + this.gutter) * 1
35056 x : x + (this.unitWidth + this.gutter) * 2,
35057 y : y + (this.unitWidth + this.gutter) * 2
35064 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35068 if(box[0].size == 'md-left'){
35070 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35077 if(box[0].size == 'md-right'){
35079 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35080 y : minY + (this.unitWidth + this.gutter) * 1
35086 var rand = Math.floor(Math.random() * (4 - box[0].y));
35089 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35090 y : minY + (this.unitWidth + this.gutter) * rand
35097 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35101 if(box[0].size == 'xs'){
35104 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35109 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35110 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35118 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35123 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35124 y : minY + (this.unitWidth + this.gutter) * 2
35131 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35135 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35138 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35143 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35144 y : minY + (this.unitWidth + this.gutter) * 1
35148 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35149 y : minY + (this.unitWidth + this.gutter) * 2
35156 if(box[0].size == 'xs' && box[1].size == 'xs'){
35159 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35164 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35169 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35170 y : minY + (this.unitWidth + this.gutter) * 1
35178 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35183 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35184 y : minY + (this.unitWidth + this.gutter) * 2
35188 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35189 y : minY + (this.unitWidth + this.gutter) * 2
35196 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35200 if(box[0].size == 'xs'){
35203 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35208 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35213 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35218 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35219 y : minY + (this.unitWidth + this.gutter) * 1
35227 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35232 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35233 y : minY + (this.unitWidth + this.gutter) * 2
35237 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35238 y : minY + (this.unitWidth + this.gutter) * 2
35242 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35243 y : minY + (this.unitWidth + this.gutter) * 2
35251 * remove a Masonry Brick
35252 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35254 removeBrick : function(brick_id)
35260 for (var i = 0; i<this.bricks.length; i++) {
35261 if (this.bricks[i].id == brick_id) {
35262 this.bricks.splice(i,1);
35263 this.el.dom.removeChild(Roo.get(brick_id).dom);
35270 * adds a Masonry Brick
35271 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35273 addBrick : function(cfg)
35275 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35276 //this.register(cn);
35277 cn.parentId = this.id;
35278 cn.render(this.el);
35283 * register a Masonry Brick
35284 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35287 register : function(brick)
35289 this.bricks.push(brick);
35290 brick.masonryId = this.id;
35294 * clear all the Masonry Brick
35296 clearAll : function()
35299 //this.getChildContainer().dom.innerHTML = "";
35300 this.el.dom.innerHTML = '';
35303 getSelected : function()
35305 if (!this.selectedBrick) {
35309 return this.selectedBrick;
35313 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35317 * register a Masonry Layout
35318 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35321 register : function(layout)
35323 this.groups[layout.id] = layout;
35326 * fetch a Masonry Layout based on the masonry layout ID
35327 * @param {string} the masonry layout to add
35328 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35331 get: function(layout_id) {
35332 if (typeof(this.groups[layout_id]) == 'undefined') {
35335 return this.groups[layout_id] ;
35347 * http://masonry.desandro.com
35349 * The idea is to render all the bricks based on vertical width...
35351 * The original code extends 'outlayer' - we might need to use that....
35357 * @class Roo.bootstrap.LayoutMasonryAuto
35358 * @extends Roo.bootstrap.Component
35359 * Bootstrap Layout Masonry class
35362 * Create a new Element
35363 * @param {Object} config The config object
35366 Roo.bootstrap.LayoutMasonryAuto = function(config){
35367 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35370 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35373 * @cfg {Boolean} isFitWidth - resize the width..
35375 isFitWidth : false, // options..
35377 * @cfg {Boolean} isOriginLeft = left align?
35379 isOriginLeft : true,
35381 * @cfg {Boolean} isOriginTop = top align?
35383 isOriginTop : false,
35385 * @cfg {Boolean} isLayoutInstant = no animation?
35387 isLayoutInstant : false, // needed?
35389 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35391 isResizingContainer : true,
35393 * @cfg {Number} columnWidth width of the columns
35399 * @cfg {Number} maxCols maximum number of columns
35404 * @cfg {Number} padHeight padding below box..
35410 * @cfg {Boolean} isAutoInitial defalut true
35413 isAutoInitial : true,
35419 initialColumnWidth : 0,
35420 currentSize : null,
35422 colYs : null, // array.
35429 bricks: null, //CompositeElement
35430 cols : 0, // array?
35431 // element : null, // wrapped now this.el
35432 _isLayoutInited : null,
35435 getAutoCreate : function(){
35439 cls: 'blog-masonary-wrapper ' + this.cls,
35441 cls : 'mas-boxes masonary'
35448 getChildContainer: function( )
35450 if (this.boxesEl) {
35451 return this.boxesEl;
35454 this.boxesEl = this.el.select('.mas-boxes').first();
35456 return this.boxesEl;
35460 initEvents : function()
35464 if(this.isAutoInitial){
35465 Roo.log('hook children rendered');
35466 this.on('childrenrendered', function() {
35467 Roo.log('children rendered');
35474 initial : function()
35476 this.reloadItems();
35478 this.currentSize = this.el.getBox(true);
35480 /// was window resize... - let's see if this works..
35481 Roo.EventManager.onWindowResize(this.resize, this);
35483 if(!this.isAutoInitial){
35488 this.layout.defer(500,this);
35491 reloadItems: function()
35493 this.bricks = this.el.select('.masonry-brick', true);
35495 this.bricks.each(function(b) {
35496 //Roo.log(b.getSize());
35497 if (!b.attr('originalwidth')) {
35498 b.attr('originalwidth', b.getSize().width);
35503 Roo.log(this.bricks.elements.length);
35506 resize : function()
35509 var cs = this.el.getBox(true);
35511 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35512 Roo.log("no change in with or X");
35515 this.currentSize = cs;
35519 layout : function()
35522 this._resetLayout();
35523 //this._manageStamps();
35525 // don't animate first layout
35526 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35527 this.layoutItems( isInstant );
35529 // flag for initalized
35530 this._isLayoutInited = true;
35533 layoutItems : function( isInstant )
35535 //var items = this._getItemsForLayout( this.items );
35536 // original code supports filtering layout items.. we just ignore it..
35538 this._layoutItems( this.bricks , isInstant );
35540 this._postLayout();
35542 _layoutItems : function ( items , isInstant)
35544 //this.fireEvent( 'layout', this, items );
35547 if ( !items || !items.elements.length ) {
35548 // no items, emit event with empty array
35553 items.each(function(item) {
35554 Roo.log("layout item");
35556 // get x/y object from method
35557 var position = this._getItemLayoutPosition( item );
35559 position.item = item;
35560 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35561 queue.push( position );
35564 this._processLayoutQueue( queue );
35566 /** Sets position of item in DOM
35567 * @param {Element} item
35568 * @param {Number} x - horizontal position
35569 * @param {Number} y - vertical position
35570 * @param {Boolean} isInstant - disables transitions
35572 _processLayoutQueue : function( queue )
35574 for ( var i=0, len = queue.length; i < len; i++ ) {
35575 var obj = queue[i];
35576 obj.item.position('absolute');
35577 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35583 * Any logic you want to do after each layout,
35584 * i.e. size the container
35586 _postLayout : function()
35588 this.resizeContainer();
35591 resizeContainer : function()
35593 if ( !this.isResizingContainer ) {
35596 var size = this._getContainerSize();
35598 this.el.setSize(size.width,size.height);
35599 this.boxesEl.setSize(size.width,size.height);
35605 _resetLayout : function()
35607 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35608 this.colWidth = this.el.getWidth();
35609 //this.gutter = this.el.getWidth();
35611 this.measureColumns();
35617 this.colYs.push( 0 );
35623 measureColumns : function()
35625 this.getContainerWidth();
35626 // if columnWidth is 0, default to outerWidth of first item
35627 if ( !this.columnWidth ) {
35628 var firstItem = this.bricks.first();
35629 Roo.log(firstItem);
35630 this.columnWidth = this.containerWidth;
35631 if (firstItem && firstItem.attr('originalwidth') ) {
35632 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35634 // columnWidth fall back to item of first element
35635 Roo.log("set column width?");
35636 this.initialColumnWidth = this.columnWidth ;
35638 // if first elem has no width, default to size of container
35643 if (this.initialColumnWidth) {
35644 this.columnWidth = this.initialColumnWidth;
35649 // column width is fixed at the top - however if container width get's smaller we should
35652 // this bit calcs how man columns..
35654 var columnWidth = this.columnWidth += this.gutter;
35656 // calculate columns
35657 var containerWidth = this.containerWidth + this.gutter;
35659 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35660 // fix rounding errors, typically with gutters
35661 var excess = columnWidth - containerWidth % columnWidth;
35664 // if overshoot is less than a pixel, round up, otherwise floor it
35665 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35666 cols = Math[ mathMethod ]( cols );
35667 this.cols = Math.max( cols, 1 );
35668 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35670 // padding positioning..
35671 var totalColWidth = this.cols * this.columnWidth;
35672 var padavail = this.containerWidth - totalColWidth;
35673 // so for 2 columns - we need 3 'pads'
35675 var padNeeded = (1+this.cols) * this.padWidth;
35677 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35679 this.columnWidth += padExtra
35680 //this.padWidth = Math.floor(padavail / ( this.cols));
35682 // adjust colum width so that padding is fixed??
35684 // we have 3 columns ... total = width * 3
35685 // we have X left over... that should be used by
35687 //if (this.expandC) {
35695 getContainerWidth : function()
35697 /* // container is parent if fit width
35698 var container = this.isFitWidth ? this.element.parentNode : this.element;
35699 // check that this.size and size are there
35700 // IE8 triggers resize on body size change, so they might not be
35702 var size = getSize( container ); //FIXME
35703 this.containerWidth = size && size.innerWidth; //FIXME
35706 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35710 _getItemLayoutPosition : function( item ) // what is item?
35712 // we resize the item to our columnWidth..
35714 item.setWidth(this.columnWidth);
35715 item.autoBoxAdjust = false;
35717 var sz = item.getSize();
35719 // how many columns does this brick span
35720 var remainder = this.containerWidth % this.columnWidth;
35722 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35723 // round if off by 1 pixel, otherwise use ceil
35724 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35725 colSpan = Math.min( colSpan, this.cols );
35727 // normally this should be '1' as we dont' currently allow multi width columns..
35729 var colGroup = this._getColGroup( colSpan );
35730 // get the minimum Y value from the columns
35731 var minimumY = Math.min.apply( Math, colGroup );
35732 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35734 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35736 // position the brick
35738 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35739 y: this.currentSize.y + minimumY + this.padHeight
35743 // apply setHeight to necessary columns
35744 var setHeight = minimumY + sz.height + this.padHeight;
35745 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35747 var setSpan = this.cols + 1 - colGroup.length;
35748 for ( var i = 0; i < setSpan; i++ ) {
35749 this.colYs[ shortColIndex + i ] = setHeight ;
35756 * @param {Number} colSpan - number of columns the element spans
35757 * @returns {Array} colGroup
35759 _getColGroup : function( colSpan )
35761 if ( colSpan < 2 ) {
35762 // if brick spans only one column, use all the column Ys
35767 // how many different places could this brick fit horizontally
35768 var groupCount = this.cols + 1 - colSpan;
35769 // for each group potential horizontal position
35770 for ( var i = 0; i < groupCount; i++ ) {
35771 // make an array of colY values for that one group
35772 var groupColYs = this.colYs.slice( i, i + colSpan );
35773 // and get the max value of the array
35774 colGroup[i] = Math.max.apply( Math, groupColYs );
35779 _manageStamp : function( stamp )
35781 var stampSize = stamp.getSize();
35782 var offset = stamp.getBox();
35783 // get the columns that this stamp affects
35784 var firstX = this.isOriginLeft ? offset.x : offset.right;
35785 var lastX = firstX + stampSize.width;
35786 var firstCol = Math.floor( firstX / this.columnWidth );
35787 firstCol = Math.max( 0, firstCol );
35789 var lastCol = Math.floor( lastX / this.columnWidth );
35790 // lastCol should not go over if multiple of columnWidth #425
35791 lastCol -= lastX % this.columnWidth ? 0 : 1;
35792 lastCol = Math.min( this.cols - 1, lastCol );
35794 // set colYs to bottom of the stamp
35795 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35798 for ( var i = firstCol; i <= lastCol; i++ ) {
35799 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35804 _getContainerSize : function()
35806 this.maxY = Math.max.apply( Math, this.colYs );
35811 if ( this.isFitWidth ) {
35812 size.width = this._getContainerFitWidth();
35818 _getContainerFitWidth : function()
35820 var unusedCols = 0;
35821 // count unused columns
35824 if ( this.colYs[i] !== 0 ) {
35829 // fit container to columns that have been used
35830 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35833 needsResizeLayout : function()
35835 var previousWidth = this.containerWidth;
35836 this.getContainerWidth();
35837 return previousWidth !== this.containerWidth;
35852 * @class Roo.bootstrap.MasonryBrick
35853 * @extends Roo.bootstrap.Component
35854 * Bootstrap MasonryBrick class
35857 * Create a new MasonryBrick
35858 * @param {Object} config The config object
35861 Roo.bootstrap.MasonryBrick = function(config){
35863 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35865 Roo.bootstrap.MasonryBrick.register(this);
35871 * When a MasonryBrick is clcik
35872 * @param {Roo.bootstrap.MasonryBrick} this
35873 * @param {Roo.EventObject} e
35879 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35882 * @cfg {String} title
35886 * @cfg {String} html
35890 * @cfg {String} bgimage
35894 * @cfg {String} videourl
35898 * @cfg {String} cls
35902 * @cfg {String} href
35906 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35911 * @cfg {String} placetitle (center|bottom)
35916 * @cfg {Boolean} isFitContainer defalut true
35918 isFitContainer : true,
35921 * @cfg {Boolean} preventDefault defalut false
35923 preventDefault : false,
35926 * @cfg {Boolean} inverse defalut false
35928 maskInverse : false,
35930 getAutoCreate : function()
35932 if(!this.isFitContainer){
35933 return this.getSplitAutoCreate();
35936 var cls = 'masonry-brick masonry-brick-full';
35938 if(this.href.length){
35939 cls += ' masonry-brick-link';
35942 if(this.bgimage.length){
35943 cls += ' masonry-brick-image';
35946 if(this.maskInverse){
35947 cls += ' mask-inverse';
35950 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35951 cls += ' enable-mask';
35955 cls += ' masonry-' + this.size + '-brick';
35958 if(this.placetitle.length){
35960 switch (this.placetitle) {
35962 cls += ' masonry-center-title';
35965 cls += ' masonry-bottom-title';
35972 if(!this.html.length && !this.bgimage.length){
35973 cls += ' masonry-center-title';
35976 if(!this.html.length && this.bgimage.length){
35977 cls += ' masonry-bottom-title';
35982 cls += ' ' + this.cls;
35986 tag: (this.href.length) ? 'a' : 'div',
35991 cls: 'masonry-brick-mask'
35995 cls: 'masonry-brick-paragraph',
36001 if(this.href.length){
36002 cfg.href = this.href;
36005 var cn = cfg.cn[1].cn;
36007 if(this.title.length){
36010 cls: 'masonry-brick-title',
36015 if(this.html.length){
36018 cls: 'masonry-brick-text',
36023 if (!this.title.length && !this.html.length) {
36024 cfg.cn[1].cls += ' hide';
36027 if(this.bgimage.length){
36030 cls: 'masonry-brick-image-view',
36035 if(this.videourl.length){
36036 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36037 // youtube support only?
36040 cls: 'masonry-brick-image-view',
36043 allowfullscreen : true
36051 getSplitAutoCreate : function()
36053 var cls = 'masonry-brick masonry-brick-split';
36055 if(this.href.length){
36056 cls += ' masonry-brick-link';
36059 if(this.bgimage.length){
36060 cls += ' masonry-brick-image';
36064 cls += ' masonry-' + this.size + '-brick';
36067 switch (this.placetitle) {
36069 cls += ' masonry-center-title';
36072 cls += ' masonry-bottom-title';
36075 if(!this.bgimage.length){
36076 cls += ' masonry-center-title';
36079 if(this.bgimage.length){
36080 cls += ' masonry-bottom-title';
36086 cls += ' ' + this.cls;
36090 tag: (this.href.length) ? 'a' : 'div',
36095 cls: 'masonry-brick-split-head',
36099 cls: 'masonry-brick-paragraph',
36106 cls: 'masonry-brick-split-body',
36112 if(this.href.length){
36113 cfg.href = this.href;
36116 if(this.title.length){
36117 cfg.cn[0].cn[0].cn.push({
36119 cls: 'masonry-brick-title',
36124 if(this.html.length){
36125 cfg.cn[1].cn.push({
36127 cls: 'masonry-brick-text',
36132 if(this.bgimage.length){
36133 cfg.cn[0].cn.push({
36135 cls: 'masonry-brick-image-view',
36140 if(this.videourl.length){
36141 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36142 // youtube support only?
36143 cfg.cn[0].cn.cn.push({
36145 cls: 'masonry-brick-image-view',
36148 allowfullscreen : true
36155 initEvents: function()
36157 switch (this.size) {
36190 this.el.on('touchstart', this.onTouchStart, this);
36191 this.el.on('touchmove', this.onTouchMove, this);
36192 this.el.on('touchend', this.onTouchEnd, this);
36193 this.el.on('contextmenu', this.onContextMenu, this);
36195 this.el.on('mouseenter' ,this.enter, this);
36196 this.el.on('mouseleave', this.leave, this);
36197 this.el.on('click', this.onClick, this);
36200 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36201 this.parent().bricks.push(this);
36206 onClick: function(e, el)
36208 var time = this.endTimer - this.startTimer;
36209 // Roo.log(e.preventDefault());
36212 e.preventDefault();
36217 if(!this.preventDefault){
36221 e.preventDefault();
36223 if (this.activeClass != '') {
36224 this.selectBrick();
36227 this.fireEvent('click', this, e);
36230 enter: function(e, el)
36232 e.preventDefault();
36234 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36238 if(this.bgimage.length && this.html.length){
36239 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36243 leave: function(e, el)
36245 e.preventDefault();
36247 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36251 if(this.bgimage.length && this.html.length){
36252 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36256 onTouchStart: function(e, el)
36258 // e.preventDefault();
36260 this.touchmoved = false;
36262 if(!this.isFitContainer){
36266 if(!this.bgimage.length || !this.html.length){
36270 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36272 this.timer = new Date().getTime();
36276 onTouchMove: function(e, el)
36278 this.touchmoved = true;
36281 onContextMenu : function(e,el)
36283 e.preventDefault();
36284 e.stopPropagation();
36288 onTouchEnd: function(e, el)
36290 // e.preventDefault();
36292 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36299 if(!this.bgimage.length || !this.html.length){
36301 if(this.href.length){
36302 window.location.href = this.href;
36308 if(!this.isFitContainer){
36312 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36314 window.location.href = this.href;
36317 //selection on single brick only
36318 selectBrick : function() {
36320 if (!this.parentId) {
36324 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36325 var index = m.selectedBrick.indexOf(this.id);
36328 m.selectedBrick.splice(index,1);
36329 this.el.removeClass(this.activeClass);
36333 for(var i = 0; i < m.selectedBrick.length; i++) {
36334 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36335 b.el.removeClass(b.activeClass);
36338 m.selectedBrick = [];
36340 m.selectedBrick.push(this.id);
36341 this.el.addClass(this.activeClass);
36345 isSelected : function(){
36346 return this.el.hasClass(this.activeClass);
36351 Roo.apply(Roo.bootstrap.MasonryBrick, {
36354 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36356 * register a Masonry Brick
36357 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36360 register : function(brick)
36362 //this.groups[brick.id] = brick;
36363 this.groups.add(brick.id, brick);
36366 * fetch a masonry brick based on the masonry brick ID
36367 * @param {string} the masonry brick to add
36368 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36371 get: function(brick_id)
36373 // if (typeof(this.groups[brick_id]) == 'undefined') {
36376 // return this.groups[brick_id] ;
36378 if(this.groups.key(brick_id)) {
36379 return this.groups.key(brick_id);
36397 * @class Roo.bootstrap.Brick
36398 * @extends Roo.bootstrap.Component
36399 * Bootstrap Brick class
36402 * Create a new Brick
36403 * @param {Object} config The config object
36406 Roo.bootstrap.Brick = function(config){
36407 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36413 * When a Brick is click
36414 * @param {Roo.bootstrap.Brick} this
36415 * @param {Roo.EventObject} e
36421 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36424 * @cfg {String} title
36428 * @cfg {String} html
36432 * @cfg {String} bgimage
36436 * @cfg {String} cls
36440 * @cfg {String} href
36444 * @cfg {String} video
36448 * @cfg {Boolean} square
36452 getAutoCreate : function()
36454 var cls = 'roo-brick';
36456 if(this.href.length){
36457 cls += ' roo-brick-link';
36460 if(this.bgimage.length){
36461 cls += ' roo-brick-image';
36464 if(!this.html.length && !this.bgimage.length){
36465 cls += ' roo-brick-center-title';
36468 if(!this.html.length && this.bgimage.length){
36469 cls += ' roo-brick-bottom-title';
36473 cls += ' ' + this.cls;
36477 tag: (this.href.length) ? 'a' : 'div',
36482 cls: 'roo-brick-paragraph',
36488 if(this.href.length){
36489 cfg.href = this.href;
36492 var cn = cfg.cn[0].cn;
36494 if(this.title.length){
36497 cls: 'roo-brick-title',
36502 if(this.html.length){
36505 cls: 'roo-brick-text',
36512 if(this.bgimage.length){
36515 cls: 'roo-brick-image-view',
36523 initEvents: function()
36525 if(this.title.length || this.html.length){
36526 this.el.on('mouseenter' ,this.enter, this);
36527 this.el.on('mouseleave', this.leave, this);
36530 Roo.EventManager.onWindowResize(this.resize, this);
36532 if(this.bgimage.length){
36533 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36534 this.imageEl.on('load', this.onImageLoad, this);
36541 onImageLoad : function()
36546 resize : function()
36548 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36550 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36552 if(this.bgimage.length){
36553 var image = this.el.select('.roo-brick-image-view', true).first();
36555 image.setWidth(paragraph.getWidth());
36558 image.setHeight(paragraph.getWidth());
36561 this.el.setHeight(image.getHeight());
36562 paragraph.setHeight(image.getHeight());
36568 enter: function(e, el)
36570 e.preventDefault();
36572 if(this.bgimage.length){
36573 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36574 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36578 leave: function(e, el)
36580 e.preventDefault();
36582 if(this.bgimage.length){
36583 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36584 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36599 * @class Roo.bootstrap.form.NumberField
36600 * @extends Roo.bootstrap.form.Input
36601 * Bootstrap NumberField class
36607 * Create a new NumberField
36608 * @param {Object} config The config object
36611 Roo.bootstrap.form.NumberField = function(config){
36612 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36615 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36618 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36620 allowDecimals : true,
36622 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36624 decimalSeparator : ".",
36626 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36628 decimalPrecision : 2,
36630 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36632 allowNegative : true,
36635 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36639 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36641 minValue : Number.NEGATIVE_INFINITY,
36643 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36645 maxValue : Number.MAX_VALUE,
36647 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36649 minText : "The minimum value for this field is {0}",
36651 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36653 maxText : "The maximum value for this field is {0}",
36655 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36656 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36658 nanText : "{0} is not a valid number",
36660 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36662 thousandsDelimiter : false,
36664 * @cfg {String} valueAlign alignment of value
36666 valueAlign : "left",
36668 getAutoCreate : function()
36670 var hiddenInput = {
36674 cls: 'hidden-number-input'
36678 hiddenInput.name = this.name;
36683 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36685 this.name = hiddenInput.name;
36687 if(cfg.cn.length > 0) {
36688 cfg.cn.push(hiddenInput);
36695 initEvents : function()
36697 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36699 var allowed = "0123456789";
36701 if(this.allowDecimals){
36702 allowed += this.decimalSeparator;
36705 if(this.allowNegative){
36709 if(this.thousandsDelimiter) {
36713 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36715 var keyPress = function(e){
36717 var k = e.getKey();
36719 var c = e.getCharCode();
36722 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36723 allowed.indexOf(String.fromCharCode(c)) === -1
36729 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36733 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36738 this.el.on("keypress", keyPress, this);
36741 validateValue : function(value)
36744 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36748 var num = this.parseValue(value);
36751 this.markInvalid(String.format(this.nanText, value));
36755 if(num < this.minValue){
36756 this.markInvalid(String.format(this.minText, this.minValue));
36760 if(num > this.maxValue){
36761 this.markInvalid(String.format(this.maxText, this.maxValue));
36768 getValue : function()
36770 var v = this.hiddenEl().getValue();
36772 return this.fixPrecision(this.parseValue(v));
36775 parseValue : function(value)
36777 if(this.thousandsDelimiter) {
36779 r = new RegExp(",", "g");
36780 value = value.replace(r, "");
36783 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36784 return isNaN(value) ? '' : value;
36787 fixPrecision : function(value)
36789 if(this.thousandsDelimiter) {
36791 r = new RegExp(",", "g");
36792 value = value.replace(r, "");
36795 var nan = isNaN(value);
36797 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36798 return nan ? '' : value;
36800 return parseFloat(value).toFixed(this.decimalPrecision);
36803 setValue : function(v)
36805 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36811 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36813 this.inputEl().dom.value = (v == '') ? '' :
36814 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36816 if(!this.allowZero && v === '0') {
36817 this.hiddenEl().dom.value = '';
36818 this.inputEl().dom.value = '';
36825 decimalPrecisionFcn : function(v)
36827 return Math.floor(v);
36830 beforeBlur : function()
36832 var v = this.parseValue(this.getRawValue());
36834 if(v || v === 0 || v === ''){
36839 hiddenEl : function()
36841 return this.el.select('input.hidden-number-input',true).first();
36853 * @class Roo.bootstrap.DocumentSlider
36854 * @extends Roo.bootstrap.Component
36855 * Bootstrap DocumentSlider class
36858 * Create a new DocumentViewer
36859 * @param {Object} config The config object
36862 Roo.bootstrap.DocumentSlider = function(config){
36863 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36870 * Fire after initEvent
36871 * @param {Roo.bootstrap.DocumentSlider} this
36876 * Fire after update
36877 * @param {Roo.bootstrap.DocumentSlider} this
36883 * @param {Roo.bootstrap.DocumentSlider} this
36889 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36895 getAutoCreate : function()
36899 cls : 'roo-document-slider',
36903 cls : 'roo-document-slider-header',
36907 cls : 'roo-document-slider-header-title'
36913 cls : 'roo-document-slider-body',
36917 cls : 'roo-document-slider-prev',
36921 cls : 'fa fa-chevron-left'
36927 cls : 'roo-document-slider-thumb',
36931 cls : 'roo-document-slider-image'
36937 cls : 'roo-document-slider-next',
36941 cls : 'fa fa-chevron-right'
36953 initEvents : function()
36955 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36956 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36958 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36959 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36961 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36962 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36964 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36965 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36967 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36968 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36970 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36971 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36973 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36974 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36976 this.thumbEl.on('click', this.onClick, this);
36978 this.prevIndicator.on('click', this.prev, this);
36980 this.nextIndicator.on('click', this.next, this);
36984 initial : function()
36986 if(this.files.length){
36987 this.indicator = 1;
36991 this.fireEvent('initial', this);
36994 update : function()
36996 this.imageEl.attr('src', this.files[this.indicator - 1]);
36998 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37000 this.prevIndicator.show();
37002 if(this.indicator == 1){
37003 this.prevIndicator.hide();
37006 this.nextIndicator.show();
37008 if(this.indicator == this.files.length){
37009 this.nextIndicator.hide();
37012 this.thumbEl.scrollTo('top');
37014 this.fireEvent('update', this);
37017 onClick : function(e)
37019 e.preventDefault();
37021 this.fireEvent('click', this);
37026 e.preventDefault();
37028 this.indicator = Math.max(1, this.indicator - 1);
37035 e.preventDefault();
37037 this.indicator = Math.min(this.files.length, this.indicator + 1);
37051 * @class Roo.bootstrap.form.RadioSet
37052 * @extends Roo.bootstrap.form.Input
37053 * @children Roo.bootstrap.form.Radio
37054 * Bootstrap RadioSet class
37055 * @cfg {String} indicatorpos (left|right) default left
37056 * @cfg {Boolean} inline (true|false) inline the element (default true)
37057 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37059 * Create a new RadioSet
37060 * @param {Object} config The config object
37063 Roo.bootstrap.form.RadioSet = function(config){
37065 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37069 Roo.bootstrap.form.RadioSet.register(this);
37074 * Fires when the element is checked or unchecked.
37075 * @param {Roo.bootstrap.form.RadioSet} this This radio
37076 * @param {Roo.bootstrap.form.Radio} item The checked item
37081 * Fires when the element is click.
37082 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37083 * @param {Roo.bootstrap.form.Radio} item The checked item
37084 * @param {Roo.EventObject} e The event object
37091 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37099 indicatorpos : 'left',
37101 getAutoCreate : function()
37105 cls : 'roo-radio-set-label',
37109 html : this.fieldLabel
37113 if (Roo.bootstrap.version == 3) {
37116 if(this.indicatorpos == 'left'){
37119 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37120 tooltip : 'This field is required'
37125 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37126 tooltip : 'This field is required'
37132 cls : 'roo-radio-set-items'
37135 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37137 if (align === 'left' && this.fieldLabel.length) {
37140 cls : "roo-radio-set-right",
37146 if(this.labelWidth > 12){
37147 label.style = "width: " + this.labelWidth + 'px';
37150 if(this.labelWidth < 13 && this.labelmd == 0){
37151 this.labelmd = this.labelWidth;
37154 if(this.labellg > 0){
37155 label.cls += ' col-lg-' + this.labellg;
37156 items.cls += ' col-lg-' + (12 - this.labellg);
37159 if(this.labelmd > 0){
37160 label.cls += ' col-md-' + this.labelmd;
37161 items.cls += ' col-md-' + (12 - this.labelmd);
37164 if(this.labelsm > 0){
37165 label.cls += ' col-sm-' + this.labelsm;
37166 items.cls += ' col-sm-' + (12 - this.labelsm);
37169 if(this.labelxs > 0){
37170 label.cls += ' col-xs-' + this.labelxs;
37171 items.cls += ' col-xs-' + (12 - this.labelxs);
37177 cls : 'roo-radio-set',
37181 cls : 'roo-radio-set-input',
37184 value : this.value ? this.value : ''
37191 if(this.weight.length){
37192 cfg.cls += ' roo-radio-' + this.weight;
37196 cfg.cls += ' roo-radio-set-inline';
37200 ['xs','sm','md','lg'].map(function(size){
37201 if (settings[size]) {
37202 cfg.cls += ' col-' + size + '-' + settings[size];
37210 initEvents : function()
37212 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37213 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37215 if(!this.fieldLabel.length){
37216 this.labelEl.hide();
37219 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37220 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37222 this.indicator = this.indicatorEl();
37224 if(this.indicator){
37225 this.indicator.addClass('invisible');
37228 this.originalValue = this.getValue();
37232 inputEl: function ()
37234 return this.el.select('.roo-radio-set-input', true).first();
37237 getChildContainer : function()
37239 return this.itemsEl;
37242 register : function(item)
37244 this.radioes.push(item);
37248 validate : function()
37250 if(this.getVisibilityEl().hasClass('hidden')){
37256 Roo.each(this.radioes, function(i){
37265 if(this.allowBlank) {
37269 if(this.disabled || valid){
37274 this.markInvalid();
37279 markValid : function()
37281 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37282 this.indicatorEl().removeClass('visible');
37283 this.indicatorEl().addClass('invisible');
37287 if (Roo.bootstrap.version == 3) {
37288 this.el.removeClass([this.invalidClass, this.validClass]);
37289 this.el.addClass(this.validClass);
37291 this.el.removeClass(['is-invalid','is-valid']);
37292 this.el.addClass(['is-valid']);
37294 this.fireEvent('valid', this);
37297 markInvalid : function(msg)
37299 if(this.allowBlank || this.disabled){
37303 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37304 this.indicatorEl().removeClass('invisible');
37305 this.indicatorEl().addClass('visible');
37307 if (Roo.bootstrap.version == 3) {
37308 this.el.removeClass([this.invalidClass, this.validClass]);
37309 this.el.addClass(this.invalidClass);
37311 this.el.removeClass(['is-invalid','is-valid']);
37312 this.el.addClass(['is-invalid']);
37315 this.fireEvent('invalid', this, msg);
37319 setValue : function(v, suppressEvent)
37321 if(this.value === v){
37328 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37331 Roo.each(this.radioes, function(i){
37333 i.el.removeClass('checked');
37336 Roo.each(this.radioes, function(i){
37338 if(i.value === v || i.value.toString() === v.toString()){
37340 i.el.addClass('checked');
37342 if(suppressEvent !== true){
37343 this.fireEvent('check', this, i);
37354 clearInvalid : function(){
37356 if(!this.el || this.preventMark){
37360 this.el.removeClass([this.invalidClass]);
37362 this.fireEvent('valid', this);
37367 Roo.apply(Roo.bootstrap.form.RadioSet, {
37371 register : function(set)
37373 this.groups[set.name] = set;
37376 get: function(name)
37378 if (typeof(this.groups[name]) == 'undefined') {
37382 return this.groups[name] ;
37388 * Ext JS Library 1.1.1
37389 * Copyright(c) 2006-2007, Ext JS, LLC.
37391 * Originally Released Under LGPL - original licence link has changed is not relivant.
37394 * <script type="text/javascript">
37399 * @class Roo.bootstrap.SplitBar
37400 * @extends Roo.util.Observable
37401 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37405 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37406 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37407 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37408 split.minSize = 100;
37409 split.maxSize = 600;
37410 split.animate = true;
37411 split.on('moved', splitterMoved);
37414 * Create a new SplitBar
37415 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37416 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37417 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37418 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37419 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37420 position of the SplitBar).
37422 Roo.bootstrap.SplitBar = function(cfg){
37427 // dragElement : elm
37428 // resizingElement: el,
37430 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37431 // placement : Roo.bootstrap.SplitBar.LEFT ,
37432 // existingProxy ???
37435 this.el = Roo.get(cfg.dragElement, true);
37436 this.el.dom.unselectable = "on";
37438 this.resizingEl = Roo.get(cfg.resizingElement, true);
37442 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37443 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37446 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37449 * The minimum size of the resizing element. (Defaults to 0)
37455 * The maximum size of the resizing element. (Defaults to 2000)
37458 this.maxSize = 2000;
37461 * Whether to animate the transition to the new size
37464 this.animate = false;
37467 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37470 this.useShim = false;
37475 if(!cfg.existingProxy){
37477 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37479 this.proxy = Roo.get(cfg.existingProxy).dom;
37482 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37485 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37488 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37491 this.dragSpecs = {};
37494 * @private The adapter to use to positon and resize elements
37496 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37497 this.adapter.init(this);
37499 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37501 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37502 this.el.addClass("roo-splitbar-h");
37505 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37506 this.el.addClass("roo-splitbar-v");
37512 * Fires when the splitter is moved (alias for {@link #event-moved})
37513 * @param {Roo.bootstrap.SplitBar} this
37514 * @param {Number} newSize the new width or height
37519 * Fires when the splitter is moved
37520 * @param {Roo.bootstrap.SplitBar} this
37521 * @param {Number} newSize the new width or height
37525 * @event beforeresize
37526 * Fires before the splitter is dragged
37527 * @param {Roo.bootstrap.SplitBar} this
37529 "beforeresize" : true,
37531 "beforeapply" : true
37534 Roo.util.Observable.call(this);
37537 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37538 onStartProxyDrag : function(x, y){
37539 this.fireEvent("beforeresize", this);
37541 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37543 o.enableDisplayMode("block");
37544 // all splitbars share the same overlay
37545 Roo.bootstrap.SplitBar.prototype.overlay = o;
37547 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37548 this.overlay.show();
37549 Roo.get(this.proxy).setDisplayed("block");
37550 var size = this.adapter.getElementSize(this);
37551 this.activeMinSize = this.getMinimumSize();;
37552 this.activeMaxSize = this.getMaximumSize();;
37553 var c1 = size - this.activeMinSize;
37554 var c2 = Math.max(this.activeMaxSize - size, 0);
37555 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37556 this.dd.resetConstraints();
37557 this.dd.setXConstraint(
37558 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37559 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37561 this.dd.setYConstraint(0, 0);
37563 this.dd.resetConstraints();
37564 this.dd.setXConstraint(0, 0);
37565 this.dd.setYConstraint(
37566 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37567 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37570 this.dragSpecs.startSize = size;
37571 this.dragSpecs.startPoint = [x, y];
37572 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37576 * @private Called after the drag operation by the DDProxy
37578 onEndProxyDrag : function(e){
37579 Roo.get(this.proxy).setDisplayed(false);
37580 var endPoint = Roo.lib.Event.getXY(e);
37582 this.overlay.hide();
37585 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37586 newSize = this.dragSpecs.startSize +
37587 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37588 endPoint[0] - this.dragSpecs.startPoint[0] :
37589 this.dragSpecs.startPoint[0] - endPoint[0]
37592 newSize = this.dragSpecs.startSize +
37593 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37594 endPoint[1] - this.dragSpecs.startPoint[1] :
37595 this.dragSpecs.startPoint[1] - endPoint[1]
37598 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37599 if(newSize != this.dragSpecs.startSize){
37600 if(this.fireEvent('beforeapply', this, newSize) !== false){
37601 this.adapter.setElementSize(this, newSize);
37602 this.fireEvent("moved", this, newSize);
37603 this.fireEvent("resize", this, newSize);
37609 * Get the adapter this SplitBar uses
37610 * @return The adapter object
37612 getAdapter : function(){
37613 return this.adapter;
37617 * Set the adapter this SplitBar uses
37618 * @param {Object} adapter A SplitBar adapter object
37620 setAdapter : function(adapter){
37621 this.adapter = adapter;
37622 this.adapter.init(this);
37626 * Gets the minimum size for the resizing element
37627 * @return {Number} The minimum size
37629 getMinimumSize : function(){
37630 return this.minSize;
37634 * Sets the minimum size for the resizing element
37635 * @param {Number} minSize The minimum size
37637 setMinimumSize : function(minSize){
37638 this.minSize = minSize;
37642 * Gets the maximum size for the resizing element
37643 * @return {Number} The maximum size
37645 getMaximumSize : function(){
37646 return this.maxSize;
37650 * Sets the maximum size for the resizing element
37651 * @param {Number} maxSize The maximum size
37653 setMaximumSize : function(maxSize){
37654 this.maxSize = maxSize;
37658 * Sets the initialize size for the resizing element
37659 * @param {Number} size The initial size
37661 setCurrentSize : function(size){
37662 var oldAnimate = this.animate;
37663 this.animate = false;
37664 this.adapter.setElementSize(this, size);
37665 this.animate = oldAnimate;
37669 * Destroy this splitbar.
37670 * @param {Boolean} removeEl True to remove the element
37672 destroy : function(removeEl){
37674 this.shim.remove();
37677 this.proxy.parentNode.removeChild(this.proxy);
37685 * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
37687 Roo.bootstrap.SplitBar.createProxy = function(dir){
37688 var proxy = new Roo.Element(document.createElement("div"));
37689 proxy.unselectable();
37690 var cls = 'roo-splitbar-proxy';
37691 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37692 document.body.appendChild(proxy.dom);
37697 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37698 * Default Adapter. It assumes the splitter and resizing element are not positioned
37699 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37701 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37704 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37705 // do nothing for now
37706 init : function(s){
37710 * Called before drag operations to get the current size of the resizing element.
37711 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37713 getElementSize : function(s){
37714 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37715 return s.resizingEl.getWidth();
37717 return s.resizingEl.getHeight();
37722 * Called after drag operations to set the size of the resizing element.
37723 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37724 * @param {Number} newSize The new size to set
37725 * @param {Function} onComplete A function to be invoked when resizing is complete
37727 setElementSize : function(s, newSize, onComplete){
37728 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37730 s.resizingEl.setWidth(newSize);
37732 onComplete(s, newSize);
37735 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37740 s.resizingEl.setHeight(newSize);
37742 onComplete(s, newSize);
37745 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37752 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37753 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37754 * Adapter that moves the splitter element to align with the resized sizing element.
37755 * Used with an absolute positioned SplitBar.
37756 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37757 * document.body, make sure you assign an id to the body element.
37759 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37760 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37761 this.container = Roo.get(container);
37764 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37765 init : function(s){
37766 this.basic.init(s);
37769 getElementSize : function(s){
37770 return this.basic.getElementSize(s);
37773 setElementSize : function(s, newSize, onComplete){
37774 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37777 moveSplitter : function(s){
37778 var yes = Roo.bootstrap.SplitBar;
37779 switch(s.placement){
37781 s.el.setX(s.resizingEl.getRight());
37784 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37787 s.el.setY(s.resizingEl.getBottom());
37790 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37797 * Orientation constant - Create a vertical SplitBar
37801 Roo.bootstrap.SplitBar.VERTICAL = 1;
37804 * Orientation constant - Create a horizontal SplitBar
37808 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37811 * Placement constant - The resizing element is to the left of the splitter element
37815 Roo.bootstrap.SplitBar.LEFT = 1;
37818 * Placement constant - The resizing element is to the right of the splitter element
37822 Roo.bootstrap.SplitBar.RIGHT = 2;
37825 * Placement constant - The resizing element is positioned above the splitter element
37829 Roo.bootstrap.SplitBar.TOP = 3;
37832 * Placement constant - The resizing element is positioned under splitter element
37836 Roo.bootstrap.SplitBar.BOTTOM = 4;
37839 * Ext JS Library 1.1.1
37840 * Copyright(c) 2006-2007, Ext JS, LLC.
37842 * Originally Released Under LGPL - original licence link has changed is not relivant.
37845 * <script type="text/javascript">
37849 * @class Roo.bootstrap.layout.Manager
37850 * @extends Roo.bootstrap.Component
37852 * Base class for layout managers.
37854 Roo.bootstrap.layout.Manager = function(config)
37856 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37862 /** false to disable window resize monitoring @type Boolean */
37863 this.monitorWindowResize = true;
37868 * Fires when a layout is performed.
37869 * @param {Roo.LayoutManager} this
37873 * @event regionresized
37874 * Fires when the user resizes a region.
37875 * @param {Roo.LayoutRegion} region The resized region
37876 * @param {Number} newSize The new size (width for east/west, height for north/south)
37878 "regionresized" : true,
37880 * @event regioncollapsed
37881 * Fires when a region is collapsed.
37882 * @param {Roo.LayoutRegion} region The collapsed region
37884 "regioncollapsed" : true,
37886 * @event regionexpanded
37887 * Fires when a region is expanded.
37888 * @param {Roo.LayoutRegion} region The expanded region
37890 "regionexpanded" : true
37892 this.updating = false;
37895 this.el = Roo.get(config.el);
37901 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37906 monitorWindowResize : true,
37912 onRender : function(ct, position)
37915 this.el = Roo.get(ct);
37918 //this.fireEvent('render',this);
37922 initEvents: function()
37926 // ie scrollbar fix
37927 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37928 document.body.scroll = "no";
37929 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37930 this.el.position('relative');
37932 this.id = this.el.id;
37933 this.el.addClass("roo-layout-container");
37934 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37935 if(this.el.dom != document.body ) {
37936 this.el.on('resize', this.layout,this);
37937 this.el.on('show', this.layout,this);
37943 * Returns true if this layout is currently being updated
37944 * @return {Boolean}
37946 isUpdating : function(){
37947 return this.updating;
37951 * Suspend the LayoutManager from doing auto-layouts while
37952 * making multiple add or remove calls
37954 beginUpdate : function(){
37955 this.updating = true;
37959 * Restore auto-layouts and optionally disable the manager from performing a layout
37960 * @param {Boolean} noLayout true to disable a layout update
37962 endUpdate : function(noLayout){
37963 this.updating = false;
37969 layout: function(){
37973 onRegionResized : function(region, newSize){
37974 this.fireEvent("regionresized", region, newSize);
37978 onRegionCollapsed : function(region){
37979 this.fireEvent("regioncollapsed", region);
37982 onRegionExpanded : function(region){
37983 this.fireEvent("regionexpanded", region);
37987 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37988 * performs box-model adjustments.
37989 * @return {Object} The size as an object {width: (the width), height: (the height)}
37991 getViewSize : function()
37994 if(this.el.dom != document.body){
37995 size = this.el.getSize();
37997 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37999 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38000 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38005 * Returns the Element this layout is bound to.
38006 * @return {Roo.Element}
38008 getEl : function(){
38013 * Returns the specified region.
38014 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38015 * @return {Roo.LayoutRegion}
38017 getRegion : function(target){
38018 return this.regions[target.toLowerCase()];
38021 onWindowResize : function(){
38022 if(this.monitorWindowResize){
38029 * Ext JS Library 1.1.1
38030 * Copyright(c) 2006-2007, Ext JS, LLC.
38032 * Originally Released Under LGPL - original licence link has changed is not relivant.
38035 * <script type="text/javascript">
38038 * @class Roo.bootstrap.layout.Border
38039 * @extends Roo.bootstrap.layout.Manager
38040 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38041 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38042 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38043 * please see: examples/bootstrap/nested.html<br><br>
38045 <b>The container the layout is rendered into can be either the body element or any other element.
38046 If it is not the body element, the container needs to either be an absolute positioned element,
38047 or you will need to add "position:relative" to the css of the container. You will also need to specify
38048 the container size if it is not the body element.</b>
38051 * Create a new Border
38052 * @param {Object} config Configuration options
38054 Roo.bootstrap.layout.Border = function(config){
38055 config = config || {};
38056 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38060 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38061 if(config[region]){
38062 config[region].region = region;
38063 this.addRegion(config[region]);
38069 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38071 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38074 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38077 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38080 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38083 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38086 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38092 parent : false, // this might point to a 'nest' or a ???
38095 * Creates and adds a new region if it doesn't already exist.
38096 * @param {String} target The target region key (north, south, east, west or center).
38097 * @param {Object} config The regions config object
38098 * @return {BorderLayoutRegion} The new region
38100 addRegion : function(config)
38102 if(!this.regions[config.region]){
38103 var r = this.factory(config);
38104 this.bindRegion(r);
38106 return this.regions[config.region];
38110 bindRegion : function(r){
38111 this.regions[r.config.region] = r;
38113 r.on("visibilitychange", this.layout, this);
38114 r.on("paneladded", this.layout, this);
38115 r.on("panelremoved", this.layout, this);
38116 r.on("invalidated", this.layout, this);
38117 r.on("resized", this.onRegionResized, this);
38118 r.on("collapsed", this.onRegionCollapsed, this);
38119 r.on("expanded", this.onRegionExpanded, this);
38123 * Performs a layout update.
38125 layout : function()
38127 if(this.updating) {
38131 // render all the rebions if they have not been done alreayd?
38132 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38133 if(this.regions[region] && !this.regions[region].bodyEl){
38134 this.regions[region].onRender(this.el)
38138 var size = this.getViewSize();
38139 var w = size.width;
38140 var h = size.height;
38145 //var x = 0, y = 0;
38147 var rs = this.regions;
38148 var north = rs["north"];
38149 var south = rs["south"];
38150 var west = rs["west"];
38151 var east = rs["east"];
38152 var center = rs["center"];
38153 //if(this.hideOnLayout){ // not supported anymore
38154 //c.el.setStyle("display", "none");
38156 if(north && north.isVisible()){
38157 var b = north.getBox();
38158 var m = north.getMargins();
38159 b.width = w - (m.left+m.right);
38162 centerY = b.height + b.y + m.bottom;
38163 centerH -= centerY;
38164 north.updateBox(this.safeBox(b));
38166 if(south && south.isVisible()){
38167 var b = south.getBox();
38168 var m = south.getMargins();
38169 b.width = w - (m.left+m.right);
38171 var totalHeight = (b.height + m.top + m.bottom);
38172 b.y = h - totalHeight + m.top;
38173 centerH -= totalHeight;
38174 south.updateBox(this.safeBox(b));
38176 if(west && west.isVisible()){
38177 var b = west.getBox();
38178 var m = west.getMargins();
38179 b.height = centerH - (m.top+m.bottom);
38181 b.y = centerY + m.top;
38182 var totalWidth = (b.width + m.left + m.right);
38183 centerX += totalWidth;
38184 centerW -= totalWidth;
38185 west.updateBox(this.safeBox(b));
38187 if(east && east.isVisible()){
38188 var b = east.getBox();
38189 var m = east.getMargins();
38190 b.height = centerH - (m.top+m.bottom);
38191 var totalWidth = (b.width + m.left + m.right);
38192 b.x = w - totalWidth + m.left;
38193 b.y = centerY + m.top;
38194 centerW -= totalWidth;
38195 east.updateBox(this.safeBox(b));
38198 var m = center.getMargins();
38200 x: centerX + m.left,
38201 y: centerY + m.top,
38202 width: centerW - (m.left+m.right),
38203 height: centerH - (m.top+m.bottom)
38205 //if(this.hideOnLayout){
38206 //center.el.setStyle("display", "block");
38208 center.updateBox(this.safeBox(centerBox));
38211 this.fireEvent("layout", this);
38215 safeBox : function(box){
38216 box.width = Math.max(0, box.width);
38217 box.height = Math.max(0, box.height);
38222 * Adds a ContentPanel (or subclass) to this layout.
38223 * @param {String} target The target region key (north, south, east, west or center).
38224 * @param {Roo.ContentPanel} panel The panel to add
38225 * @return {Roo.ContentPanel} The added panel
38227 add : function(target, panel){
38229 target = target.toLowerCase();
38230 return this.regions[target].add(panel);
38234 * Remove a ContentPanel (or subclass) to this layout.
38235 * @param {String} target The target region key (north, south, east, west or center).
38236 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38237 * @return {Roo.ContentPanel} The removed panel
38239 remove : function(target, panel){
38240 target = target.toLowerCase();
38241 return this.regions[target].remove(panel);
38245 * Searches all regions for a panel with the specified id
38246 * @param {String} panelId
38247 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38249 findPanel : function(panelId){
38250 var rs = this.regions;
38251 for(var target in rs){
38252 if(typeof rs[target] != "function"){
38253 var p = rs[target].getPanel(panelId);
38263 * Searches all regions for a panel with the specified id and activates (shows) it.
38264 * @param {String/ContentPanel} panelId The panels id or the panel itself
38265 * @return {Roo.ContentPanel} The shown panel or null
38267 showPanel : function(panelId) {
38268 var rs = this.regions;
38269 for(var target in rs){
38270 var r = rs[target];
38271 if(typeof r != "function"){
38272 if(r.hasPanel(panelId)){
38273 return r.showPanel(panelId);
38281 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38282 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38285 restoreState : function(provider){
38287 provider = Roo.state.Manager;
38289 var sm = new Roo.LayoutStateManager();
38290 sm.init(this, provider);
38296 * Adds a xtype elements to the layout.
38300 xtype : 'ContentPanel',
38307 xtype : 'NestedLayoutPanel',
38313 items : [ ... list of content panels or nested layout panels.. ]
38317 * @param {Object} cfg Xtype definition of item to add.
38319 addxtype : function(cfg)
38321 // basically accepts a pannel...
38322 // can accept a layout region..!?!?
38323 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38326 // theory? children can only be panels??
38328 //if (!cfg.xtype.match(/Panel$/)) {
38333 if (typeof(cfg.region) == 'undefined') {
38334 Roo.log("Failed to add Panel, region was not set");
38338 var region = cfg.region;
38344 xitems = cfg.items;
38349 if ( region == 'center') {
38350 Roo.log("Center: " + cfg.title);
38356 case 'Content': // ContentPanel (el, cfg)
38357 case 'Scroll': // ContentPanel (el, cfg)
38359 cfg.autoCreate = cfg.autoCreate || true;
38360 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38362 // var el = this.el.createChild();
38363 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38366 this.add(region, ret);
38370 case 'TreePanel': // our new panel!
38371 cfg.el = this.el.createChild();
38372 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38373 this.add(region, ret);
38378 // create a new Layout (which is a Border Layout...
38380 var clayout = cfg.layout;
38381 clayout.el = this.el.createChild();
38382 clayout.items = clayout.items || [];
38386 // replace this exitems with the clayout ones..
38387 xitems = clayout.items;
38389 // force background off if it's in center...
38390 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38391 cfg.background = false;
38393 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38396 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38397 //console.log('adding nested layout panel ' + cfg.toSource());
38398 this.add(region, ret);
38399 nb = {}; /// find first...
38404 // needs grid and region
38406 //var el = this.getRegion(region).el.createChild();
38408 *var el = this.el.createChild();
38409 // create the grid first...
38410 cfg.grid.container = el;
38411 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38414 if (region == 'center' && this.active ) {
38415 cfg.background = false;
38418 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38420 this.add(region, ret);
38422 if (cfg.background) {
38423 // render grid on panel activation (if panel background)
38424 ret.on('activate', function(gp) {
38425 if (!gp.grid.rendered) {
38426 // gp.grid.render(el);
38430 // cfg.grid.render(el);
38436 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38437 // it was the old xcomponent building that caused this before.
38438 // espeically if border is the top element in the tree.
38448 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38450 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38451 this.add(region, ret);
38455 throw "Can not add '" + cfg.xtype + "' to Border";
38461 this.beginUpdate();
38465 Roo.each(xitems, function(i) {
38466 region = nb && i.region ? i.region : false;
38468 var add = ret.addxtype(i);
38471 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38472 if (!i.background) {
38473 abn[region] = nb[region] ;
38480 // make the last non-background panel active..
38481 //if (nb) { Roo.log(abn); }
38484 for(var r in abn) {
38485 region = this.getRegion(r);
38487 // tried using nb[r], but it does not work..
38489 region.showPanel(abn[r]);
38500 factory : function(cfg)
38503 var validRegions = Roo.bootstrap.layout.Border.regions;
38505 var target = cfg.region;
38508 var r = Roo.bootstrap.layout;
38512 return new r.North(cfg);
38514 return new r.South(cfg);
38516 return new r.East(cfg);
38518 return new r.West(cfg);
38520 return new r.Center(cfg);
38522 throw 'Layout region "'+target+'" not supported.';
38529 * Ext JS Library 1.1.1
38530 * Copyright(c) 2006-2007, Ext JS, LLC.
38532 * Originally Released Under LGPL - original licence link has changed is not relivant.
38535 * <script type="text/javascript">
38539 * @class Roo.bootstrap.layout.Basic
38540 * @extends Roo.util.Observable
38541 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38542 * and does not have a titlebar, tabs or any other features. All it does is size and position
38543 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38544 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38545 * @cfg {string} region the region that it inhabits..
38546 * @cfg {bool} skipConfig skip config?
38550 Roo.bootstrap.layout.Basic = function(config){
38552 this.mgr = config.mgr;
38554 this.position = config.region;
38556 var skipConfig = config.skipConfig;
38560 * @scope Roo.BasicLayoutRegion
38564 * @event beforeremove
38565 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38566 * @param {Roo.LayoutRegion} this
38567 * @param {Roo.ContentPanel} panel The panel
38568 * @param {Object} e The cancel event object
38570 "beforeremove" : true,
38572 * @event invalidated
38573 * Fires when the layout for this region is changed.
38574 * @param {Roo.LayoutRegion} this
38576 "invalidated" : true,
38578 * @event visibilitychange
38579 * Fires when this region is shown or hidden
38580 * @param {Roo.LayoutRegion} this
38581 * @param {Boolean} visibility true or false
38583 "visibilitychange" : true,
38585 * @event paneladded
38586 * Fires when a panel is added.
38587 * @param {Roo.LayoutRegion} this
38588 * @param {Roo.ContentPanel} panel The panel
38590 "paneladded" : true,
38592 * @event panelremoved
38593 * Fires when a panel is removed.
38594 * @param {Roo.LayoutRegion} this
38595 * @param {Roo.ContentPanel} panel The panel
38597 "panelremoved" : true,
38599 * @event beforecollapse
38600 * Fires when this region before collapse.
38601 * @param {Roo.LayoutRegion} this
38603 "beforecollapse" : true,
38606 * Fires when this region is collapsed.
38607 * @param {Roo.LayoutRegion} this
38609 "collapsed" : true,
38612 * Fires when this region is expanded.
38613 * @param {Roo.LayoutRegion} this
38618 * Fires when this region is slid into view.
38619 * @param {Roo.LayoutRegion} this
38621 "slideshow" : true,
38624 * Fires when this region slides out of view.
38625 * @param {Roo.LayoutRegion} this
38627 "slidehide" : true,
38629 * @event panelactivated
38630 * Fires when a panel is activated.
38631 * @param {Roo.LayoutRegion} this
38632 * @param {Roo.ContentPanel} panel The activated panel
38634 "panelactivated" : true,
38637 * Fires when the user resizes this region.
38638 * @param {Roo.LayoutRegion} this
38639 * @param {Number} newSize The new size (width for east/west, height for north/south)
38643 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38644 this.panels = new Roo.util.MixedCollection();
38645 this.panels.getKey = this.getPanelId.createDelegate(this);
38647 this.activePanel = null;
38648 // ensure listeners are added...
38650 if (config.listeners || config.events) {
38651 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38652 listeners : config.listeners || {},
38653 events : config.events || {}
38657 if(skipConfig !== true){
38658 this.applyConfig(config);
38662 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38664 getPanelId : function(p){
38668 applyConfig : function(config){
38669 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38670 this.config = config;
38675 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38676 * the width, for horizontal (north, south) the height.
38677 * @param {Number} newSize The new width or height
38679 resizeTo : function(newSize){
38680 var el = this.el ? this.el :
38681 (this.activePanel ? this.activePanel.getEl() : null);
38683 switch(this.position){
38686 el.setWidth(newSize);
38687 this.fireEvent("resized", this, newSize);
38691 el.setHeight(newSize);
38692 this.fireEvent("resized", this, newSize);
38698 getBox : function(){
38699 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38702 getMargins : function(){
38703 return this.margins;
38706 updateBox : function(box){
38708 var el = this.activePanel.getEl();
38709 el.dom.style.left = box.x + "px";
38710 el.dom.style.top = box.y + "px";
38711 this.activePanel.setSize(box.width, box.height);
38715 * Returns the container element for this region.
38716 * @return {Roo.Element}
38718 getEl : function(){
38719 return this.activePanel;
38723 * Returns true if this region is currently visible.
38724 * @return {Boolean}
38726 isVisible : function(){
38727 return this.activePanel ? true : false;
38730 setActivePanel : function(panel){
38731 panel = this.getPanel(panel);
38732 if(this.activePanel && this.activePanel != panel){
38733 this.activePanel.setActiveState(false);
38734 this.activePanel.getEl().setLeftTop(-10000,-10000);
38736 this.activePanel = panel;
38737 panel.setActiveState(true);
38739 panel.setSize(this.box.width, this.box.height);
38741 this.fireEvent("panelactivated", this, panel);
38742 this.fireEvent("invalidated");
38746 * Show the specified panel.
38747 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38748 * @return {Roo.ContentPanel} The shown panel or null
38750 showPanel : function(panel){
38751 panel = this.getPanel(panel);
38753 this.setActivePanel(panel);
38759 * Get the active panel for this region.
38760 * @return {Roo.ContentPanel} The active panel or null
38762 getActivePanel : function(){
38763 return this.activePanel;
38767 * Add the passed ContentPanel(s)
38768 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38769 * @return {Roo.ContentPanel} The panel added (if only one was added)
38771 add : function(panel){
38772 if(arguments.length > 1){
38773 for(var i = 0, len = arguments.length; i < len; i++) {
38774 this.add(arguments[i]);
38778 if(this.hasPanel(panel)){
38779 this.showPanel(panel);
38782 var el = panel.getEl();
38783 if(el.dom.parentNode != this.mgr.el.dom){
38784 this.mgr.el.dom.appendChild(el.dom);
38786 if(panel.setRegion){
38787 panel.setRegion(this);
38789 this.panels.add(panel);
38790 el.setStyle("position", "absolute");
38791 if(!panel.background){
38792 this.setActivePanel(panel);
38793 if(this.config.initialSize && this.panels.getCount()==1){
38794 this.resizeTo(this.config.initialSize);
38797 this.fireEvent("paneladded", this, panel);
38802 * Returns true if the panel is in this region.
38803 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38804 * @return {Boolean}
38806 hasPanel : function(panel){
38807 if(typeof panel == "object"){ // must be panel obj
38808 panel = panel.getId();
38810 return this.getPanel(panel) ? true : false;
38814 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38815 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38816 * @param {Boolean} preservePanel Overrides the config preservePanel option
38817 * @return {Roo.ContentPanel} The panel that was removed
38819 remove : function(panel, preservePanel){
38820 panel = this.getPanel(panel);
38825 this.fireEvent("beforeremove", this, panel, e);
38826 if(e.cancel === true){
38829 var panelId = panel.getId();
38830 this.panels.removeKey(panelId);
38835 * Returns the panel specified or null if it's not in this region.
38836 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38837 * @return {Roo.ContentPanel}
38839 getPanel : function(id){
38840 if(typeof id == "object"){ // must be panel obj
38843 return this.panels.get(id);
38847 * Returns this regions position (north/south/east/west/center).
38850 getPosition: function(){
38851 return this.position;
38855 * Ext JS Library 1.1.1
38856 * Copyright(c) 2006-2007, Ext JS, LLC.
38858 * Originally Released Under LGPL - original licence link has changed is not relivant.
38861 * <script type="text/javascript">
38865 * @class Roo.bootstrap.layout.Region
38866 * @extends Roo.bootstrap.layout.Basic
38867 * This class represents a region in a layout manager.
38869 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38870 * @cfg {Object} cmargins Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
38871 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38872 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38873 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38874 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38875 * @cfg {String} title The title for the region (overrides panel titles)
38876 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38877 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38878 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38879 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38880 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38881 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38882 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38883 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38884 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38885 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38887 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38888 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38889 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38890 * @cfg {Number} width For East/West panels
38891 * @cfg {Number} height For North/South panels
38892 * @cfg {Boolean} split To show the splitter
38893 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38895 * @cfg {string} cls Extra CSS classes to add to region
38897 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38898 * @cfg {string} region the region that it inhabits..
38901 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38902 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38904 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38905 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38906 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38908 Roo.bootstrap.layout.Region = function(config)
38910 this.applyConfig(config);
38912 var mgr = config.mgr;
38913 var pos = config.region;
38914 config.skipConfig = true;
38915 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38918 this.onRender(mgr.el);
38921 this.visible = true;
38922 this.collapsed = false;
38923 this.unrendered_panels = [];
38926 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38928 position: '', // set by wrapper (eg. north/south etc..)
38929 unrendered_panels : null, // unrendered panels.
38931 tabPosition : false,
38933 mgr: false, // points to 'Border'
38936 createBody : function(){
38937 /** This region's body element
38938 * @type Roo.Element */
38939 this.bodyEl = this.el.createChild({
38941 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38945 onRender: function(ctr, pos)
38947 var dh = Roo.DomHelper;
38948 /** This region's container element
38949 * @type Roo.Element */
38950 this.el = dh.append(ctr.dom, {
38952 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38954 /** This region's title element
38955 * @type Roo.Element */
38957 this.titleEl = dh.append(this.el.dom, {
38959 unselectable: "on",
38960 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38962 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38963 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38967 this.titleEl.enableDisplayMode();
38968 /** This region's title text element
38969 * @type HTMLElement */
38970 this.titleTextEl = this.titleEl.dom.firstChild;
38971 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38973 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38974 this.closeBtn.enableDisplayMode();
38975 this.closeBtn.on("click", this.closeClicked, this);
38976 this.closeBtn.hide();
38978 this.createBody(this.config);
38979 if(this.config.hideWhenEmpty){
38981 this.on("paneladded", this.validateVisibility, this);
38982 this.on("panelremoved", this.validateVisibility, this);
38984 if(this.autoScroll){
38985 this.bodyEl.setStyle("overflow", "auto");
38987 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38989 //if(c.titlebar !== false){
38990 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38991 this.titleEl.hide();
38993 this.titleEl.show();
38994 if(this.config.title){
38995 this.titleTextEl.innerHTML = this.config.title;
38999 if(this.config.collapsed){
39000 this.collapse(true);
39002 if(this.config.hidden){
39006 if (this.unrendered_panels && this.unrendered_panels.length) {
39007 for (var i =0;i< this.unrendered_panels.length; i++) {
39008 this.add(this.unrendered_panels[i]);
39010 this.unrendered_panels = null;
39016 applyConfig : function(c)
39019 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39020 var dh = Roo.DomHelper;
39021 if(c.titlebar !== false){
39022 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39023 this.collapseBtn.on("click", this.collapse, this);
39024 this.collapseBtn.enableDisplayMode();
39026 if(c.showPin === true || this.showPin){
39027 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39028 this.stickBtn.enableDisplayMode();
39029 this.stickBtn.on("click", this.expand, this);
39030 this.stickBtn.hide();
39035 /** This region's collapsed element
39036 * @type Roo.Element */
39039 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39040 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39043 if(c.floatable !== false){
39044 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39045 this.collapsedEl.on("click", this.collapseClick, this);
39048 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39049 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39050 id: "message", unselectable: "on", style:{"float":"left"}});
39051 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39053 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39054 this.expandBtn.on("click", this.expand, this);
39058 if(this.collapseBtn){
39059 this.collapseBtn.setVisible(c.collapsible == true);
39062 this.cmargins = c.cmargins || this.cmargins ||
39063 (this.position == "west" || this.position == "east" ?
39064 {top: 0, left: 2, right:2, bottom: 0} :
39065 {top: 2, left: 0, right:0, bottom: 2});
39067 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39070 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39072 this.autoScroll = c.autoScroll || false;
39077 this.duration = c.duration || .30;
39078 this.slideDuration = c.slideDuration || .45;
39083 * Returns true if this region is currently visible.
39084 * @return {Boolean}
39086 isVisible : function(){
39087 return this.visible;
39091 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39092 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39094 //setCollapsedTitle : function(title){
39095 // title = title || " ";
39096 // if(this.collapsedTitleTextEl){
39097 // this.collapsedTitleTextEl.innerHTML = title;
39101 getBox : function(){
39103 // if(!this.collapsed){
39104 b = this.el.getBox(false, true);
39106 // b = this.collapsedEl.getBox(false, true);
39111 getMargins : function(){
39112 return this.margins;
39113 //return this.collapsed ? this.cmargins : this.margins;
39116 highlight : function(){
39117 this.el.addClass("x-layout-panel-dragover");
39120 unhighlight : function(){
39121 this.el.removeClass("x-layout-panel-dragover");
39124 updateBox : function(box)
39126 if (!this.bodyEl) {
39127 return; // not rendered yet..
39131 if(!this.collapsed){
39132 this.el.dom.style.left = box.x + "px";
39133 this.el.dom.style.top = box.y + "px";
39134 this.updateBody(box.width, box.height);
39136 this.collapsedEl.dom.style.left = box.x + "px";
39137 this.collapsedEl.dom.style.top = box.y + "px";
39138 this.collapsedEl.setSize(box.width, box.height);
39141 this.tabs.autoSizeTabs();
39145 updateBody : function(w, h)
39148 this.el.setWidth(w);
39149 w -= this.el.getBorderWidth("rl");
39150 if(this.config.adjustments){
39151 w += this.config.adjustments[0];
39154 if(h !== null && h > 0){
39155 this.el.setHeight(h);
39156 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39157 h -= this.el.getBorderWidth("tb");
39158 if(this.config.adjustments){
39159 h += this.config.adjustments[1];
39161 this.bodyEl.setHeight(h);
39163 h = this.tabs.syncHeight(h);
39166 if(this.panelSize){
39167 w = w !== null ? w : this.panelSize.width;
39168 h = h !== null ? h : this.panelSize.height;
39170 if(this.activePanel){
39171 var el = this.activePanel.getEl();
39172 w = w !== null ? w : el.getWidth();
39173 h = h !== null ? h : el.getHeight();
39174 this.panelSize = {width: w, height: h};
39175 this.activePanel.setSize(w, h);
39177 if(Roo.isIE && this.tabs){
39178 this.tabs.el.repaint();
39183 * Returns the container element for this region.
39184 * @return {Roo.Element}
39186 getEl : function(){
39191 * Hides this region.
39194 //if(!this.collapsed){
39195 this.el.dom.style.left = "-2000px";
39198 // this.collapsedEl.dom.style.left = "-2000px";
39199 // this.collapsedEl.hide();
39201 this.visible = false;
39202 this.fireEvent("visibilitychange", this, false);
39206 * Shows this region if it was previously hidden.
39209 //if(!this.collapsed){
39212 // this.collapsedEl.show();
39214 this.visible = true;
39215 this.fireEvent("visibilitychange", this, true);
39218 closeClicked : function(){
39219 if(this.activePanel){
39220 this.remove(this.activePanel);
39224 collapseClick : function(e){
39226 e.stopPropagation();
39229 e.stopPropagation();
39235 * Collapses this region.
39236 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39239 collapse : function(skipAnim, skipCheck = false){
39240 if(this.collapsed) {
39244 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39246 this.collapsed = true;
39248 this.split.el.hide();
39250 if(this.config.animate && skipAnim !== true){
39251 this.fireEvent("invalidated", this);
39252 this.animateCollapse();
39254 this.el.setLocation(-20000,-20000);
39256 this.collapsedEl.show();
39257 this.fireEvent("collapsed", this);
39258 this.fireEvent("invalidated", this);
39264 animateCollapse : function(){
39269 * Expands this region if it was previously collapsed.
39270 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39271 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39274 expand : function(e, skipAnim){
39276 e.stopPropagation();
39278 if(!this.collapsed || this.el.hasActiveFx()) {
39282 this.afterSlideIn();
39285 this.collapsed = false;
39286 if(this.config.animate && skipAnim !== true){
39287 this.animateExpand();
39291 this.split.el.show();
39293 this.collapsedEl.setLocation(-2000,-2000);
39294 this.collapsedEl.hide();
39295 this.fireEvent("invalidated", this);
39296 this.fireEvent("expanded", this);
39300 animateExpand : function(){
39304 initTabs : function()
39306 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39308 var ts = new Roo.bootstrap.panel.Tabs({
39309 el: this.bodyEl.dom,
39311 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39312 disableTooltips: this.config.disableTabTips,
39313 toolbar : this.config.toolbar
39316 if(this.config.hideTabs){
39317 ts.stripWrap.setDisplayed(false);
39320 ts.resizeTabs = this.config.resizeTabs === true;
39321 ts.minTabWidth = this.config.minTabWidth || 40;
39322 ts.maxTabWidth = this.config.maxTabWidth || 250;
39323 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39324 ts.monitorResize = false;
39325 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39326 ts.bodyEl.addClass('roo-layout-tabs-body');
39327 this.panels.each(this.initPanelAsTab, this);
39330 initPanelAsTab : function(panel){
39331 var ti = this.tabs.addTab(
39335 this.config.closeOnTab && panel.isClosable(),
39338 if(panel.tabTip !== undefined){
39339 ti.setTooltip(panel.tabTip);
39341 ti.on("activate", function(){
39342 this.setActivePanel(panel);
39345 if(this.config.closeOnTab){
39346 ti.on("beforeclose", function(t, e){
39348 this.remove(panel);
39352 panel.tabItem = ti;
39357 updatePanelTitle : function(panel, title)
39359 if(this.activePanel == panel){
39360 this.updateTitle(title);
39363 var ti = this.tabs.getTab(panel.getEl().id);
39365 if(panel.tabTip !== undefined){
39366 ti.setTooltip(panel.tabTip);
39371 updateTitle : function(title){
39372 if(this.titleTextEl && !this.config.title){
39373 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39377 setActivePanel : function(panel)
39379 panel = this.getPanel(panel);
39380 if(this.activePanel && this.activePanel != panel){
39381 if(this.activePanel.setActiveState(false) === false){
39385 this.activePanel = panel;
39386 panel.setActiveState(true);
39387 if(this.panelSize){
39388 panel.setSize(this.panelSize.width, this.panelSize.height);
39391 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39393 this.updateTitle(panel.getTitle());
39395 this.fireEvent("invalidated", this);
39397 this.fireEvent("panelactivated", this, panel);
39401 * Shows the specified panel.
39402 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39403 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39405 showPanel : function(panel)
39407 panel = this.getPanel(panel);
39410 var tab = this.tabs.getTab(panel.getEl().id);
39411 if(tab.isHidden()){
39412 this.tabs.unhideTab(tab.id);
39416 this.setActivePanel(panel);
39423 * Get the active panel for this region.
39424 * @return {Roo.ContentPanel} The active panel or null
39426 getActivePanel : function(){
39427 return this.activePanel;
39430 validateVisibility : function(){
39431 if(this.panels.getCount() < 1){
39432 this.updateTitle(" ");
39433 this.closeBtn.hide();
39436 if(!this.isVisible()){
39443 * Adds the passed ContentPanel(s) to this region.
39444 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39445 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39447 add : function(panel)
39449 if(arguments.length > 1){
39450 for(var i = 0, len = arguments.length; i < len; i++) {
39451 this.add(arguments[i]);
39456 // if we have not been rendered yet, then we can not really do much of this..
39457 if (!this.bodyEl) {
39458 this.unrendered_panels.push(panel);
39465 if(this.hasPanel(panel)){
39466 this.showPanel(panel);
39469 panel.setRegion(this);
39470 this.panels.add(panel);
39471 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39472 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39473 // and hide them... ???
39474 this.bodyEl.dom.appendChild(panel.getEl().dom);
39475 if(panel.background !== true){
39476 this.setActivePanel(panel);
39478 this.fireEvent("paneladded", this, panel);
39485 this.initPanelAsTab(panel);
39489 if(panel.background !== true){
39490 this.tabs.activate(panel.getEl().id);
39492 this.fireEvent("paneladded", this, panel);
39497 * Hides the tab for the specified panel.
39498 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39500 hidePanel : function(panel){
39501 if(this.tabs && (panel = this.getPanel(panel))){
39502 this.tabs.hideTab(panel.getEl().id);
39507 * Unhides the tab for a previously hidden panel.
39508 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39510 unhidePanel : function(panel){
39511 if(this.tabs && (panel = this.getPanel(panel))){
39512 this.tabs.unhideTab(panel.getEl().id);
39516 clearPanels : function(){
39517 while(this.panels.getCount() > 0){
39518 this.remove(this.panels.first());
39523 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39524 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39525 * @param {Boolean} preservePanel Overrides the config preservePanel option
39526 * @return {Roo.ContentPanel} The panel that was removed
39528 remove : function(panel, preservePanel)
39530 panel = this.getPanel(panel);
39535 this.fireEvent("beforeremove", this, panel, e);
39536 if(e.cancel === true){
39539 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39540 var panelId = panel.getId();
39541 this.panels.removeKey(panelId);
39543 document.body.appendChild(panel.getEl().dom);
39546 this.tabs.removeTab(panel.getEl().id);
39547 }else if (!preservePanel){
39548 this.bodyEl.dom.removeChild(panel.getEl().dom);
39550 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39551 var p = this.panels.first();
39552 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39553 tempEl.appendChild(p.getEl().dom);
39554 this.bodyEl.update("");
39555 this.bodyEl.dom.appendChild(p.getEl().dom);
39557 this.updateTitle(p.getTitle());
39559 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39560 this.setActivePanel(p);
39562 panel.setRegion(null);
39563 if(this.activePanel == panel){
39564 this.activePanel = null;
39566 if(this.config.autoDestroy !== false && preservePanel !== true){
39567 try{panel.destroy();}catch(e){}
39569 this.fireEvent("panelremoved", this, panel);
39574 * Returns the TabPanel component used by this region
39575 * @return {Roo.TabPanel}
39577 getTabs : function(){
39581 createTool : function(parentEl, className){
39582 var btn = Roo.DomHelper.append(parentEl, {
39584 cls: "x-layout-tools-button",
39587 cls: "roo-layout-tools-button-inner " + className,
39591 btn.addClassOnOver("roo-layout-tools-button-over");
39596 * Ext JS Library 1.1.1
39597 * Copyright(c) 2006-2007, Ext JS, LLC.
39599 * Originally Released Under LGPL - original licence link has changed is not relivant.
39602 * <script type="text/javascript">
39608 * @class Roo.SplitLayoutRegion
39609 * @extends Roo.LayoutRegion
39610 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39612 Roo.bootstrap.layout.Split = function(config){
39613 this.cursor = config.cursor;
39614 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39617 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39619 splitTip : "Drag to resize.",
39620 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39621 useSplitTips : false,
39623 applyConfig : function(config){
39624 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39627 onRender : function(ctr,pos) {
39629 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39630 if(!this.config.split){
39635 var splitEl = Roo.DomHelper.append(ctr.dom, {
39637 id: this.el.id + "-split",
39638 cls: "roo-layout-split roo-layout-split-"+this.position,
39641 /** The SplitBar for this region
39642 * @type Roo.SplitBar */
39643 // does not exist yet...
39644 Roo.log([this.position, this.orientation]);
39646 this.split = new Roo.bootstrap.SplitBar({
39647 dragElement : splitEl,
39648 resizingElement: this.el,
39649 orientation : this.orientation
39652 this.split.on("moved", this.onSplitMove, this);
39653 this.split.useShim = this.config.useShim === true;
39654 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39655 if(this.useSplitTips){
39656 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39658 //if(config.collapsible){
39659 // this.split.el.on("dblclick", this.collapse, this);
39662 if(typeof this.config.minSize != "undefined"){
39663 this.split.minSize = this.config.minSize;
39665 if(typeof this.config.maxSize != "undefined"){
39666 this.split.maxSize = this.config.maxSize;
39668 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39669 this.hideSplitter();
39674 getHMaxSize : function(){
39675 var cmax = this.config.maxSize || 10000;
39676 var center = this.mgr.getRegion("center");
39677 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39680 getVMaxSize : function(){
39681 var cmax = this.config.maxSize || 10000;
39682 var center = this.mgr.getRegion("center");
39683 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39686 onSplitMove : function(split, newSize){
39687 this.fireEvent("resized", this, newSize);
39691 * Returns the {@link Roo.SplitBar} for this region.
39692 * @return {Roo.SplitBar}
39694 getSplitBar : function(){
39699 this.hideSplitter();
39700 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39703 hideSplitter : function(){
39705 this.split.el.setLocation(-2000,-2000);
39706 this.split.el.hide();
39712 this.split.el.show();
39714 Roo.bootstrap.layout.Split.superclass.show.call(this);
39717 beforeSlide: function(){
39718 if(Roo.isGecko){// firefox overflow auto bug workaround
39719 this.bodyEl.clip();
39721 this.tabs.bodyEl.clip();
39723 if(this.activePanel){
39724 this.activePanel.getEl().clip();
39726 if(this.activePanel.beforeSlide){
39727 this.activePanel.beforeSlide();
39733 afterSlide : function(){
39734 if(Roo.isGecko){// firefox overflow auto bug workaround
39735 this.bodyEl.unclip();
39737 this.tabs.bodyEl.unclip();
39739 if(this.activePanel){
39740 this.activePanel.getEl().unclip();
39741 if(this.activePanel.afterSlide){
39742 this.activePanel.afterSlide();
39748 initAutoHide : function(){
39749 if(this.autoHide !== false){
39750 if(!this.autoHideHd){
39751 var st = new Roo.util.DelayedTask(this.slideIn, this);
39752 this.autoHideHd = {
39753 "mouseout": function(e){
39754 if(!e.within(this.el, true)){
39758 "mouseover" : function(e){
39764 this.el.on(this.autoHideHd);
39768 clearAutoHide : function(){
39769 if(this.autoHide !== false){
39770 this.el.un("mouseout", this.autoHideHd.mouseout);
39771 this.el.un("mouseover", this.autoHideHd.mouseover);
39775 clearMonitor : function(){
39776 Roo.get(document).un("click", this.slideInIf, this);
39779 // these names are backwards but not changed for compat
39780 slideOut : function(){
39781 if(this.isSlid || this.el.hasActiveFx()){
39784 this.isSlid = true;
39785 if(this.collapseBtn){
39786 this.collapseBtn.hide();
39788 this.closeBtnState = this.closeBtn.getStyle('display');
39789 this.closeBtn.hide();
39791 this.stickBtn.show();
39794 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39795 this.beforeSlide();
39796 this.el.setStyle("z-index", 10001);
39797 this.el.slideIn(this.getSlideAnchor(), {
39798 callback: function(){
39800 this.initAutoHide();
39801 Roo.get(document).on("click", this.slideInIf, this);
39802 this.fireEvent("slideshow", this);
39809 afterSlideIn : function(){
39810 this.clearAutoHide();
39811 this.isSlid = false;
39812 this.clearMonitor();
39813 this.el.setStyle("z-index", "");
39814 if(this.collapseBtn){
39815 this.collapseBtn.show();
39817 this.closeBtn.setStyle('display', this.closeBtnState);
39819 this.stickBtn.hide();
39821 this.fireEvent("slidehide", this);
39824 slideIn : function(cb){
39825 if(!this.isSlid || this.el.hasActiveFx()){
39829 this.isSlid = false;
39830 this.beforeSlide();
39831 this.el.slideOut(this.getSlideAnchor(), {
39832 callback: function(){
39833 this.el.setLeftTop(-10000, -10000);
39835 this.afterSlideIn();
39843 slideInIf : function(e){
39844 if(!e.within(this.el)){
39849 animateCollapse : function(){
39850 this.beforeSlide();
39851 this.el.setStyle("z-index", 20000);
39852 var anchor = this.getSlideAnchor();
39853 this.el.slideOut(anchor, {
39854 callback : function(){
39855 this.el.setStyle("z-index", "");
39856 this.collapsedEl.slideIn(anchor, {duration:.3});
39858 this.el.setLocation(-10000,-10000);
39860 this.fireEvent("collapsed", this);
39867 animateExpand : function(){
39868 this.beforeSlide();
39869 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39870 this.el.setStyle("z-index", 20000);
39871 this.collapsedEl.hide({
39874 this.el.slideIn(this.getSlideAnchor(), {
39875 callback : function(){
39876 this.el.setStyle("z-index", "");
39879 this.split.el.show();
39881 this.fireEvent("invalidated", this);
39882 this.fireEvent("expanded", this);
39910 getAnchor : function(){
39911 return this.anchors[this.position];
39914 getCollapseAnchor : function(){
39915 return this.canchors[this.position];
39918 getSlideAnchor : function(){
39919 return this.sanchors[this.position];
39922 getAlignAdj : function(){
39923 var cm = this.cmargins;
39924 switch(this.position){
39940 getExpandAdj : function(){
39941 var c = this.collapsedEl, cm = this.cmargins;
39942 switch(this.position){
39944 return [-(cm.right+c.getWidth()+cm.left), 0];
39947 return [cm.right+c.getWidth()+cm.left, 0];
39950 return [0, -(cm.top+cm.bottom+c.getHeight())];
39953 return [0, cm.top+cm.bottom+c.getHeight()];
39959 * Ext JS Library 1.1.1
39960 * Copyright(c) 2006-2007, Ext JS, LLC.
39962 * Originally Released Under LGPL - original licence link has changed is not relivant.
39965 * <script type="text/javascript">
39968 * These classes are private internal classes
39970 Roo.bootstrap.layout.Center = function(config){
39971 config.region = "center";
39972 Roo.bootstrap.layout.Region.call(this, config);
39973 this.visible = true;
39974 this.minWidth = config.minWidth || 20;
39975 this.minHeight = config.minHeight || 20;
39978 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39980 // center panel can't be hidden
39984 // center panel can't be hidden
39987 getMinWidth: function(){
39988 return this.minWidth;
39991 getMinHeight: function(){
39992 return this.minHeight;
40006 Roo.bootstrap.layout.North = function(config)
40008 config.region = 'north';
40009 config.cursor = 'n-resize';
40011 Roo.bootstrap.layout.Split.call(this, config);
40015 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40016 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40017 this.split.el.addClass("roo-layout-split-v");
40019 //var size = config.initialSize || config.height;
40020 //if(this.el && typeof size != "undefined"){
40021 // this.el.setHeight(size);
40024 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40026 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40029 onRender : function(ctr, pos)
40031 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40032 var size = this.config.initialSize || this.config.height;
40033 if(this.el && typeof size != "undefined"){
40034 this.el.setHeight(size);
40039 getBox : function(){
40040 if(this.collapsed){
40041 return this.collapsedEl.getBox();
40043 var box = this.el.getBox();
40045 box.height += this.split.el.getHeight();
40050 updateBox : function(box){
40051 if(this.split && !this.collapsed){
40052 box.height -= this.split.el.getHeight();
40053 this.split.el.setLeft(box.x);
40054 this.split.el.setTop(box.y+box.height);
40055 this.split.el.setWidth(box.width);
40057 if(this.collapsed){
40058 this.updateBody(box.width, null);
40060 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40068 Roo.bootstrap.layout.South = function(config){
40069 config.region = 'south';
40070 config.cursor = 's-resize';
40071 Roo.bootstrap.layout.Split.call(this, config);
40073 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40074 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40075 this.split.el.addClass("roo-layout-split-v");
40080 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40081 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40083 onRender : function(ctr, pos)
40085 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40086 var size = this.config.initialSize || this.config.height;
40087 if(this.el && typeof size != "undefined"){
40088 this.el.setHeight(size);
40093 getBox : function(){
40094 if(this.collapsed){
40095 return this.collapsedEl.getBox();
40097 var box = this.el.getBox();
40099 var sh = this.split.el.getHeight();
40106 updateBox : function(box){
40107 if(this.split && !this.collapsed){
40108 var sh = this.split.el.getHeight();
40111 this.split.el.setLeft(box.x);
40112 this.split.el.setTop(box.y-sh);
40113 this.split.el.setWidth(box.width);
40115 if(this.collapsed){
40116 this.updateBody(box.width, null);
40118 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40122 Roo.bootstrap.layout.East = function(config){
40123 config.region = "east";
40124 config.cursor = "e-resize";
40125 Roo.bootstrap.layout.Split.call(this, config);
40127 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40128 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40129 this.split.el.addClass("roo-layout-split-h");
40133 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40134 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40136 onRender : function(ctr, pos)
40138 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40139 var size = this.config.initialSize || this.config.width;
40140 if(this.el && typeof size != "undefined"){
40141 this.el.setWidth(size);
40146 getBox : function(){
40147 if(this.collapsed){
40148 return this.collapsedEl.getBox();
40150 var box = this.el.getBox();
40152 var sw = this.split.el.getWidth();
40159 updateBox : function(box){
40160 if(this.split && !this.collapsed){
40161 var sw = this.split.el.getWidth();
40163 this.split.el.setLeft(box.x);
40164 this.split.el.setTop(box.y);
40165 this.split.el.setHeight(box.height);
40168 if(this.collapsed){
40169 this.updateBody(null, box.height);
40171 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40175 Roo.bootstrap.layout.West = function(config){
40176 config.region = "west";
40177 config.cursor = "w-resize";
40179 Roo.bootstrap.layout.Split.call(this, config);
40181 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40182 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40183 this.split.el.addClass("roo-layout-split-h");
40187 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40188 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40190 onRender: function(ctr, pos)
40192 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40193 var size = this.config.initialSize || this.config.width;
40194 if(typeof size != "undefined"){
40195 this.el.setWidth(size);
40199 getBox : function(){
40200 if(this.collapsed){
40201 return this.collapsedEl.getBox();
40203 var box = this.el.getBox();
40204 if (box.width == 0) {
40205 box.width = this.config.width; // kludge?
40208 box.width += this.split.el.getWidth();
40213 updateBox : function(box){
40214 if(this.split && !this.collapsed){
40215 var sw = this.split.el.getWidth();
40217 this.split.el.setLeft(box.x+box.width);
40218 this.split.el.setTop(box.y);
40219 this.split.el.setHeight(box.height);
40221 if(this.collapsed){
40222 this.updateBody(null, box.height);
40224 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40228 * Ext JS Library 1.1.1
40229 * Copyright(c) 2006-2007, Ext JS, LLC.
40231 * Originally Released Under LGPL - original licence link has changed is not relivant.
40234 * <script type="text/javascript">
40237 * @class Roo.bootstrap.paenl.Content
40238 * @extends Roo.util.Observable
40239 * @children Roo.bootstrap.Component
40240 * @parent builder Roo.bootstrap.layout.Border
40241 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40242 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40243 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40244 * @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
40245 * @cfg {Boolean} closable True if the panel can be closed/removed
40246 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40247 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40248 * @cfg {Toolbar} toolbar A toolbar for this panel
40249 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40250 * @cfg {String} title The title for this panel
40251 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40252 * @cfg {String} url Calls {@link #setUrl} with this value
40253 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40254 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40255 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40256 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40257 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40258 * @cfg {Boolean} badges render the badges
40259 * @cfg {String} cls extra classes to use
40260 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40263 * Create a new ContentPanel.
40264 * @param {String/Object} config A string to set only the title or a config object
40267 Roo.bootstrap.panel.Content = function( config){
40269 this.tpl = config.tpl || false;
40271 var el = config.el;
40272 var content = config.content;
40274 if(config.autoCreate){ // xtype is available if this is called from factory
40277 this.el = Roo.get(el);
40278 if(!this.el && config && config.autoCreate){
40279 if(typeof config.autoCreate == "object"){
40280 if(!config.autoCreate.id){
40281 config.autoCreate.id = config.id||el;
40283 this.el = Roo.DomHelper.append(document.body,
40284 config.autoCreate, true);
40288 cls: (config.cls || '') +
40289 (config.background ? ' bg-' + config.background : '') +
40290 " roo-layout-inactive-content",
40293 if (config.iframe) {
40297 style : 'border: 0px',
40298 src : 'about:blank'
40304 elcfg.html = config.html;
40308 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40309 if (config.iframe) {
40310 this.iframeEl = this.el.select('iframe',true).first();
40315 this.closable = false;
40316 this.loaded = false;
40317 this.active = false;
40320 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40322 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40324 this.wrapEl = this.el; //this.el.wrap();
40326 if (config.toolbar.items) {
40327 ti = config.toolbar.items ;
40328 delete config.toolbar.items ;
40332 this.toolbar.render(this.wrapEl, 'before');
40333 for(var i =0;i < ti.length;i++) {
40334 // Roo.log(['add child', items[i]]);
40335 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40337 this.toolbar.items = nitems;
40338 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40339 delete config.toolbar;
40343 // xtype created footer. - not sure if will work as we normally have to render first..
40344 if (this.footer && !this.footer.el && this.footer.xtype) {
40345 if (!this.wrapEl) {
40346 this.wrapEl = this.el.wrap();
40349 this.footer.container = this.wrapEl.createChild();
40351 this.footer = Roo.factory(this.footer, Roo);
40356 if(typeof config == "string"){
40357 this.title = config;
40359 Roo.apply(this, config);
40363 this.resizeEl = Roo.get(this.resizeEl, true);
40365 this.resizeEl = this.el;
40367 // handle view.xtype
40375 * Fires when this panel is activated.
40376 * @param {Roo.ContentPanel} this
40380 * @event deactivate
40381 * Fires when this panel is activated.
40382 * @param {Roo.ContentPanel} this
40384 "deactivate" : true,
40388 * Fires when this panel is resized if fitToFrame is true.
40389 * @param {Roo.ContentPanel} this
40390 * @param {Number} width The width after any component adjustments
40391 * @param {Number} height The height after any component adjustments
40397 * Fires when this tab is created
40398 * @param {Roo.ContentPanel} this
40404 * Fires when this content is scrolled
40405 * @param {Roo.ContentPanel} this
40406 * @param {Event} scrollEvent
40417 if(this.autoScroll && !this.iframe){
40418 this.resizeEl.setStyle("overflow", "auto");
40419 this.resizeEl.on('scroll', this.onScroll, this);
40421 // fix randome scrolling
40422 //this.el.on('scroll', function() {
40423 // Roo.log('fix random scolling');
40424 // this.scrollTo('top',0);
40427 content = content || this.content;
40429 this.setContent(content);
40431 if(config && config.url){
40432 this.setUrl(this.url, this.params, this.loadOnce);
40437 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40439 if (this.view && typeof(this.view.xtype) != 'undefined') {
40440 this.view.el = this.el.appendChild(document.createElement("div"));
40441 this.view = Roo.factory(this.view);
40442 this.view.render && this.view.render(false, '');
40446 this.fireEvent('render', this);
40449 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40459 /* Resize Element - use this to work out scroll etc. */
40462 setRegion : function(region){
40463 this.region = region;
40464 this.setActiveClass(region && !this.background);
40468 setActiveClass: function(state)
40471 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40472 this.el.setStyle('position','relative');
40474 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40475 this.el.setStyle('position', 'absolute');
40480 * Returns the toolbar for this Panel if one was configured.
40481 * @return {Roo.Toolbar}
40483 getToolbar : function(){
40484 return this.toolbar;
40487 setActiveState : function(active)
40489 this.active = active;
40490 this.setActiveClass(active);
40492 if(this.fireEvent("deactivate", this) === false){
40497 this.fireEvent("activate", this);
40501 * Updates this panel's element (not for iframe)
40502 * @param {String} content The new content
40503 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40505 setContent : function(content, loadScripts){
40510 this.el.update(content, loadScripts);
40513 ignoreResize : function(w, h)
40515 return false; // always resize?
40516 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40519 this.lastSize = {width: w, height: h};
40524 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40525 * @return {Roo.UpdateManager} The UpdateManager
40527 getUpdateManager : function(){
40531 return this.el.getUpdateManager();
40534 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40535 * Does not work with IFRAME contents
40536 * @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:
40539 url: "your-url.php",
40540 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40541 callback: yourFunction,
40542 scope: yourObject, //(optional scope)
40545 text: "Loading...",
40551 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40552 * 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.
40553 * @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}
40554 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40555 * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
40556 * @return {Roo.ContentPanel} this
40564 var um = this.el.getUpdateManager();
40565 um.update.apply(um, arguments);
40571 * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
40572 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40573 * @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)
40574 * @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)
40575 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40577 setUrl : function(url, params, loadOnce){
40579 this.iframeEl.dom.src = url;
40583 if(this.refreshDelegate){
40584 this.removeListener("activate", this.refreshDelegate);
40586 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40587 this.on("activate", this.refreshDelegate);
40588 return this.el.getUpdateManager();
40591 _handleRefresh : function(url, params, loadOnce){
40592 if(!loadOnce || !this.loaded){
40593 var updater = this.el.getUpdateManager();
40594 updater.update(url, params, this._setLoaded.createDelegate(this));
40598 _setLoaded : function(){
40599 this.loaded = true;
40603 * Returns this panel's id
40606 getId : function(){
40611 * Returns this panel's element - used by regiosn to add.
40612 * @return {Roo.Element}
40614 getEl : function(){
40615 return this.wrapEl || this.el;
40620 adjustForComponents : function(width, height)
40622 //Roo.log('adjustForComponents ');
40623 if(this.resizeEl != this.el){
40624 width -= this.el.getFrameWidth('lr');
40625 height -= this.el.getFrameWidth('tb');
40628 var te = this.toolbar.getEl();
40629 te.setWidth(width);
40630 height -= te.getHeight();
40633 var te = this.footer.getEl();
40634 te.setWidth(width);
40635 height -= te.getHeight();
40639 if(this.adjustments){
40640 width += this.adjustments[0];
40641 height += this.adjustments[1];
40643 return {"width": width, "height": height};
40646 setSize : function(width, height){
40647 if(this.fitToFrame && !this.ignoreResize(width, height)){
40648 if(this.fitContainer && this.resizeEl != this.el){
40649 this.el.setSize(width, height);
40651 var size = this.adjustForComponents(width, height);
40653 this.iframeEl.setSize(width,height);
40656 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40657 this.fireEvent('resize', this, size.width, size.height);
40664 * Returns this panel's title
40667 getTitle : function(){
40669 if (typeof(this.title) != 'object') {
40674 for (var k in this.title) {
40675 if (!this.title.hasOwnProperty(k)) {
40679 if (k.indexOf('-') >= 0) {
40680 var s = k.split('-');
40681 for (var i = 0; i<s.length; i++) {
40682 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40685 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40692 * Set this panel's title
40693 * @param {String} title
40695 setTitle : function(title){
40696 this.title = title;
40698 this.region.updatePanelTitle(this, title);
40703 * Returns true is this panel was configured to be closable
40704 * @return {Boolean}
40706 isClosable : function(){
40707 return this.closable;
40710 beforeSlide : function(){
40712 this.resizeEl.clip();
40715 afterSlide : function(){
40717 this.resizeEl.unclip();
40721 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40722 * Will fail silently if the {@link #setUrl} method has not been called.
40723 * This does not activate the panel, just updates its content.
40725 refresh : function(){
40726 if(this.refreshDelegate){
40727 this.loaded = false;
40728 this.refreshDelegate();
40733 * Destroys this panel
40735 destroy : function(){
40736 this.el.removeAllListeners();
40737 var tempEl = document.createElement("span");
40738 tempEl.appendChild(this.el.dom);
40739 tempEl.innerHTML = "";
40745 * form - if the content panel contains a form - this is a reference to it.
40746 * @type {Roo.form.Form}
40750 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40751 * This contains a reference to it.
40757 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40767 * @param {Object} cfg Xtype definition of item to add.
40771 getChildContainer: function () {
40772 return this.getEl();
40776 onScroll : function(e)
40778 this.fireEvent('scroll', this, e);
40783 var ret = new Roo.factory(cfg);
40788 if (cfg.xtype.match(/^Form$/)) {
40791 //if (this.footer) {
40792 // el = this.footer.container.insertSibling(false, 'before');
40794 el = this.el.createChild();
40797 this.form = new Roo.form.Form(cfg);
40800 if ( this.form.allItems.length) {
40801 this.form.render(el.dom);
40805 // should only have one of theses..
40806 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40807 // views.. should not be just added - used named prop 'view''
40809 cfg.el = this.el.appendChild(document.createElement("div"));
40812 var ret = new Roo.factory(cfg);
40814 ret.render && ret.render(false, ''); // render blank..
40824 * @class Roo.bootstrap.panel.Grid
40825 * @extends Roo.bootstrap.panel.Content
40827 * Create a new GridPanel.
40828 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40829 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40830 * @param {Object} config A the config object
40836 Roo.bootstrap.panel.Grid = function(config)
40840 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40841 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40843 config.el = this.wrapper;
40844 //this.el = this.wrapper;
40846 if (config.container) {
40847 // ctor'ed from a Border/panel.grid
40850 this.wrapper.setStyle("overflow", "hidden");
40851 this.wrapper.addClass('roo-grid-container');
40856 if(config.toolbar){
40857 var tool_el = this.wrapper.createChild();
40858 this.toolbar = Roo.factory(config.toolbar);
40860 if (config.toolbar.items) {
40861 ti = config.toolbar.items ;
40862 delete config.toolbar.items ;
40866 this.toolbar.render(tool_el);
40867 for(var i =0;i < ti.length;i++) {
40868 // Roo.log(['add child', items[i]]);
40869 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40871 this.toolbar.items = nitems;
40873 delete config.toolbar;
40876 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40877 config.grid.scrollBody = true;;
40878 config.grid.monitorWindowResize = false; // turn off autosizing
40879 config.grid.autoHeight = false;
40880 config.grid.autoWidth = false;
40882 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40884 if (config.background) {
40885 // render grid on panel activation (if panel background)
40886 this.on('activate', function(gp) {
40887 if (!gp.grid.rendered) {
40888 gp.grid.render(this.wrapper);
40889 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40894 this.grid.render(this.wrapper);
40895 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40898 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40899 // ??? needed ??? config.el = this.wrapper;
40904 // xtype created footer. - not sure if will work as we normally have to render first..
40905 if (this.footer && !this.footer.el && this.footer.xtype) {
40907 var ctr = this.grid.getView().getFooterPanel(true);
40908 this.footer.dataSource = this.grid.dataSource;
40909 this.footer = Roo.factory(this.footer, Roo);
40910 this.footer.render(ctr);
40920 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40923 is_resizing : false,
40925 getId : function(){
40926 return this.grid.id;
40930 * Returns the grid for this panel
40931 * @return {Roo.bootstrap.Table}
40933 getGrid : function(){
40937 setSize : function(width, height)
40939 if (this.is_resizing) {
40943 this.is_resizing = true;
40944 if(!this.ignoreResize(width, height)){
40945 var grid = this.grid;
40946 var size = this.adjustForComponents(width, height);
40947 // tfoot is not a footer?
40950 var gridel = grid.getGridEl();
40951 gridel.setSize(size.width, size.height);
40953 var tbd = grid.getGridEl().select('tbody', true).first();
40954 var thd = grid.getGridEl().select('thead',true).first();
40955 var tbf= grid.getGridEl().select('tfoot', true).first();
40958 size.height -= tbf.getHeight();
40961 size.height -= thd.getHeight();
40964 tbd.setSize(size.width, size.height );
40965 // this is for the account management tab -seems to work there.
40966 var thd = grid.getGridEl().select('thead',true).first();
40968 // tbd.setSize(size.width, size.height - thd.getHeight());
40973 this.is_resizing = false;
40978 beforeSlide : function(){
40979 this.grid.getView().scroller.clip();
40982 afterSlide : function(){
40983 this.grid.getView().scroller.unclip();
40986 destroy : function(){
40987 this.grid.destroy();
40989 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40994 * @class Roo.bootstrap.panel.Nest
40995 * @extends Roo.bootstrap.panel.Content
40997 * Create a new Panel, that can contain a layout.Border.
41000 * @param {String/Object} config A string to set only the title or a config object
41002 Roo.bootstrap.panel.Nest = function(config)
41004 // construct with only one argument..
41005 /* FIXME - implement nicer consturctors
41006 if (layout.layout) {
41008 layout = config.layout;
41009 delete config.layout;
41011 if (layout.xtype && !layout.getEl) {
41012 // then layout needs constructing..
41013 layout = Roo.factory(layout, Roo);
41017 config.el = config.layout.getEl();
41019 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41021 config.layout.monitorWindowResize = false; // turn off autosizing
41022 this.layout = config.layout;
41023 this.layout.getEl().addClass("roo-layout-nested-layout");
41024 this.layout.parent = this;
41031 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41033 * @cfg {Roo.BorderLayout} layout The layout for this panel
41037 setSize : function(width, height){
41038 if(!this.ignoreResize(width, height)){
41039 var size = this.adjustForComponents(width, height);
41040 var el = this.layout.getEl();
41041 if (size.height < 1) {
41042 el.setWidth(size.width);
41044 el.setSize(size.width, size.height);
41046 var touch = el.dom.offsetWidth;
41047 this.layout.layout();
41048 // ie requires a double layout on the first pass
41049 if(Roo.isIE && !this.initialized){
41050 this.initialized = true;
41051 this.layout.layout();
41056 // activate all subpanels if not currently active..
41058 setActiveState : function(active){
41059 this.active = active;
41060 this.setActiveClass(active);
41063 this.fireEvent("deactivate", this);
41067 this.fireEvent("activate", this);
41068 // not sure if this should happen before or after..
41069 if (!this.layout) {
41070 return; // should not happen..
41073 for (var r in this.layout.regions) {
41074 reg = this.layout.getRegion(r);
41075 if (reg.getActivePanel()) {
41076 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41077 reg.setActivePanel(reg.getActivePanel());
41080 if (!reg.panels.length) {
41083 reg.showPanel(reg.getPanel(0));
41092 * Returns the nested BorderLayout for this panel
41093 * @return {Roo.BorderLayout}
41095 getLayout : function(){
41096 return this.layout;
41100 * Adds a xtype elements to the layout of the nested panel
41104 xtype : 'ContentPanel',
41111 xtype : 'NestedLayoutPanel',
41117 items : [ ... list of content panels or nested layout panels.. ]
41121 * @param {Object} cfg Xtype definition of item to add.
41123 addxtype : function(cfg) {
41124 return this.layout.addxtype(cfg);
41129 * Ext JS Library 1.1.1
41130 * Copyright(c) 2006-2007, Ext JS, LLC.
41132 * Originally Released Under LGPL - original licence link has changed is not relivant.
41135 * <script type="text/javascript">
41138 * @class Roo.TabPanel
41139 * @extends Roo.util.Observable
41140 * A lightweight tab container.
41144 // basic tabs 1, built from existing content
41145 var tabs = new Roo.TabPanel("tabs1");
41146 tabs.addTab("script", "View Script");
41147 tabs.addTab("markup", "View Markup");
41148 tabs.activate("script");
41150 // more advanced tabs, built from javascript
41151 var jtabs = new Roo.TabPanel("jtabs");
41152 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41154 // set up the UpdateManager
41155 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41156 var updater = tab2.getUpdateManager();
41157 updater.setDefaultUrl("ajax1.htm");
41158 tab2.on('activate', updater.refresh, updater, true);
41160 // Use setUrl for Ajax loading
41161 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41162 tab3.setUrl("ajax2.htm", null, true);
41165 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41168 jtabs.activate("jtabs-1");
41171 * Create a new TabPanel.
41172 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41173 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41175 Roo.bootstrap.panel.Tabs = function(config){
41177 * The container element for this TabPanel.
41178 * @type Roo.Element
41180 this.el = Roo.get(config.el);
41183 if(typeof config == "boolean"){
41184 this.tabPosition = config ? "bottom" : "top";
41186 Roo.apply(this, config);
41190 if(this.tabPosition == "bottom"){
41191 // if tabs are at the bottom = create the body first.
41192 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41193 this.el.addClass("roo-tabs-bottom");
41195 // next create the tabs holders
41197 if (this.tabPosition == "west"){
41199 var reg = this.region; // fake it..
41201 if (!reg.mgr.parent) {
41204 reg = reg.mgr.parent.region;
41206 Roo.log("got nest?");
41208 if (reg.mgr.getRegion('west')) {
41209 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41210 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41211 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41212 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41213 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41221 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41222 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41223 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41224 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41229 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41232 // finally - if tabs are at the top, then create the body last..
41233 if(this.tabPosition != "bottom"){
41234 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41235 * @type Roo.Element
41237 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41238 this.el.addClass("roo-tabs-top");
41242 this.bodyEl.setStyle("position", "relative");
41244 this.active = null;
41245 this.activateDelegate = this.activate.createDelegate(this);
41250 * Fires when the active tab changes
41251 * @param {Roo.TabPanel} this
41252 * @param {Roo.TabPanelItem} activePanel The new active tab
41256 * @event beforetabchange
41257 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41258 * @param {Roo.TabPanel} this
41259 * @param {Object} e Set cancel to true on this object to cancel the tab change
41260 * @param {Roo.TabPanelItem} tab The tab being changed to
41262 "beforetabchange" : true
41265 Roo.EventManager.onWindowResize(this.onResize, this);
41266 this.cpad = this.el.getPadding("lr");
41267 this.hiddenCount = 0;
41270 // toolbar on the tabbar support...
41271 if (this.toolbar) {
41272 alert("no toolbar support yet");
41273 this.toolbar = false;
41275 var tcfg = this.toolbar;
41276 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41277 this.toolbar = new Roo.Toolbar(tcfg);
41278 if (Roo.isSafari) {
41279 var tbl = tcfg.container.child('table', true);
41280 tbl.setAttribute('width', '100%');
41288 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41291 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41293 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41295 tabPosition : "top",
41297 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41299 currentTabWidth : 0,
41301 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41305 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41309 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41311 preferredTabWidth : 175,
41313 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41315 resizeTabs : false,
41317 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41319 monitorResize : true,
41321 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41323 toolbar : false, // set by caller..
41325 region : false, /// set by caller
41327 disableTooltips : true, // not used yet...
41330 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41331 * @param {String} id The id of the div to use <b>or create</b>
41332 * @param {String} text The text for the tab
41333 * @param {String} content (optional) Content to put in the TabPanelItem body
41334 * @param {Boolean} closable (optional) True to create a close icon on the tab
41335 * @return {Roo.TabPanelItem} The created TabPanelItem
41337 addTab : function(id, text, content, closable, tpl)
41339 var item = new Roo.bootstrap.panel.TabItem({
41343 closable : closable,
41346 this.addTabItem(item);
41348 item.setContent(content);
41354 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41355 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41356 * @return {Roo.TabPanelItem}
41358 getTab : function(id){
41359 return this.items[id];
41363 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41364 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41366 hideTab : function(id){
41367 var t = this.items[id];
41370 this.hiddenCount++;
41371 this.autoSizeTabs();
41376 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41377 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41379 unhideTab : function(id){
41380 var t = this.items[id];
41382 t.setHidden(false);
41383 this.hiddenCount--;
41384 this.autoSizeTabs();
41389 * Adds an existing {@link Roo.TabPanelItem}.
41390 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41392 addTabItem : function(item)
41394 this.items[item.id] = item;
41395 this.items.push(item);
41396 this.autoSizeTabs();
41397 // if(this.resizeTabs){
41398 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41399 // this.autoSizeTabs();
41401 // item.autoSize();
41406 * Removes a {@link Roo.TabPanelItem}.
41407 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41409 removeTab : function(id){
41410 var items = this.items;
41411 var tab = items[id];
41412 if(!tab) { return; }
41413 var index = items.indexOf(tab);
41414 if(this.active == tab && items.length > 1){
41415 var newTab = this.getNextAvailable(index);
41420 this.stripEl.dom.removeChild(tab.pnode.dom);
41421 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41422 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41424 items.splice(index, 1);
41425 delete this.items[tab.id];
41426 tab.fireEvent("close", tab);
41427 tab.purgeListeners();
41428 this.autoSizeTabs();
41431 getNextAvailable : function(start){
41432 var items = this.items;
41434 // look for a next tab that will slide over to
41435 // replace the one being removed
41436 while(index < items.length){
41437 var item = items[++index];
41438 if(item && !item.isHidden()){
41442 // if one isn't found select the previous tab (on the left)
41445 var item = items[--index];
41446 if(item && !item.isHidden()){
41454 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41455 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41457 disableTab : function(id){
41458 var tab = this.items[id];
41459 if(tab && this.active != tab){
41465 * Enables a {@link Roo.TabPanelItem} that is disabled.
41466 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41468 enableTab : function(id){
41469 var tab = this.items[id];
41474 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41475 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41476 * @return {Roo.TabPanelItem} The TabPanelItem.
41478 activate : function(id)
41480 //Roo.log('activite:' + id);
41482 var tab = this.items[id];
41486 if(tab == this.active || tab.disabled){
41490 this.fireEvent("beforetabchange", this, e, tab);
41491 if(e.cancel !== true && !tab.disabled){
41493 this.active.hide();
41495 this.active = this.items[id];
41496 this.active.show();
41497 this.fireEvent("tabchange", this, this.active);
41503 * Gets the active {@link Roo.TabPanelItem}.
41504 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41506 getActiveTab : function(){
41507 return this.active;
41511 * Updates the tab body element to fit the height of the container element
41512 * for overflow scrolling
41513 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41515 syncHeight : function(targetHeight){
41516 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41517 var bm = this.bodyEl.getMargins();
41518 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41519 this.bodyEl.setHeight(newHeight);
41523 onResize : function(){
41524 if(this.monitorResize){
41525 this.autoSizeTabs();
41530 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41532 beginUpdate : function(){
41533 this.updating = true;
41537 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41539 endUpdate : function(){
41540 this.updating = false;
41541 this.autoSizeTabs();
41545 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41547 autoSizeTabs : function()
41549 var count = this.items.length;
41550 var vcount = count - this.hiddenCount;
41553 this.stripEl.hide();
41555 this.stripEl.show();
41558 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41563 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41564 var availWidth = Math.floor(w / vcount);
41565 var b = this.stripBody;
41566 if(b.getWidth() > w){
41567 var tabs = this.items;
41568 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41569 if(availWidth < this.minTabWidth){
41570 /*if(!this.sleft){ // incomplete scrolling code
41571 this.createScrollButtons();
41574 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41577 if(this.currentTabWidth < this.preferredTabWidth){
41578 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41584 * Returns the number of tabs in this TabPanel.
41587 getCount : function(){
41588 return this.items.length;
41592 * Resizes all the tabs to the passed width
41593 * @param {Number} The new width
41595 setTabWidth : function(width){
41596 this.currentTabWidth = width;
41597 for(var i = 0, len = this.items.length; i < len; i++) {
41598 if(!this.items[i].isHidden()) {
41599 this.items[i].setWidth(width);
41605 * Destroys this TabPanel
41606 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41608 destroy : function(removeEl){
41609 Roo.EventManager.removeResizeListener(this.onResize, this);
41610 for(var i = 0, len = this.items.length; i < len; i++){
41611 this.items[i].purgeListeners();
41613 if(removeEl === true){
41614 this.el.update("");
41619 createStrip : function(container)
41621 var strip = document.createElement("nav");
41622 strip.className = Roo.bootstrap.version == 4 ?
41623 "navbar-light bg-light" :
41624 "navbar navbar-default"; //"x-tabs-wrap";
41625 container.appendChild(strip);
41629 createStripList : function(strip)
41631 // div wrapper for retard IE
41632 // returns the "tr" element.
41633 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41634 //'<div class="x-tabs-strip-wrap">'+
41635 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41636 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41637 return strip.firstChild; //.firstChild.firstChild.firstChild;
41639 createBody : function(container)
41641 var body = document.createElement("div");
41642 Roo.id(body, "tab-body");
41643 //Roo.fly(body).addClass("x-tabs-body");
41644 Roo.fly(body).addClass("tab-content");
41645 container.appendChild(body);
41648 createItemBody :function(bodyEl, id){
41649 var body = Roo.getDom(id);
41651 body = document.createElement("div");
41654 //Roo.fly(body).addClass("x-tabs-item-body");
41655 Roo.fly(body).addClass("tab-pane");
41656 bodyEl.insertBefore(body, bodyEl.firstChild);
41660 createStripElements : function(stripEl, text, closable, tpl)
41662 var td = document.createElement("li"); // was td..
41663 td.className = 'nav-item';
41665 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41668 stripEl.appendChild(td);
41670 td.className = "x-tabs-closable";
41671 if(!this.closeTpl){
41672 this.closeTpl = new Roo.Template(
41673 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41674 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41675 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41678 var el = this.closeTpl.overwrite(td, {"text": text});
41679 var close = el.getElementsByTagName("div")[0];
41680 var inner = el.getElementsByTagName("em")[0];
41681 return {"el": el, "close": close, "inner": inner};
41684 // not sure what this is..
41685 // if(!this.tabTpl){
41686 //this.tabTpl = new Roo.Template(
41687 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41688 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41690 // this.tabTpl = new Roo.Template(
41691 // '<a href="#">' +
41692 // '<span unselectable="on"' +
41693 // (this.disableTooltips ? '' : ' title="{text}"') +
41694 // ' >{text}</span></a>'
41700 var template = tpl || this.tabTpl || false;
41703 template = new Roo.Template(
41704 Roo.bootstrap.version == 4 ?
41706 '<a class="nav-link" href="#" unselectable="on"' +
41707 (this.disableTooltips ? '' : ' title="{text}"') +
41710 '<a class="nav-link" href="#">' +
41711 '<span unselectable="on"' +
41712 (this.disableTooltips ? '' : ' title="{text}"') +
41713 ' >{text}</span></a>'
41718 switch (typeof(template)) {
41722 template = new Roo.Template(template);
41728 var el = template.overwrite(td, {"text": text});
41730 var inner = el.getElementsByTagName("span")[0];
41732 return {"el": el, "inner": inner};
41740 * @class Roo.TabPanelItem
41741 * @extends Roo.util.Observable
41742 * Represents an individual item (tab plus body) in a TabPanel.
41743 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41744 * @param {String} id The id of this TabPanelItem
41745 * @param {String} text The text for the tab of this TabPanelItem
41746 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41748 Roo.bootstrap.panel.TabItem = function(config){
41750 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41751 * @type Roo.TabPanel
41753 this.tabPanel = config.panel;
41755 * The id for this TabPanelItem
41758 this.id = config.id;
41760 this.disabled = false;
41762 this.text = config.text;
41764 this.loaded = false;
41765 this.closable = config.closable;
41768 * The body element for this TabPanelItem.
41769 * @type Roo.Element
41771 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41772 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41773 this.bodyEl.setStyle("display", "block");
41774 this.bodyEl.setStyle("zoom", "1");
41775 //this.hideAction();
41777 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41779 this.el = Roo.get(els.el);
41780 this.inner = Roo.get(els.inner, true);
41781 this.textEl = Roo.bootstrap.version == 4 ?
41782 this.el : Roo.get(this.el.dom.firstChild, true);
41784 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41785 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41788 // this.el.on("mousedown", this.onTabMouseDown, this);
41789 this.el.on("click", this.onTabClick, this);
41791 if(config.closable){
41792 var c = Roo.get(els.close, true);
41793 c.dom.title = this.closeText;
41794 c.addClassOnOver("close-over");
41795 c.on("click", this.closeClick, this);
41801 * Fires when this tab becomes the active tab.
41802 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41803 * @param {Roo.TabPanelItem} this
41807 * @event beforeclose
41808 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41809 * @param {Roo.TabPanelItem} this
41810 * @param {Object} e Set cancel to true on this object to cancel the close.
41812 "beforeclose": true,
41815 * Fires when this tab is closed.
41816 * @param {Roo.TabPanelItem} this
41820 * @event deactivate
41821 * Fires when this tab is no longer the active tab.
41822 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41823 * @param {Roo.TabPanelItem} this
41825 "deactivate" : true
41827 this.hidden = false;
41829 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41832 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41834 purgeListeners : function(){
41835 Roo.util.Observable.prototype.purgeListeners.call(this);
41836 this.el.removeAllListeners();
41839 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41842 this.status_node.addClass("active");
41845 this.tabPanel.stripWrap.repaint();
41847 this.fireEvent("activate", this.tabPanel, this);
41851 * Returns true if this tab is the active tab.
41852 * @return {Boolean}
41854 isActive : function(){
41855 return this.tabPanel.getActiveTab() == this;
41859 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41862 this.status_node.removeClass("active");
41864 this.fireEvent("deactivate", this.tabPanel, this);
41867 hideAction : function(){
41868 this.bodyEl.hide();
41869 this.bodyEl.setStyle("position", "absolute");
41870 this.bodyEl.setLeft("-20000px");
41871 this.bodyEl.setTop("-20000px");
41874 showAction : function(){
41875 this.bodyEl.setStyle("position", "relative");
41876 this.bodyEl.setTop("");
41877 this.bodyEl.setLeft("");
41878 this.bodyEl.show();
41882 * Set the tooltip for the tab.
41883 * @param {String} tooltip The tab's tooltip
41885 setTooltip : function(text){
41886 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41887 this.textEl.dom.qtip = text;
41888 this.textEl.dom.removeAttribute('title');
41890 this.textEl.dom.title = text;
41894 onTabClick : function(e){
41895 e.preventDefault();
41896 this.tabPanel.activate(this.id);
41899 onTabMouseDown : function(e){
41900 e.preventDefault();
41901 this.tabPanel.activate(this.id);
41904 getWidth : function(){
41905 return this.inner.getWidth();
41908 setWidth : function(width){
41909 var iwidth = width - this.linode.getPadding("lr");
41910 this.inner.setWidth(iwidth);
41911 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41912 this.linode.setWidth(width);
41916 * Show or hide the tab
41917 * @param {Boolean} hidden True to hide or false to show.
41919 setHidden : function(hidden){
41920 this.hidden = hidden;
41921 this.linode.setStyle("display", hidden ? "none" : "");
41925 * Returns true if this tab is "hidden"
41926 * @return {Boolean}
41928 isHidden : function(){
41929 return this.hidden;
41933 * Returns the text for this tab
41936 getText : function(){
41940 autoSize : function(){
41941 //this.el.beginMeasure();
41942 this.textEl.setWidth(1);
41944 * #2804 [new] Tabs in Roojs
41945 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41947 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41948 //this.el.endMeasure();
41952 * Sets the text for the tab (Note: this also sets the tooltip text)
41953 * @param {String} text The tab's text and tooltip
41955 setText : function(text){
41957 this.textEl.update(text);
41958 this.setTooltip(text);
41959 //if(!this.tabPanel.resizeTabs){
41960 // this.autoSize();
41964 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41966 activate : function(){
41967 this.tabPanel.activate(this.id);
41971 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41973 disable : function(){
41974 if(this.tabPanel.active != this){
41975 this.disabled = true;
41976 this.status_node.addClass("disabled");
41981 * Enables this TabPanelItem if it was previously disabled.
41983 enable : function(){
41984 this.disabled = false;
41985 this.status_node.removeClass("disabled");
41989 * Sets the content for this TabPanelItem.
41990 * @param {String} content The content
41991 * @param {Boolean} loadScripts true to look for and load scripts
41993 setContent : function(content, loadScripts){
41994 this.bodyEl.update(content, loadScripts);
41998 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41999 * @return {Roo.UpdateManager} The UpdateManager
42001 getUpdateManager : function(){
42002 return this.bodyEl.getUpdateManager();
42006 * Set a URL to be used to load the content for this TabPanelItem.
42007 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42008 * @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)
42009 * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
42010 * @return {Roo.UpdateManager} The UpdateManager
42012 setUrl : function(url, params, loadOnce){
42013 if(this.refreshDelegate){
42014 this.un('activate', this.refreshDelegate);
42016 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42017 this.on("activate", this.refreshDelegate);
42018 return this.bodyEl.getUpdateManager();
42022 _handleRefresh : function(url, params, loadOnce){
42023 if(!loadOnce || !this.loaded){
42024 var updater = this.bodyEl.getUpdateManager();
42025 updater.update(url, params, this._setLoaded.createDelegate(this));
42030 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42031 * Will fail silently if the setUrl method has not been called.
42032 * This does not activate the panel, just updates its content.
42034 refresh : function(){
42035 if(this.refreshDelegate){
42036 this.loaded = false;
42037 this.refreshDelegate();
42042 _setLoaded : function(){
42043 this.loaded = true;
42047 closeClick : function(e){
42050 this.fireEvent("beforeclose", this, o);
42051 if(o.cancel !== true){
42052 this.tabPanel.removeTab(this.id);
42056 * The text displayed in the tooltip for the close icon.
42059 closeText : "Close this tab"
42062 * This script refer to:
42063 * Title: International Telephone Input
42064 * Author: Jack O'Connor
42065 * Code version: v12.1.12
42066 * Availability: https://github.com/jackocnr/intl-tel-input.git
42069 Roo.bootstrap.form.PhoneInputData = function() {
42072 "Afghanistan (افغانستان)",
42077 "Albania (Shqipëri)",
42082 "Algeria (الجزائر)",
42107 "Antigua and Barbuda",
42117 "Armenia (Հայաստան)",
42133 "Austria (Österreich)",
42138 "Azerbaijan (Azərbaycan)",
42148 "Bahrain (البحرين)",
42153 "Bangladesh (বাংলাদেশ)",
42163 "Belarus (Беларусь)",
42168 "Belgium (België)",
42198 "Bosnia and Herzegovina (Босна и Херцеговина)",
42213 "British Indian Ocean Territory",
42218 "British Virgin Islands",
42228 "Bulgaria (България)",
42238 "Burundi (Uburundi)",
42243 "Cambodia (កម្ពុជា)",
42248 "Cameroon (Cameroun)",
42257 ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
42260 "Cape Verde (Kabu Verdi)",
42265 "Caribbean Netherlands",
42276 "Central African Republic (République centrafricaine)",
42296 "Christmas Island",
42302 "Cocos (Keeling) Islands",
42313 "Comoros (جزر القمر)",
42318 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42323 "Congo (Republic) (Congo-Brazzaville)",
42343 "Croatia (Hrvatska)",
42364 "Czech Republic (Česká republika)",
42369 "Denmark (Danmark)",
42384 "Dominican Republic (República Dominicana)",
42388 ["809", "829", "849"]
42406 "Equatorial Guinea (Guinea Ecuatorial)",
42426 "Falkland Islands (Islas Malvinas)",
42431 "Faroe Islands (Føroyar)",
42452 "French Guiana (Guyane française)",
42457 "French Polynesia (Polynésie française)",
42472 "Georgia (საქართველო)",
42477 "Germany (Deutschland)",
42497 "Greenland (Kalaallit Nunaat)",
42534 "Guinea-Bissau (Guiné Bissau)",
42559 "Hungary (Magyarország)",
42564 "Iceland (Ísland)",
42584 "Iraq (العراق)",
42600 "Israel (ישראל)",
42627 "Jordan (الأردن)",
42632 "Kazakhstan (Казахстан)",
42653 "Kuwait (الكويت)",
42658 "Kyrgyzstan (Кыргызстан)",
42668 "Latvia (Latvija)",
42673 "Lebanon (لبنان)",
42688 "Libya (ليبيا)",
42698 "Lithuania (Lietuva)",
42713 "Macedonia (FYROM) (Македонија)",
42718 "Madagascar (Madagasikara)",
42748 "Marshall Islands",
42758 "Mauritania (موريتانيا)",
42763 "Mauritius (Moris)",
42784 "Moldova (Republica Moldova)",
42794 "Mongolia (Монгол)",
42799 "Montenegro (Crna Gora)",
42809 "Morocco (المغرب)",
42815 "Mozambique (Moçambique)",
42820 "Myanmar (Burma) (မြန်မာ)",
42825 "Namibia (Namibië)",
42840 "Netherlands (Nederland)",
42845 "New Caledonia (Nouvelle-Calédonie)",
42880 "North Korea (조선 민주주의 인민 공화국)",
42885 "Northern Mariana Islands",
42901 "Pakistan (پاکستان)",
42911 "Palestine (فلسطين)",
42921 "Papua New Guinea",
42963 "Réunion (La Réunion)",
42969 "Romania (România)",
42985 "Saint Barthélemy",
42996 "Saint Kitts and Nevis",
43006 "Saint Martin (Saint-Martin (partie française))",
43012 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43017 "Saint Vincent and the Grenadines",
43032 "São Tomé and Príncipe (São Tomé e Príncipe)",
43037 "Saudi Arabia (المملكة العربية السعودية)",
43042 "Senegal (Sénégal)",
43072 "Slovakia (Slovensko)",
43077 "Slovenia (Slovenija)",
43087 "Somalia (Soomaaliya)",
43097 "South Korea (대한민국)",
43102 "South Sudan (جنوب السودان)",
43112 "Sri Lanka (ශ්රී ලංකාව)",
43117 "Sudan (السودان)",
43127 "Svalbard and Jan Mayen",
43138 "Sweden (Sverige)",
43143 "Switzerland (Schweiz)",
43148 "Syria (سوريا)",
43193 "Trinidad and Tobago",
43198 "Tunisia (تونس)",
43203 "Turkey (Türkiye)",
43213 "Turks and Caicos Islands",
43223 "U.S. Virgin Islands",
43233 "Ukraine (Україна)",
43238 "United Arab Emirates (الإمارات العربية المتحدة)",
43260 "Uzbekistan (Oʻzbekiston)",
43270 "Vatican City (Città del Vaticano)",
43281 "Vietnam (Việt Nam)",
43286 "Wallis and Futuna (Wallis-et-Futuna)",
43291 "Western Sahara (الصحراء الغربية)",
43297 "Yemen (اليمن)",
43321 * This script refer to:
43322 * Title: International Telephone Input
43323 * Author: Jack O'Connor
43324 * Code version: v12.1.12
43325 * Availability: https://github.com/jackocnr/intl-tel-input.git
43329 * @class Roo.bootstrap.form.PhoneInput
43330 * @extends Roo.bootstrap.form.TriggerField
43331 * An input with International dial-code selection
43333 * @cfg {String} defaultDialCode default '+852'
43334 * @cfg {Array} preferedCountries default []
43337 * Create a new PhoneInput.
43338 * @param {Object} config Configuration options
43341 Roo.bootstrap.form.PhoneInput = function(config) {
43342 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43345 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43347 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43349 listWidth: undefined,
43351 selectedClass: 'active',
43353 invalidClass : "has-warning",
43355 validClass: 'has-success',
43357 allowed: '0123456789',
43362 * @cfg {String} defaultDialCode The default dial code when initializing the input
43364 defaultDialCode: '+852',
43367 * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
43369 preferedCountries: false,
43371 getAutoCreate : function()
43373 var data = Roo.bootstrap.form.PhoneInputData();
43374 var align = this.labelAlign || this.parentLabelAlign();
43377 this.allCountries = [];
43378 this.dialCodeMapping = [];
43380 for (var i = 0; i < data.length; i++) {
43382 this.allCountries[i] = {
43386 priority: c[3] || 0,
43387 areaCodes: c[4] || null
43389 this.dialCodeMapping[c[2]] = {
43392 priority: c[3] || 0,
43393 areaCodes: c[4] || null
43405 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43406 maxlength: this.max_length,
43407 cls : 'form-control tel-input',
43408 autocomplete: 'new-password'
43411 var hiddenInput = {
43414 cls: 'hidden-tel-input'
43418 hiddenInput.name = this.name;
43421 if (this.disabled) {
43422 input.disabled = true;
43425 var flag_container = {
43442 cls: this.hasFeedback ? 'has-feedback' : '',
43448 cls: 'dial-code-holder',
43455 cls: 'roo-select2-container input-group',
43462 if (this.fieldLabel.length) {
43465 tooltip: 'This field is required'
43471 cls: 'control-label',
43477 html: this.fieldLabel
43480 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43486 if(this.indicatorpos == 'right') {
43487 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43494 if(align == 'left') {
43502 if(this.labelWidth > 12){
43503 label.style = "width: " + this.labelWidth + 'px';
43505 if(this.labelWidth < 13 && this.labelmd == 0){
43506 this.labelmd = this.labelWidth;
43508 if(this.labellg > 0){
43509 label.cls += ' col-lg-' + this.labellg;
43510 input.cls += ' col-lg-' + (12 - this.labellg);
43512 if(this.labelmd > 0){
43513 label.cls += ' col-md-' + this.labelmd;
43514 container.cls += ' col-md-' + (12 - this.labelmd);
43516 if(this.labelsm > 0){
43517 label.cls += ' col-sm-' + this.labelsm;
43518 container.cls += ' col-sm-' + (12 - this.labelsm);
43520 if(this.labelxs > 0){
43521 label.cls += ' col-xs-' + this.labelxs;
43522 container.cls += ' col-xs-' + (12 - this.labelxs);
43532 var settings = this;
43534 ['xs','sm','md','lg'].map(function(size){
43535 if (settings[size]) {
43536 cfg.cls += ' col-' + size + '-' + settings[size];
43540 this.store = new Roo.data.Store({
43541 proxy : new Roo.data.MemoryProxy({}),
43542 reader : new Roo.data.JsonReader({
43553 'name' : 'dialCode',
43557 'name' : 'priority',
43561 'name' : 'areaCodes',
43568 if(!this.preferedCountries) {
43569 this.preferedCountries = [
43576 var p = this.preferedCountries.reverse();
43579 for (var i = 0; i < p.length; i++) {
43580 for (var j = 0; j < this.allCountries.length; j++) {
43581 if(this.allCountries[j].iso2 == p[i]) {
43582 var t = this.allCountries[j];
43583 this.allCountries.splice(j,1);
43584 this.allCountries.unshift(t);
43590 this.store.proxy.data = {
43592 data: this.allCountries
43598 initEvents : function()
43601 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43603 this.indicator = this.indicatorEl();
43604 this.flag = this.flagEl();
43605 this.dialCodeHolder = this.dialCodeHolderEl();
43607 this.trigger = this.el.select('div.flag-box',true).first();
43608 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43613 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43614 _this.list.setWidth(lw);
43617 this.list.on('mouseover', this.onViewOver, this);
43618 this.list.on('mousemove', this.onViewMove, this);
43619 this.inputEl().on("keyup", this.onKeyUp, this);
43620 this.inputEl().on("keypress", this.onKeyPress, this);
43622 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43624 this.view = new Roo.View(this.list, this.tpl, {
43625 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43628 this.view.on('click', this.onViewClick, this);
43629 this.setValue(this.defaultDialCode);
43632 onTriggerClick : function(e)
43634 Roo.log('trigger click');
43639 if(this.isExpanded()){
43641 this.hasFocus = false;
43643 this.store.load({});
43644 this.hasFocus = true;
43649 isExpanded : function()
43651 return this.list.isVisible();
43654 collapse : function()
43656 if(!this.isExpanded()){
43660 Roo.get(document).un('mousedown', this.collapseIf, this);
43661 Roo.get(document).un('mousewheel', this.collapseIf, this);
43662 this.fireEvent('collapse', this);
43666 expand : function()
43670 if(this.isExpanded() || !this.hasFocus){
43674 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43675 this.list.setWidth(lw);
43678 this.restrictHeight();
43680 Roo.get(document).on('mousedown', this.collapseIf, this);
43681 Roo.get(document).on('mousewheel', this.collapseIf, this);
43683 this.fireEvent('expand', this);
43686 restrictHeight : function()
43688 this.list.alignTo(this.inputEl(), this.listAlign);
43689 this.list.alignTo(this.inputEl(), this.listAlign);
43692 onViewOver : function(e, t)
43694 if(this.inKeyMode){
43697 var item = this.view.findItemFromChild(t);
43700 var index = this.view.indexOf(item);
43701 this.select(index, false);
43706 onViewClick : function(view, doFocus, el, e)
43708 var index = this.view.getSelectedIndexes()[0];
43710 var r = this.store.getAt(index);
43713 this.onSelect(r, index);
43715 if(doFocus !== false && !this.blockFocus){
43716 this.inputEl().focus();
43720 onViewMove : function(e, t)
43722 this.inKeyMode = false;
43725 select : function(index, scrollIntoView)
43727 this.selectedIndex = index;
43728 this.view.select(index);
43729 if(scrollIntoView !== false){
43730 var el = this.view.getNode(index);
43732 this.list.scrollChildIntoView(el, false);
43737 createList : function()
43739 this.list = Roo.get(document.body).createChild({
43741 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43742 style: 'display:none'
43745 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43748 collapseIf : function(e)
43750 var in_combo = e.within(this.el);
43751 var in_list = e.within(this.list);
43752 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43754 if (in_combo || in_list || is_list) {
43760 onSelect : function(record, index)
43762 if(this.fireEvent('beforeselect', this, record, index) !== false){
43764 this.setFlagClass(record.data.iso2);
43765 this.setDialCode(record.data.dialCode);
43766 this.hasFocus = false;
43768 this.fireEvent('select', this, record, index);
43772 flagEl : function()
43774 var flag = this.el.select('div.flag',true).first();
43781 dialCodeHolderEl : function()
43783 var d = this.el.select('input.dial-code-holder',true).first();
43790 setDialCode : function(v)
43792 this.dialCodeHolder.dom.value = '+'+v;
43795 setFlagClass : function(n)
43797 this.flag.dom.className = 'flag '+n;
43800 getValue : function()
43802 var v = this.inputEl().getValue();
43803 if(this.dialCodeHolder) {
43804 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43809 setValue : function(v)
43811 var d = this.getDialCode(v);
43813 //invalid dial code
43814 if(v.length == 0 || !d || d.length == 0) {
43816 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43817 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43823 this.setFlagClass(this.dialCodeMapping[d].iso2);
43824 this.setDialCode(d);
43825 this.inputEl().dom.value = v.replace('+'+d,'');
43826 this.hiddenEl().dom.value = this.getValue();
43831 getDialCode : function(v)
43835 if (v.length == 0) {
43836 return this.dialCodeHolder.dom.value;
43840 if (v.charAt(0) != "+") {
43843 var numericChars = "";
43844 for (var i = 1; i < v.length; i++) {
43845 var c = v.charAt(i);
43848 if (this.dialCodeMapping[numericChars]) {
43849 dialCode = v.substr(1, i);
43851 if (numericChars.length == 4) {
43861 this.setValue(this.defaultDialCode);
43865 hiddenEl : function()
43867 return this.el.select('input.hidden-tel-input',true).first();
43870 // after setting val
43871 onKeyUp : function(e){
43872 this.setValue(this.getValue());
43875 onKeyPress : function(e){
43876 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43883 * @class Roo.bootstrap.form.MoneyField
43884 * @extends Roo.bootstrap.form.ComboBox
43885 * Bootstrap MoneyField class
43888 * Create a new MoneyField.
43889 * @param {Object} config Configuration options
43892 Roo.bootstrap.form.MoneyField = function(config) {
43894 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43898 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43901 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43903 allowDecimals : true,
43905 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43907 decimalSeparator : ".",
43909 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43911 decimalPrecision : 0,
43913 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43915 allowNegative : true,
43917 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43921 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43923 minValue : Number.NEGATIVE_INFINITY,
43925 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43927 maxValue : Number.MAX_VALUE,
43929 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43931 minText : "The minimum value for this field is {0}",
43933 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43935 maxText : "The maximum value for this field is {0}",
43937 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43938 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43940 nanText : "{0} is not a valid number",
43942 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43946 * @cfg {String} defaults currency of the MoneyField
43947 * value should be in lkey
43949 defaultCurrency : false,
43951 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43953 thousandsDelimiter : false,
43955 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43964 * @cfg {Roo.data.Store} store Store to lookup currency??
43968 getAutoCreate : function()
43970 var align = this.labelAlign || this.parentLabelAlign();
43982 cls : 'form-control roo-money-amount-input',
43983 autocomplete: 'new-password'
43986 var hiddenInput = {
43990 cls: 'hidden-number-input'
43993 if(this.max_length) {
43994 input.maxlength = this.max_length;
43998 hiddenInput.name = this.name;
44001 if (this.disabled) {
44002 input.disabled = true;
44005 var clg = 12 - this.inputlg;
44006 var cmd = 12 - this.inputmd;
44007 var csm = 12 - this.inputsm;
44008 var cxs = 12 - this.inputxs;
44012 cls : 'row roo-money-field',
44016 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44020 cls: 'roo-select2-container input-group',
44024 cls : 'form-control roo-money-currency-input',
44025 autocomplete: 'new-password',
44027 name : this.currencyName
44031 cls : 'input-group-addon',
44045 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44049 cls: this.hasFeedback ? 'has-feedback' : '',
44060 if (this.fieldLabel.length) {
44063 tooltip: 'This field is required'
44069 cls: 'control-label',
44075 html: this.fieldLabel
44078 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44084 if(this.indicatorpos == 'right') {
44085 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44092 if(align == 'left') {
44100 if(this.labelWidth > 12){
44101 label.style = "width: " + this.labelWidth + 'px';
44103 if(this.labelWidth < 13 && this.labelmd == 0){
44104 this.labelmd = this.labelWidth;
44106 if(this.labellg > 0){
44107 label.cls += ' col-lg-' + this.labellg;
44108 input.cls += ' col-lg-' + (12 - this.labellg);
44110 if(this.labelmd > 0){
44111 label.cls += ' col-md-' + this.labelmd;
44112 container.cls += ' col-md-' + (12 - this.labelmd);
44114 if(this.labelsm > 0){
44115 label.cls += ' col-sm-' + this.labelsm;
44116 container.cls += ' col-sm-' + (12 - this.labelsm);
44118 if(this.labelxs > 0){
44119 label.cls += ' col-xs-' + this.labelxs;
44120 container.cls += ' col-xs-' + (12 - this.labelxs);
44131 var settings = this;
44133 ['xs','sm','md','lg'].map(function(size){
44134 if (settings[size]) {
44135 cfg.cls += ' col-' + size + '-' + settings[size];
44142 initEvents : function()
44144 this.indicator = this.indicatorEl();
44146 this.initCurrencyEvent();
44148 this.initNumberEvent();
44151 initCurrencyEvent : function()
44154 throw "can not find store for combo";
44157 this.store = Roo.factory(this.store, Roo.data);
44158 this.store.parent = this;
44162 this.triggerEl = this.el.select('.input-group-addon', true).first();
44164 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44169 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44170 _this.list.setWidth(lw);
44173 this.list.on('mouseover', this.onViewOver, this);
44174 this.list.on('mousemove', this.onViewMove, this);
44175 this.list.on('scroll', this.onViewScroll, this);
44178 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44181 this.view = new Roo.View(this.list, this.tpl, {
44182 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44185 this.view.on('click', this.onViewClick, this);
44187 this.store.on('beforeload', this.onBeforeLoad, this);
44188 this.store.on('load', this.onLoad, this);
44189 this.store.on('loadexception', this.onLoadException, this);
44191 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44192 "up" : function(e){
44193 this.inKeyMode = true;
44197 "down" : function(e){
44198 if(!this.isExpanded()){
44199 this.onTriggerClick();
44201 this.inKeyMode = true;
44206 "enter" : function(e){
44209 if(this.fireEvent("specialkey", this, e)){
44210 this.onViewClick(false);
44216 "esc" : function(e){
44220 "tab" : function(e){
44223 if(this.fireEvent("specialkey", this, e)){
44224 this.onViewClick(false);
44232 doRelay : function(foo, bar, hname){
44233 if(hname == 'down' || this.scope.isExpanded()){
44234 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44242 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44246 initNumberEvent : function(e)
44248 this.inputEl().on("keydown" , this.fireKey, this);
44249 this.inputEl().on("focus", this.onFocus, this);
44250 this.inputEl().on("blur", this.onBlur, this);
44252 this.inputEl().relayEvent('keyup', this);
44254 if(this.indicator){
44255 this.indicator.addClass('invisible');
44258 this.originalValue = this.getValue();
44260 if(this.validationEvent == 'keyup'){
44261 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44262 this.inputEl().on('keyup', this.filterValidation, this);
44264 else if(this.validationEvent !== false){
44265 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44268 if(this.selectOnFocus){
44269 this.on("focus", this.preFocus, this);
44272 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44273 this.inputEl().on("keypress", this.filterKeys, this);
44275 this.inputEl().relayEvent('keypress', this);
44278 var allowed = "0123456789";
44280 if(this.allowDecimals){
44281 allowed += this.decimalSeparator;
44284 if(this.allowNegative){
44288 if(this.thousandsDelimiter) {
44292 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44294 var keyPress = function(e){
44296 var k = e.getKey();
44298 var c = e.getCharCode();
44301 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44302 allowed.indexOf(String.fromCharCode(c)) === -1
44308 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44312 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44317 this.inputEl().on("keypress", keyPress, this);
44321 onTriggerClick : function(e)
44328 this.loadNext = false;
44330 if(this.isExpanded()){
44335 this.hasFocus = true;
44337 if(this.triggerAction == 'all') {
44338 this.doQuery(this.allQuery, true);
44342 this.doQuery(this.getRawValue());
44345 getCurrency : function()
44347 var v = this.currencyEl().getValue();
44352 restrictHeight : function()
44354 this.list.alignTo(this.currencyEl(), this.listAlign);
44355 this.list.alignTo(this.currencyEl(), this.listAlign);
44358 onViewClick : function(view, doFocus, el, e)
44360 var index = this.view.getSelectedIndexes()[0];
44362 var r = this.store.getAt(index);
44365 this.onSelect(r, index);
44369 onSelect : function(record, index){
44371 if(this.fireEvent('beforeselect', this, record, index) !== false){
44373 this.setFromCurrencyData(index > -1 ? record.data : false);
44377 this.fireEvent('select', this, record, index);
44381 setFromCurrencyData : function(o)
44385 this.lastCurrency = o;
44387 if (this.currencyField) {
44388 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44390 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44393 this.lastSelectionText = currency;
44395 //setting default currency
44396 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44397 this.setCurrency(this.defaultCurrency);
44401 this.setCurrency(currency);
44404 setFromData : function(o)
44408 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44410 this.setFromCurrencyData(c);
44415 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44417 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44420 this.setValue(value);
44424 setCurrency : function(v)
44426 this.currencyValue = v;
44429 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44434 setValue : function(v)
44436 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44442 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44444 this.inputEl().dom.value = (v == '') ? '' :
44445 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44447 if(!this.allowZero && v === '0') {
44448 this.hiddenEl().dom.value = '';
44449 this.inputEl().dom.value = '';
44456 getRawValue : function()
44458 var v = this.inputEl().getValue();
44463 getValue : function()
44465 return this.fixPrecision(this.parseValue(this.getRawValue()));
44468 parseValue : function(value)
44470 if(this.thousandsDelimiter) {
44472 r = new RegExp(",", "g");
44473 value = value.replace(r, "");
44476 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44477 return isNaN(value) ? '' : value;
44481 fixPrecision : function(value)
44483 if(this.thousandsDelimiter) {
44485 r = new RegExp(",", "g");
44486 value = value.replace(r, "");
44489 var nan = isNaN(value);
44491 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44492 return nan ? '' : value;
44494 return parseFloat(value).toFixed(this.decimalPrecision);
44497 decimalPrecisionFcn : function(v)
44499 return Math.floor(v);
44502 validateValue : function(value)
44504 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44508 var num = this.parseValue(value);
44511 this.markInvalid(String.format(this.nanText, value));
44515 if(num < this.minValue){
44516 this.markInvalid(String.format(this.minText, this.minValue));
44520 if(num > this.maxValue){
44521 this.markInvalid(String.format(this.maxText, this.maxValue));
44528 validate : function()
44530 if(this.disabled || this.allowBlank){
44535 var currency = this.getCurrency();
44537 if(this.validateValue(this.getRawValue()) && currency.length){
44542 this.markInvalid();
44546 getName: function()
44551 beforeBlur : function()
44557 var v = this.parseValue(this.getRawValue());
44564 onBlur : function()
44568 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44569 //this.el.removeClass(this.focusClass);
44572 this.hasFocus = false;
44574 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44578 var v = this.getValue();
44580 if(String(v) !== String(this.startValue)){
44581 this.fireEvent('change', this, v, this.startValue);
44584 this.fireEvent("blur", this);
44587 inputEl : function()
44589 return this.el.select('.roo-money-amount-input', true).first();
44592 currencyEl : function()
44594 return this.el.select('.roo-money-currency-input', true).first();
44597 hiddenEl : function()
44599 return this.el.select('input.hidden-number-input',true).first();
44603 * @class Roo.bootstrap.BezierSignature
44604 * @extends Roo.bootstrap.Component
44605 * Bootstrap BezierSignature class
44606 * This script refer to:
44607 * Title: Signature Pad
44609 * Availability: https://github.com/szimek/signature_pad
44612 * Create a new BezierSignature
44613 * @param {Object} config The config object
44616 Roo.bootstrap.BezierSignature = function(config){
44617 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44623 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44630 mouse_btn_down: true,
44633 * @cfg {int} canvas height
44635 canvas_height: '200px',
44638 * @cfg {float|function} Radius of a single dot.
44643 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44648 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44653 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44658 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44663 * @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.
44665 bg_color: 'rgba(0, 0, 0, 0)',
44668 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44670 dot_color: 'black',
44673 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44675 velocity_filter_weight: 0.7,
44678 * @cfg {function} Callback when stroke begin.
44683 * @cfg {function} Callback when stroke end.
44687 getAutoCreate : function()
44689 var cls = 'roo-signature column';
44692 cls += ' ' + this.cls;
44702 for(var i = 0; i < col_sizes.length; i++) {
44703 if(this[col_sizes[i]]) {
44704 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44714 cls: 'roo-signature-body',
44718 cls: 'roo-signature-body-canvas',
44719 height: this.canvas_height,
44720 width: this.canvas_width
44727 style: 'display: none'
44735 initEvents: function()
44737 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44739 var canvas = this.canvasEl();
44741 // mouse && touch event swapping...
44742 canvas.dom.style.touchAction = 'none';
44743 canvas.dom.style.msTouchAction = 'none';
44745 this.mouse_btn_down = false;
44746 canvas.on('mousedown', this._handleMouseDown, this);
44747 canvas.on('mousemove', this._handleMouseMove, this);
44748 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44750 if (window.PointerEvent) {
44751 canvas.on('pointerdown', this._handleMouseDown, this);
44752 canvas.on('pointermove', this._handleMouseMove, this);
44753 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44756 if ('ontouchstart' in window) {
44757 canvas.on('touchstart', this._handleTouchStart, this);
44758 canvas.on('touchmove', this._handleTouchMove, this);
44759 canvas.on('touchend', this._handleTouchEnd, this);
44762 Roo.EventManager.onWindowResize(this.resize, this, true);
44764 // file input event
44765 this.fileEl().on('change', this.uploadImage, this);
44772 resize: function(){
44774 var canvas = this.canvasEl().dom;
44775 var ctx = this.canvasElCtx();
44776 var img_data = false;
44778 if(canvas.width > 0) {
44779 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44781 // setting canvas width will clean img data
44784 var style = window.getComputedStyle ?
44785 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44787 var padding_left = parseInt(style.paddingLeft) || 0;
44788 var padding_right = parseInt(style.paddingRight) || 0;
44790 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44793 ctx.putImageData(img_data, 0, 0);
44797 _handleMouseDown: function(e)
44799 if (e.browserEvent.which === 1) {
44800 this.mouse_btn_down = true;
44801 this.strokeBegin(e);
44805 _handleMouseMove: function (e)
44807 if (this.mouse_btn_down) {
44808 this.strokeMoveUpdate(e);
44812 _handleMouseUp: function (e)
44814 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44815 this.mouse_btn_down = false;
44820 _handleTouchStart: function (e) {
44822 e.preventDefault();
44823 if (e.browserEvent.targetTouches.length === 1) {
44824 // var touch = e.browserEvent.changedTouches[0];
44825 // this.strokeBegin(touch);
44827 this.strokeBegin(e); // assume e catching the correct xy...
44831 _handleTouchMove: function (e) {
44832 e.preventDefault();
44833 // var touch = event.targetTouches[0];
44834 // _this._strokeMoveUpdate(touch);
44835 this.strokeMoveUpdate(e);
44838 _handleTouchEnd: function (e) {
44839 var wasCanvasTouched = e.target === this.canvasEl().dom;
44840 if (wasCanvasTouched) {
44841 e.preventDefault();
44842 // var touch = event.changedTouches[0];
44843 // _this._strokeEnd(touch);
44848 reset: function () {
44849 this._lastPoints = [];
44850 this._lastVelocity = 0;
44851 this._lastWidth = (this.min_width + this.max_width) / 2;
44852 this.canvasElCtx().fillStyle = this.dot_color;
44855 strokeMoveUpdate: function(e)
44857 this.strokeUpdate(e);
44859 if (this.throttle) {
44860 this.throttleStroke(this.strokeUpdate, this.throttle);
44863 this.strokeUpdate(e);
44867 strokeBegin: function(e)
44869 var newPointGroup = {
44870 color: this.dot_color,
44874 if (typeof this.onBegin === 'function') {
44878 this.curve_data.push(newPointGroup);
44880 this.strokeUpdate(e);
44883 strokeUpdate: function(e)
44885 var rect = this.canvasEl().dom.getBoundingClientRect();
44886 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44887 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44888 var lastPoints = lastPointGroup.points;
44889 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44890 var isLastPointTooClose = lastPoint
44891 ? point.distanceTo(lastPoint) <= this.min_distance
44893 var color = lastPointGroup.color;
44894 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44895 var curve = this.addPoint(point);
44897 this.drawDot({color: color, point: point});
44900 this.drawCurve({color: color, curve: curve});
44910 strokeEnd: function(e)
44912 this.strokeUpdate(e);
44913 if (typeof this.onEnd === 'function') {
44918 addPoint: function (point) {
44919 var _lastPoints = this._lastPoints;
44920 _lastPoints.push(point);
44921 if (_lastPoints.length > 2) {
44922 if (_lastPoints.length === 3) {
44923 _lastPoints.unshift(_lastPoints[0]);
44925 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44926 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44927 _lastPoints.shift();
44933 calculateCurveWidths: function (startPoint, endPoint) {
44934 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44935 (1 - this.velocity_filter_weight) * this._lastVelocity;
44937 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44940 start: this._lastWidth
44943 this._lastVelocity = velocity;
44944 this._lastWidth = newWidth;
44948 drawDot: function (_a) {
44949 var color = _a.color, point = _a.point;
44950 var ctx = this.canvasElCtx();
44951 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44953 this.drawCurveSegment(point.x, point.y, width);
44955 ctx.fillStyle = color;
44959 drawCurve: function (_a) {
44960 var color = _a.color, curve = _a.curve;
44961 var ctx = this.canvasElCtx();
44962 var widthDelta = curve.endWidth - curve.startWidth;
44963 var drawSteps = Math.floor(curve.length()) * 2;
44965 ctx.fillStyle = color;
44966 for (var i = 0; i < drawSteps; i += 1) {
44967 var t = i / drawSteps;
44973 var x = uuu * curve.startPoint.x;
44974 x += 3 * uu * t * curve.control1.x;
44975 x += 3 * u * tt * curve.control2.x;
44976 x += ttt * curve.endPoint.x;
44977 var y = uuu * curve.startPoint.y;
44978 y += 3 * uu * t * curve.control1.y;
44979 y += 3 * u * tt * curve.control2.y;
44980 y += ttt * curve.endPoint.y;
44981 var width = curve.startWidth + ttt * widthDelta;
44982 this.drawCurveSegment(x, y, width);
44988 drawCurveSegment: function (x, y, width) {
44989 var ctx = this.canvasElCtx();
44991 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44992 this.is_empty = false;
44997 var ctx = this.canvasElCtx();
44998 var canvas = this.canvasEl().dom;
44999 ctx.fillStyle = this.bg_color;
45000 ctx.clearRect(0, 0, canvas.width, canvas.height);
45001 ctx.fillRect(0, 0, canvas.width, canvas.height);
45002 this.curve_data = [];
45004 this.is_empty = true;
45009 return this.el.select('input',true).first();
45012 canvasEl: function()
45014 return this.el.select('canvas',true).first();
45017 canvasElCtx: function()
45019 return this.el.select('canvas',true).first().dom.getContext('2d');
45022 getImage: function(type)
45024 if(this.is_empty) {
45029 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45032 drawFromImage: function(img_src)
45034 var img = new Image();
45036 img.onload = function(){
45037 this.canvasElCtx().drawImage(img, 0, 0);
45042 this.is_empty = false;
45045 selectImage: function()
45047 this.fileEl().dom.click();
45050 uploadImage: function(e)
45052 var reader = new FileReader();
45054 reader.onload = function(e){
45055 var img = new Image();
45056 img.onload = function(){
45058 this.canvasElCtx().drawImage(img, 0, 0);
45060 img.src = e.target.result;
45063 reader.readAsDataURL(e.target.files[0]);
45066 // Bezier Point Constructor
45067 Point: (function () {
45068 function Point(x, y, time) {
45071 this.time = time || Date.now();
45073 Point.prototype.distanceTo = function (start) {
45074 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45076 Point.prototype.equals = function (other) {
45077 return this.x === other.x && this.y === other.y && this.time === other.time;
45079 Point.prototype.velocityFrom = function (start) {
45080 return this.time !== start.time
45081 ? this.distanceTo(start) / (this.time - start.time)
45088 // Bezier Constructor
45089 Bezier: (function () {
45090 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45091 this.startPoint = startPoint;
45092 this.control2 = control2;
45093 this.control1 = control1;
45094 this.endPoint = endPoint;
45095 this.startWidth = startWidth;
45096 this.endWidth = endWidth;
45098 Bezier.fromPoints = function (points, widths, scope) {
45099 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45100 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45101 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45103 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45104 var dx1 = s1.x - s2.x;
45105 var dy1 = s1.y - s2.y;
45106 var dx2 = s2.x - s3.x;
45107 var dy2 = s2.y - s3.y;
45108 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45109 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45110 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45111 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45112 var dxm = m1.x - m2.x;
45113 var dym = m1.y - m2.y;
45114 var k = l2 / (l1 + l2);
45115 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45116 var tx = s2.x - cm.x;
45117 var ty = s2.y - cm.y;
45119 c1: new scope.Point(m1.x + tx, m1.y + ty),
45120 c2: new scope.Point(m2.x + tx, m2.y + ty)
45123 Bezier.prototype.length = function () {
45128 for (var i = 0; i <= steps; i += 1) {
45130 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45131 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45133 var xdiff = cx - px;
45134 var ydiff = cy - py;
45135 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45142 Bezier.prototype.point = function (t, start, c1, c2, end) {
45143 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45144 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45145 + (3.0 * c2 * (1.0 - t) * t * t)
45146 + (end * t * t * t);
45151 throttleStroke: function(fn, wait) {
45152 if (wait === void 0) { wait = 250; }
45154 var timeout = null;
45158 var later = function () {
45159 previous = Date.now();
45161 result = fn.apply(storedContext, storedArgs);
45163 storedContext = null;
45167 return function wrapper() {
45169 for (var _i = 0; _i < arguments.length; _i++) {
45170 args[_i] = arguments[_i];
45172 var now = Date.now();
45173 var remaining = wait - (now - previous);
45174 storedContext = this;
45176 if (remaining <= 0 || remaining > wait) {
45178 clearTimeout(timeout);
45182 result = fn.apply(storedContext, storedArgs);
45184 storedContext = null;
45188 else if (!timeout) {
45189 timeout = window.setTimeout(later, remaining);
45199 // old names for form elements
45200 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45201 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45202 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45203 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45204 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45205 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45206 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45207 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45208 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45209 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45210 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45211 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45212 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45213 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45214 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45215 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45216 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45217 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45218 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45219 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45220 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45221 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45222 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45223 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45224 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45225 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45227 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45228 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45230 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45231 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45233 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45234 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45235 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45236 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator