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
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.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 {boolean} striped Should the rows be alternative striped
9141 * @cfg {boolean} bordered Add borders to the table
9142 * @cfg {boolean} hover Add hover highlighting
9143 * @cfg {boolean} condensed Format condensed
9144 * @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,
9145 * also adds table-responsive (see bootstrap docs for details)
9146 * @cfg {Boolean} loadMask (true|false) default false
9147 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9148 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9149 * @cfg {Boolean} rowSelection (true|false) default false
9150 * @cfg {Boolean} cellSelection (true|false) default false
9151 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9152 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9153 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9154 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9155 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9156 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9159 * Create a new Table
9160 * @param {Object} config The config object
9163 Roo.bootstrap.Table = function(config)
9165 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9168 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9169 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9170 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9171 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9173 this.view = this; // compat with grid.
9175 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9177 this.sm.grid = this;
9178 this.selModel = Roo.factory(this.sm, Roo.grid);
9179 this.sm = this.selModel;
9180 this.sm.xmodule = this.xmodule || false;
9183 if (this.cm && typeof(this.cm.config) == 'undefined') {
9184 this.colModel = new Roo.grid.ColumnModel(this.cm);
9185 this.cm = this.colModel;
9186 this.cm.xmodule = this.xmodule || false;
9189 this.store= Roo.factory(this.store, Roo.data);
9190 this.ds = this.store;
9191 this.ds.xmodule = this.xmodule || false;
9194 if (this.footer && this.store) {
9195 this.footer.dataSource = this.ds;
9196 this.footer = Roo.factory(this.footer);
9203 * Fires when a cell is clicked
9204 * @param {Roo.bootstrap.Table} this
9205 * @param {Roo.Element} el
9206 * @param {Number} rowIndex
9207 * @param {Number} columnIndex
9208 * @param {Roo.EventObject} e
9212 * @event celldblclick
9213 * Fires when a cell is double clicked
9214 * @param {Roo.bootstrap.Table} this
9215 * @param {Roo.Element} el
9216 * @param {Number} rowIndex
9217 * @param {Number} columnIndex
9218 * @param {Roo.EventObject} e
9220 "celldblclick" : true,
9223 * Fires when a row is clicked
9224 * @param {Roo.bootstrap.Table} this
9225 * @param {Roo.Element} el
9226 * @param {Number} rowIndex
9227 * @param {Roo.EventObject} e
9231 * @event rowdblclick
9232 * Fires when a row is double clicked
9233 * @param {Roo.bootstrap.Table} this
9234 * @param {Roo.Element} el
9235 * @param {Number} rowIndex
9236 * @param {Roo.EventObject} e
9238 "rowdblclick" : true,
9241 * Fires when a mouseover occur
9242 * @param {Roo.bootstrap.Table} this
9243 * @param {Roo.Element} el
9244 * @param {Number} rowIndex
9245 * @param {Number} columnIndex
9246 * @param {Roo.EventObject} e
9251 * Fires when a mouseout occur
9252 * @param {Roo.bootstrap.Table} this
9253 * @param {Roo.Element} el
9254 * @param {Number} rowIndex
9255 * @param {Number} columnIndex
9256 * @param {Roo.EventObject} e
9261 * Fires when a row is rendered, so you can change add a style to it.
9262 * @param {Roo.bootstrap.Table} this
9263 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9267 * @event rowsrendered
9268 * Fires when all the rows have been rendered
9269 * @param {Roo.bootstrap.Table} this
9271 'rowsrendered' : true,
9273 * @event contextmenu
9274 * The raw contextmenu event for the entire grid.
9275 * @param {Roo.EventObject} e
9277 "contextmenu" : true,
9279 * @event rowcontextmenu
9280 * Fires when a row is right clicked
9281 * @param {Roo.bootstrap.Table} this
9282 * @param {Number} rowIndex
9283 * @param {Roo.EventObject} e
9285 "rowcontextmenu" : true,
9287 * @event cellcontextmenu
9288 * Fires when a cell is right clicked
9289 * @param {Roo.bootstrap.Table} this
9290 * @param {Number} rowIndex
9291 * @param {Number} cellIndex
9292 * @param {Roo.EventObject} e
9294 "cellcontextmenu" : true,
9296 * @event headercontextmenu
9297 * Fires when a header is right clicked
9298 * @param {Roo.bootstrap.Table} this
9299 * @param {Number} columnIndex
9300 * @param {Roo.EventObject} e
9302 "headercontextmenu" : true,
9305 * The raw mousedown event for the entire grid.
9306 * @param {Roo.EventObject} e
9313 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9329 enableColumnResize: true,
9331 rowSelection : false,
9332 cellSelection : false,
9335 minColumnWidth : 50,
9337 // Roo.Element - the tbody
9338 bodyEl: false, // <tbody> Roo.Element - thead element
9339 headEl: false, // <thead> Roo.Element - thead element
9340 resizeProxy : false, // proxy element for dragging?
9344 container: false, // used by gridpanel...
9350 auto_hide_footer : false,
9352 view: false, // actually points to this..
9354 getAutoCreate : function()
9356 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9363 // this get's auto added by panel.Grid
9364 if (this.scrollBody) {
9365 cfg.cls += ' table-body-fixed';
9368 cfg.cls += ' table-striped';
9372 cfg.cls += ' table-hover';
9374 if (this.bordered) {
9375 cfg.cls += ' table-bordered';
9377 if (this.condensed) {
9378 cfg.cls += ' table-condensed';
9381 if (this.responsive) {
9382 cfg.cls += ' table-responsive';
9386 cfg.cls+= ' ' +this.cls;
9392 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9395 if(this.store || this.cm){
9396 if(this.headerShow){
9397 cfg.cn.push(this.renderHeader());
9400 cfg.cn.push(this.renderBody());
9402 if(this.footerShow){
9403 cfg.cn.push(this.renderFooter());
9405 // where does this come from?
9406 //cfg.cls+= ' TableGrid';
9409 return { cn : [ cfg ] };
9412 initEvents : function()
9414 if(!this.store || !this.cm){
9417 if (this.selModel) {
9418 this.selModel.initEvents();
9422 //Roo.log('initEvents with ds!!!!');
9424 this.bodyEl = this.el.select('tbody', true).first();
9425 this.headEl = this.el.select('thead', true).first();
9426 this.mainFoot = this.el.select('tfoot', true).first();
9431 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9432 e.on('click', this.sort, this);
9436 // why is this done????? = it breaks dialogs??
9437 //this.parent().el.setStyle('position', 'relative');
9441 this.footer.parentId = this.id;
9442 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9445 this.el.select('tfoot tr td').first().addClass('hide');
9450 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9453 this.store.on('load', this.onLoad, this);
9454 this.store.on('beforeload', this.onBeforeLoad, this);
9455 this.store.on('update', this.onUpdate, this);
9456 this.store.on('add', this.onAdd, this);
9457 this.store.on("clear", this.clear, this);
9459 this.el.on("contextmenu", this.onContextMenu, this);
9462 this.cm.on("headerchange", this.onHeaderChange, this);
9463 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9465 //?? does bodyEl get replaced on render?
9466 this.bodyEl.on("click", this.onClick, this);
9467 this.bodyEl.on("dblclick", this.onDblClick, this);
9468 this.bodyEl.on('scroll', this.onBodyScroll, this);
9470 // guessing mainbody will work - this relays usually caught by selmodel at present.
9471 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9474 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9477 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9478 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9483 // Compatibility with grid - we implement all the view features at present.
9484 getView : function()
9489 initCSS : function()
9493 var cm = this.cm, styles = [];
9494 this.CSS.removeStyleSheet(this.id + '-cssrules');
9495 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9496 // we can honour xs/sm/md/xl as widths...
9497 // we first have to decide what widht we are currently at...
9498 var sz = Roo.getGridSize();
9502 var cols = []; // visable cols.
9504 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9505 var w = cm.getColumnWidth(i, false);
9507 cols.push( { rel : false, abs : 0 });
9511 cols.push( { rel : false, abs : w });
9513 last = i; // not really..
9516 var w = cm.getColumnWidth(i, sz);
9521 cols.push( { rel : w, abs : false });
9524 var avail = this.bodyEl.dom.clientWidth - total_abs;
9526 var unitWidth = Math.floor(avail / total);
9527 var rem = avail - (unitWidth * total);
9529 var hidden, width, pos = 0 , splithide , left;
9530 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9532 hidden = 'display:none;';
9534 width = 'width:0px;';
9536 if(!cm.isHidden(i)){
9540 // we can honour xs/sm/md/xl ?
9541 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9543 hidden = 'display:none;';
9545 // width should return a small number...
9547 w+=rem; // add the remaining with..
9550 left = "left:" + (pos -4) + "px;";
9551 width = "width:" + w+ "px;";
9554 if (this.responsive) {
9557 hidden = cm.isHidden(i) ? 'display:none;' : '';
9558 splithide = 'display: none;';
9561 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9564 splithide = 'display:none;';
9567 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9568 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9573 //Roo.log(styles.join(''));
9574 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9580 onContextMenu : function(e, t)
9582 this.processEvent("contextmenu", e);
9585 processEvent : function(name, e)
9587 if (name != 'touchstart' ) {
9588 this.fireEvent(name, e);
9591 var t = e.getTarget();
9593 var cell = Roo.get(t);
9599 if(cell.findParent('tfoot', false, true)){
9603 if(cell.findParent('thead', false, true)){
9605 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9606 cell = Roo.get(t).findParent('th', false, true);
9608 Roo.log("failed to find th in thead?");
9609 Roo.log(e.getTarget());
9614 var cellIndex = cell.dom.cellIndex;
9616 var ename = name == 'touchstart' ? 'click' : name;
9617 this.fireEvent("header" + ename, this, cellIndex, e);
9622 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9623 cell = Roo.get(t).findParent('td', false, true);
9625 Roo.log("failed to find th in tbody?");
9626 Roo.log(e.getTarget());
9631 var row = cell.findParent('tr', false, true);
9632 var cellIndex = cell.dom.cellIndex;
9633 var rowIndex = row.dom.rowIndex - 1;
9637 this.fireEvent("row" + name, this, rowIndex, e);
9641 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9647 onMouseover : function(e, el)
9649 var cell = Roo.get(el);
9655 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9656 cell = cell.findParent('td', false, true);
9659 var row = cell.findParent('tr', false, true);
9660 var cellIndex = cell.dom.cellIndex;
9661 var rowIndex = row.dom.rowIndex - 1; // start from 0
9663 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9667 onMouseout : function(e, el)
9669 var cell = Roo.get(el);
9675 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9676 cell = cell.findParent('td', false, true);
9679 var row = cell.findParent('tr', false, true);
9680 var cellIndex = cell.dom.cellIndex;
9681 var rowIndex = row.dom.rowIndex - 1; // start from 0
9683 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9687 onClick : function(e, el)
9689 var cell = Roo.get(el);
9691 if(!cell || (!this.cellSelection && !this.rowSelection)){
9695 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9696 cell = cell.findParent('td', false, true);
9699 if(!cell || typeof(cell) == 'undefined'){
9703 var row = cell.findParent('tr', false, true);
9705 if(!row || typeof(row) == 'undefined'){
9709 var cellIndex = cell.dom.cellIndex;
9710 var rowIndex = this.getRowIndex(row);
9712 // why??? - should these not be based on SelectionModel?
9713 //if(this.cellSelection){
9714 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9717 //if(this.rowSelection){
9718 this.fireEvent('rowclick', this, row, rowIndex, e);
9723 onDblClick : function(e,el)
9725 var cell = Roo.get(el);
9727 if(!cell || (!this.cellSelection && !this.rowSelection)){
9731 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9732 cell = cell.findParent('td', false, true);
9735 if(!cell || typeof(cell) == 'undefined'){
9739 var row = cell.findParent('tr', false, true);
9741 if(!row || typeof(row) == 'undefined'){
9745 var cellIndex = cell.dom.cellIndex;
9746 var rowIndex = this.getRowIndex(row);
9748 if(this.cellSelection){
9749 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9752 if(this.rowSelection){
9753 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9756 findRowIndex : function(el)
9758 var cell = Roo.get(el);
9762 var row = cell.findParent('tr', false, true);
9764 if(!row || typeof(row) == 'undefined'){
9767 return this.getRowIndex(row);
9769 sort : function(e,el)
9771 var col = Roo.get(el);
9773 if(!col.hasClass('sortable')){
9777 var sort = col.attr('sort');
9780 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9784 this.store.sortInfo = {field : sort, direction : dir};
9787 Roo.log("calling footer first");
9788 this.footer.onClick('first');
9791 this.store.load({ params : { start : 0 } });
9795 renderHeader : function()
9803 this.totalWidth = 0;
9805 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9807 var config = cm.config[i];
9811 cls : 'x-hcol-' + i,
9814 html: cm.getColumnHeader(i)
9817 var tooltip = cm.getColumnTooltip(i);
9819 c.tooltip = tooltip;
9825 if(typeof(config.sortable) != 'undefined' && config.sortable){
9826 c.cls += ' sortable';
9827 c.html = '<i class="fa"></i>' + c.html;
9830 // could use BS4 hidden-..-down
9832 if(typeof(config.lgHeader) != 'undefined'){
9833 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9836 if(typeof(config.mdHeader) != 'undefined'){
9837 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9840 if(typeof(config.smHeader) != 'undefined'){
9841 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9844 if(typeof(config.xsHeader) != 'undefined'){
9845 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9852 if(typeof(config.tooltip) != 'undefined'){
9853 c.tooltip = config.tooltip;
9856 if(typeof(config.colspan) != 'undefined'){
9857 c.colspan = config.colspan;
9860 // hidden is handled by CSS now
9862 if(typeof(config.dataIndex) != 'undefined'){
9863 c.sort = config.dataIndex;
9868 if(typeof(config.align) != 'undefined' && config.align.length){
9869 c.style += ' text-align:' + config.align + ';';
9872 /* width is done in CSS
9873 *if(typeof(config.width) != 'undefined'){
9874 c.style += ' width:' + config.width + 'px;';
9875 this.totalWidth += config.width;
9877 this.totalWidth += 100; // assume minimum of 100 per column?
9881 if(typeof(config.cls) != 'undefined'){
9882 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9884 // this is the bit that doesnt reall work at all...
9886 if (this.responsive) {
9889 ['xs','sm','md','lg'].map(function(size){
9891 if(typeof(config[size]) == 'undefined'){
9895 if (!config[size]) { // 0 = hidden
9896 // BS 4 '0' is treated as hide that column and below.
9897 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9901 c.cls += ' col-' + size + '-' + config[size] + (
9902 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9910 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9921 renderBody : function()
9931 colspan : this.cm.getColumnCount()
9941 renderFooter : function()
9951 colspan : this.cm.getColumnCount()
9965 // Roo.log('ds onload');
9970 var ds = this.store;
9972 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9973 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9974 if (_this.store.sortInfo) {
9976 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9977 e.select('i', true).addClass(['fa-arrow-up']);
9980 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9981 e.select('i', true).addClass(['fa-arrow-down']);
9986 var tbody = this.bodyEl;
9988 if(ds.getCount() > 0){
9989 ds.data.each(function(d,rowIndex){
9990 var row = this.renderRow(cm, ds, rowIndex);
9992 tbody.createChild(row);
9996 if(row.cellObjects.length){
9997 Roo.each(row.cellObjects, function(r){
9998 _this.renderCellObject(r);
10005 var tfoot = this.el.select('tfoot', true).first();
10007 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10009 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10011 var total = this.ds.getTotalCount();
10013 if(this.footer.pageSize < total){
10014 this.mainFoot.show();
10018 Roo.each(this.el.select('tbody td', true).elements, function(e){
10019 e.on('mouseover', _this.onMouseover, _this);
10022 Roo.each(this.el.select('tbody td', true).elements, function(e){
10023 e.on('mouseout', _this.onMouseout, _this);
10025 this.fireEvent('rowsrendered', this);
10029 this.initCSS(); /// resize cols
10035 onUpdate : function(ds,record)
10037 this.refreshRow(record);
10041 onRemove : function(ds, record, index, isUpdate){
10042 if(isUpdate !== true){
10043 this.fireEvent("beforerowremoved", this, index, record);
10045 var bt = this.bodyEl.dom;
10047 var rows = this.el.select('tbody > tr', true).elements;
10049 if(typeof(rows[index]) != 'undefined'){
10050 bt.removeChild(rows[index].dom);
10053 // if(bt.rows[index]){
10054 // bt.removeChild(bt.rows[index]);
10057 if(isUpdate !== true){
10058 //this.stripeRows(index);
10059 //this.syncRowHeights(index, index);
10061 this.fireEvent("rowremoved", this, index, record);
10065 onAdd : function(ds, records, rowIndex)
10067 //Roo.log('on Add called');
10068 // - note this does not handle multiple adding very well..
10069 var bt = this.bodyEl.dom;
10070 for (var i =0 ; i < records.length;i++) {
10071 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10072 //Roo.log(records[i]);
10073 //Roo.log(this.store.getAt(rowIndex+i));
10074 this.insertRow(this.store, rowIndex + i, false);
10081 refreshRow : function(record){
10082 var ds = this.store, index;
10083 if(typeof record == 'number'){
10085 record = ds.getAt(index);
10087 index = ds.indexOf(record);
10089 return; // should not happen - but seems to
10092 this.insertRow(ds, index, true);
10094 this.onRemove(ds, record, index+1, true);
10096 //this.syncRowHeights(index, index);
10098 this.fireEvent("rowupdated", this, index, record);
10100 // private - called by RowSelection
10101 onRowSelect : function(rowIndex){
10102 var row = this.getRowDom(rowIndex);
10103 row.addClass(['bg-info','info']);
10105 // private - called by RowSelection
10106 onRowDeselect : function(rowIndex)
10108 if (rowIndex < 0) {
10111 var row = this.getRowDom(rowIndex);
10112 row.removeClass(['bg-info','info']);
10115 * Focuses the specified row.
10116 * @param {Number} row The row index
10118 focusRow : function(row)
10120 //Roo.log('GridView.focusRow');
10121 var x = this.bodyEl.dom.scrollLeft;
10122 this.focusCell(row, 0, false);
10123 this.bodyEl.dom.scrollLeft = x;
10127 * Focuses the specified cell.
10128 * @param {Number} row The row index
10129 * @param {Number} col The column index
10130 * @param {Boolean} hscroll false to disable horizontal scrolling
10132 focusCell : function(row, col, hscroll)
10134 //Roo.log('GridView.focusCell');
10135 var el = this.ensureVisible(row, col, hscroll);
10136 // not sure what focusEL achives = it's a <a> pos relative
10137 //this.focusEl.alignTo(el, "tl-tl");
10139 // this.focusEl.focus();
10141 // this.focusEl.focus.defer(1, this.focusEl);
10146 * Scrolls the specified cell into view
10147 * @param {Number} row The row index
10148 * @param {Number} col The column index
10149 * @param {Boolean} hscroll false to disable horizontal scrolling
10151 ensureVisible : function(row, col, hscroll)
10153 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10154 //return null; //disable for testing.
10155 if(typeof row != "number"){
10156 row = row.rowIndex;
10158 if(row < 0 && row >= this.ds.getCount()){
10161 col = (col !== undefined ? col : 0);
10163 while(cm.isHidden(col)){
10167 var el = this.getCellDom(row, col);
10171 var c = this.bodyEl.dom;
10173 var ctop = parseInt(el.offsetTop, 10);
10174 var cleft = parseInt(el.offsetLeft, 10);
10175 var cbot = ctop + el.offsetHeight;
10176 var cright = cleft + el.offsetWidth;
10178 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10179 var ch = 0; //?? header is not withing the area?
10180 var stop = parseInt(c.scrollTop, 10);
10181 var sleft = parseInt(c.scrollLeft, 10);
10182 var sbot = stop + ch;
10183 var sright = sleft + c.clientWidth;
10185 Roo.log('GridView.ensureVisible:' +
10187 ' c.clientHeight:' + c.clientHeight +
10188 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10196 c.scrollTop = ctop;
10197 //Roo.log("set scrolltop to ctop DISABLE?");
10198 }else if(cbot > sbot){
10199 //Roo.log("set scrolltop to cbot-ch");
10200 c.scrollTop = cbot-ch;
10203 if(hscroll !== false){
10205 c.scrollLeft = cleft;
10206 }else if(cright > sright){
10207 c.scrollLeft = cright-c.clientWidth;
10215 insertRow : function(dm, rowIndex, isUpdate){
10218 this.fireEvent("beforerowsinserted", this, rowIndex);
10220 //var s = this.getScrollState();
10221 var row = this.renderRow(this.cm, this.store, rowIndex);
10222 // insert before rowIndex..
10223 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10227 if(row.cellObjects.length){
10228 Roo.each(row.cellObjects, function(r){
10229 _this.renderCellObject(r);
10234 this.fireEvent("rowsinserted", this, rowIndex);
10235 //this.syncRowHeights(firstRow, lastRow);
10236 //this.stripeRows(firstRow);
10243 getRowDom : function(rowIndex)
10245 var rows = this.el.select('tbody > tr', true).elements;
10247 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10250 getCellDom : function(rowIndex, colIndex)
10252 var row = this.getRowDom(rowIndex);
10253 if (row === false) {
10256 var cols = row.select('td', true).elements;
10257 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10261 // returns the object tree for a tr..
10264 renderRow : function(cm, ds, rowIndex)
10266 var d = ds.getAt(rowIndex);
10270 cls : 'x-row-' + rowIndex,
10274 var cellObjects = [];
10276 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10277 var config = cm.config[i];
10279 var renderer = cm.getRenderer(i);
10283 if(typeof(renderer) !== 'undefined'){
10284 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10286 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10287 // and are rendered into the cells after the row is rendered - using the id for the element.
10289 if(typeof(value) === 'object'){
10299 rowIndex : rowIndex,
10304 this.fireEvent('rowclass', this, rowcfg);
10308 // this might end up displaying HTML?
10309 // this is too messy... - better to only do it on columsn you know are going to be too long
10310 //tooltip : (typeof(value) === 'object') ? '' : value,
10311 cls : rowcfg.rowClass + ' x-col-' + i,
10313 html: (typeof(value) === 'object') ? '' : value
10320 if(typeof(config.colspan) != 'undefined'){
10321 td.colspan = config.colspan;
10326 if(typeof(config.align) != 'undefined' && config.align.length){
10327 td.style += ' text-align:' + config.align + ';';
10329 if(typeof(config.valign) != 'undefined' && config.valign.length){
10330 td.style += ' vertical-align:' + config.valign + ';';
10333 if(typeof(config.width) != 'undefined'){
10334 td.style += ' width:' + config.width + 'px;';
10338 if(typeof(config.cursor) != 'undefined'){
10339 td.style += ' cursor:' + config.cursor + ';';
10342 if(typeof(config.cls) != 'undefined'){
10343 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10345 if (this.responsive) {
10346 ['xs','sm','md','lg'].map(function(size){
10348 if(typeof(config[size]) == 'undefined'){
10354 if (!config[size]) { // 0 = hidden
10355 // BS 4 '0' is treated as hide that column and below.
10356 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10360 td.cls += ' col-' + size + '-' + config[size] + (
10361 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10371 row.cellObjects = cellObjects;
10379 onBeforeLoad : function()
10388 this.el.select('tbody', true).first().dom.innerHTML = '';
10391 * Show or hide a row.
10392 * @param {Number} rowIndex to show or hide
10393 * @param {Boolean} state hide
10395 setRowVisibility : function(rowIndex, state)
10397 var bt = this.bodyEl.dom;
10399 var rows = this.el.select('tbody > tr', true).elements;
10401 if(typeof(rows[rowIndex]) == 'undefined'){
10404 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10409 getSelectionModel : function(){
10410 if(!this.selModel){
10411 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10413 return this.selModel;
10416 * Render the Roo.bootstrap object from renderder
10418 renderCellObject : function(r)
10422 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10424 var t = r.cfg.render(r.container);
10427 Roo.each(r.cfg.cn, function(c){
10429 container: t.getChildContainer(),
10432 _this.renderCellObject(child);
10437 * get the Row Index from a dom element.
10438 * @param {Roo.Element} row The row to look for
10439 * @returns {Number} the row
10441 getRowIndex : function(row)
10445 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10456 * get the header TH element for columnIndex
10457 * @param {Number} columnIndex
10458 * @returns {Roo.Element}
10460 getHeaderIndex: function(colIndex)
10462 var cols = this.headEl.select('th', true).elements;
10463 return cols[colIndex];
10466 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10467 * @param {domElement} cell to look for
10468 * @returns {Number} the column
10470 getCellIndex : function(cell)
10472 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10474 return parseInt(id[1], 10);
10479 * Returns the grid's underlying element = used by panel.Grid
10480 * @return {Element} The element
10482 getGridEl : function(){
10486 * Forces a resize - used by panel.Grid
10487 * @return {Element} The element
10489 autoSize : function()
10491 //var ctr = Roo.get(this.container.dom.parentElement);
10492 var ctr = Roo.get(this.el.dom);
10494 var thd = this.getGridEl().select('thead',true).first();
10495 var tbd = this.getGridEl().select('tbody', true).first();
10496 var tfd = this.getGridEl().select('tfoot', true).first();
10498 var cw = ctr.getWidth();
10499 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10503 tbd.setWidth(ctr.getWidth());
10504 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10505 // this needs fixing for various usage - currently only hydra job advers I think..
10507 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10509 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10512 cw = Math.max(cw, this.totalWidth);
10513 this.getGridEl().select('tbody tr',true).setWidth(cw);
10516 // resize 'expandable coloumn?
10518 return; // we doe not have a view in this design..
10521 onBodyScroll: function()
10523 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10525 this.headEl.setStyle({
10526 'position' : 'relative',
10527 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10533 var scrollHeight = this.bodyEl.dom.scrollHeight;
10535 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10537 var height = this.bodyEl.getHeight();
10539 if(scrollHeight - height == scrollTop) {
10541 var total = this.ds.getTotalCount();
10543 if(this.footer.cursor + this.footer.pageSize < total){
10545 this.footer.ds.load({
10547 start : this.footer.cursor + this.footer.pageSize,
10548 limit : this.footer.pageSize
10557 onColumnSplitterMoved : function(i, diff)
10559 this.userResized = true;
10561 var cm = this.colModel;
10563 var w = this.getHeaderIndex(i).getWidth() + diff;
10566 cm.setColumnWidth(i, w, true);
10568 //var cid = cm.getColumnId(i); << not used in this version?
10569 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10571 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10572 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10573 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10575 //this.updateSplitters();
10576 //this.layout(); << ??
10577 this.fireEvent("columnresize", i, w);
10579 onHeaderChange : function()
10581 var header = this.renderHeader();
10582 var table = this.el.select('table', true).first();
10584 this.headEl.remove();
10585 this.headEl = table.createChild(header, this.bodyEl, false);
10587 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10588 e.on('click', this.sort, this);
10591 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10592 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10597 onHiddenChange : function(colModel, colIndex, hidden)
10600 this.cm.setHidden()
10601 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10602 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10604 this.CSS.updateRule(thSelector, "display", "");
10605 this.CSS.updateRule(tdSelector, "display", "");
10608 this.CSS.updateRule(thSelector, "display", "none");
10609 this.CSS.updateRule(tdSelector, "display", "none");
10612 // onload calls initCSS()
10613 this.onHeaderChange();
10617 setColumnWidth: function(col_index, width)
10619 // width = "md-2 xs-2..."
10620 if(!this.colModel.config[col_index]) {
10624 var w = width.split(" ");
10626 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10628 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10631 for(var j = 0; j < w.length; j++) {
10637 var size_cls = w[j].split("-");
10639 if(!Number.isInteger(size_cls[1] * 1)) {
10643 if(!this.colModel.config[col_index][size_cls[0]]) {
10647 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10651 h_row[0].classList.replace(
10652 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10653 "col-"+size_cls[0]+"-"+size_cls[1]
10656 for(var i = 0; i < rows.length; i++) {
10658 var size_cls = w[j].split("-");
10660 if(!Number.isInteger(size_cls[1] * 1)) {
10664 if(!this.colModel.config[col_index][size_cls[0]]) {
10668 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10672 rows[i].classList.replace(
10673 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10674 "col-"+size_cls[0]+"-"+size_cls[1]
10678 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10683 // currently only used to find the split on drag..
10684 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10689 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10690 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10699 * @class Roo.bootstrap.TableCell
10700 * @extends Roo.bootstrap.Component
10701 * @children Roo.bootstrap.Component
10702 * @parent Roo.bootstrap.TableRow
10703 * Bootstrap TableCell class
10705 * @cfg {String} html cell contain text
10706 * @cfg {String} cls cell class
10707 * @cfg {String} tag cell tag (td|th) default td
10708 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10709 * @cfg {String} align Aligns the content in a cell
10710 * @cfg {String} axis Categorizes cells
10711 * @cfg {String} bgcolor Specifies the background color of a cell
10712 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10713 * @cfg {Number} colspan Specifies the number of columns a cell should span
10714 * @cfg {String} headers Specifies one or more header cells a cell is related to
10715 * @cfg {Number} height Sets the height of a cell
10716 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10717 * @cfg {Number} rowspan Sets the number of rows a cell should span
10718 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10719 * @cfg {String} valign Vertical aligns the content in a cell
10720 * @cfg {Number} width Specifies the width of a cell
10723 * Create a new TableCell
10724 * @param {Object} config The config object
10727 Roo.bootstrap.TableCell = function(config){
10728 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10731 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10751 getAutoCreate : function(){
10752 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10759 cfg.tag = this.tag;
10772 cfg.align=this.align
10777 if (this.bgcolor) {
10778 cfg.bgcolor=this.bgcolor
10780 if (this.charoff) {
10781 cfg.charoff=this.charoff
10783 if (this.colspan) {
10784 cfg.colspan=this.colspan
10786 if (this.headers) {
10787 cfg.headers=this.headers
10790 cfg.height=this.height
10793 cfg.nowrap=this.nowrap
10795 if (this.rowspan) {
10796 cfg.rowspan=this.rowspan
10799 cfg.scope=this.scope
10802 cfg.valign=this.valign
10805 cfg.width=this.width
10824 * @class Roo.bootstrap.TableRow
10825 * @extends Roo.bootstrap.Component
10826 * @children Roo.bootstrap.TableCell
10827 * @parent Roo.bootstrap.TableBody
10828 * Bootstrap TableRow class
10829 * @cfg {String} cls row class
10830 * @cfg {String} align Aligns the content in a table row
10831 * @cfg {String} bgcolor Specifies a background color for a table row
10832 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10833 * @cfg {String} valign Vertical aligns the content in a table row
10836 * Create a new TableRow
10837 * @param {Object} config The config object
10840 Roo.bootstrap.TableRow = function(config){
10841 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10844 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10852 getAutoCreate : function(){
10853 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10860 cfg.cls = this.cls;
10863 cfg.align = this.align;
10866 cfg.bgcolor = this.bgcolor;
10869 cfg.charoff = this.charoff;
10872 cfg.valign = this.valign;
10890 * @class Roo.bootstrap.TableBody
10891 * @extends Roo.bootstrap.Component
10892 * @children Roo.bootstrap.TableRow
10893 * @parent Roo.bootstrap.Table
10894 * Bootstrap TableBody class
10895 * @cfg {String} cls element class
10896 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10897 * @cfg {String} align Aligns the content inside the element
10898 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10899 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10902 * Create a new TableBody
10903 * @param {Object} config The config object
10906 Roo.bootstrap.TableBody = function(config){
10907 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10910 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10918 getAutoCreate : function(){
10919 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10929 cfg.tag = this.tag;
10933 cfg.align = this.align;
10936 cfg.charoff = this.charoff;
10939 cfg.valign = this.valign;
10946 // initEvents : function()
10949 // if(!this.store){
10953 // this.store = Roo.factory(this.store, Roo.data);
10954 // this.store.on('load', this.onLoad, this);
10956 // this.store.load();
10960 // onLoad: function ()
10962 // this.fireEvent('load', this);
10972 * Ext JS Library 1.1.1
10973 * Copyright(c) 2006-2007, Ext JS, LLC.
10975 * Originally Released Under LGPL - original licence link has changed is not relivant.
10978 * <script type="text/javascript">
10981 // as we use this in bootstrap.
10982 Roo.namespace('Roo.form');
10984 * @class Roo.form.Action
10985 * Internal Class used to handle form actions
10987 * @param {Roo.form.BasicForm} el The form element or its id
10988 * @param {Object} config Configuration options
10993 // define the action interface
10994 Roo.form.Action = function(form, options){
10996 this.options = options || {};
10999 * Client Validation Failed
11002 Roo.form.Action.CLIENT_INVALID = 'client';
11004 * Server Validation Failed
11007 Roo.form.Action.SERVER_INVALID = 'server';
11009 * Connect to Server Failed
11012 Roo.form.Action.CONNECT_FAILURE = 'connect';
11014 * Reading Data from Server Failed
11017 Roo.form.Action.LOAD_FAILURE = 'load';
11019 Roo.form.Action.prototype = {
11021 failureType : undefined,
11022 response : undefined,
11023 result : undefined,
11025 // interface method
11026 run : function(options){
11030 // interface method
11031 success : function(response){
11035 // interface method
11036 handleResponse : function(response){
11040 // default connection failure
11041 failure : function(response){
11043 this.response = response;
11044 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11045 this.form.afterAction(this, false);
11048 processResponse : function(response){
11049 this.response = response;
11050 if(!response.responseText){
11053 this.result = this.handleResponse(response);
11054 return this.result;
11057 // utility functions used internally
11058 getUrl : function(appendParams){
11059 var url = this.options.url || this.form.url || this.form.el.dom.action;
11061 var p = this.getParams();
11063 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11069 getMethod : function(){
11070 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11073 getParams : function(){
11074 var bp = this.form.baseParams;
11075 var p = this.options.params;
11077 if(typeof p == "object"){
11078 p = Roo.urlEncode(Roo.applyIf(p, bp));
11079 }else if(typeof p == 'string' && bp){
11080 p += '&' + Roo.urlEncode(bp);
11083 p = Roo.urlEncode(bp);
11088 createCallback : function(){
11090 success: this.success,
11091 failure: this.failure,
11093 timeout: (this.form.timeout*1000),
11094 upload: this.form.fileUpload ? this.success : undefined
11099 Roo.form.Action.Submit = function(form, options){
11100 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11103 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11106 haveProgress : false,
11107 uploadComplete : false,
11109 // uploadProgress indicator.
11110 uploadProgress : function()
11112 if (!this.form.progressUrl) {
11116 if (!this.haveProgress) {
11117 Roo.MessageBox.progress("Uploading", "Uploading");
11119 if (this.uploadComplete) {
11120 Roo.MessageBox.hide();
11124 this.haveProgress = true;
11126 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11128 var c = new Roo.data.Connection();
11130 url : this.form.progressUrl,
11135 success : function(req){
11136 //console.log(data);
11140 rdata = Roo.decode(req.responseText)
11142 Roo.log("Invalid data from server..");
11146 if (!rdata || !rdata.success) {
11148 Roo.MessageBox.alert(Roo.encode(rdata));
11151 var data = rdata.data;
11153 if (this.uploadComplete) {
11154 Roo.MessageBox.hide();
11159 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11160 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11163 this.uploadProgress.defer(2000,this);
11166 failure: function(data) {
11167 Roo.log('progress url failed ');
11178 // run get Values on the form, so it syncs any secondary forms.
11179 this.form.getValues();
11181 var o = this.options;
11182 var method = this.getMethod();
11183 var isPost = method == 'POST';
11184 if(o.clientValidation === false || this.form.isValid()){
11186 if (this.form.progressUrl) {
11187 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11188 (new Date() * 1) + '' + Math.random());
11193 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11194 form:this.form.el.dom,
11195 url:this.getUrl(!isPost),
11197 params:isPost ? this.getParams() : null,
11198 isUpload: this.form.fileUpload,
11199 formData : this.form.formData
11202 this.uploadProgress();
11204 }else if (o.clientValidation !== false){ // client validation failed
11205 this.failureType = Roo.form.Action.CLIENT_INVALID;
11206 this.form.afterAction(this, false);
11210 success : function(response)
11212 this.uploadComplete= true;
11213 if (this.haveProgress) {
11214 Roo.MessageBox.hide();
11218 var result = this.processResponse(response);
11219 if(result === true || result.success){
11220 this.form.afterAction(this, true);
11224 this.form.markInvalid(result.errors);
11225 this.failureType = Roo.form.Action.SERVER_INVALID;
11227 this.form.afterAction(this, false);
11229 failure : function(response)
11231 this.uploadComplete= true;
11232 if (this.haveProgress) {
11233 Roo.MessageBox.hide();
11236 this.response = response;
11237 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11238 this.form.afterAction(this, false);
11241 handleResponse : function(response){
11242 if(this.form.errorReader){
11243 var rs = this.form.errorReader.read(response);
11246 for(var i = 0, len = rs.records.length; i < len; i++) {
11247 var r = rs.records[i];
11248 errors[i] = r.data;
11251 if(errors.length < 1){
11255 success : rs.success,
11261 ret = Roo.decode(response.responseText);
11265 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11275 Roo.form.Action.Load = function(form, options){
11276 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11277 this.reader = this.form.reader;
11280 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11285 Roo.Ajax.request(Roo.apply(
11286 this.createCallback(), {
11287 method:this.getMethod(),
11288 url:this.getUrl(false),
11289 params:this.getParams()
11293 success : function(response){
11295 var result = this.processResponse(response);
11296 if(result === true || !result.success || !result.data){
11297 this.failureType = Roo.form.Action.LOAD_FAILURE;
11298 this.form.afterAction(this, false);
11301 this.form.clearInvalid();
11302 this.form.setValues(result.data);
11303 this.form.afterAction(this, true);
11306 handleResponse : function(response){
11307 if(this.form.reader){
11308 var rs = this.form.reader.read(response);
11309 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11311 success : rs.success,
11315 return Roo.decode(response.responseText);
11319 Roo.form.Action.ACTION_TYPES = {
11320 'load' : Roo.form.Action.Load,
11321 'submit' : Roo.form.Action.Submit
11330 * @class Roo.bootstrap.form.Form
11331 * @extends Roo.bootstrap.Component
11332 * @children Roo.bootstrap.Component
11333 * Bootstrap Form class
11334 * @cfg {String} method GET | POST (default POST)
11335 * @cfg {String} labelAlign top | left (default top)
11336 * @cfg {String} align left | right - for navbars
11337 * @cfg {Boolean} loadMask load mask when submit (default true)
11341 * Create a new Form
11342 * @param {Object} config The config object
11346 Roo.bootstrap.form.Form = function(config){
11348 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11350 Roo.bootstrap.form.Form.popover.apply();
11354 * @event clientvalidation
11355 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11356 * @param {Form} this
11357 * @param {Boolean} valid true if the form has passed client-side validation
11359 clientvalidation: true,
11361 * @event beforeaction
11362 * Fires before any action is performed. Return false to cancel the action.
11363 * @param {Form} this
11364 * @param {Action} action The action to be performed
11366 beforeaction: true,
11368 * @event actionfailed
11369 * Fires when an action fails.
11370 * @param {Form} this
11371 * @param {Action} action The action that failed
11373 actionfailed : true,
11375 * @event actioncomplete
11376 * Fires when an action is completed.
11377 * @param {Form} this
11378 * @param {Action} action The action that completed
11380 actioncomplete : true
11384 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11387 * @cfg {String} method
11388 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11392 * @cfg {String} url
11393 * The URL to use for form actions if one isn't supplied in the action options.
11396 * @cfg {Boolean} fileUpload
11397 * Set to true if this form is a file upload.
11401 * @cfg {Object} baseParams
11402 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11406 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11410 * @cfg {Sting} align (left|right) for navbar forms
11415 activeAction : null,
11418 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11419 * element by passing it or its id or mask the form itself by passing in true.
11422 waitMsgTarget : false,
11427 * @cfg {Boolean} errorMask (true|false) default false
11432 * @cfg {Number} maskOffset Default 100
11437 * @cfg {Boolean} maskBody
11441 getAutoCreate : function(){
11445 method : this.method || 'POST',
11446 id : this.id || Roo.id(),
11449 if (this.parent().xtype.match(/^Nav/)) {
11450 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11454 if (this.labelAlign == 'left' ) {
11455 cfg.cls += ' form-horizontal';
11461 initEvents : function()
11463 this.el.on('submit', this.onSubmit, this);
11464 // this was added as random key presses on the form where triggering form submit.
11465 this.el.on('keypress', function(e) {
11466 if (e.getCharCode() != 13) {
11469 // we might need to allow it for textareas.. and some other items.
11470 // check e.getTarget().
11472 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11476 Roo.log("keypress blocked");
11478 e.preventDefault();
11484 onSubmit : function(e){
11489 * Returns true if client-side validation on the form is successful.
11492 isValid : function(){
11493 var items = this.getItems();
11495 var target = false;
11497 items.each(function(f){
11503 Roo.log('invalid field: ' + f.name);
11507 if(!target && f.el.isVisible(true)){
11513 if(this.errorMask && !valid){
11514 Roo.bootstrap.form.Form.popover.mask(this, target);
11521 * Returns true if any fields in this form have changed since their original load.
11524 isDirty : function(){
11526 var items = this.getItems();
11527 items.each(function(f){
11537 * Performs a predefined action (submit or load) or custom actions you define on this form.
11538 * @param {String} actionName The name of the action type
11539 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11540 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11541 * accept other config options):
11543 Property Type Description
11544 ---------------- --------------- ----------------------------------------------------------------------------------
11545 url String The url for the action (defaults to the form's url)
11546 method String The form method to use (defaults to the form's method, or POST if not defined)
11547 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11548 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11549 validate the form on the client (defaults to false)
11551 * @return {BasicForm} this
11553 doAction : function(action, options){
11554 if(typeof action == 'string'){
11555 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11557 if(this.fireEvent('beforeaction', this, action) !== false){
11558 this.beforeAction(action);
11559 action.run.defer(100, action);
11565 beforeAction : function(action){
11566 var o = action.options;
11571 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11573 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11576 // not really supported yet.. ??
11578 //if(this.waitMsgTarget === true){
11579 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11580 //}else if(this.waitMsgTarget){
11581 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11582 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11584 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11590 afterAction : function(action, success){
11591 this.activeAction = null;
11592 var o = action.options;
11597 Roo.get(document.body).unmask();
11603 //if(this.waitMsgTarget === true){
11604 // this.el.unmask();
11605 //}else if(this.waitMsgTarget){
11606 // this.waitMsgTarget.unmask();
11608 // Roo.MessageBox.updateProgress(1);
11609 // Roo.MessageBox.hide();
11616 Roo.callback(o.success, o.scope, [this, action]);
11617 this.fireEvent('actioncomplete', this, action);
11621 // failure condition..
11622 // we have a scenario where updates need confirming.
11623 // eg. if a locking scenario exists..
11624 // we look for { errors : { needs_confirm : true }} in the response.
11626 (typeof(action.result) != 'undefined') &&
11627 (typeof(action.result.errors) != 'undefined') &&
11628 (typeof(action.result.errors.needs_confirm) != 'undefined')
11631 Roo.log("not supported yet");
11634 Roo.MessageBox.confirm(
11635 "Change requires confirmation",
11636 action.result.errorMsg,
11641 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11651 Roo.callback(o.failure, o.scope, [this, action]);
11652 // show an error message if no failed handler is set..
11653 if (!this.hasListener('actionfailed')) {
11654 Roo.log("need to add dialog support");
11656 Roo.MessageBox.alert("Error",
11657 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11658 action.result.errorMsg :
11659 "Saving Failed, please check your entries or try again"
11664 this.fireEvent('actionfailed', this, action);
11669 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11670 * @param {String} id The value to search for
11673 findField : function(id){
11674 var items = this.getItems();
11675 var field = items.get(id);
11677 items.each(function(f){
11678 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11685 return field || null;
11688 * Mark fields in this form invalid in bulk.
11689 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11690 * @return {BasicForm} this
11692 markInvalid : function(errors){
11693 if(errors instanceof Array){
11694 for(var i = 0, len = errors.length; i < len; i++){
11695 var fieldError = errors[i];
11696 var f = this.findField(fieldError.id);
11698 f.markInvalid(fieldError.msg);
11704 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11705 field.markInvalid(errors[id]);
11709 //Roo.each(this.childForms || [], function (f) {
11710 // f.markInvalid(errors);
11717 * Set values for fields in this form in bulk.
11718 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11719 * @return {BasicForm} this
11721 setValues : function(values){
11722 if(values instanceof Array){ // array of objects
11723 for(var i = 0, len = values.length; i < len; i++){
11725 var f = this.findField(v.id);
11727 f.setValue(v.value);
11728 if(this.trackResetOnLoad){
11729 f.originalValue = f.getValue();
11733 }else{ // object hash
11736 if(typeof values[id] != 'function' && (field = this.findField(id))){
11738 if (field.setFromData &&
11739 field.valueField &&
11740 field.displayField &&
11741 // combos' with local stores can
11742 // be queried via setValue()
11743 // to set their value..
11744 (field.store && !field.store.isLocal)
11748 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11749 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11750 field.setFromData(sd);
11752 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11754 field.setFromData(values);
11757 field.setValue(values[id]);
11761 if(this.trackResetOnLoad){
11762 field.originalValue = field.getValue();
11768 //Roo.each(this.childForms || [], function (f) {
11769 // f.setValues(values);
11776 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11777 * they are returned as an array.
11778 * @param {Boolean} asString
11781 getValues : function(asString){
11782 //if (this.childForms) {
11783 // copy values from the child forms
11784 // Roo.each(this.childForms, function (f) {
11785 // this.setValues(f.getValues());
11791 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11792 if(asString === true){
11795 return Roo.urlDecode(fs);
11799 * Returns the fields in this form as an object with key/value pairs.
11800 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11803 getFieldValues : function(with_hidden)
11805 var items = this.getItems();
11807 items.each(function(f){
11809 if (!f.getName()) {
11813 var v = f.getValue();
11815 if (f.inputType =='radio') {
11816 if (typeof(ret[f.getName()]) == 'undefined') {
11817 ret[f.getName()] = ''; // empty..
11820 if (!f.el.dom.checked) {
11824 v = f.el.dom.value;
11828 if(f.xtype == 'MoneyField'){
11829 ret[f.currencyName] = f.getCurrency();
11832 // not sure if this supported any more..
11833 if ((typeof(v) == 'object') && f.getRawValue) {
11834 v = f.getRawValue() ; // dates..
11836 // combo boxes where name != hiddenName...
11837 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11838 ret[f.name] = f.getRawValue();
11840 ret[f.getName()] = v;
11847 * Clears all invalid messages in this form.
11848 * @return {BasicForm} this
11850 clearInvalid : function(){
11851 var items = this.getItems();
11853 items.each(function(f){
11861 * Resets this form.
11862 * @return {BasicForm} this
11864 reset : function(){
11865 var items = this.getItems();
11866 items.each(function(f){
11870 Roo.each(this.childForms || [], function (f) {
11878 getItems : function()
11880 var r=new Roo.util.MixedCollection(false, function(o){
11881 return o.id || (o.id = Roo.id());
11883 var iter = function(el) {
11890 Roo.each(el.items,function(e) {
11899 hideFields : function(items)
11901 Roo.each(items, function(i){
11903 var f = this.findField(i);
11914 showFields : function(items)
11916 Roo.each(items, function(i){
11918 var f = this.findField(i);
11931 Roo.apply(Roo.bootstrap.form.Form, {
11947 intervalID : false,
11953 if(this.isApplied){
11958 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11959 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11960 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11961 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11964 this.maskEl.top.enableDisplayMode("block");
11965 this.maskEl.left.enableDisplayMode("block");
11966 this.maskEl.bottom.enableDisplayMode("block");
11967 this.maskEl.right.enableDisplayMode("block");
11969 this.toolTip = new Roo.bootstrap.Tooltip({
11970 cls : 'roo-form-error-popover',
11972 'left' : ['r-l', [-2,0], 'right'],
11973 'right' : ['l-r', [2,0], 'left'],
11974 'bottom' : ['tl-bl', [0,2], 'top'],
11975 'top' : [ 'bl-tl', [0,-2], 'bottom']
11979 this.toolTip.render(Roo.get(document.body));
11981 this.toolTip.el.enableDisplayMode("block");
11983 Roo.get(document.body).on('click', function(){
11987 Roo.get(document.body).on('touchstart', function(){
11991 this.isApplied = true
11994 mask : function(form, target)
11998 this.target = target;
12000 if(!this.form.errorMask || !target.el){
12004 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12006 Roo.log(scrollable);
12008 var ot = this.target.el.calcOffsetsTo(scrollable);
12010 var scrollTo = ot[1] - this.form.maskOffset;
12012 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12014 scrollable.scrollTo('top', scrollTo);
12016 var box = this.target.el.getBox();
12018 var zIndex = Roo.bootstrap.Modal.zIndex++;
12021 this.maskEl.top.setStyle('position', 'absolute');
12022 this.maskEl.top.setStyle('z-index', zIndex);
12023 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12024 this.maskEl.top.setLeft(0);
12025 this.maskEl.top.setTop(0);
12026 this.maskEl.top.show();
12028 this.maskEl.left.setStyle('position', 'absolute');
12029 this.maskEl.left.setStyle('z-index', zIndex);
12030 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12031 this.maskEl.left.setLeft(0);
12032 this.maskEl.left.setTop(box.y - this.padding);
12033 this.maskEl.left.show();
12035 this.maskEl.bottom.setStyle('position', 'absolute');
12036 this.maskEl.bottom.setStyle('z-index', zIndex);
12037 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12038 this.maskEl.bottom.setLeft(0);
12039 this.maskEl.bottom.setTop(box.bottom + this.padding);
12040 this.maskEl.bottom.show();
12042 this.maskEl.right.setStyle('position', 'absolute');
12043 this.maskEl.right.setStyle('z-index', zIndex);
12044 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12045 this.maskEl.right.setLeft(box.right + this.padding);
12046 this.maskEl.right.setTop(box.y - this.padding);
12047 this.maskEl.right.show();
12049 this.toolTip.bindEl = this.target.el;
12051 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12053 var tip = this.target.blankText;
12055 if(this.target.getValue() !== '' ) {
12057 if (this.target.invalidText.length) {
12058 tip = this.target.invalidText;
12059 } else if (this.target.regexText.length){
12060 tip = this.target.regexText;
12064 this.toolTip.show(tip);
12066 this.intervalID = window.setInterval(function() {
12067 Roo.bootstrap.form.Form.popover.unmask();
12070 window.onwheel = function(){ return false;};
12072 (function(){ this.isMasked = true; }).defer(500, this);
12076 unmask : function()
12078 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12082 this.maskEl.top.setStyle('position', 'absolute');
12083 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12084 this.maskEl.top.hide();
12086 this.maskEl.left.setStyle('position', 'absolute');
12087 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12088 this.maskEl.left.hide();
12090 this.maskEl.bottom.setStyle('position', 'absolute');
12091 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12092 this.maskEl.bottom.hide();
12094 this.maskEl.right.setStyle('position', 'absolute');
12095 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12096 this.maskEl.right.hide();
12098 this.toolTip.hide();
12100 this.toolTip.el.hide();
12102 window.onwheel = function(){ return true;};
12104 if(this.intervalID){
12105 window.clearInterval(this.intervalID);
12106 this.intervalID = false;
12109 this.isMasked = false;
12119 * Ext JS Library 1.1.1
12120 * Copyright(c) 2006-2007, Ext JS, LLC.
12122 * Originally Released Under LGPL - original licence link has changed is not relivant.
12125 * <script type="text/javascript">
12128 * @class Roo.form.VTypes
12129 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12132 Roo.form.VTypes = function(){
12133 // closure these in so they are only created once.
12134 var alpha = /^[a-zA-Z_]+$/;
12135 var alphanum = /^[a-zA-Z0-9_]+$/;
12136 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12137 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12139 // All these messages and functions are configurable
12142 * The function used to validate email addresses
12143 * @param {String} value The email address
12145 'email' : function(v){
12146 return email.test(v);
12149 * The error text to display when the email validation function returns false
12152 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12154 * The keystroke filter mask to be applied on email input
12157 'emailMask' : /[a-z0-9_\.\-@]/i,
12160 * The function used to validate URLs
12161 * @param {String} value The URL
12163 'url' : function(v){
12164 return url.test(v);
12167 * The error text to display when the url validation function returns false
12170 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12173 * The function used to validate alpha values
12174 * @param {String} value The value
12176 'alpha' : function(v){
12177 return alpha.test(v);
12180 * The error text to display when the alpha validation function returns false
12183 'alphaText' : 'This field should only contain letters and _',
12185 * The keystroke filter mask to be applied on alpha input
12188 'alphaMask' : /[a-z_]/i,
12191 * The function used to validate alphanumeric values
12192 * @param {String} value The value
12194 'alphanum' : function(v){
12195 return alphanum.test(v);
12198 * The error text to display when the alphanumeric validation function returns false
12201 'alphanumText' : 'This field should only contain letters, numbers and _',
12203 * The keystroke filter mask to be applied on alphanumeric input
12206 'alphanumMask' : /[a-z0-9_]/i
12216 * @class Roo.bootstrap.form.Input
12217 * @extends Roo.bootstrap.Component
12218 * Bootstrap Input class
12219 * @cfg {Boolean} disabled is it disabled
12220 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12221 * @cfg {String} name name of the input
12222 * @cfg {string} fieldLabel - the label associated
12223 * @cfg {string} placeholder - placeholder to put in text.
12224 * @cfg {string} before - input group add on before
12225 * @cfg {string} after - input group add on after
12226 * @cfg {string} size - (lg|sm) or leave empty..
12227 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12228 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12229 * @cfg {Number} md colspan out of 12 for computer-sized screens
12230 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12231 * @cfg {string} value default value of the input
12232 * @cfg {Number} labelWidth set the width of label
12233 * @cfg {Number} labellg set the width of label (1-12)
12234 * @cfg {Number} labelmd set the width of label (1-12)
12235 * @cfg {Number} labelsm set the width of label (1-12)
12236 * @cfg {Number} labelxs set the width of label (1-12)
12237 * @cfg {String} labelAlign (top|left)
12238 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12239 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12240 * @cfg {String} indicatorpos (left|right) default left
12241 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12242 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12243 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12244 * @cfg {Roo.bootstrap.Button} before Button to show before
12245 * @cfg {Roo.bootstrap.Button} afterButton to show before
12246 * @cfg {String} align (left|center|right) Default left
12247 * @cfg {Boolean} forceFeedback (true|false) Default false
12250 * Create a new Input
12251 * @param {Object} config The config object
12254 Roo.bootstrap.form.Input = function(config){
12256 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12261 * Fires when this field receives input focus.
12262 * @param {Roo.form.Field} this
12267 * Fires when this field loses input focus.
12268 * @param {Roo.form.Field} this
12272 * @event specialkey
12273 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12274 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12275 * @param {Roo.form.Field} this
12276 * @param {Roo.EventObject} e The event object
12281 * Fires just before the field blurs if the field value has changed.
12282 * @param {Roo.form.Field} this
12283 * @param {Mixed} newValue The new value
12284 * @param {Mixed} oldValue The original value
12289 * Fires after the field has been marked as invalid.
12290 * @param {Roo.form.Field} this
12291 * @param {String} msg The validation message
12296 * Fires after the field has been validated with no errors.
12297 * @param {Roo.form.Field} this
12302 * Fires after the key up
12303 * @param {Roo.form.Field} this
12304 * @param {Roo.EventObject} e The event Object
12309 * Fires after the user pastes into input
12310 * @param {Roo.form.Field} this
12311 * @param {Roo.EventObject} e The event Object
12317 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12319 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12320 automatic validation (defaults to "keyup").
12322 validationEvent : "keyup",
12324 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12326 validateOnBlur : true,
12328 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12330 validationDelay : 250,
12332 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12334 focusClass : "x-form-focus", // not needed???
12338 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12340 invalidClass : "has-warning",
12343 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12345 validClass : "has-success",
12348 * @cfg {Boolean} hasFeedback (true|false) default true
12350 hasFeedback : true,
12353 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12355 invalidFeedbackClass : "glyphicon-warning-sign",
12358 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12360 validFeedbackClass : "glyphicon-ok",
12363 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12365 selectOnFocus : false,
12368 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12372 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12377 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12379 disableKeyFilter : false,
12382 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12386 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12390 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12392 blankText : "Please complete this mandatory field",
12395 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12399 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12401 maxLength : Number.MAX_VALUE,
12403 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12405 minLengthText : "The minimum length for this field is {0}",
12407 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12409 maxLengthText : "The maximum length for this field is {0}",
12413 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12414 * If available, this function will be called only after the basic validators all return true, and will be passed the
12415 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12419 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12420 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12421 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12425 * @cfg {String} regexText -- Depricated - use Invalid Text
12430 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12436 autocomplete: false,
12440 inputType : 'text',
12443 placeholder: false,
12448 preventMark: false,
12449 isFormField : true,
12452 labelAlign : false,
12455 formatedValue : false,
12456 forceFeedback : false,
12458 indicatorpos : 'left',
12468 parentLabelAlign : function()
12471 while (parent.parent()) {
12472 parent = parent.parent();
12473 if (typeof(parent.labelAlign) !='undefined') {
12474 return parent.labelAlign;
12481 getAutoCreate : function()
12483 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12489 if(this.inputType != 'hidden'){
12490 cfg.cls = 'form-group' //input-group
12496 type : this.inputType,
12497 value : this.value,
12498 cls : 'form-control',
12499 placeholder : this.placeholder || '',
12500 autocomplete : this.autocomplete || 'new-password'
12502 if (this.inputType == 'file') {
12503 input.style = 'overflow:hidden'; // why not in CSS?
12506 if(this.capture.length){
12507 input.capture = this.capture;
12510 if(this.accept.length){
12511 input.accept = this.accept + "/*";
12515 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12518 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12519 input.maxLength = this.maxLength;
12522 if (this.disabled) {
12523 input.disabled=true;
12526 if (this.readOnly) {
12527 input.readonly=true;
12531 input.name = this.name;
12535 input.cls += ' input-' + this.size;
12539 ['xs','sm','md','lg'].map(function(size){
12540 if (settings[size]) {
12541 cfg.cls += ' col-' + size + '-' + settings[size];
12545 var inputblock = input;
12549 cls: 'glyphicon form-control-feedback'
12552 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12555 cls : 'has-feedback',
12563 if (this.before || this.after) {
12566 cls : 'input-group',
12570 if (this.before && typeof(this.before) == 'string') {
12572 inputblock.cn.push({
12574 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12578 if (this.before && typeof(this.before) == 'object') {
12579 this.before = Roo.factory(this.before);
12581 inputblock.cn.push({
12583 cls : 'roo-input-before input-group-prepend input-group-' +
12584 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12588 inputblock.cn.push(input);
12590 if (this.after && typeof(this.after) == 'string') {
12591 inputblock.cn.push({
12593 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12597 if (this.after && typeof(this.after) == 'object') {
12598 this.after = Roo.factory(this.after);
12600 inputblock.cn.push({
12602 cls : 'roo-input-after input-group-append input-group-' +
12603 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12607 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12608 inputblock.cls += ' has-feedback';
12609 inputblock.cn.push(feedback);
12614 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12615 tooltip : 'This field is required'
12617 if (this.allowBlank ) {
12618 indicator.style = this.allowBlank ? ' display:none' : '';
12620 if (align ==='left' && this.fieldLabel.length) {
12622 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12629 cls : 'control-label col-form-label',
12630 html : this.fieldLabel
12641 var labelCfg = cfg.cn[1];
12642 var contentCfg = cfg.cn[2];
12644 if(this.indicatorpos == 'right'){
12649 cls : 'control-label col-form-label',
12653 html : this.fieldLabel
12667 labelCfg = cfg.cn[0];
12668 contentCfg = cfg.cn[1];
12672 if(this.labelWidth > 12){
12673 labelCfg.style = "width: " + this.labelWidth + 'px';
12676 if(this.labelWidth < 13 && this.labelmd == 0){
12677 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12680 if(this.labellg > 0){
12681 labelCfg.cls += ' col-lg-' + this.labellg;
12682 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12685 if(this.labelmd > 0){
12686 labelCfg.cls += ' col-md-' + this.labelmd;
12687 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12690 if(this.labelsm > 0){
12691 labelCfg.cls += ' col-sm-' + this.labelsm;
12692 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12695 if(this.labelxs > 0){
12696 labelCfg.cls += ' col-xs-' + this.labelxs;
12697 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12701 } else if ( this.fieldLabel.length) {
12708 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12709 tooltip : 'This field is required',
12710 style : this.allowBlank ? ' display:none' : ''
12714 //cls : 'input-group-addon',
12715 html : this.fieldLabel
12723 if(this.indicatorpos == 'right'){
12728 //cls : 'input-group-addon',
12729 html : this.fieldLabel
12734 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12735 tooltip : 'This field is required',
12736 style : this.allowBlank ? ' display:none' : ''
12756 if (this.parentType === 'Navbar' && this.parent().bar) {
12757 cfg.cls += ' navbar-form';
12760 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12761 // on BS4 we do this only if not form
12762 cfg.cls += ' navbar-form';
12770 * return the real input element.
12772 inputEl: function ()
12774 return this.el.select('input.form-control',true).first();
12777 tooltipEl : function()
12779 return this.inputEl();
12782 indicatorEl : function()
12784 if (Roo.bootstrap.version == 4) {
12785 return false; // not enabled in v4 yet.
12788 var indicator = this.el.select('i.roo-required-indicator',true).first();
12798 setDisabled : function(v)
12800 var i = this.inputEl().dom;
12802 i.removeAttribute('disabled');
12806 i.setAttribute('disabled','true');
12808 initEvents : function()
12811 this.inputEl().on("keydown" , this.fireKey, this);
12812 this.inputEl().on("focus", this.onFocus, this);
12813 this.inputEl().on("blur", this.onBlur, this);
12815 this.inputEl().relayEvent('keyup', this);
12816 this.inputEl().relayEvent('paste', this);
12818 this.indicator = this.indicatorEl();
12820 if(this.indicator){
12821 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12824 // reference to original value for reset
12825 this.originalValue = this.getValue();
12826 //Roo.form.TextField.superclass.initEvents.call(this);
12827 if(this.validationEvent == 'keyup'){
12828 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12829 this.inputEl().on('keyup', this.filterValidation, this);
12831 else if(this.validationEvent !== false){
12832 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12835 if(this.selectOnFocus){
12836 this.on("focus", this.preFocus, this);
12839 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12840 this.inputEl().on("keypress", this.filterKeys, this);
12842 this.inputEl().relayEvent('keypress', this);
12845 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12846 this.el.on("click", this.autoSize, this);
12849 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12850 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12853 if (typeof(this.before) == 'object') {
12854 this.before.render(this.el.select('.roo-input-before',true).first());
12856 if (typeof(this.after) == 'object') {
12857 this.after.render(this.el.select('.roo-input-after',true).first());
12860 this.inputEl().on('change', this.onChange, this);
12863 filterValidation : function(e){
12864 if(!e.isNavKeyPress()){
12865 this.validationTask.delay(this.validationDelay);
12869 * Validates the field value
12870 * @return {Boolean} True if the value is valid, else false
12872 validate : function(){
12873 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12874 if(this.disabled || this.validateValue(this.getRawValue())){
12879 this.markInvalid();
12885 * Validates a value according to the field's validation rules and marks the field as invalid
12886 * if the validation fails
12887 * @param {Mixed} value The value to validate
12888 * @return {Boolean} True if the value is valid, else false
12890 validateValue : function(value)
12892 if(this.getVisibilityEl().hasClass('hidden')){
12896 if(value.length < 1) { // if it's blank
12897 if(this.allowBlank){
12903 if(value.length < this.minLength){
12906 if(value.length > this.maxLength){
12910 var vt = Roo.form.VTypes;
12911 if(!vt[this.vtype](value, this)){
12915 if(typeof this.validator == "function"){
12916 var msg = this.validator(value);
12920 if (typeof(msg) == 'string') {
12921 this.invalidText = msg;
12925 if(this.regex && !this.regex.test(value)){
12933 fireKey : function(e){
12934 //Roo.log('field ' + e.getKey());
12935 if(e.isNavKeyPress()){
12936 this.fireEvent("specialkey", this, e);
12939 focus : function (selectText){
12941 this.inputEl().focus();
12942 if(selectText === true){
12943 this.inputEl().dom.select();
12949 onFocus : function(){
12950 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12951 // this.el.addClass(this.focusClass);
12953 if(!this.hasFocus){
12954 this.hasFocus = true;
12955 this.startValue = this.getValue();
12956 this.fireEvent("focus", this);
12960 beforeBlur : Roo.emptyFn,
12964 onBlur : function(){
12966 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12967 //this.el.removeClass(this.focusClass);
12969 this.hasFocus = false;
12970 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12973 var v = this.getValue();
12974 if(String(v) !== String(this.startValue)){
12975 this.fireEvent('change', this, v, this.startValue);
12977 this.fireEvent("blur", this);
12980 onChange : function(e)
12982 var v = this.getValue();
12983 if(String(v) !== String(this.startValue)){
12984 this.fireEvent('change', this, v, this.startValue);
12990 * Resets the current field value to the originally loaded value and clears any validation messages
12992 reset : function(){
12993 this.setValue(this.originalValue);
12997 * Returns the name of the field
12998 * @return {Mixed} name The name field
13000 getName: function(){
13004 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13005 * @return {Mixed} value The field value
13007 getValue : function(){
13009 var v = this.inputEl().getValue();
13014 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13015 * @return {Mixed} value The field value
13017 getRawValue : function(){
13018 var v = this.inputEl().getValue();
13024 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13025 * @param {Mixed} value The value to set
13027 setRawValue : function(v){
13028 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13031 selectText : function(start, end){
13032 var v = this.getRawValue();
13034 start = start === undefined ? 0 : start;
13035 end = end === undefined ? v.length : end;
13036 var d = this.inputEl().dom;
13037 if(d.setSelectionRange){
13038 d.setSelectionRange(start, end);
13039 }else if(d.createTextRange){
13040 var range = d.createTextRange();
13041 range.moveStart("character", start);
13042 range.moveEnd("character", v.length-end);
13049 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13050 * @param {Mixed} value The value to set
13052 setValue : function(v){
13055 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13061 processValue : function(value){
13062 if(this.stripCharsRe){
13063 var newValue = value.replace(this.stripCharsRe, '');
13064 if(newValue !== value){
13065 this.setRawValue(newValue);
13072 preFocus : function(){
13074 if(this.selectOnFocus){
13075 this.inputEl().dom.select();
13078 filterKeys : function(e){
13079 var k = e.getKey();
13080 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13083 var c = e.getCharCode(), cc = String.fromCharCode(c);
13084 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13087 if(!this.maskRe.test(cc)){
13092 * Clear any invalid styles/messages for this field
13094 clearInvalid : function(){
13096 if(!this.el || this.preventMark){ // not rendered
13101 this.el.removeClass([this.invalidClass, 'is-invalid']);
13103 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13105 var feedback = this.el.select('.form-control-feedback', true).first();
13108 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13113 if(this.indicator){
13114 this.indicator.removeClass('visible');
13115 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13118 this.fireEvent('valid', this);
13122 * Mark this field as valid
13124 markValid : function()
13126 if(!this.el || this.preventMark){ // not rendered...
13130 this.el.removeClass([this.invalidClass, this.validClass]);
13131 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13133 var feedback = this.el.select('.form-control-feedback', true).first();
13136 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13139 if(this.indicator){
13140 this.indicator.removeClass('visible');
13141 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13149 if(this.allowBlank && !this.getRawValue().length){
13152 if (Roo.bootstrap.version == 3) {
13153 this.el.addClass(this.validClass);
13155 this.inputEl().addClass('is-valid');
13158 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13160 var feedback = this.el.select('.form-control-feedback', true).first();
13163 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13164 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13169 this.fireEvent('valid', this);
13173 * Mark this field as invalid
13174 * @param {String} msg The validation message
13176 markInvalid : function(msg)
13178 if(!this.el || this.preventMark){ // not rendered
13182 this.el.removeClass([this.invalidClass, this.validClass]);
13183 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13185 var feedback = this.el.select('.form-control-feedback', true).first();
13188 this.el.select('.form-control-feedback', true).first().removeClass(
13189 [this.invalidFeedbackClass, this.validFeedbackClass]);
13196 if(this.allowBlank && !this.getRawValue().length){
13200 if(this.indicator){
13201 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13202 this.indicator.addClass('visible');
13204 if (Roo.bootstrap.version == 3) {
13205 this.el.addClass(this.invalidClass);
13207 this.inputEl().addClass('is-invalid');
13212 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13214 var feedback = this.el.select('.form-control-feedback', true).first();
13217 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13219 if(this.getValue().length || this.forceFeedback){
13220 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13227 this.fireEvent('invalid', this, msg);
13230 SafariOnKeyDown : function(event)
13232 // this is a workaround for a password hang bug on chrome/ webkit.
13233 if (this.inputEl().dom.type != 'password') {
13237 var isSelectAll = false;
13239 if(this.inputEl().dom.selectionEnd > 0){
13240 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13242 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13243 event.preventDefault();
13248 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13250 event.preventDefault();
13251 // this is very hacky as keydown always get's upper case.
13253 var cc = String.fromCharCode(event.getCharCode());
13254 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13258 adjustWidth : function(tag, w){
13259 tag = tag.toLowerCase();
13260 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13261 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13262 if(tag == 'input'){
13265 if(tag == 'textarea'){
13268 }else if(Roo.isOpera){
13269 if(tag == 'input'){
13272 if(tag == 'textarea'){
13280 setFieldLabel : function(v)
13282 if(!this.rendered){
13286 if(this.indicatorEl()){
13287 var ar = this.el.select('label > span',true);
13289 if (ar.elements.length) {
13290 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13291 this.fieldLabel = v;
13295 var br = this.el.select('label',true);
13297 if(br.elements.length) {
13298 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13299 this.fieldLabel = v;
13303 Roo.log('Cannot Found any of label > span || label in input');
13307 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13308 this.fieldLabel = v;
13323 * @class Roo.bootstrap.form.TextArea
13324 * @extends Roo.bootstrap.form.Input
13325 * Bootstrap TextArea class
13326 * @cfg {Number} cols Specifies the visible width of a text area
13327 * @cfg {Number} rows Specifies the visible number of lines in a text area
13328 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13329 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13330 * @cfg {string} html text
13333 * Create a new TextArea
13334 * @param {Object} config The config object
13337 Roo.bootstrap.form.TextArea = function(config){
13338 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13342 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13352 getAutoCreate : function(){
13354 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13360 if(this.inputType != 'hidden'){
13361 cfg.cls = 'form-group' //input-group
13369 value : this.value || '',
13370 html: this.html || '',
13371 cls : 'form-control',
13372 placeholder : this.placeholder || ''
13376 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13377 input.maxLength = this.maxLength;
13381 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13385 input.cols = this.cols;
13388 if (this.readOnly) {
13389 input.readonly = true;
13393 input.name = this.name;
13397 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13401 ['xs','sm','md','lg'].map(function(size){
13402 if (settings[size]) {
13403 cfg.cls += ' col-' + size + '-' + settings[size];
13407 var inputblock = input;
13409 if(this.hasFeedback && !this.allowBlank){
13413 cls: 'glyphicon form-control-feedback'
13417 cls : 'has-feedback',
13426 if (this.before || this.after) {
13429 cls : 'input-group',
13433 inputblock.cn.push({
13435 cls : 'input-group-addon',
13440 inputblock.cn.push(input);
13442 if(this.hasFeedback && !this.allowBlank){
13443 inputblock.cls += ' has-feedback';
13444 inputblock.cn.push(feedback);
13448 inputblock.cn.push({
13450 cls : 'input-group-addon',
13457 if (align ==='left' && this.fieldLabel.length) {
13462 cls : 'control-label',
13463 html : this.fieldLabel
13474 if(this.labelWidth > 12){
13475 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13478 if(this.labelWidth < 13 && this.labelmd == 0){
13479 this.labelmd = this.labelWidth;
13482 if(this.labellg > 0){
13483 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13484 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13487 if(this.labelmd > 0){
13488 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13489 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13492 if(this.labelsm > 0){
13493 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13494 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13497 if(this.labelxs > 0){
13498 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13499 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13502 } else if ( this.fieldLabel.length) {
13507 //cls : 'input-group-addon',
13508 html : this.fieldLabel
13526 if (this.disabled) {
13527 input.disabled=true;
13534 * return the real textarea element.
13536 inputEl: function ()
13538 return this.el.select('textarea.form-control',true).first();
13542 * Clear any invalid styles/messages for this field
13544 clearInvalid : function()
13547 if(!this.el || this.preventMark){ // not rendered
13551 var label = this.el.select('label', true).first();
13552 var icon = this.el.select('i.fa-star', true).first();
13557 this.el.removeClass( this.validClass);
13558 this.inputEl().removeClass('is-invalid');
13560 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13562 var feedback = this.el.select('.form-control-feedback', true).first();
13565 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13570 this.fireEvent('valid', this);
13574 * Mark this field as valid
13576 markValid : function()
13578 if(!this.el || this.preventMark){ // not rendered
13582 this.el.removeClass([this.invalidClass, this.validClass]);
13583 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13585 var feedback = this.el.select('.form-control-feedback', true).first();
13588 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13591 if(this.disabled || this.allowBlank){
13595 var label = this.el.select('label', true).first();
13596 var icon = this.el.select('i.fa-star', true).first();
13601 if (Roo.bootstrap.version == 3) {
13602 this.el.addClass(this.validClass);
13604 this.inputEl().addClass('is-valid');
13608 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13610 var feedback = this.el.select('.form-control-feedback', true).first();
13613 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13614 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13619 this.fireEvent('valid', this);
13623 * Mark this field as invalid
13624 * @param {String} msg The validation message
13626 markInvalid : function(msg)
13628 if(!this.el || this.preventMark){ // not rendered
13632 this.el.removeClass([this.invalidClass, this.validClass]);
13633 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13635 var feedback = this.el.select('.form-control-feedback', true).first();
13638 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13641 if(this.disabled || this.allowBlank){
13645 var label = this.el.select('label', true).first();
13646 var icon = this.el.select('i.fa-star', true).first();
13648 if(!this.getValue().length && label && !icon){
13649 this.el.createChild({
13651 cls : 'text-danger fa fa-lg fa-star',
13652 tooltip : 'This field is required',
13653 style : 'margin-right:5px;'
13657 if (Roo.bootstrap.version == 3) {
13658 this.el.addClass(this.invalidClass);
13660 this.inputEl().addClass('is-invalid');
13663 // fixme ... this may be depricated need to test..
13664 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13666 var feedback = this.el.select('.form-control-feedback', true).first();
13669 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13671 if(this.getValue().length || this.forceFeedback){
13672 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13679 this.fireEvent('invalid', this, msg);
13687 * trigger field - base class for combo..
13692 * @class Roo.bootstrap.form.TriggerField
13693 * @extends Roo.bootstrap.form.Input
13694 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13695 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13696 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13697 * for which you can provide a custom implementation. For example:
13699 var trigger = new Roo.bootstrap.form.TriggerField();
13700 trigger.onTriggerClick = myTriggerFn;
13701 trigger.applyTo('my-field');
13704 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13705 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13706 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13707 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13708 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13711 * Create a new TriggerField.
13712 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13713 * to the base TextField)
13715 Roo.bootstrap.form.TriggerField = function(config){
13716 this.mimicing = false;
13717 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13720 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13722 * @cfg {String} triggerClass A CSS class to apply to the trigger
13725 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13730 * @cfg {Boolean} removable (true|false) special filter default false
13734 /** @cfg {Boolean} grow @hide */
13735 /** @cfg {Number} growMin @hide */
13736 /** @cfg {Number} growMax @hide */
13742 autoSize: Roo.emptyFn,
13746 deferHeight : true,
13749 actionMode : 'wrap',
13754 getAutoCreate : function(){
13756 var align = this.labelAlign || this.parentLabelAlign();
13761 cls: 'form-group' //input-group
13768 type : this.inputType,
13769 cls : 'form-control',
13770 autocomplete: 'new-password',
13771 placeholder : this.placeholder || ''
13775 input.name = this.name;
13778 input.cls += ' input-' + this.size;
13781 if (this.disabled) {
13782 input.disabled=true;
13785 var inputblock = input;
13787 if(this.hasFeedback && !this.allowBlank){
13791 cls: 'glyphicon form-control-feedback'
13794 if(this.removable && !this.editable ){
13796 cls : 'has-feedback',
13802 cls : 'roo-combo-removable-btn close'
13809 cls : 'has-feedback',
13818 if(this.removable && !this.editable ){
13820 cls : 'roo-removable',
13826 cls : 'roo-combo-removable-btn close'
13833 if (this.before || this.after) {
13836 cls : 'input-group',
13840 inputblock.cn.push({
13842 cls : 'input-group-addon input-group-prepend input-group-text',
13847 inputblock.cn.push(input);
13849 if(this.hasFeedback && !this.allowBlank){
13850 inputblock.cls += ' has-feedback';
13851 inputblock.cn.push(feedback);
13855 inputblock.cn.push({
13857 cls : 'input-group-addon input-group-append input-group-text',
13866 var ibwrap = inputblock;
13871 cls: 'roo-select2-choices',
13875 cls: 'roo-select2-search-field',
13887 cls: 'roo-select2-container input-group',
13892 cls: 'form-hidden-field'
13898 if(!this.multiple && this.showToggleBtn){
13904 if (this.caret != false) {
13907 cls: 'fa fa-' + this.caret
13914 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13916 Roo.bootstrap.version == 3 ? caret : '',
13919 cls: 'combobox-clear',
13933 combobox.cls += ' roo-select2-container-multi';
13937 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13938 tooltip : 'This field is required'
13940 if (Roo.bootstrap.version == 4) {
13943 style : 'display:none'
13948 if (align ==='left' && this.fieldLabel.length) {
13950 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13957 cls : 'control-label',
13958 html : this.fieldLabel
13970 var labelCfg = cfg.cn[1];
13971 var contentCfg = cfg.cn[2];
13973 if(this.indicatorpos == 'right'){
13978 cls : 'control-label',
13982 html : this.fieldLabel
13996 labelCfg = cfg.cn[0];
13997 contentCfg = cfg.cn[1];
14000 if(this.labelWidth > 12){
14001 labelCfg.style = "width: " + this.labelWidth + 'px';
14004 if(this.labelWidth < 13 && this.labelmd == 0){
14005 this.labelmd = this.labelWidth;
14008 if(this.labellg > 0){
14009 labelCfg.cls += ' col-lg-' + this.labellg;
14010 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14013 if(this.labelmd > 0){
14014 labelCfg.cls += ' col-md-' + this.labelmd;
14015 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14018 if(this.labelsm > 0){
14019 labelCfg.cls += ' col-sm-' + this.labelsm;
14020 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14023 if(this.labelxs > 0){
14024 labelCfg.cls += ' col-xs-' + this.labelxs;
14025 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14028 } else if ( this.fieldLabel.length) {
14029 // Roo.log(" label");
14034 //cls : 'input-group-addon',
14035 html : this.fieldLabel
14043 if(this.indicatorpos == 'right'){
14051 html : this.fieldLabel
14065 // Roo.log(" no label && no align");
14072 ['xs','sm','md','lg'].map(function(size){
14073 if (settings[size]) {
14074 cfg.cls += ' col-' + size + '-' + settings[size];
14085 onResize : function(w, h){
14086 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14087 // if(typeof w == 'number'){
14088 // var x = w - this.trigger.getWidth();
14089 // this.inputEl().setWidth(this.adjustWidth('input', x));
14090 // this.trigger.setStyle('left', x+'px');
14095 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14098 getResizeEl : function(){
14099 return this.inputEl();
14103 getPositionEl : function(){
14104 return this.inputEl();
14108 alignErrorIcon : function(){
14109 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14113 initEvents : function(){
14117 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14118 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14119 if(!this.multiple && this.showToggleBtn){
14120 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14121 if(this.hideTrigger){
14122 this.trigger.setDisplayed(false);
14124 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14128 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14131 if(this.removable && !this.editable && !this.tickable){
14132 var close = this.closeTriggerEl();
14135 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14136 close.on('click', this.removeBtnClick, this, close);
14140 //this.trigger.addClassOnOver('x-form-trigger-over');
14141 //this.trigger.addClassOnClick('x-form-trigger-click');
14144 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14148 closeTriggerEl : function()
14150 var close = this.el.select('.roo-combo-removable-btn', true).first();
14151 return close ? close : false;
14154 removeBtnClick : function(e, h, el)
14156 e.preventDefault();
14158 if(this.fireEvent("remove", this) !== false){
14160 this.fireEvent("afterremove", this)
14164 createList : function()
14166 this.list = Roo.get(document.body).createChild({
14167 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14168 cls: 'typeahead typeahead-long dropdown-menu shadow',
14169 style: 'display:none'
14172 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14177 initTrigger : function(){
14182 onDestroy : function(){
14184 this.trigger.removeAllListeners();
14185 // this.trigger.remove();
14188 // this.wrap.remove();
14190 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14194 onFocus : function(){
14195 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14197 if(!this.mimicing){
14198 this.wrap.addClass('x-trigger-wrap-focus');
14199 this.mimicing = true;
14200 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14201 if(this.monitorTab){
14202 this.el.on("keydown", this.checkTab, this);
14209 checkTab : function(e){
14210 if(e.getKey() == e.TAB){
14211 this.triggerBlur();
14216 onBlur : function(){
14221 mimicBlur : function(e, t){
14223 if(!this.wrap.contains(t) && this.validateBlur()){
14224 this.triggerBlur();
14230 triggerBlur : function(){
14231 this.mimicing = false;
14232 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14233 if(this.monitorTab){
14234 this.el.un("keydown", this.checkTab, this);
14236 //this.wrap.removeClass('x-trigger-wrap-focus');
14237 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14241 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14242 validateBlur : function(e, t){
14247 onDisable : function(){
14248 this.inputEl().dom.disabled = true;
14249 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14251 // this.wrap.addClass('x-item-disabled');
14256 onEnable : function(){
14257 this.inputEl().dom.disabled = false;
14258 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14260 // this.el.removeClass('x-item-disabled');
14265 onShow : function(){
14266 var ae = this.getActionEl();
14269 ae.dom.style.display = '';
14270 ae.dom.style.visibility = 'visible';
14276 onHide : function(){
14277 var ae = this.getActionEl();
14278 ae.dom.style.display = 'none';
14282 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14283 * by an implementing function.
14285 * @param {EventObject} e
14287 onTriggerClick : Roo.emptyFn
14295 * @class Roo.bootstrap.form.CardUploader
14296 * @extends Roo.bootstrap.Button
14297 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14298 * @cfg {Number} errorTimeout default 3000
14299 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14300 * @cfg {Array} html The button text.
14304 * Create a new CardUploader
14305 * @param {Object} config The config object
14308 Roo.bootstrap.form.CardUploader = function(config){
14312 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14315 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14323 * When a image is clicked on - and needs to display a slideshow or similar..
14324 * @param {Roo.bootstrap.Card} this
14325 * @param {Object} The image information data
14331 * When a the download link is clicked
14332 * @param {Roo.bootstrap.Card} this
14333 * @param {Object} The image information data contains
14340 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14343 errorTimeout : 3000,
14347 fileCollection : false,
14350 getAutoCreate : function()
14354 cls :'form-group' ,
14359 //cls : 'input-group-addon',
14360 html : this.fieldLabel
14368 value : this.value,
14369 cls : 'd-none form-control'
14374 multiple : 'multiple',
14376 cls : 'd-none roo-card-upload-selector'
14380 cls : 'roo-card-uploader-button-container w-100 mb-2'
14383 cls : 'card-columns roo-card-uploader-container'
14393 getChildContainer : function() /// what children are added to.
14395 return this.containerEl;
14398 getButtonContainer : function() /// what children are added to.
14400 return this.el.select(".roo-card-uploader-button-container").first();
14403 initEvents : function()
14406 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14410 xns: Roo.bootstrap,
14413 container_method : 'getButtonContainer' ,
14414 html : this.html, // fix changable?
14417 'click' : function(btn, e) {
14426 this.urlAPI = (window.createObjectURL && window) ||
14427 (window.URL && URL.revokeObjectURL && URL) ||
14428 (window.webkitURL && webkitURL);
14433 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14435 this.selectorEl.on('change', this.onFileSelected, this);
14438 this.images.forEach(function(img) {
14441 this.images = false;
14443 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14449 onClick : function(e)
14451 e.preventDefault();
14453 this.selectorEl.dom.click();
14457 onFileSelected : function(e)
14459 e.preventDefault();
14461 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14465 Roo.each(this.selectorEl.dom.files, function(file){
14466 this.addFile(file);
14475 addFile : function(file)
14478 if(typeof(file) === 'string'){
14479 throw "Add file by name?"; // should not happen
14483 if(!file || !this.urlAPI){
14493 var url = _this.urlAPI.createObjectURL( file);
14496 id : Roo.bootstrap.form.CardUploader.ID--,
14497 is_uploaded : false,
14501 mimetype : file.type,
14509 * addCard - add an Attachment to the uploader
14510 * @param data - the data about the image to upload
14514 title : "Title of file",
14515 is_uploaded : false,
14516 src : "http://.....",
14517 srcfile : { the File upload object },
14518 mimetype : file.type,
14521 .. any other data...
14527 addCard : function (data)
14529 // hidden input element?
14530 // if the file is not an image...
14531 //then we need to use something other that and header_image
14536 xns : Roo.bootstrap,
14537 xtype : 'CardFooter',
14540 xns : Roo.bootstrap,
14546 xns : Roo.bootstrap,
14548 html : String.format("<small>{0}</small>", data.title),
14549 cls : 'col-10 text-left',
14554 click : function() {
14556 t.fireEvent( "download", t, data );
14562 xns : Roo.bootstrap,
14564 style: 'max-height: 28px; ',
14570 click : function() {
14571 t.removeCard(data.id)
14583 var cn = this.addxtype(
14586 xns : Roo.bootstrap,
14589 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14590 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14591 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14596 initEvents : function() {
14597 Roo.bootstrap.Card.prototype.initEvents.call(this);
14599 this.imgEl = this.el.select('.card-img-top').first();
14601 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14602 this.imgEl.set({ 'pointer' : 'cursor' });
14605 this.getCardFooter().addClass('p-1');
14612 // dont' really need ot update items.
14613 // this.items.push(cn);
14614 this.fileCollection.add(cn);
14616 if (!data.srcfile) {
14617 this.updateInput();
14622 var reader = new FileReader();
14623 reader.addEventListener("load", function() {
14624 data.srcdata = reader.result;
14627 reader.readAsDataURL(data.srcfile);
14632 removeCard : function(id)
14635 var card = this.fileCollection.get(id);
14636 card.data.is_deleted = 1;
14637 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14638 //this.fileCollection.remove(card);
14639 //this.items = this.items.filter(function(e) { return e != card });
14640 // dont' really need ot update items.
14641 card.el.dom.parentNode.removeChild(card.el.dom);
14642 this.updateInput();
14648 this.fileCollection.each(function(card) {
14649 if (card.el.dom && card.el.dom.parentNode) {
14650 card.el.dom.parentNode.removeChild(card.el.dom);
14653 this.fileCollection.clear();
14654 this.updateInput();
14657 updateInput : function()
14660 this.fileCollection.each(function(e) {
14664 this.inputEl().dom.value = JSON.stringify(data);
14674 Roo.bootstrap.form.CardUploader.ID = -1;/*
14676 * Ext JS Library 1.1.1
14677 * Copyright(c) 2006-2007, Ext JS, LLC.
14679 * Originally Released Under LGPL - original licence link has changed is not relivant.
14682 * <script type="text/javascript">
14687 * @class Roo.data.SortTypes
14689 * Defines the default sorting (casting?) comparison functions used when sorting data.
14691 Roo.data.SortTypes = {
14693 * Default sort that does nothing
14694 * @param {Mixed} s The value being converted
14695 * @return {Mixed} The comparison value
14697 none : function(s){
14702 * The regular expression used to strip tags
14706 stripTagsRE : /<\/?[^>]+>/gi,
14709 * Strips all HTML tags to sort on text only
14710 * @param {Mixed} s The value being converted
14711 * @return {String} The comparison value
14713 asText : function(s){
14714 return String(s).replace(this.stripTagsRE, "");
14718 * Strips all HTML tags to sort on text only - Case insensitive
14719 * @param {Mixed} s The value being converted
14720 * @return {String} The comparison value
14722 asUCText : function(s){
14723 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14727 * Case insensitive string
14728 * @param {Mixed} s The value being converted
14729 * @return {String} The comparison value
14731 asUCString : function(s) {
14732 return String(s).toUpperCase();
14737 * @param {Mixed} s The value being converted
14738 * @return {Number} The comparison value
14740 asDate : function(s) {
14744 if(s instanceof Date){
14745 return s.getTime();
14747 return Date.parse(String(s));
14752 * @param {Mixed} s The value being converted
14753 * @return {Float} The comparison value
14755 asFloat : function(s) {
14756 var val = parseFloat(String(s).replace(/,/g, ""));
14765 * @param {Mixed} s The value being converted
14766 * @return {Number} The comparison value
14768 asInt : function(s) {
14769 var val = parseInt(String(s).replace(/,/g, ""));
14777 * Ext JS Library 1.1.1
14778 * Copyright(c) 2006-2007, Ext JS, LLC.
14780 * Originally Released Under LGPL - original licence link has changed is not relivant.
14783 * <script type="text/javascript">
14787 * @class Roo.data.Record
14788 * Instances of this class encapsulate both record <em>definition</em> information, and record
14789 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14790 * to access Records cached in an {@link Roo.data.Store} object.<br>
14792 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14793 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14796 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14798 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14799 * {@link #create}. The parameters are the same.
14800 * @param {Array} data An associative Array of data values keyed by the field name.
14801 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14802 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14803 * not specified an integer id is generated.
14805 Roo.data.Record = function(data, id){
14806 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14811 * Generate a constructor for a specific record layout.
14812 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14813 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14814 * Each field definition object may contain the following properties: <ul>
14815 * <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,
14816 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14817 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14818 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14819 * is being used, then this is a string containing the javascript expression to reference the data relative to
14820 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14821 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14822 * this may be omitted.</p></li>
14823 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14824 * <ul><li>auto (Default, implies no conversion)</li>
14829 * <li>date</li></ul></p></li>
14830 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14831 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14832 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14833 * by the Reader into an object that will be stored in the Record. It is passed the
14834 * following parameters:<ul>
14835 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14837 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14839 * <br>usage:<br><pre><code>
14840 var TopicRecord = Roo.data.Record.create(
14841 {name: 'title', mapping: 'topic_title'},
14842 {name: 'author', mapping: 'username'},
14843 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14844 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14845 {name: 'lastPoster', mapping: 'user2'},
14846 {name: 'excerpt', mapping: 'post_text'}
14849 var myNewRecord = new TopicRecord({
14850 title: 'Do my job please',
14853 lastPost: new Date(),
14854 lastPoster: 'Animal',
14855 excerpt: 'No way dude!'
14857 myStore.add(myNewRecord);
14862 Roo.data.Record.create = function(o){
14863 var f = function(){
14864 f.superclass.constructor.apply(this, arguments);
14866 Roo.extend(f, Roo.data.Record);
14867 var p = f.prototype;
14868 p.fields = new Roo.util.MixedCollection(false, function(field){
14871 for(var i = 0, len = o.length; i < len; i++){
14872 p.fields.add(new Roo.data.Field(o[i]));
14874 f.getField = function(name){
14875 return p.fields.get(name);
14880 Roo.data.Record.AUTO_ID = 1000;
14881 Roo.data.Record.EDIT = 'edit';
14882 Roo.data.Record.REJECT = 'reject';
14883 Roo.data.Record.COMMIT = 'commit';
14885 Roo.data.Record.prototype = {
14887 * Readonly flag - true if this record has been modified.
14896 join : function(store){
14897 this.store = store;
14901 * Set the named field to the specified value.
14902 * @param {String} name The name of the field to set.
14903 * @param {Object} value The value to set the field to.
14905 set : function(name, value){
14906 if(this.data[name] == value){
14910 if(!this.modified){
14911 this.modified = {};
14913 if(typeof this.modified[name] == 'undefined'){
14914 this.modified[name] = this.data[name];
14916 this.data[name] = value;
14917 if(!this.editing && this.store){
14918 this.store.afterEdit(this);
14923 * Get the value of the named field.
14924 * @param {String} name The name of the field to get the value of.
14925 * @return {Object} The value of the field.
14927 get : function(name){
14928 return this.data[name];
14932 beginEdit : function(){
14933 this.editing = true;
14934 this.modified = {};
14938 cancelEdit : function(){
14939 this.editing = false;
14940 delete this.modified;
14944 endEdit : function(){
14945 this.editing = false;
14946 if(this.dirty && this.store){
14947 this.store.afterEdit(this);
14952 * Usually called by the {@link Roo.data.Store} which owns the Record.
14953 * Rejects all changes made to the Record since either creation, or the last commit operation.
14954 * Modified fields are reverted to their original values.
14956 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14957 * of reject operations.
14959 reject : function(){
14960 var m = this.modified;
14962 if(typeof m[n] != "function"){
14963 this.data[n] = m[n];
14966 this.dirty = false;
14967 delete this.modified;
14968 this.editing = false;
14970 this.store.afterReject(this);
14975 * Usually called by the {@link Roo.data.Store} which owns the Record.
14976 * Commits all changes made to the Record since either creation, or the last commit operation.
14978 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14979 * of commit operations.
14981 commit : function(){
14982 this.dirty = false;
14983 delete this.modified;
14984 this.editing = false;
14986 this.store.afterCommit(this);
14991 hasError : function(){
14992 return this.error != null;
14996 clearError : function(){
15001 * Creates a copy of this record.
15002 * @param {String} id (optional) A new record id if you don't want to use this record's id
15005 copy : function(newId) {
15006 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15010 * Ext JS Library 1.1.1
15011 * Copyright(c) 2006-2007, Ext JS, LLC.
15013 * Originally Released Under LGPL - original licence link has changed is not relivant.
15016 * <script type="text/javascript">
15022 * @class Roo.data.Store
15023 * @extends Roo.util.Observable
15024 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15025 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15027 * 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
15028 * has no knowledge of the format of the data returned by the Proxy.<br>
15030 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15031 * instances from the data object. These records are cached and made available through accessor functions.
15033 * Creates a new Store.
15034 * @param {Object} config A config object containing the objects needed for the Store to access data,
15035 * and read the data into Records.
15037 Roo.data.Store = function(config){
15038 this.data = new Roo.util.MixedCollection(false);
15039 this.data.getKey = function(o){
15042 this.baseParams = {};
15044 this.paramNames = {
15049 "multisort" : "_multisort"
15052 if(config && config.data){
15053 this.inlineData = config.data;
15054 delete config.data;
15057 Roo.apply(this, config);
15059 if(this.reader){ // reader passed
15060 this.reader = Roo.factory(this.reader, Roo.data);
15061 this.reader.xmodule = this.xmodule || false;
15062 if(!this.recordType){
15063 this.recordType = this.reader.recordType;
15065 if(this.reader.onMetaChange){
15066 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15070 if(this.recordType){
15071 this.fields = this.recordType.prototype.fields;
15073 this.modified = [];
15077 * @event datachanged
15078 * Fires when the data cache has changed, and a widget which is using this Store
15079 * as a Record cache should refresh its view.
15080 * @param {Store} this
15082 datachanged : true,
15084 * @event metachange
15085 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15086 * @param {Store} this
15087 * @param {Object} meta The JSON metadata
15092 * Fires when Records have been added to the Store
15093 * @param {Store} this
15094 * @param {Roo.data.Record[]} records The array of Records added
15095 * @param {Number} index The index at which the record(s) were added
15100 * Fires when a Record has been removed from the Store
15101 * @param {Store} this
15102 * @param {Roo.data.Record} record The Record that was removed
15103 * @param {Number} index The index at which the record was removed
15108 * Fires when a Record has been updated
15109 * @param {Store} this
15110 * @param {Roo.data.Record} record The Record that was updated
15111 * @param {String} operation The update operation being performed. Value may be one of:
15113 Roo.data.Record.EDIT
15114 Roo.data.Record.REJECT
15115 Roo.data.Record.COMMIT
15121 * Fires when the data cache has been cleared.
15122 * @param {Store} this
15126 * @event beforeload
15127 * Fires before a request is made for a new data object. If the beforeload handler returns false
15128 * the load action will be canceled.
15129 * @param {Store} this
15130 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15134 * @event beforeloadadd
15135 * Fires after a new set of Records has been loaded.
15136 * @param {Store} this
15137 * @param {Roo.data.Record[]} records The Records that were loaded
15138 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15140 beforeloadadd : true,
15143 * Fires after a new set of Records has been loaded, before they are added to the store.
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)
15147 * @params {Object} return from reader
15151 * @event loadexception
15152 * Fires if an exception occurs in the Proxy during loading.
15153 * Called with the signature of the Proxy's "loadexception" event.
15154 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15157 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15158 * @param {Object} load options
15159 * @param {Object} jsonData from your request (normally this contains the Exception)
15161 loadexception : true
15165 this.proxy = Roo.factory(this.proxy, Roo.data);
15166 this.proxy.xmodule = this.xmodule || false;
15167 this.relayEvents(this.proxy, ["loadexception"]);
15169 this.sortToggle = {};
15170 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15172 Roo.data.Store.superclass.constructor.call(this);
15174 if(this.inlineData){
15175 this.loadData(this.inlineData);
15176 delete this.inlineData;
15180 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15182 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15183 * without a remote query - used by combo/forms at present.
15187 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15190 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15193 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15194 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15197 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15198 * on any HTTP request
15201 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15204 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15208 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15209 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15211 remoteSort : false,
15214 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15215 * loaded or when a record is removed. (defaults to false).
15217 pruneModifiedRecords : false,
15220 lastOptions : null,
15223 * Add Records to the Store and fires the add event.
15224 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15226 add : function(records){
15227 records = [].concat(records);
15228 for(var i = 0, len = records.length; i < len; i++){
15229 records[i].join(this);
15231 var index = this.data.length;
15232 this.data.addAll(records);
15233 this.fireEvent("add", this, records, index);
15237 * Remove a Record from the Store and fires the remove event.
15238 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15240 remove : function(record){
15241 var index = this.data.indexOf(record);
15242 this.data.removeAt(index);
15244 if(this.pruneModifiedRecords){
15245 this.modified.remove(record);
15247 this.fireEvent("remove", this, record, index);
15251 * Remove all Records from the Store and fires the clear event.
15253 removeAll : function(){
15255 if(this.pruneModifiedRecords){
15256 this.modified = [];
15258 this.fireEvent("clear", this);
15262 * Inserts Records to the Store at the given index and fires the add event.
15263 * @param {Number} index The start index at which to insert the passed Records.
15264 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15266 insert : function(index, records){
15267 records = [].concat(records);
15268 for(var i = 0, len = records.length; i < len; i++){
15269 this.data.insert(index, records[i]);
15270 records[i].join(this);
15272 this.fireEvent("add", this, records, index);
15276 * Get the index within the cache of the passed Record.
15277 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15278 * @return {Number} The index of the passed Record. Returns -1 if not found.
15280 indexOf : function(record){
15281 return this.data.indexOf(record);
15285 * Get the index within the cache of the Record with the passed id.
15286 * @param {String} id The id of the Record to find.
15287 * @return {Number} The index of the Record. Returns -1 if not found.
15289 indexOfId : function(id){
15290 return this.data.indexOfKey(id);
15294 * Get the Record with the specified id.
15295 * @param {String} id The id of the Record to find.
15296 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15298 getById : function(id){
15299 return this.data.key(id);
15303 * Get the Record at the specified index.
15304 * @param {Number} index The index of the Record to find.
15305 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15307 getAt : function(index){
15308 return this.data.itemAt(index);
15312 * Returns a range of Records between specified indices.
15313 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15314 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15315 * @return {Roo.data.Record[]} An array of Records
15317 getRange : function(start, end){
15318 return this.data.getRange(start, end);
15322 storeOptions : function(o){
15323 o = Roo.apply({}, o);
15326 this.lastOptions = o;
15330 * Loads the Record cache from the configured Proxy using the configured Reader.
15332 * If using remote paging, then the first load call must specify the <em>start</em>
15333 * and <em>limit</em> properties in the options.params property to establish the initial
15334 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15336 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15337 * and this call will return before the new data has been loaded. Perform any post-processing
15338 * in a callback function, or in a "load" event handler.</strong>
15340 * @param {Object} options An object containing properties which control loading options:<ul>
15341 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15342 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15343 * passed the following arguments:<ul>
15344 * <li>r : Roo.data.Record[]</li>
15345 * <li>options: Options object from the load call</li>
15346 * <li>success: Boolean success indicator</li></ul></li>
15347 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15348 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15351 load : function(options){
15352 options = options || {};
15353 if(this.fireEvent("beforeload", this, options) !== false){
15354 this.storeOptions(options);
15355 var p = Roo.apply(options.params || {}, this.baseParams);
15356 // if meta was not loaded from remote source.. try requesting it.
15357 if (!this.reader.metaFromRemote) {
15358 p._requestMeta = 1;
15360 if(this.sortInfo && this.remoteSort){
15361 var pn = this.paramNames;
15362 p[pn["sort"]] = this.sortInfo.field;
15363 p[pn["dir"]] = this.sortInfo.direction;
15365 if (this.multiSort) {
15366 var pn = this.paramNames;
15367 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15370 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15375 * Reloads the Record cache from the configured Proxy using the configured Reader and
15376 * the options from the last load operation performed.
15377 * @param {Object} options (optional) An object containing properties which may override the options
15378 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15379 * the most recently used options are reused).
15381 reload : function(options){
15382 this.load(Roo.applyIf(options||{}, this.lastOptions));
15386 // Called as a callback by the Reader during a load operation.
15387 loadRecords : function(o, options, success){
15388 if(!o || success === false){
15389 if(success !== false){
15390 this.fireEvent("load", this, [], options, o);
15392 if(options.callback){
15393 options.callback.call(options.scope || this, [], options, false);
15397 // if data returned failure - throw an exception.
15398 if (o.success === false) {
15399 // show a message if no listener is registered.
15400 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15401 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15403 // loadmask wil be hooked into this..
15404 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15407 var r = o.records, t = o.totalRecords || r.length;
15409 this.fireEvent("beforeloadadd", this, r, options, o);
15411 if(!options || options.add !== true){
15412 if(this.pruneModifiedRecords){
15413 this.modified = [];
15415 for(var i = 0, len = r.length; i < len; i++){
15419 this.data = this.snapshot;
15420 delete this.snapshot;
15423 this.data.addAll(r);
15424 this.totalLength = t;
15426 this.fireEvent("datachanged", this);
15428 this.totalLength = Math.max(t, this.data.length+r.length);
15432 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15434 var e = new Roo.data.Record({});
15436 e.set(this.parent.displayField, this.parent.emptyTitle);
15437 e.set(this.parent.valueField, '');
15442 this.fireEvent("load", this, r, options, o);
15443 if(options.callback){
15444 options.callback.call(options.scope || this, r, options, true);
15450 * Loads data from a passed data block. A Reader which understands the format of the data
15451 * must have been configured in the constructor.
15452 * @param {Object} data The data block from which to read the Records. The format of the data expected
15453 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15454 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15456 loadData : function(o, append){
15457 var r = this.reader.readRecords(o);
15458 this.loadRecords(r, {add: append}, true);
15462 * using 'cn' the nested child reader read the child array into it's child stores.
15463 * @param {Object} rec The record with a 'children array
15465 loadDataFromChildren : function(rec)
15467 this.loadData(this.reader.toLoadData(rec));
15472 * Gets the number of cached records.
15474 * <em>If using paging, this may not be the total size of the dataset. If the data object
15475 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15476 * the data set size</em>
15478 getCount : function(){
15479 return this.data.length || 0;
15483 * Gets the total number of records in the dataset as returned by the server.
15485 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15486 * the dataset size</em>
15488 getTotalCount : function(){
15489 return this.totalLength || 0;
15493 * Returns the sort state of the Store as an object with two properties:
15495 field {String} The name of the field by which the Records are sorted
15496 direction {String} The sort order, "ASC" or "DESC"
15499 getSortState : function(){
15500 return this.sortInfo;
15504 applySort : function(){
15505 if(this.sortInfo && !this.remoteSort){
15506 var s = this.sortInfo, f = s.field;
15507 var st = this.fields.get(f).sortType;
15508 var fn = function(r1, r2){
15509 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15510 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15512 this.data.sort(s.direction, fn);
15513 if(this.snapshot && this.snapshot != this.data){
15514 this.snapshot.sort(s.direction, fn);
15520 * Sets the default sort column and order to be used by the next load operation.
15521 * @param {String} fieldName The name of the field to sort by.
15522 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15524 setDefaultSort : function(field, dir){
15525 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15529 * Sort the Records.
15530 * If remote sorting is used, the sort is performed on the server, and the cache is
15531 * reloaded. If local sorting is used, the cache is sorted internally.
15532 * @param {String} fieldName The name of the field to sort by.
15533 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15535 sort : function(fieldName, dir){
15536 var f = this.fields.get(fieldName);
15538 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15540 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15541 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15546 this.sortToggle[f.name] = dir;
15547 this.sortInfo = {field: f.name, direction: dir};
15548 if(!this.remoteSort){
15550 this.fireEvent("datachanged", this);
15552 this.load(this.lastOptions);
15557 * Calls the specified function for each of the Records in the cache.
15558 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15559 * Returning <em>false</em> aborts and exits the iteration.
15560 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15562 each : function(fn, scope){
15563 this.data.each(fn, scope);
15567 * Gets all records modified since the last commit. Modified records are persisted across load operations
15568 * (e.g., during paging).
15569 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15571 getModifiedRecords : function(){
15572 return this.modified;
15576 createFilterFn : function(property, value, anyMatch){
15577 if(!value.exec){ // not a regex
15578 value = String(value);
15579 if(value.length == 0){
15582 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15584 return function(r){
15585 return value.test(r.data[property]);
15590 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15591 * @param {String} property A field on your records
15592 * @param {Number} start The record index to start at (defaults to 0)
15593 * @param {Number} end The last record index to include (defaults to length - 1)
15594 * @return {Number} The sum
15596 sum : function(property, start, end){
15597 var rs = this.data.items, v = 0;
15598 start = start || 0;
15599 end = (end || end === 0) ? end : rs.length-1;
15601 for(var i = start; i <= end; i++){
15602 v += (rs[i].data[property] || 0);
15608 * Filter the records by a specified property.
15609 * @param {String} field A field on your records
15610 * @param {String/RegExp} value Either a string that the field
15611 * should start with or a RegExp to test against the field
15612 * @param {Boolean} anyMatch True to match any part not just the beginning
15614 filter : function(property, value, anyMatch){
15615 var fn = this.createFilterFn(property, value, anyMatch);
15616 return fn ? this.filterBy(fn) : this.clearFilter();
15620 * Filter by a function. The specified function will be called with each
15621 * record in this data source. If the function returns true the record is included,
15622 * otherwise it is filtered.
15623 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15624 * @param {Object} scope (optional) The scope of the function (defaults to this)
15626 filterBy : function(fn, scope){
15627 this.snapshot = this.snapshot || this.data;
15628 this.data = this.queryBy(fn, scope||this);
15629 this.fireEvent("datachanged", this);
15633 * Query the records by a specified property.
15634 * @param {String} field A field on your records
15635 * @param {String/RegExp} value Either a string that the field
15636 * should start with or a RegExp to test against the field
15637 * @param {Boolean} anyMatch True to match any part not just the beginning
15638 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15640 query : function(property, value, anyMatch){
15641 var fn = this.createFilterFn(property, value, anyMatch);
15642 return fn ? this.queryBy(fn) : this.data.clone();
15646 * Query by a function. The specified function will be called with each
15647 * record in this data source. If the function returns true the record is included
15649 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15650 * @param {Object} scope (optional) The scope of the function (defaults to this)
15651 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15653 queryBy : function(fn, scope){
15654 var data = this.snapshot || this.data;
15655 return data.filterBy(fn, scope||this);
15659 * Collects unique values for a particular dataIndex from this store.
15660 * @param {String} dataIndex The property to collect
15661 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15662 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15663 * @return {Array} An array of the unique values
15665 collect : function(dataIndex, allowNull, bypassFilter){
15666 var d = (bypassFilter === true && this.snapshot) ?
15667 this.snapshot.items : this.data.items;
15668 var v, sv, r = [], l = {};
15669 for(var i = 0, len = d.length; i < len; i++){
15670 v = d[i].data[dataIndex];
15672 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15681 * Revert to a view of the Record cache with no filtering applied.
15682 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15684 clearFilter : function(suppressEvent){
15685 if(this.snapshot && this.snapshot != this.data){
15686 this.data = this.snapshot;
15687 delete this.snapshot;
15688 if(suppressEvent !== true){
15689 this.fireEvent("datachanged", this);
15695 afterEdit : function(record){
15696 if(this.modified.indexOf(record) == -1){
15697 this.modified.push(record);
15699 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15703 afterReject : function(record){
15704 this.modified.remove(record);
15705 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15709 afterCommit : function(record){
15710 this.modified.remove(record);
15711 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15715 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15716 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15718 commitChanges : function(){
15719 var m = this.modified.slice(0);
15720 this.modified = [];
15721 for(var i = 0, len = m.length; i < len; i++){
15727 * Cancel outstanding changes on all changed records.
15729 rejectChanges : function(){
15730 var m = this.modified.slice(0);
15731 this.modified = [];
15732 for(var i = 0, len = m.length; i < len; i++){
15737 onMetaChange : function(meta, rtype, o){
15738 this.recordType = rtype;
15739 this.fields = rtype.prototype.fields;
15740 delete this.snapshot;
15741 this.sortInfo = meta.sortInfo || this.sortInfo;
15742 this.modified = [];
15743 this.fireEvent('metachange', this, this.reader.meta);
15746 moveIndex : function(data, type)
15748 var index = this.indexOf(data);
15750 var newIndex = index + type;
15754 this.insert(newIndex, data);
15759 * Ext JS Library 1.1.1
15760 * Copyright(c) 2006-2007, Ext JS, LLC.
15762 * Originally Released Under LGPL - original licence link has changed is not relivant.
15765 * <script type="text/javascript">
15769 * @class Roo.data.SimpleStore
15770 * @extends Roo.data.Store
15771 * Small helper class to make creating Stores from Array data easier.
15772 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15773 * @cfg {Array} fields An array of field definition objects, or field name strings.
15774 * @cfg {Object} an existing reader (eg. copied from another store)
15775 * @cfg {Array} data The multi-dimensional array of data
15776 * @cfg {Roo.data.DataProxy} proxy [not-required]
15777 * @cfg {Roo.data.Reader} reader [not-required]
15779 * @param {Object} config
15781 Roo.data.SimpleStore = function(config)
15783 Roo.data.SimpleStore.superclass.constructor.call(this, {
15785 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15788 Roo.data.Record.create(config.fields)
15790 proxy : new Roo.data.MemoryProxy(config.data)
15794 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15796 * Ext JS Library 1.1.1
15797 * Copyright(c) 2006-2007, Ext JS, LLC.
15799 * Originally Released Under LGPL - original licence link has changed is not relivant.
15802 * <script type="text/javascript">
15807 * @extends Roo.data.Store
15808 * @class Roo.data.JsonStore
15809 * Small helper class to make creating Stores for JSON data easier. <br/>
15811 var store = new Roo.data.JsonStore({
15812 url: 'get-images.php',
15814 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15817 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15818 * JsonReader and HttpProxy (unless inline data is provided).</b>
15819 * @cfg {Array} fields An array of field definition objects, or field name strings.
15821 * @param {Object} config
15823 Roo.data.JsonStore = function(c){
15824 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15825 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15826 reader: new Roo.data.JsonReader(c, c.fields)
15829 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15831 * Ext JS Library 1.1.1
15832 * Copyright(c) 2006-2007, Ext JS, LLC.
15834 * Originally Released Under LGPL - original licence link has changed is not relivant.
15837 * <script type="text/javascript">
15841 Roo.data.Field = function(config){
15842 if(typeof config == "string"){
15843 config = {name: config};
15845 Roo.apply(this, config);
15848 this.type = "auto";
15851 var st = Roo.data.SortTypes;
15852 // named sortTypes are supported, here we look them up
15853 if(typeof this.sortType == "string"){
15854 this.sortType = st[this.sortType];
15857 // set default sortType for strings and dates
15858 if(!this.sortType){
15861 this.sortType = st.asUCString;
15864 this.sortType = st.asDate;
15867 this.sortType = st.none;
15872 var stripRe = /[\$,%]/g;
15874 // prebuilt conversion function for this field, instead of
15875 // switching every time we're reading a value
15877 var cv, dateFormat = this.dateFormat;
15882 cv = function(v){ return v; };
15885 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15889 return v !== undefined && v !== null && v !== '' ?
15890 parseInt(String(v).replace(stripRe, ""), 10) : '';
15895 return v !== undefined && v !== null && v !== '' ?
15896 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15901 cv = function(v){ return v === true || v === "true" || v == 1; };
15908 if(v instanceof Date){
15912 if(dateFormat == "timestamp"){
15913 return new Date(v*1000);
15915 return Date.parseDate(v, dateFormat);
15917 var parsed = Date.parse(v);
15918 return parsed ? new Date(parsed) : null;
15927 Roo.data.Field.prototype = {
15935 * Ext JS Library 1.1.1
15936 * Copyright(c) 2006-2007, Ext JS, LLC.
15938 * Originally Released Under LGPL - original licence link has changed is not relivant.
15941 * <script type="text/javascript">
15944 // Base class for reading structured data from a data source. This class is intended to be
15945 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15948 * @class Roo.data.DataReader
15950 * Base class for reading structured data from a data source. This class is intended to be
15951 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15954 Roo.data.DataReader = function(meta, recordType){
15958 this.recordType = recordType instanceof Array ?
15959 Roo.data.Record.create(recordType) : recordType;
15962 Roo.data.DataReader.prototype = {
15965 readerType : 'Data',
15967 * Create an empty record
15968 * @param {Object} data (optional) - overlay some values
15969 * @return {Roo.data.Record} record created.
15971 newRow : function(d) {
15973 this.recordType.prototype.fields.each(function(c) {
15975 case 'int' : da[c.name] = 0; break;
15976 case 'date' : da[c.name] = new Date(); break;
15977 case 'float' : da[c.name] = 0.0; break;
15978 case 'boolean' : da[c.name] = false; break;
15979 default : da[c.name] = ""; break;
15983 return new this.recordType(Roo.apply(da, d));
15989 * Ext JS Library 1.1.1
15990 * Copyright(c) 2006-2007, Ext JS, LLC.
15992 * Originally Released Under LGPL - original licence link has changed is not relivant.
15995 * <script type="text/javascript">
15999 * @class Roo.data.DataProxy
16000 * @extends Roo.util.Observable
16002 * This class is an abstract base class for implementations which provide retrieval of
16003 * unformatted data objects.<br>
16005 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16006 * (of the appropriate type which knows how to parse the data object) to provide a block of
16007 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16009 * Custom implementations must implement the load method as described in
16010 * {@link Roo.data.HttpProxy#load}.
16012 Roo.data.DataProxy = function(){
16015 * @event beforeload
16016 * Fires before a network request is made to retrieve a data object.
16017 * @param {Object} This DataProxy object.
16018 * @param {Object} params The params parameter to the load function.
16023 * Fires before the load method's callback is called.
16024 * @param {Object} This DataProxy object.
16025 * @param {Object} o The data object.
16026 * @param {Object} arg The callback argument object passed to the load function.
16030 * @event loadexception
16031 * Fires if an Exception occurs during data retrieval.
16032 * @param {Object} This DataProxy object.
16033 * @param {Object} o The data object.
16034 * @param {Object} arg The callback argument object passed to the load function.
16035 * @param {Object} e The Exception.
16037 loadexception : true
16039 Roo.data.DataProxy.superclass.constructor.call(this);
16042 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16045 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16049 * Ext JS Library 1.1.1
16050 * Copyright(c) 2006-2007, Ext JS, LLC.
16052 * Originally Released Under LGPL - original licence link has changed is not relivant.
16055 * <script type="text/javascript">
16058 * @class Roo.data.MemoryProxy
16059 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16060 * to the Reader when its load method is called.
16062 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16064 Roo.data.MemoryProxy = function(data){
16068 Roo.data.MemoryProxy.superclass.constructor.call(this);
16072 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16075 * Load data from the requested source (in this case an in-memory
16076 * data object passed to the constructor), read the data object into
16077 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16078 * process that block using the passed callback.
16079 * @param {Object} params This parameter is not used by the MemoryProxy class.
16080 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16081 * object into a block of Roo.data.Records.
16082 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16083 * The function must be passed <ul>
16084 * <li>The Record block object</li>
16085 * <li>The "arg" argument from the load function</li>
16086 * <li>A boolean success indicator</li>
16088 * @param {Object} scope The scope in which to call the callback
16089 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16091 load : function(params, reader, callback, scope, arg){
16092 params = params || {};
16095 result = reader.readRecords(params.data ? params.data :this.data);
16097 this.fireEvent("loadexception", this, arg, null, e);
16098 callback.call(scope, null, arg, false);
16101 callback.call(scope, result, arg, true);
16105 update : function(params, records){
16110 * Ext JS Library 1.1.1
16111 * Copyright(c) 2006-2007, Ext JS, LLC.
16113 * Originally Released Under LGPL - original licence link has changed is not relivant.
16116 * <script type="text/javascript">
16119 * @class Roo.data.HttpProxy
16120 * @extends Roo.data.DataProxy
16121 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16122 * configured to reference a certain URL.<br><br>
16124 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16125 * from which the running page was served.<br><br>
16127 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16129 * Be aware that to enable the browser to parse an XML document, the server must set
16130 * the Content-Type header in the HTTP response to "text/xml".
16132 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16133 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16134 * will be used to make the request.
16136 Roo.data.HttpProxy = function(conn){
16137 Roo.data.HttpProxy.superclass.constructor.call(this);
16138 // is conn a conn config or a real conn?
16140 this.useAjax = !conn || !conn.events;
16144 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16145 // thse are take from connection...
16148 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16151 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16152 * extra parameters to each request made by this object. (defaults to undefined)
16155 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16156 * to each request made by this object. (defaults to undefined)
16159 * @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)
16162 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16165 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16171 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16175 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16176 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16177 * a finer-grained basis than the DataProxy events.
16179 getConnection : function(){
16180 return this.useAjax ? Roo.Ajax : this.conn;
16184 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16185 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16186 * process that block using the passed callback.
16187 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16188 * for the request to the remote server.
16189 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16190 * object into a block of Roo.data.Records.
16191 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16192 * The function must be passed <ul>
16193 * <li>The Record block object</li>
16194 * <li>The "arg" argument from the load function</li>
16195 * <li>A boolean success indicator</li>
16197 * @param {Object} scope The scope in which to call the callback
16198 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16200 load : function(params, reader, callback, scope, arg){
16201 if(this.fireEvent("beforeload", this, params) !== false){
16203 params : params || {},
16205 callback : callback,
16210 callback : this.loadResponse,
16214 Roo.applyIf(o, this.conn);
16215 if(this.activeRequest){
16216 Roo.Ajax.abort(this.activeRequest);
16218 this.activeRequest = Roo.Ajax.request(o);
16220 this.conn.request(o);
16223 callback.call(scope||this, null, arg, false);
16228 loadResponse : function(o, success, response){
16229 delete this.activeRequest;
16231 this.fireEvent("loadexception", this, o, response);
16232 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16237 result = o.reader.read(response);
16239 this.fireEvent("loadexception", this, o, response, e);
16240 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16244 this.fireEvent("load", this, o, o.request.arg);
16245 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16249 update : function(dataSet){
16254 updateResponse : function(dataSet){
16259 * Ext JS Library 1.1.1
16260 * Copyright(c) 2006-2007, Ext JS, LLC.
16262 * Originally Released Under LGPL - original licence link has changed is not relivant.
16265 * <script type="text/javascript">
16269 * @class Roo.data.ScriptTagProxy
16270 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16271 * other than the originating domain of the running page.<br><br>
16273 * <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
16274 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16276 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16277 * source code that is used as the source inside a <script> tag.<br><br>
16279 * In order for the browser to process the returned data, the server must wrap the data object
16280 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16281 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16282 * depending on whether the callback name was passed:
16285 boolean scriptTag = false;
16286 String cb = request.getParameter("callback");
16289 response.setContentType("text/javascript");
16291 response.setContentType("application/x-json");
16293 Writer out = response.getWriter();
16295 out.write(cb + "(");
16297 out.print(dataBlock.toJsonString());
16304 * @param {Object} config A configuration object.
16306 Roo.data.ScriptTagProxy = function(config){
16307 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16308 Roo.apply(this, config);
16309 this.head = document.getElementsByTagName("head")[0];
16312 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16314 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16316 * @cfg {String} url The URL from which to request the data object.
16319 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16323 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16324 * the server the name of the callback function set up by the load call to process the returned data object.
16325 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16326 * javascript output which calls this named function passing the data object as its only parameter.
16328 callbackParam : "callback",
16330 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16331 * name to the request.
16336 * Load data from the configured URL, read the data object into
16337 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16338 * process that block using the passed callback.
16339 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16340 * for the request to the remote server.
16341 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16342 * object into a block of Roo.data.Records.
16343 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16344 * The function must be passed <ul>
16345 * <li>The Record block object</li>
16346 * <li>The "arg" argument from the load function</li>
16347 * <li>A boolean success indicator</li>
16349 * @param {Object} scope The scope in which to call the callback
16350 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16352 load : function(params, reader, callback, scope, arg){
16353 if(this.fireEvent("beforeload", this, params) !== false){
16355 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16357 var url = this.url;
16358 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16360 url += "&_dc=" + (new Date().getTime());
16362 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16365 cb : "stcCallback"+transId,
16366 scriptId : "stcScript"+transId,
16370 callback : callback,
16376 window[trans.cb] = function(o){
16377 conn.handleResponse(o, trans);
16380 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16382 if(this.autoAbort !== false){
16386 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16388 var script = document.createElement("script");
16389 script.setAttribute("src", url);
16390 script.setAttribute("type", "text/javascript");
16391 script.setAttribute("id", trans.scriptId);
16392 this.head.appendChild(script);
16394 this.trans = trans;
16396 callback.call(scope||this, null, arg, false);
16401 isLoading : function(){
16402 return this.trans ? true : false;
16406 * Abort the current server request.
16408 abort : function(){
16409 if(this.isLoading()){
16410 this.destroyTrans(this.trans);
16415 destroyTrans : function(trans, isLoaded){
16416 this.head.removeChild(document.getElementById(trans.scriptId));
16417 clearTimeout(trans.timeoutId);
16419 window[trans.cb] = undefined;
16421 delete window[trans.cb];
16424 // if hasn't been loaded, wait for load to remove it to prevent script error
16425 window[trans.cb] = function(){
16426 window[trans.cb] = undefined;
16428 delete window[trans.cb];
16435 handleResponse : function(o, trans){
16436 this.trans = false;
16437 this.destroyTrans(trans, true);
16440 result = trans.reader.readRecords(o);
16442 this.fireEvent("loadexception", this, o, trans.arg, e);
16443 trans.callback.call(trans.scope||window, null, trans.arg, false);
16446 this.fireEvent("load", this, o, trans.arg);
16447 trans.callback.call(trans.scope||window, result, trans.arg, true);
16451 handleFailure : function(trans){
16452 this.trans = false;
16453 this.destroyTrans(trans, false);
16454 this.fireEvent("loadexception", this, null, trans.arg);
16455 trans.callback.call(trans.scope||window, null, trans.arg, false);
16459 * Ext JS Library 1.1.1
16460 * Copyright(c) 2006-2007, Ext JS, LLC.
16462 * Originally Released Under LGPL - original licence link has changed is not relivant.
16465 * <script type="text/javascript">
16469 * @class Roo.data.JsonReader
16470 * @extends Roo.data.DataReader
16471 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16472 * based on mappings in a provided Roo.data.Record constructor.
16474 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16475 * in the reply previously.
16480 var RecordDef = Roo.data.Record.create([
16481 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16482 {name: 'occupation'} // This field will use "occupation" as the mapping.
16484 var myReader = new Roo.data.JsonReader({
16485 totalProperty: "results", // The property which contains the total dataset size (optional)
16486 root: "rows", // The property which contains an Array of row objects
16487 id: "id" // The property within each row object that provides an ID for the record (optional)
16491 * This would consume a JSON file like this:
16493 { 'results': 2, 'rows': [
16494 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16495 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16498 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16499 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16500 * paged from the remote server.
16501 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16502 * @cfg {String} root name of the property which contains the Array of row objects.
16503 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16504 * @cfg {Array} fields Array of field definition objects
16506 * Create a new JsonReader
16507 * @param {Object} meta Metadata configuration options
16508 * @param {Object} recordType Either an Array of field definition objects,
16509 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16511 Roo.data.JsonReader = function(meta, recordType){
16514 // set some defaults:
16515 Roo.applyIf(meta, {
16516 totalProperty: 'total',
16517 successProperty : 'success',
16522 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16524 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16526 readerType : 'Json',
16529 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16530 * Used by Store query builder to append _requestMeta to params.
16533 metaFromRemote : false,
16535 * This method is only used by a DataProxy which has retrieved data from a remote server.
16536 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16537 * @return {Object} data A data block which is used by an Roo.data.Store object as
16538 * a cache of Roo.data.Records.
16540 read : function(response){
16541 var json = response.responseText;
16543 var o = /* eval:var:o */ eval("("+json+")");
16545 throw {message: "JsonReader.read: Json object not found"};
16551 this.metaFromRemote = true;
16552 this.meta = o.metaData;
16553 this.recordType = Roo.data.Record.create(o.metaData.fields);
16554 this.onMetaChange(this.meta, this.recordType, o);
16556 return this.readRecords(o);
16559 // private function a store will implement
16560 onMetaChange : function(meta, recordType, o){
16567 simpleAccess: function(obj, subsc) {
16574 getJsonAccessor: function(){
16576 return function(expr) {
16578 return(re.test(expr))
16579 ? new Function("obj", "return obj." + expr)
16584 return Roo.emptyFn;
16589 * Create a data block containing Roo.data.Records from an XML document.
16590 * @param {Object} o An object which contains an Array of row objects in the property specified
16591 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16592 * which contains the total size of the dataset.
16593 * @return {Object} data A data block which is used by an Roo.data.Store object as
16594 * a cache of Roo.data.Records.
16596 readRecords : function(o){
16598 * After any data loads, the raw JSON data is available for further custom processing.
16602 var s = this.meta, Record = this.recordType,
16603 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16605 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16607 if(s.totalProperty) {
16608 this.getTotal = this.getJsonAccessor(s.totalProperty);
16610 if(s.successProperty) {
16611 this.getSuccess = this.getJsonAccessor(s.successProperty);
16613 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16615 var g = this.getJsonAccessor(s.id);
16616 this.getId = function(rec) {
16618 return (r === undefined || r === "") ? null : r;
16621 this.getId = function(){return null;};
16624 for(var jj = 0; jj < fl; jj++){
16626 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16627 this.ef[jj] = this.getJsonAccessor(map);
16631 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16632 if(s.totalProperty){
16633 var vt = parseInt(this.getTotal(o), 10);
16638 if(s.successProperty){
16639 var vs = this.getSuccess(o);
16640 if(vs === false || vs === 'false'){
16645 for(var i = 0; i < c; i++){
16648 var id = this.getId(n);
16649 for(var j = 0; j < fl; j++){
16651 var v = this.ef[j](n);
16653 Roo.log('missing convert for ' + f.name);
16657 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16659 var record = new Record(values, id);
16661 records[i] = record;
16667 totalRecords : totalRecords
16670 // used when loading children.. @see loadDataFromChildren
16671 toLoadData: function(rec)
16673 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16674 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16675 return { data : data, total : data.length };
16680 * Ext JS Library 1.1.1
16681 * Copyright(c) 2006-2007, Ext JS, LLC.
16683 * Originally Released Under LGPL - original licence link has changed is not relivant.
16686 * <script type="text/javascript">
16690 * @class Roo.data.ArrayReader
16691 * @extends Roo.data.DataReader
16692 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16693 * Each element of that Array represents a row of data fields. The
16694 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16695 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16699 var RecordDef = Roo.data.Record.create([
16700 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16701 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16703 var myReader = new Roo.data.ArrayReader({
16704 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16708 * This would consume an Array like this:
16710 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16714 * Create a new JsonReader
16715 * @param {Object} meta Metadata configuration options.
16716 * @param {Object|Array} recordType Either an Array of field definition objects
16718 * @cfg {Array} fields Array of field definition objects
16719 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16720 * as specified to {@link Roo.data.Record#create},
16721 * or an {@link Roo.data.Record} object
16724 * created using {@link Roo.data.Record#create}.
16726 Roo.data.ArrayReader = function(meta, recordType)
16728 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16731 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16734 * Create a data block containing Roo.data.Records from an XML document.
16735 * @param {Object} o An Array of row objects which represents the dataset.
16736 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16737 * a cache of Roo.data.Records.
16739 readRecords : function(o)
16741 var sid = this.meta ? this.meta.id : null;
16742 var recordType = this.recordType, fields = recordType.prototype.fields;
16745 for(var i = 0; i < root.length; i++){
16748 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16749 for(var j = 0, jlen = fields.length; j < jlen; j++){
16750 var f = fields.items[j];
16751 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16752 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16754 values[f.name] = v;
16756 var record = new recordType(values, id);
16758 records[records.length] = record;
16762 totalRecords : records.length
16765 // used when loading children.. @see loadDataFromChildren
16766 toLoadData: function(rec)
16768 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16769 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16780 * @class Roo.bootstrap.form.ComboBox
16781 * @extends Roo.bootstrap.form.TriggerField
16782 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16783 * @cfg {Boolean} append (true|false) default false
16784 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16785 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16786 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16787 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16788 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16789 * @cfg {Boolean} animate default true
16790 * @cfg {Boolean} emptyResultText only for touch device
16791 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16792 * @cfg {String} emptyTitle default ''
16793 * @cfg {Number} width fixed with? experimental
16795 * Create a new ComboBox.
16796 * @param {Object} config Configuration options
16798 Roo.bootstrap.form.ComboBox = function(config){
16799 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16803 * Fires when the dropdown list is expanded
16804 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16809 * Fires when the dropdown list is collapsed
16810 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16814 * @event beforeselect
16815 * Fires before a list item is selected. Return false to cancel the selection.
16816 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16817 * @param {Roo.data.Record} record The data record returned from the underlying store
16818 * @param {Number} index The index of the selected item in the dropdown list
16820 'beforeselect' : true,
16823 * Fires when a list item is selected
16824 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16825 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16826 * @param {Number} index The index of the selected item in the dropdown list
16830 * @event beforequery
16831 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16832 * The event object passed has these properties:
16833 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16834 * @param {String} query The query
16835 * @param {Boolean} forceAll true to force "all" query
16836 * @param {Boolean} cancel true to cancel the query
16837 * @param {Object} e The query event object
16839 'beforequery': true,
16842 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16843 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16848 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16849 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16850 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16855 * Fires when the remove value from the combobox array
16856 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16860 * @event afterremove
16861 * Fires when the remove value from the combobox array
16862 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16864 'afterremove' : true,
16866 * @event specialfilter
16867 * Fires when specialfilter
16868 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16870 'specialfilter' : true,
16873 * Fires when tick the element
16874 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16878 * @event touchviewdisplay
16879 * Fires when touch view require special display (default is using displayField)
16880 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881 * @param {Object} cfg set html .
16883 'touchviewdisplay' : true
16888 this.tickItems = [];
16890 this.selectedIndex = -1;
16891 if(this.mode == 'local'){
16892 if(config.queryDelay === undefined){
16893 this.queryDelay = 10;
16895 if(config.minChars === undefined){
16901 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16904 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16905 * rendering into an Roo.Editor, defaults to false)
16908 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16909 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16912 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16915 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16916 * the dropdown list (defaults to undefined, with no header element)
16920 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16924 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16926 listWidth: undefined,
16928 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16929 * mode = 'remote' or 'text' if mode = 'local')
16931 displayField: undefined,
16934 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16935 * mode = 'remote' or 'value' if mode = 'local').
16936 * Note: use of a valueField requires the user make a selection
16937 * in order for a value to be mapped.
16939 valueField: undefined,
16941 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16946 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16947 * field's data value (defaults to the underlying DOM element's name)
16949 hiddenName: undefined,
16951 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16955 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16957 selectedClass: 'active',
16960 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16964 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16965 * anchor positions (defaults to 'tl-bl')
16967 listAlign: 'tl-bl?',
16969 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16973 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16974 * query specified by the allQuery config option (defaults to 'query')
16976 triggerAction: 'query',
16978 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16979 * (defaults to 4, does not apply if editable = false)
16983 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16984 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16988 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16989 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16993 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16994 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16998 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16999 * when editable = true (defaults to false)
17001 selectOnFocus:false,
17003 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17005 queryParam: 'query',
17007 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17008 * when mode = 'remote' (defaults to 'Loading...')
17010 loadingText: 'Loading...',
17012 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17016 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17020 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17021 * traditional select (defaults to true)
17025 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17029 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17033 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17034 * listWidth has a higher value)
17038 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17039 * allow the user to set arbitrary text into the field (defaults to false)
17041 forceSelection:false,
17043 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17044 * if typeAhead = true (defaults to 250)
17046 typeAheadDelay : 250,
17048 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17049 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17051 valueNotFoundText : undefined,
17053 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17055 blockFocus : false,
17058 * @cfg {Boolean} disableClear Disable showing of clear button.
17060 disableClear : false,
17062 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17064 alwaysQuery : false,
17067 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17072 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17074 invalidClass : "has-warning",
17077 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17079 validClass : "has-success",
17082 * @cfg {Boolean} specialFilter (true|false) special filter default false
17084 specialFilter : false,
17087 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17089 mobileTouchView : true,
17092 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17094 useNativeIOS : false,
17097 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17099 mobile_restrict_height : false,
17101 ios_options : false,
17113 btnPosition : 'right',
17114 triggerList : true,
17115 showToggleBtn : true,
17117 emptyResultText: 'Empty',
17118 triggerText : 'Select',
17122 // element that contains real text value.. (when hidden is used..)
17124 getAutoCreate : function()
17129 * Render classic select for iso
17132 if(Roo.isIOS && this.useNativeIOS){
17133 cfg = this.getAutoCreateNativeIOS();
17141 if(Roo.isTouch && this.mobileTouchView){
17142 cfg = this.getAutoCreateTouchView();
17149 if(!this.tickable){
17150 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17155 * ComboBox with tickable selections
17158 var align = this.labelAlign || this.parentLabelAlign();
17161 cls : 'form-group roo-combobox-tickable' //input-group
17164 var btn_text_select = '';
17165 var btn_text_done = '';
17166 var btn_text_cancel = '';
17168 if (this.btn_text_show) {
17169 btn_text_select = 'Select';
17170 btn_text_done = 'Done';
17171 btn_text_cancel = 'Cancel';
17176 cls : 'tickable-buttons',
17181 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17182 //html : this.triggerText
17183 html: btn_text_select
17189 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17191 html: btn_text_done
17197 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17199 html: btn_text_cancel
17205 buttons.cn.unshift({
17207 cls: 'roo-select2-search-field-input'
17213 Roo.each(buttons.cn, function(c){
17215 c.cls += ' btn-' + _this.size;
17218 if (_this.disabled) {
17225 style : 'display: contents',
17230 cls: 'form-hidden-field'
17234 cls: 'roo-select2-choices',
17238 cls: 'roo-select2-search-field',
17249 cls: 'roo-select2-container input-group roo-select2-container-multi',
17255 // cls: 'typeahead typeahead-long dropdown-menu',
17256 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17261 if(this.hasFeedback && !this.allowBlank){
17265 cls: 'glyphicon form-control-feedback'
17268 combobox.cn.push(feedback);
17275 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17276 tooltip : 'This field is required'
17278 if (Roo.bootstrap.version == 4) {
17281 style : 'display:none'
17284 if (align ==='left' && this.fieldLabel.length) {
17286 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17293 cls : 'control-label col-form-label',
17294 html : this.fieldLabel
17306 var labelCfg = cfg.cn[1];
17307 var contentCfg = cfg.cn[2];
17310 if(this.indicatorpos == 'right'){
17316 cls : 'control-label col-form-label',
17320 html : this.fieldLabel
17336 labelCfg = cfg.cn[0];
17337 contentCfg = cfg.cn[1];
17341 if(this.labelWidth > 12){
17342 labelCfg.style = "width: " + this.labelWidth + 'px';
17344 if(this.width * 1 > 0){
17345 contentCfg.style = "width: " + this.width + 'px';
17347 if(this.labelWidth < 13 && this.labelmd == 0){
17348 this.labelmd = this.labelWidth;
17351 if(this.labellg > 0){
17352 labelCfg.cls += ' col-lg-' + this.labellg;
17353 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17356 if(this.labelmd > 0){
17357 labelCfg.cls += ' col-md-' + this.labelmd;
17358 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17361 if(this.labelsm > 0){
17362 labelCfg.cls += ' col-sm-' + this.labelsm;
17363 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17366 if(this.labelxs > 0){
17367 labelCfg.cls += ' col-xs-' + this.labelxs;
17368 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17372 } else if ( this.fieldLabel.length) {
17373 // Roo.log(" label");
17378 //cls : 'input-group-addon',
17379 html : this.fieldLabel
17384 if(this.indicatorpos == 'right'){
17388 //cls : 'input-group-addon',
17389 html : this.fieldLabel
17399 // Roo.log(" no label && no align");
17406 ['xs','sm','md','lg'].map(function(size){
17407 if (settings[size]) {
17408 cfg.cls += ' col-' + size + '-' + settings[size];
17416 _initEventsCalled : false,
17419 initEvents: function()
17421 if (this._initEventsCalled) { // as we call render... prevent looping...
17424 this._initEventsCalled = true;
17427 throw "can not find store for combo";
17430 this.indicator = this.indicatorEl();
17432 this.store = Roo.factory(this.store, Roo.data);
17433 this.store.parent = this;
17435 // if we are building from html. then this element is so complex, that we can not really
17436 // use the rendered HTML.
17437 // so we have to trash and replace the previous code.
17438 if (Roo.XComponent.build_from_html) {
17439 // remove this element....
17440 var e = this.el.dom, k=0;
17441 while (e ) { e = e.previousSibling; ++k;}
17446 this.rendered = false;
17448 this.render(this.parent().getChildContainer(true), k);
17451 if(Roo.isIOS && this.useNativeIOS){
17452 this.initIOSView();
17460 if(Roo.isTouch && this.mobileTouchView){
17461 this.initTouchView();
17466 this.initTickableEvents();
17470 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17472 if(this.hiddenName){
17474 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17476 this.hiddenField.dom.value =
17477 this.hiddenValue !== undefined ? this.hiddenValue :
17478 this.value !== undefined ? this.value : '';
17480 // prevent input submission
17481 this.el.dom.removeAttribute('name');
17482 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17487 // this.el.dom.setAttribute('autocomplete', 'off');
17490 var cls = 'x-combo-list';
17492 //this.list = new Roo.Layer({
17493 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17499 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17500 _this.list.setWidth(lw);
17503 this.list.on('mouseover', this.onViewOver, this);
17504 this.list.on('mousemove', this.onViewMove, this);
17505 this.list.on('scroll', this.onViewScroll, this);
17508 this.list.swallowEvent('mousewheel');
17509 this.assetHeight = 0;
17512 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17513 this.assetHeight += this.header.getHeight();
17516 this.innerList = this.list.createChild({cls:cls+'-inner'});
17517 this.innerList.on('mouseover', this.onViewOver, this);
17518 this.innerList.on('mousemove', this.onViewMove, this);
17519 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17521 if(this.allowBlank && !this.pageSize && !this.disableClear){
17522 this.footer = this.list.createChild({cls:cls+'-ft'});
17523 this.pageTb = new Roo.Toolbar(this.footer);
17527 this.footer = this.list.createChild({cls:cls+'-ft'});
17528 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17529 {pageSize: this.pageSize});
17533 if (this.pageTb && this.allowBlank && !this.disableClear) {
17535 this.pageTb.add(new Roo.Toolbar.Fill(), {
17536 cls: 'x-btn-icon x-btn-clear',
17538 handler: function()
17541 _this.clearValue();
17542 _this.onSelect(false, -1);
17547 this.assetHeight += this.footer.getHeight();
17552 this.tpl = Roo.bootstrap.version == 4 ?
17553 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17554 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17557 this.view = new Roo.View(this.list, this.tpl, {
17558 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17560 //this.view.wrapEl.setDisplayed(false);
17561 this.view.on('click', this.onViewClick, this);
17564 this.store.on('beforeload', this.onBeforeLoad, this);
17565 this.store.on('load', this.onLoad, this);
17566 this.store.on('loadexception', this.onLoadException, this);
17568 if(this.resizable){
17569 this.resizer = new Roo.Resizable(this.list, {
17570 pinned:true, handles:'se'
17572 this.resizer.on('resize', function(r, w, h){
17573 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17574 this.listWidth = w;
17575 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17576 this.restrictHeight();
17578 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17581 if(!this.editable){
17582 this.editable = true;
17583 this.setEditable(false);
17588 if (typeof(this.events.add.listeners) != 'undefined') {
17590 this.addicon = this.wrap.createChild(
17591 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17593 this.addicon.on('click', function(e) {
17594 this.fireEvent('add', this);
17597 if (typeof(this.events.edit.listeners) != 'undefined') {
17599 this.editicon = this.wrap.createChild(
17600 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17601 if (this.addicon) {
17602 this.editicon.setStyle('margin-left', '40px');
17604 this.editicon.on('click', function(e) {
17606 // we fire even if inothing is selected..
17607 this.fireEvent('edit', this, this.lastData );
17613 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17614 "up" : function(e){
17615 this.inKeyMode = true;
17619 "down" : function(e){
17620 if(!this.isExpanded()){
17621 this.onTriggerClick();
17623 this.inKeyMode = true;
17628 "enter" : function(e){
17629 // this.onViewClick();
17633 if(this.fireEvent("specialkey", this, e)){
17634 this.onViewClick(false);
17640 "esc" : function(e){
17644 "tab" : function(e){
17647 if(this.fireEvent("specialkey", this, e)){
17648 this.onViewClick(false);
17656 doRelay : function(foo, bar, hname){
17657 if(hname == 'down' || this.scope.isExpanded()){
17658 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17667 this.queryDelay = Math.max(this.queryDelay || 10,
17668 this.mode == 'local' ? 10 : 250);
17671 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17673 if(this.typeAhead){
17674 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17676 if(this.editable !== false){
17677 this.inputEl().on("keyup", this.onKeyUp, this);
17679 if(this.forceSelection){
17680 this.inputEl().on('blur', this.doForce, this);
17684 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17685 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17689 initTickableEvents: function()
17693 if(this.hiddenName){
17695 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17697 this.hiddenField.dom.value =
17698 this.hiddenValue !== undefined ? this.hiddenValue :
17699 this.value !== undefined ? this.value : '';
17701 // prevent input submission
17702 this.el.dom.removeAttribute('name');
17703 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17708 // this.list = this.el.select('ul.dropdown-menu',true).first();
17710 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17711 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17712 if(this.triggerList){
17713 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17716 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17717 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17719 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17720 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17722 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17723 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17725 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17726 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17727 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17730 this.cancelBtn.hide();
17735 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17736 _this.list.setWidth(lw);
17739 this.list.on('mouseover', this.onViewOver, this);
17740 this.list.on('mousemove', this.onViewMove, this);
17742 this.list.on('scroll', this.onViewScroll, this);
17745 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17746 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17749 this.view = new Roo.View(this.list, this.tpl, {
17754 selectedClass: this.selectedClass
17757 //this.view.wrapEl.setDisplayed(false);
17758 this.view.on('click', this.onViewClick, this);
17762 this.store.on('beforeload', this.onBeforeLoad, this);
17763 this.store.on('load', this.onLoad, this);
17764 this.store.on('loadexception', this.onLoadException, this);
17767 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17768 "up" : function(e){
17769 this.inKeyMode = true;
17773 "down" : function(e){
17774 this.inKeyMode = true;
17778 "enter" : function(e){
17779 if(this.fireEvent("specialkey", this, e)){
17780 this.onViewClick(false);
17786 "esc" : function(e){
17787 this.onTickableFooterButtonClick(e, false, false);
17790 "tab" : function(e){
17791 this.fireEvent("specialkey", this, e);
17793 this.onTickableFooterButtonClick(e, false, false);
17800 doRelay : function(e, fn, key){
17801 if(this.scope.isExpanded()){
17802 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17811 this.queryDelay = Math.max(this.queryDelay || 10,
17812 this.mode == 'local' ? 10 : 250);
17815 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17817 if(this.typeAhead){
17818 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17821 if(this.editable !== false){
17822 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17825 this.indicator = this.indicatorEl();
17827 if(this.indicator){
17828 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17829 this.indicator.hide();
17834 onDestroy : function(){
17836 this.view.setStore(null);
17837 this.view.el.removeAllListeners();
17838 this.view.el.remove();
17839 this.view.purgeListeners();
17842 this.list.dom.innerHTML = '';
17846 this.store.un('beforeload', this.onBeforeLoad, this);
17847 this.store.un('load', this.onLoad, this);
17848 this.store.un('loadexception', this.onLoadException, this);
17850 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17854 fireKey : function(e){
17855 if(e.isNavKeyPress() && !this.list.isVisible()){
17856 this.fireEvent("specialkey", this, e);
17861 onResize: function(w, h)
17865 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17867 // if(typeof w != 'number'){
17868 // // we do not handle it!?!?
17871 // var tw = this.trigger.getWidth();
17872 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17873 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17875 // this.inputEl().setWidth( this.adjustWidth('input', x));
17877 // //this.trigger.setStyle('left', x+'px');
17879 // if(this.list && this.listWidth === undefined){
17880 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17881 // this.list.setWidth(lw);
17882 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17890 * Allow or prevent the user from directly editing the field text. If false is passed,
17891 * the user will only be able to select from the items defined in the dropdown list. This method
17892 * is the runtime equivalent of setting the 'editable' config option at config time.
17893 * @param {Boolean} value True to allow the user to directly edit the field text
17895 setEditable : function(value){
17896 if(value == this.editable){
17899 this.editable = value;
17901 this.inputEl().dom.setAttribute('readOnly', true);
17902 this.inputEl().on('mousedown', this.onTriggerClick, this);
17903 this.inputEl().addClass('x-combo-noedit');
17905 this.inputEl().dom.removeAttribute('readOnly');
17906 this.inputEl().un('mousedown', this.onTriggerClick, this);
17907 this.inputEl().removeClass('x-combo-noedit');
17913 onBeforeLoad : function(combo,opts){
17914 if(!this.hasFocus){
17918 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17920 this.restrictHeight();
17921 this.selectedIndex = -1;
17925 onLoad : function(){
17927 this.hasQuery = false;
17929 if(!this.hasFocus){
17933 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17934 this.loading.hide();
17937 if(this.store.getCount() > 0){
17940 this.restrictHeight();
17941 if(this.lastQuery == this.allQuery){
17942 if(this.editable && !this.tickable){
17943 this.inputEl().dom.select();
17947 !this.selectByValue(this.value, true) &&
17950 !this.store.lastOptions ||
17951 typeof(this.store.lastOptions.add) == 'undefined' ||
17952 this.store.lastOptions.add != true
17955 this.select(0, true);
17958 if(this.autoFocus){
17961 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17962 this.taTask.delay(this.typeAheadDelay);
17966 this.onEmptyResults();
17972 onLoadException : function()
17974 this.hasQuery = false;
17976 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17977 this.loading.hide();
17980 if(this.tickable && this.editable){
17985 // only causes errors at present
17986 //Roo.log(this.store.reader.jsonData);
17987 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17989 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17995 onTypeAhead : function(){
17996 if(this.store.getCount() > 0){
17997 var r = this.store.getAt(0);
17998 var newValue = r.data[this.displayField];
17999 var len = newValue.length;
18000 var selStart = this.getRawValue().length;
18002 if(selStart != len){
18003 this.setRawValue(newValue);
18004 this.selectText(selStart, newValue.length);
18010 onSelect : function(record, index){
18012 if(this.fireEvent('beforeselect', this, record, index) !== false){
18014 this.setFromData(index > -1 ? record.data : false);
18017 this.fireEvent('select', this, record, index);
18022 * Returns the currently selected field value or empty string if no value is set.
18023 * @return {String} value The selected value
18025 getValue : function()
18027 if(Roo.isIOS && this.useNativeIOS){
18028 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18032 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18035 if(this.valueField){
18036 return typeof this.value != 'undefined' ? this.value : '';
18038 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18042 getRawValue : function()
18044 if(Roo.isIOS && this.useNativeIOS){
18045 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18048 var v = this.inputEl().getValue();
18054 * Clears any text/value currently set in the field
18056 clearValue : function(){
18058 if(this.hiddenField){
18059 this.hiddenField.dom.value = '';
18062 this.setRawValue('');
18063 this.lastSelectionText = '';
18064 this.lastData = false;
18066 var close = this.closeTriggerEl();
18077 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18078 * will be displayed in the field. If the value does not match the data value of an existing item,
18079 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18080 * Otherwise the field will be blank (although the value will still be set).
18081 * @param {String} value The value to match
18083 setValue : function(v)
18085 if(Roo.isIOS && this.useNativeIOS){
18086 this.setIOSValue(v);
18096 if(this.valueField){
18097 var r = this.findRecord(this.valueField, v);
18099 text = r.data[this.displayField];
18100 }else if(this.valueNotFoundText !== undefined){
18101 text = this.valueNotFoundText;
18104 this.lastSelectionText = text;
18105 if(this.hiddenField){
18106 this.hiddenField.dom.value = v;
18108 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18111 var close = this.closeTriggerEl();
18114 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18120 * @property {Object} the last set data for the element
18125 * Sets the value of the field based on a object which is related to the record format for the store.
18126 * @param {Object} value the value to set as. or false on reset?
18128 setFromData : function(o){
18135 var dv = ''; // display value
18136 var vv = ''; // value value..
18138 if (this.displayField) {
18139 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18141 // this is an error condition!!!
18142 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18145 if(this.valueField){
18146 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18149 var close = this.closeTriggerEl();
18152 if(dv.length || vv * 1 > 0){
18154 this.blockFocus=true;
18160 if(this.hiddenField){
18161 this.hiddenField.dom.value = vv;
18163 this.lastSelectionText = dv;
18164 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18168 // no hidden field.. - we store the value in 'value', but still display
18169 // display field!!!!
18170 this.lastSelectionText = dv;
18171 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18178 reset : function(){
18179 // overridden so that last data is reset..
18186 this.setValue(this.originalValue);
18187 //this.clearInvalid();
18188 this.lastData = false;
18190 this.view.clearSelections();
18196 findRecord : function(prop, value){
18198 if(this.store.getCount() > 0){
18199 this.store.each(function(r){
18200 if(r.data[prop] == value){
18210 getName: function()
18212 // returns hidden if it's set..
18213 if (!this.rendered) {return ''};
18214 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18218 onViewMove : function(e, t){
18219 this.inKeyMode = false;
18223 onViewOver : function(e, t){
18224 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18227 var item = this.view.findItemFromChild(t);
18230 var index = this.view.indexOf(item);
18231 this.select(index, false);
18236 onViewClick : function(view, doFocus, el, e)
18238 var index = this.view.getSelectedIndexes()[0];
18240 var r = this.store.getAt(index);
18244 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18251 Roo.each(this.tickItems, function(v,k){
18253 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18255 _this.tickItems.splice(k, 1);
18257 if(typeof(e) == 'undefined' && view == false){
18258 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18270 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18271 this.tickItems.push(r.data);
18274 if(typeof(e) == 'undefined' && view == false){
18275 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18282 this.onSelect(r, index);
18284 if(doFocus !== false && !this.blockFocus){
18285 this.inputEl().focus();
18290 restrictHeight : function(){
18291 //this.innerList.dom.style.height = '';
18292 //var inner = this.innerList.dom;
18293 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18294 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18295 //this.list.beginUpdate();
18296 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18297 this.list.alignTo(this.inputEl(), this.listAlign);
18298 this.list.alignTo(this.inputEl(), this.listAlign);
18299 //this.list.endUpdate();
18303 onEmptyResults : function(){
18305 if(this.tickable && this.editable){
18306 this.hasFocus = false;
18307 this.restrictHeight();
18315 * Returns true if the dropdown list is expanded, else false.
18317 isExpanded : function(){
18318 return this.list.isVisible();
18322 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18323 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18324 * @param {String} value The data value of the item to select
18325 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18326 * selected item if it is not currently in view (defaults to true)
18327 * @return {Boolean} True if the value matched an item in the list, else false
18329 selectByValue : function(v, scrollIntoView){
18330 if(v !== undefined && v !== null){
18331 var r = this.findRecord(this.valueField || this.displayField, v);
18333 this.select(this.store.indexOf(r), scrollIntoView);
18341 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18342 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18343 * @param {Number} index The zero-based index of the list item to select
18344 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18345 * selected item if it is not currently in view (defaults to true)
18347 select : function(index, scrollIntoView){
18348 this.selectedIndex = index;
18349 this.view.select(index);
18350 if(scrollIntoView !== false){
18351 var el = this.view.getNode(index);
18353 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18356 this.list.scrollChildIntoView(el, false);
18362 selectNext : function(){
18363 var ct = this.store.getCount();
18365 if(this.selectedIndex == -1){
18367 }else if(this.selectedIndex < ct-1){
18368 this.select(this.selectedIndex+1);
18374 selectPrev : function(){
18375 var ct = this.store.getCount();
18377 if(this.selectedIndex == -1){
18379 }else if(this.selectedIndex != 0){
18380 this.select(this.selectedIndex-1);
18386 onKeyUp : function(e){
18387 if(this.editable !== false && !e.isSpecialKey()){
18388 this.lastKey = e.getKey();
18389 this.dqTask.delay(this.queryDelay);
18394 validateBlur : function(){
18395 return !this.list || !this.list.isVisible();
18399 initQuery : function(){
18401 var v = this.getRawValue();
18403 if(this.tickable && this.editable){
18404 v = this.tickableInputEl().getValue();
18411 doForce : function(){
18412 if(this.inputEl().dom.value.length > 0){
18413 this.inputEl().dom.value =
18414 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18420 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18421 * query allowing the query action to be canceled if needed.
18422 * @param {String} query The SQL query to execute
18423 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18424 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18425 * saved in the current store (defaults to false)
18427 doQuery : function(q, forceAll){
18429 if(q === undefined || q === null){
18434 forceAll: forceAll,
18438 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18443 forceAll = qe.forceAll;
18444 if(forceAll === true || (q.length >= this.minChars)){
18446 this.hasQuery = true;
18448 if(this.lastQuery != q || this.alwaysQuery){
18449 this.lastQuery = q;
18450 if(this.mode == 'local'){
18451 this.selectedIndex = -1;
18453 this.store.clearFilter();
18456 if(this.specialFilter){
18457 this.fireEvent('specialfilter', this);
18462 this.store.filter(this.displayField, q);
18465 this.store.fireEvent("datachanged", this.store);
18472 this.store.baseParams[this.queryParam] = q;
18474 var options = {params : this.getParams(q)};
18477 options.add = true;
18478 options.params.start = this.page * this.pageSize;
18481 this.store.load(options);
18484 * this code will make the page width larger, at the beginning, the list not align correctly,
18485 * we should expand the list on onLoad
18486 * so command out it
18491 this.selectedIndex = -1;
18496 this.loadNext = false;
18500 getParams : function(q){
18502 //p[this.queryParam] = q;
18506 p.limit = this.pageSize;
18512 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18514 collapse : function(){
18515 if(!this.isExpanded()){
18521 this.hasFocus = false;
18525 this.cancelBtn.hide();
18526 this.trigger.show();
18529 this.tickableInputEl().dom.value = '';
18530 this.tickableInputEl().blur();
18535 Roo.get(document).un('mousedown', this.collapseIf, this);
18536 Roo.get(document).un('mousewheel', this.collapseIf, this);
18537 if (!this.editable) {
18538 Roo.get(document).un('keydown', this.listKeyPress, this);
18540 this.fireEvent('collapse', this);
18546 collapseIf : function(e){
18547 var in_combo = e.within(this.el);
18548 var in_list = e.within(this.list);
18549 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18551 if (in_combo || in_list || is_list) {
18552 //e.stopPropagation();
18557 this.onTickableFooterButtonClick(e, false, false);
18565 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18567 expand : function(){
18569 if(this.isExpanded() || !this.hasFocus){
18573 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18574 this.list.setWidth(lw);
18580 this.restrictHeight();
18584 this.tickItems = Roo.apply([], this.item);
18587 this.cancelBtn.show();
18588 this.trigger.hide();
18591 this.tickableInputEl().focus();
18596 Roo.get(document).on('mousedown', this.collapseIf, this);
18597 Roo.get(document).on('mousewheel', this.collapseIf, this);
18598 if (!this.editable) {
18599 Roo.get(document).on('keydown', this.listKeyPress, this);
18602 this.fireEvent('expand', this);
18606 // Implements the default empty TriggerField.onTriggerClick function
18607 onTriggerClick : function(e)
18609 Roo.log('trigger click');
18611 if(this.disabled || !this.triggerList){
18616 this.loadNext = false;
18618 if(this.isExpanded()){
18620 if (!this.blockFocus) {
18621 this.inputEl().focus();
18625 this.hasFocus = true;
18626 if(this.triggerAction == 'all') {
18627 this.doQuery(this.allQuery, true);
18629 this.doQuery(this.getRawValue());
18631 if (!this.blockFocus) {
18632 this.inputEl().focus();
18637 onTickableTriggerClick : function(e)
18644 this.loadNext = false;
18645 this.hasFocus = true;
18647 if(this.triggerAction == 'all') {
18648 this.doQuery(this.allQuery, true);
18650 this.doQuery(this.getRawValue());
18654 onSearchFieldClick : function(e)
18656 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18657 this.onTickableFooterButtonClick(e, false, false);
18661 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18666 this.loadNext = false;
18667 this.hasFocus = true;
18669 if(this.triggerAction == 'all') {
18670 this.doQuery(this.allQuery, true);
18672 this.doQuery(this.getRawValue());
18676 listKeyPress : function(e)
18678 //Roo.log('listkeypress');
18679 // scroll to first matching element based on key pres..
18680 if (e.isSpecialKey()) {
18683 var k = String.fromCharCode(e.getKey()).toUpperCase();
18686 var csel = this.view.getSelectedNodes();
18687 var cselitem = false;
18689 var ix = this.view.indexOf(csel[0]);
18690 cselitem = this.store.getAt(ix);
18691 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18697 this.store.each(function(v) {
18699 // start at existing selection.
18700 if (cselitem.id == v.id) {
18706 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18707 match = this.store.indexOf(v);
18713 if (match === false) {
18714 return true; // no more action?
18717 this.view.select(match);
18718 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18719 sn.scrollIntoView(sn.dom.parentNode, false);
18722 onViewScroll : function(e, t){
18724 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){
18728 this.hasQuery = true;
18730 this.loading = this.list.select('.loading', true).first();
18732 if(this.loading === null){
18733 this.list.createChild({
18735 cls: 'loading roo-select2-more-results roo-select2-active',
18736 html: 'Loading more results...'
18739 this.loading = this.list.select('.loading', true).first();
18741 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18743 this.loading.hide();
18746 this.loading.show();
18751 this.loadNext = true;
18753 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18758 addItem : function(o)
18760 var dv = ''; // display value
18762 if (this.displayField) {
18763 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18765 // this is an error condition!!!
18766 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18773 var choice = this.choices.createChild({
18775 cls: 'roo-select2-search-choice',
18784 cls: 'roo-select2-search-choice-close fa fa-times',
18789 }, this.searchField);
18791 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18793 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18801 this.inputEl().dom.value = '';
18806 onRemoveItem : function(e, _self, o)
18808 e.preventDefault();
18810 this.lastItem = Roo.apply([], this.item);
18812 var index = this.item.indexOf(o.data) * 1;
18815 Roo.log('not this item?!');
18819 this.item.splice(index, 1);
18824 this.fireEvent('remove', this, e);
18830 syncValue : function()
18832 if(!this.item.length){
18839 Roo.each(this.item, function(i){
18840 if(_this.valueField){
18841 value.push(i[_this.valueField]);
18848 this.value = value.join(',');
18850 if(this.hiddenField){
18851 this.hiddenField.dom.value = this.value;
18854 this.store.fireEvent("datachanged", this.store);
18859 clearItem : function()
18861 if(!this.multiple){
18867 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18875 if(this.tickable && !Roo.isTouch){
18876 this.view.refresh();
18880 inputEl: function ()
18882 if(Roo.isIOS && this.useNativeIOS){
18883 return this.el.select('select.roo-ios-select', true).first();
18886 if(Roo.isTouch && this.mobileTouchView){
18887 return this.el.select('input.form-control',true).first();
18891 return this.searchField;
18894 return this.el.select('input.form-control',true).first();
18897 onTickableFooterButtonClick : function(e, btn, el)
18899 e.preventDefault();
18901 this.lastItem = Roo.apply([], this.item);
18903 if(btn && btn.name == 'cancel'){
18904 this.tickItems = Roo.apply([], this.item);
18913 Roo.each(this.tickItems, function(o){
18921 validate : function()
18923 if(this.getVisibilityEl().hasClass('hidden')){
18927 var v = this.getRawValue();
18930 v = this.getValue();
18933 if(this.disabled || this.allowBlank || v.length){
18938 this.markInvalid();
18942 tickableInputEl : function()
18944 if(!this.tickable || !this.editable){
18945 return this.inputEl();
18948 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18952 getAutoCreateTouchView : function()
18957 cls: 'form-group' //input-group
18963 type : this.inputType,
18964 cls : 'form-control x-combo-noedit',
18965 autocomplete: 'new-password',
18966 placeholder : this.placeholder || '',
18971 input.name = this.name;
18975 input.cls += ' input-' + this.size;
18978 if (this.disabled) {
18979 input.disabled = true;
18983 cls : 'roo-combobox-wrap',
18990 inputblock.cls += ' input-group';
18992 inputblock.cn.unshift({
18994 cls : 'input-group-addon input-group-prepend input-group-text',
18999 if(this.removable && !this.multiple){
19000 inputblock.cls += ' roo-removable';
19002 inputblock.cn.push({
19005 cls : 'roo-combo-removable-btn close'
19009 if(this.hasFeedback && !this.allowBlank){
19011 inputblock.cls += ' has-feedback';
19013 inputblock.cn.push({
19015 cls: 'glyphicon form-control-feedback'
19022 inputblock.cls += (this.before) ? '' : ' input-group';
19024 inputblock.cn.push({
19026 cls : 'input-group-addon input-group-append input-group-text',
19032 var ibwrap = inputblock;
19037 cls: 'roo-select2-choices',
19041 cls: 'roo-select2-search-field',
19054 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19059 cls: 'form-hidden-field'
19065 if(!this.multiple && this.showToggleBtn){
19071 if (this.caret != false) {
19074 cls: 'fa fa-' + this.caret
19081 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19083 Roo.bootstrap.version == 3 ? caret : '',
19086 cls: 'combobox-clear',
19100 combobox.cls += ' roo-select2-container-multi';
19103 var required = this.allowBlank ? {
19105 style: 'display: none'
19108 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19109 tooltip : 'This field is required'
19112 var align = this.labelAlign || this.parentLabelAlign();
19114 if (align ==='left' && this.fieldLabel.length) {
19120 cls : 'control-label col-form-label',
19121 html : this.fieldLabel
19125 cls : 'roo-combobox-wrap ',
19132 var labelCfg = cfg.cn[1];
19133 var contentCfg = cfg.cn[2];
19136 if(this.indicatorpos == 'right'){
19141 cls : 'control-label col-form-label',
19145 html : this.fieldLabel
19151 cls : "roo-combobox-wrap ",
19159 labelCfg = cfg.cn[0];
19160 contentCfg = cfg.cn[1];
19165 if(this.labelWidth > 12){
19166 labelCfg.style = "width: " + this.labelWidth + 'px';
19169 if(this.labelWidth < 13 && this.labelmd == 0){
19170 this.labelmd = this.labelWidth;
19173 if(this.labellg > 0){
19174 labelCfg.cls += ' col-lg-' + this.labellg;
19175 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19178 if(this.labelmd > 0){
19179 labelCfg.cls += ' col-md-' + this.labelmd;
19180 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19183 if(this.labelsm > 0){
19184 labelCfg.cls += ' col-sm-' + this.labelsm;
19185 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19188 if(this.labelxs > 0){
19189 labelCfg.cls += ' col-xs-' + this.labelxs;
19190 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19194 } else if ( this.fieldLabel.length) {
19199 cls : 'control-label',
19200 html : this.fieldLabel
19211 if(this.indicatorpos == 'right'){
19215 cls : 'control-label',
19216 html : this.fieldLabel,
19234 var settings = this;
19236 ['xs','sm','md','lg'].map(function(size){
19237 if (settings[size]) {
19238 cfg.cls += ' col-' + size + '-' + settings[size];
19245 initTouchView : function()
19247 this.renderTouchView();
19249 this.touchViewEl.on('scroll', function(){
19250 this.el.dom.scrollTop = 0;
19253 this.originalValue = this.getValue();
19255 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19257 this.inputEl().on("click", this.showTouchView, this);
19258 if (this.triggerEl) {
19259 this.triggerEl.on("click", this.showTouchView, this);
19263 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19264 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19266 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19268 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19269 this.store.on('load', this.onTouchViewLoad, this);
19270 this.store.on('loadexception', this.onTouchViewLoadException, this);
19272 if(this.hiddenName){
19274 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19276 this.hiddenField.dom.value =
19277 this.hiddenValue !== undefined ? this.hiddenValue :
19278 this.value !== undefined ? this.value : '';
19280 this.el.dom.removeAttribute('name');
19281 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19285 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19286 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19289 if(this.removable && !this.multiple){
19290 var close = this.closeTriggerEl();
19292 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19293 close.on('click', this.removeBtnClick, this, close);
19297 * fix the bug in Safari iOS8
19299 this.inputEl().on("focus", function(e){
19300 document.activeElement.blur();
19303 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19310 renderTouchView : function()
19312 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19313 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19315 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19316 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19318 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19319 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19320 this.touchViewBodyEl.setStyle('overflow', 'auto');
19322 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19323 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19325 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19326 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19330 showTouchView : function()
19336 this.touchViewHeaderEl.hide();
19338 if(this.modalTitle.length){
19339 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19340 this.touchViewHeaderEl.show();
19343 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19344 this.touchViewEl.show();
19346 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19348 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19349 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19351 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19353 if(this.modalTitle.length){
19354 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19357 this.touchViewBodyEl.setHeight(bodyHeight);
19361 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19363 this.touchViewEl.addClass(['in','show']);
19366 if(this._touchViewMask){
19367 Roo.get(document.body).addClass("x-body-masked");
19368 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19369 this._touchViewMask.setStyle('z-index', 10000);
19370 this._touchViewMask.addClass('show');
19373 this.doTouchViewQuery();
19377 hideTouchView : function()
19379 this.touchViewEl.removeClass(['in','show']);
19383 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19385 this.touchViewEl.setStyle('display', 'none');
19388 if(this._touchViewMask){
19389 this._touchViewMask.removeClass('show');
19390 Roo.get(document.body).removeClass("x-body-masked");
19394 setTouchViewValue : function()
19401 Roo.each(this.tickItems, function(o){
19406 this.hideTouchView();
19409 doTouchViewQuery : function()
19418 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19422 if(!this.alwaysQuery || this.mode == 'local'){
19423 this.onTouchViewLoad();
19430 onTouchViewBeforeLoad : function(combo,opts)
19436 onTouchViewLoad : function()
19438 if(this.store.getCount() < 1){
19439 this.onTouchViewEmptyResults();
19443 this.clearTouchView();
19445 var rawValue = this.getRawValue();
19447 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19449 this.tickItems = [];
19451 this.store.data.each(function(d, rowIndex){
19452 var row = this.touchViewListGroup.createChild(template);
19454 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19455 row.addClass(d.data.cls);
19458 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19461 html : d.data[this.displayField]
19464 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19465 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19468 row.removeClass('selected');
19469 if(!this.multiple && this.valueField &&
19470 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19473 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19474 row.addClass('selected');
19477 if(this.multiple && this.valueField &&
19478 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19482 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19483 this.tickItems.push(d.data);
19486 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19490 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19492 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19494 if(this.modalTitle.length){
19495 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19498 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19500 if(this.mobile_restrict_height && listHeight < bodyHeight){
19501 this.touchViewBodyEl.setHeight(listHeight);
19506 if(firstChecked && listHeight > bodyHeight){
19507 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19512 onTouchViewLoadException : function()
19514 this.hideTouchView();
19517 onTouchViewEmptyResults : function()
19519 this.clearTouchView();
19521 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19523 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19527 clearTouchView : function()
19529 this.touchViewListGroup.dom.innerHTML = '';
19532 onTouchViewClick : function(e, el, o)
19534 e.preventDefault();
19537 var rowIndex = o.rowIndex;
19539 var r = this.store.getAt(rowIndex);
19541 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19543 if(!this.multiple){
19544 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19545 c.dom.removeAttribute('checked');
19548 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19550 this.setFromData(r.data);
19552 var close = this.closeTriggerEl();
19558 this.hideTouchView();
19560 this.fireEvent('select', this, r, rowIndex);
19565 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19566 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19567 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19571 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19572 this.addItem(r.data);
19573 this.tickItems.push(r.data);
19577 getAutoCreateNativeIOS : function()
19580 cls: 'form-group' //input-group,
19585 cls : 'roo-ios-select'
19589 combobox.name = this.name;
19592 if (this.disabled) {
19593 combobox.disabled = true;
19596 var settings = this;
19598 ['xs','sm','md','lg'].map(function(size){
19599 if (settings[size]) {
19600 cfg.cls += ' col-' + size + '-' + settings[size];
19610 initIOSView : function()
19612 this.store.on('load', this.onIOSViewLoad, this);
19617 onIOSViewLoad : function()
19619 if(this.store.getCount() < 1){
19623 this.clearIOSView();
19625 if(this.allowBlank) {
19627 var default_text = '-- SELECT --';
19629 if(this.placeholder.length){
19630 default_text = this.placeholder;
19633 if(this.emptyTitle.length){
19634 default_text += ' - ' + this.emptyTitle + ' -';
19637 var opt = this.inputEl().createChild({
19640 html : default_text
19644 o[this.valueField] = 0;
19645 o[this.displayField] = default_text;
19647 this.ios_options.push({
19654 this.store.data.each(function(d, rowIndex){
19658 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19659 html = d.data[this.displayField];
19664 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19665 value = d.data[this.valueField];
19674 if(this.value == d.data[this.valueField]){
19675 option['selected'] = true;
19678 var opt = this.inputEl().createChild(option);
19680 this.ios_options.push({
19687 this.inputEl().on('change', function(){
19688 this.fireEvent('select', this);
19693 clearIOSView: function()
19695 this.inputEl().dom.innerHTML = '';
19697 this.ios_options = [];
19700 setIOSValue: function(v)
19704 if(!this.ios_options){
19708 Roo.each(this.ios_options, function(opts){
19710 opts.el.dom.removeAttribute('selected');
19712 if(opts.data[this.valueField] != v){
19716 opts.el.dom.setAttribute('selected', true);
19722 * @cfg {Boolean} grow
19726 * @cfg {Number} growMin
19730 * @cfg {Number} growMax
19739 Roo.apply(Roo.bootstrap.form.ComboBox, {
19743 cls: 'modal-header',
19765 cls: 'list-group-item',
19769 cls: 'roo-combobox-list-group-item-value'
19773 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19787 listItemCheckbox : {
19789 cls: 'list-group-item',
19793 cls: 'roo-combobox-list-group-item-value'
19797 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19813 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19818 cls: 'modal-footer',
19826 cls: 'col-xs-6 text-left',
19829 cls: 'btn btn-danger roo-touch-view-cancel',
19835 cls: 'col-xs-6 text-right',
19838 cls: 'btn btn-success roo-touch-view-ok',
19849 Roo.apply(Roo.bootstrap.form.ComboBox, {
19851 touchViewTemplate : {
19853 cls: 'modal fade roo-combobox-touch-view',
19857 cls: 'modal-dialog',
19858 style : 'position:fixed', // we have to fix position....
19862 cls: 'modal-content',
19864 Roo.bootstrap.form.ComboBox.header,
19865 Roo.bootstrap.form.ComboBox.body,
19866 Roo.bootstrap.form.ComboBox.footer
19875 * Ext JS Library 1.1.1
19876 * Copyright(c) 2006-2007, Ext JS, LLC.
19878 * Originally Released Under LGPL - original licence link has changed is not relivant.
19881 * <script type="text/javascript">
19886 * @extends Roo.util.Observable
19887 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19888 * This class also supports single and multi selection modes. <br>
19889 * Create a data model bound view:
19891 var store = new Roo.data.Store(...);
19893 var view = new Roo.View({
19895 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19897 singleSelect: true,
19898 selectedClass: "ydataview-selected",
19902 // listen for node click?
19903 view.on("click", function(vw, index, node, e){
19904 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19908 dataModel.load("foobar.xml");
19910 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19912 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19913 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19915 * Note: old style constructor is still suported (container, template, config)
19918 * Create a new View
19919 * @param {Object} config The config object
19922 Roo.View = function(config, depreciated_tpl, depreciated_config){
19924 this.parent = false;
19926 if (typeof(depreciated_tpl) == 'undefined') {
19927 // new way.. - universal constructor.
19928 Roo.apply(this, config);
19929 this.el = Roo.get(this.el);
19932 this.el = Roo.get(config);
19933 this.tpl = depreciated_tpl;
19934 Roo.apply(this, depreciated_config);
19936 this.wrapEl = this.el.wrap().wrap();
19937 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19940 if(typeof(this.tpl) == "string"){
19941 this.tpl = new Roo.Template(this.tpl);
19943 // support xtype ctors..
19944 this.tpl = new Roo.factory(this.tpl, Roo);
19948 this.tpl.compile();
19953 * @event beforeclick
19954 * Fires before a click is processed. Returns false to cancel the default action.
19955 * @param {Roo.View} this
19956 * @param {Number} index The index of the target node
19957 * @param {HTMLElement} node The target node
19958 * @param {Roo.EventObject} e The raw event object
19960 "beforeclick" : true,
19963 * Fires when a template node is clicked.
19964 * @param {Roo.View} this
19965 * @param {Number} index The index of the target node
19966 * @param {HTMLElement} node The target node
19967 * @param {Roo.EventObject} e The raw event object
19972 * Fires when a template node is double clicked.
19973 * @param {Roo.View} this
19974 * @param {Number} index The index of the target node
19975 * @param {HTMLElement} node The target node
19976 * @param {Roo.EventObject} e The raw event object
19980 * @event contextmenu
19981 * Fires when a template node is right clicked.
19982 * @param {Roo.View} this
19983 * @param {Number} index The index of the target node
19984 * @param {HTMLElement} node The target node
19985 * @param {Roo.EventObject} e The raw event object
19987 "contextmenu" : true,
19989 * @event selectionchange
19990 * Fires when the selected nodes change.
19991 * @param {Roo.View} this
19992 * @param {Array} selections Array of the selected nodes
19994 "selectionchange" : true,
19997 * @event beforeselect
19998 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19999 * @param {Roo.View} this
20000 * @param {HTMLElement} node The node to be selected
20001 * @param {Array} selections Array of currently selected nodes
20003 "beforeselect" : true,
20005 * @event preparedata
20006 * Fires on every row to render, to allow you to change the data.
20007 * @param {Roo.View} this
20008 * @param {Object} data to be rendered (change this)
20010 "preparedata" : true
20018 "click": this.onClick,
20019 "dblclick": this.onDblClick,
20020 "contextmenu": this.onContextMenu,
20024 this.selections = [];
20026 this.cmp = new Roo.CompositeElementLite([]);
20028 this.store = Roo.factory(this.store, Roo.data);
20029 this.setStore(this.store, true);
20032 if ( this.footer && this.footer.xtype) {
20034 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20036 this.footer.dataSource = this.store;
20037 this.footer.container = fctr;
20038 this.footer = Roo.factory(this.footer, Roo);
20039 fctr.insertFirst(this.el);
20041 // this is a bit insane - as the paging toolbar seems to detach the el..
20042 // dom.parentNode.parentNode.parentNode
20043 // they get detached?
20047 Roo.View.superclass.constructor.call(this);
20052 Roo.extend(Roo.View, Roo.util.Observable, {
20055 * @cfg {Roo.data.Store} store Data store to load data from.
20060 * @cfg {String|Roo.Element} el The container element.
20065 * @cfg {String|Roo.Template} tpl The template used by this View
20069 * @cfg {String} dataName the named area of the template to use as the data area
20070 * Works with domtemplates roo-name="name"
20074 * @cfg {String} selectedClass The css class to add to selected nodes
20076 selectedClass : "x-view-selected",
20078 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20083 * @cfg {String} text to display on mask (default Loading)
20087 * @cfg {Boolean} multiSelect Allow multiple selection
20089 multiSelect : false,
20091 * @cfg {Boolean} singleSelect Allow single selection
20093 singleSelect: false,
20096 * @cfg {Boolean} toggleSelect - selecting
20098 toggleSelect : false,
20101 * @cfg {Boolean} tickable - selecting
20106 * Returns the element this view is bound to.
20107 * @return {Roo.Element}
20109 getEl : function(){
20110 return this.wrapEl;
20116 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20118 refresh : function(){
20119 //Roo.log('refresh');
20122 // if we are using something like 'domtemplate', then
20123 // the what gets used is:
20124 // t.applySubtemplate(NAME, data, wrapping data..)
20125 // the outer template then get' applied with
20126 // the store 'extra data'
20127 // and the body get's added to the
20128 // roo-name="data" node?
20129 // <span class='roo-tpl-{name}'></span> ?????
20133 this.clearSelections();
20134 this.el.update("");
20136 var records = this.store.getRange();
20137 if(records.length < 1) {
20139 // is this valid?? = should it render a template??
20141 this.el.update(this.emptyText);
20145 if (this.dataName) {
20146 this.el.update(t.apply(this.store.meta)); //????
20147 el = this.el.child('.roo-tpl-' + this.dataName);
20150 for(var i = 0, len = records.length; i < len; i++){
20151 var data = this.prepareData(records[i].data, i, records[i]);
20152 this.fireEvent("preparedata", this, data, i, records[i]);
20154 var d = Roo.apply({}, data);
20157 Roo.apply(d, {'roo-id' : Roo.id()});
20161 Roo.each(this.parent.item, function(item){
20162 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20165 Roo.apply(d, {'roo-data-checked' : 'checked'});
20169 html[html.length] = Roo.util.Format.trim(
20171 t.applySubtemplate(this.dataName, d, this.store.meta) :
20178 el.update(html.join(""));
20179 this.nodes = el.dom.childNodes;
20180 this.updateIndexes(0);
20185 * Function to override to reformat the data that is sent to
20186 * the template for each node.
20187 * DEPRICATED - use the preparedata event handler.
20188 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20189 * a JSON object for an UpdateManager bound view).
20191 prepareData : function(data, index, record)
20193 this.fireEvent("preparedata", this, data, index, record);
20197 onUpdate : function(ds, record){
20198 // Roo.log('on update');
20199 this.clearSelections();
20200 var index = this.store.indexOf(record);
20201 var n = this.nodes[index];
20202 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20203 n.parentNode.removeChild(n);
20204 this.updateIndexes(index, index);
20210 onAdd : function(ds, records, index)
20212 //Roo.log(['on Add', ds, records, index] );
20213 this.clearSelections();
20214 if(this.nodes.length == 0){
20218 var n = this.nodes[index];
20219 for(var i = 0, len = records.length; i < len; i++){
20220 var d = this.prepareData(records[i].data, i, records[i]);
20222 this.tpl.insertBefore(n, d);
20225 this.tpl.append(this.el, d);
20228 this.updateIndexes(index);
20231 onRemove : function(ds, record, index){
20232 // Roo.log('onRemove');
20233 this.clearSelections();
20234 var el = this.dataName ?
20235 this.el.child('.roo-tpl-' + this.dataName) :
20238 el.dom.removeChild(this.nodes[index]);
20239 this.updateIndexes(index);
20243 * Refresh an individual node.
20244 * @param {Number} index
20246 refreshNode : function(index){
20247 this.onUpdate(this.store, this.store.getAt(index));
20250 updateIndexes : function(startIndex, endIndex){
20251 var ns = this.nodes;
20252 startIndex = startIndex || 0;
20253 endIndex = endIndex || ns.length - 1;
20254 for(var i = startIndex; i <= endIndex; i++){
20255 ns[i].nodeIndex = i;
20260 * Changes the data store this view uses and refresh the view.
20261 * @param {Store} store
20263 setStore : function(store, initial){
20264 if(!initial && this.store){
20265 this.store.un("datachanged", this.refresh);
20266 this.store.un("add", this.onAdd);
20267 this.store.un("remove", this.onRemove);
20268 this.store.un("update", this.onUpdate);
20269 this.store.un("clear", this.refresh);
20270 this.store.un("beforeload", this.onBeforeLoad);
20271 this.store.un("load", this.onLoad);
20272 this.store.un("loadexception", this.onLoad);
20276 store.on("datachanged", this.refresh, this);
20277 store.on("add", this.onAdd, this);
20278 store.on("remove", this.onRemove, this);
20279 store.on("update", this.onUpdate, this);
20280 store.on("clear", this.refresh, this);
20281 store.on("beforeload", this.onBeforeLoad, this);
20282 store.on("load", this.onLoad, this);
20283 store.on("loadexception", this.onLoad, this);
20291 * onbeforeLoad - masks the loading area.
20294 onBeforeLoad : function(store,opts)
20296 //Roo.log('onBeforeLoad');
20298 this.el.update("");
20300 this.el.mask(this.mask ? this.mask : "Loading" );
20302 onLoad : function ()
20309 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20310 * @param {HTMLElement} node
20311 * @return {HTMLElement} The template node
20313 findItemFromChild : function(node){
20314 var el = this.dataName ?
20315 this.el.child('.roo-tpl-' + this.dataName,true) :
20318 if(!node || node.parentNode == el){
20321 var p = node.parentNode;
20322 while(p && p != el){
20323 if(p.parentNode == el){
20332 onClick : function(e){
20333 var item = this.findItemFromChild(e.getTarget());
20335 var index = this.indexOf(item);
20336 if(this.onItemClick(item, index, e) !== false){
20337 this.fireEvent("click", this, index, item, e);
20340 this.clearSelections();
20345 onContextMenu : function(e){
20346 var item = this.findItemFromChild(e.getTarget());
20348 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20353 onDblClick : function(e){
20354 var item = this.findItemFromChild(e.getTarget());
20356 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20360 onItemClick : function(item, index, e)
20362 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20365 if (this.toggleSelect) {
20366 var m = this.isSelected(item) ? 'unselect' : 'select';
20369 _t[m](item, true, false);
20372 if(this.multiSelect || this.singleSelect){
20373 if(this.multiSelect && e.shiftKey && this.lastSelection){
20374 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20376 this.select(item, this.multiSelect && e.ctrlKey);
20377 this.lastSelection = item;
20380 if(!this.tickable){
20381 e.preventDefault();
20389 * Get the number of selected nodes.
20392 getSelectionCount : function(){
20393 return this.selections.length;
20397 * Get the currently selected nodes.
20398 * @return {Array} An array of HTMLElements
20400 getSelectedNodes : function(){
20401 return this.selections;
20405 * Get the indexes of the selected nodes.
20408 getSelectedIndexes : function(){
20409 var indexes = [], s = this.selections;
20410 for(var i = 0, len = s.length; i < len; i++){
20411 indexes.push(s[i].nodeIndex);
20417 * Clear all selections
20418 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20420 clearSelections : function(suppressEvent){
20421 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20422 this.cmp.elements = this.selections;
20423 this.cmp.removeClass(this.selectedClass);
20424 this.selections = [];
20425 if(!suppressEvent){
20426 this.fireEvent("selectionchange", this, this.selections);
20432 * Returns true if the passed node is selected
20433 * @param {HTMLElement/Number} node The node or node index
20434 * @return {Boolean}
20436 isSelected : function(node){
20437 var s = this.selections;
20441 node = this.getNode(node);
20442 return s.indexOf(node) !== -1;
20447 * @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
20448 * @param {Boolean} keepExisting (optional) true to keep existing selections
20449 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20451 select : function(nodeInfo, keepExisting, suppressEvent){
20452 if(nodeInfo instanceof Array){
20454 this.clearSelections(true);
20456 for(var i = 0, len = nodeInfo.length; i < len; i++){
20457 this.select(nodeInfo[i], true, true);
20461 var node = this.getNode(nodeInfo);
20462 if(!node || this.isSelected(node)){
20463 return; // already selected.
20466 this.clearSelections(true);
20469 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20470 Roo.fly(node).addClass(this.selectedClass);
20471 this.selections.push(node);
20472 if(!suppressEvent){
20473 this.fireEvent("selectionchange", this, this.selections);
20481 * @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
20482 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20483 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20485 unselect : function(nodeInfo, keepExisting, suppressEvent)
20487 if(nodeInfo instanceof Array){
20488 Roo.each(this.selections, function(s) {
20489 this.unselect(s, nodeInfo);
20493 var node = this.getNode(nodeInfo);
20494 if(!node || !this.isSelected(node)){
20495 //Roo.log("not selected");
20496 return; // not selected.
20500 Roo.each(this.selections, function(s) {
20502 Roo.fly(node).removeClass(this.selectedClass);
20509 this.selections= ns;
20510 this.fireEvent("selectionchange", this, this.selections);
20514 * Gets a template node.
20515 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20516 * @return {HTMLElement} The node or null if it wasn't found
20518 getNode : function(nodeInfo){
20519 if(typeof nodeInfo == "string"){
20520 return document.getElementById(nodeInfo);
20521 }else if(typeof nodeInfo == "number"){
20522 return this.nodes[nodeInfo];
20528 * Gets a range template nodes.
20529 * @param {Number} startIndex
20530 * @param {Number} endIndex
20531 * @return {Array} An array of nodes
20533 getNodes : function(start, end){
20534 var ns = this.nodes;
20535 start = start || 0;
20536 end = typeof end == "undefined" ? ns.length - 1 : end;
20539 for(var i = start; i <= end; i++){
20543 for(var i = start; i >= end; i--){
20551 * Finds the index of the passed node
20552 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20553 * @return {Number} The index of the node or -1
20555 indexOf : function(node){
20556 node = this.getNode(node);
20557 if(typeof node.nodeIndex == "number"){
20558 return node.nodeIndex;
20560 var ns = this.nodes;
20561 for(var i = 0, len = ns.length; i < len; i++){
20572 * based on jquery fullcalendar
20576 Roo.bootstrap = Roo.bootstrap || {};
20578 * @class Roo.bootstrap.Calendar
20579 * @extends Roo.bootstrap.Component
20580 * Bootstrap Calendar class
20581 * @cfg {Boolean} loadMask (true|false) default false
20582 * @cfg {Object} header generate the user specific header of the calendar, default false
20585 * Create a new Container
20586 * @param {Object} config The config object
20591 Roo.bootstrap.Calendar = function(config){
20592 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20596 * Fires when a date is selected
20597 * @param {DatePicker} this
20598 * @param {Date} date The selected date
20602 * @event monthchange
20603 * Fires when the displayed month changes
20604 * @param {DatePicker} this
20605 * @param {Date} date The selected month
20607 'monthchange': true,
20609 * @event evententer
20610 * Fires when mouse over an event
20611 * @param {Calendar} this
20612 * @param {event} Event
20614 'evententer': true,
20616 * @event eventleave
20617 * Fires when the mouse leaves an
20618 * @param {Calendar} this
20621 'eventleave': true,
20623 * @event eventclick
20624 * Fires when the mouse click an
20625 * @param {Calendar} this
20634 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20637 * @cfg {Roo.data.Store} store
20638 * The data source for the calendar
20642 * @cfg {Number} startDay
20643 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20651 getAutoCreate : function(){
20654 var fc_button = function(name, corner, style, content ) {
20655 return Roo.apply({},{
20657 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20659 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20662 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20673 style : 'width:100%',
20680 cls : 'fc-header-left',
20682 fc_button('prev', 'left', 'arrow', '‹' ),
20683 fc_button('next', 'right', 'arrow', '›' ),
20684 { tag: 'span', cls: 'fc-header-space' },
20685 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20693 cls : 'fc-header-center',
20697 cls: 'fc-header-title',
20700 html : 'month / year'
20708 cls : 'fc-header-right',
20710 /* fc_button('month', 'left', '', 'month' ),
20711 fc_button('week', '', '', 'week' ),
20712 fc_button('day', 'right', '', 'day' )
20724 header = this.header;
20727 var cal_heads = function() {
20729 // fixme - handle this.
20731 for (var i =0; i < Date.dayNames.length; i++) {
20732 var d = Date.dayNames[i];
20735 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20736 html : d.substring(0,3)
20740 ret[0].cls += ' fc-first';
20741 ret[6].cls += ' fc-last';
20744 var cal_cell = function(n) {
20747 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20752 cls: 'fc-day-number',
20756 cls: 'fc-day-content',
20760 style: 'position: relative;' // height: 17px;
20772 var cal_rows = function() {
20775 for (var r = 0; r < 6; r++) {
20782 for (var i =0; i < Date.dayNames.length; i++) {
20783 var d = Date.dayNames[i];
20784 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20787 row.cn[0].cls+=' fc-first';
20788 row.cn[0].cn[0].style = 'min-height:90px';
20789 row.cn[6].cls+=' fc-last';
20793 ret[0].cls += ' fc-first';
20794 ret[4].cls += ' fc-prev-last';
20795 ret[5].cls += ' fc-last';
20802 cls: 'fc-border-separate',
20803 style : 'width:100%',
20811 cls : 'fc-first fc-last',
20829 cls : 'fc-content',
20830 style : "position: relative;",
20833 cls : 'fc-view fc-view-month fc-grid',
20834 style : 'position: relative',
20835 unselectable : 'on',
20838 cls : 'fc-event-container',
20839 style : 'position:absolute;z-index:8;top:0;left:0;'
20857 initEvents : function()
20860 throw "can not find store for calendar";
20866 style: "text-align:center",
20870 style: "background-color:white;width:50%;margin:250 auto",
20874 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20885 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20887 var size = this.el.select('.fc-content', true).first().getSize();
20888 this.maskEl.setSize(size.width, size.height);
20889 this.maskEl.enableDisplayMode("block");
20890 if(!this.loadMask){
20891 this.maskEl.hide();
20894 this.store = Roo.factory(this.store, Roo.data);
20895 this.store.on('load', this.onLoad, this);
20896 this.store.on('beforeload', this.onBeforeLoad, this);
20900 this.cells = this.el.select('.fc-day',true);
20901 //Roo.log(this.cells);
20902 this.textNodes = this.el.query('.fc-day-number');
20903 this.cells.addClassOnOver('fc-state-hover');
20905 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20906 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20907 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20908 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20910 this.on('monthchange', this.onMonthChange, this);
20912 this.update(new Date().clearTime());
20915 resize : function() {
20916 var sz = this.el.getSize();
20918 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20919 this.el.select('.fc-day-content div',true).setHeight(34);
20924 showPrevMonth : function(e){
20925 this.update(this.activeDate.add("mo", -1));
20927 showToday : function(e){
20928 this.update(new Date().clearTime());
20931 showNextMonth : function(e){
20932 this.update(this.activeDate.add("mo", 1));
20936 showPrevYear : function(){
20937 this.update(this.activeDate.add("y", -1));
20941 showNextYear : function(){
20942 this.update(this.activeDate.add("y", 1));
20947 update : function(date)
20949 var vd = this.activeDate;
20950 this.activeDate = date;
20951 // if(vd && this.el){
20952 // var t = date.getTime();
20953 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20954 // Roo.log('using add remove');
20956 // this.fireEvent('monthchange', this, date);
20958 // this.cells.removeClass("fc-state-highlight");
20959 // this.cells.each(function(c){
20960 // if(c.dateValue == t){
20961 // c.addClass("fc-state-highlight");
20962 // setTimeout(function(){
20963 // try{c.dom.firstChild.focus();}catch(e){}
20973 var days = date.getDaysInMonth();
20975 var firstOfMonth = date.getFirstDateOfMonth();
20976 var startingPos = firstOfMonth.getDay()-this.startDay;
20978 if(startingPos < this.startDay){
20982 var pm = date.add(Date.MONTH, -1);
20983 var prevStart = pm.getDaysInMonth()-startingPos;
20985 this.cells = this.el.select('.fc-day',true);
20986 this.textNodes = this.el.query('.fc-day-number');
20987 this.cells.addClassOnOver('fc-state-hover');
20989 var cells = this.cells.elements;
20990 var textEls = this.textNodes;
20992 Roo.each(cells, function(cell){
20993 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20996 days += startingPos;
20998 // convert everything to numbers so it's fast
20999 var day = 86400000;
21000 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21003 //Roo.log(prevStart);
21005 var today = new Date().clearTime().getTime();
21006 var sel = date.clearTime().getTime();
21007 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21008 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21009 var ddMatch = this.disabledDatesRE;
21010 var ddText = this.disabledDatesText;
21011 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21012 var ddaysText = this.disabledDaysText;
21013 var format = this.format;
21015 var setCellClass = function(cal, cell){
21019 //Roo.log('set Cell Class');
21021 var t = d.getTime();
21025 cell.dateValue = t;
21027 cell.className += " fc-today";
21028 cell.className += " fc-state-highlight";
21029 cell.title = cal.todayText;
21032 // disable highlight in other month..
21033 //cell.className += " fc-state-highlight";
21038 cell.className = " fc-state-disabled";
21039 cell.title = cal.minText;
21043 cell.className = " fc-state-disabled";
21044 cell.title = cal.maxText;
21048 if(ddays.indexOf(d.getDay()) != -1){
21049 cell.title = ddaysText;
21050 cell.className = " fc-state-disabled";
21053 if(ddMatch && format){
21054 var fvalue = d.dateFormat(format);
21055 if(ddMatch.test(fvalue)){
21056 cell.title = ddText.replace("%0", fvalue);
21057 cell.className = " fc-state-disabled";
21061 if (!cell.initialClassName) {
21062 cell.initialClassName = cell.dom.className;
21065 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21070 for(; i < startingPos; i++) {
21071 textEls[i].innerHTML = (++prevStart);
21072 d.setDate(d.getDate()+1);
21074 cells[i].className = "fc-past fc-other-month";
21075 setCellClass(this, cells[i]);
21080 for(; i < days; i++){
21081 intDay = i - startingPos + 1;
21082 textEls[i].innerHTML = (intDay);
21083 d.setDate(d.getDate()+1);
21085 cells[i].className = ''; // "x-date-active";
21086 setCellClass(this, cells[i]);
21090 for(; i < 42; i++) {
21091 textEls[i].innerHTML = (++extraDays);
21092 d.setDate(d.getDate()+1);
21094 cells[i].className = "fc-future fc-other-month";
21095 setCellClass(this, cells[i]);
21098 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21100 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21102 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21103 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21105 if(totalRows != 6){
21106 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21107 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21110 this.fireEvent('monthchange', this, date);
21114 if(!this.internalRender){
21115 var main = this.el.dom.firstChild;
21116 var w = main.offsetWidth;
21117 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21118 Roo.fly(main).setWidth(w);
21119 this.internalRender = true;
21120 // opera does not respect the auto grow header center column
21121 // then, after it gets a width opera refuses to recalculate
21122 // without a second pass
21123 if(Roo.isOpera && !this.secondPass){
21124 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21125 this.secondPass = true;
21126 this.update.defer(10, this, [date]);
21133 findCell : function(dt) {
21134 dt = dt.clearTime().getTime();
21136 this.cells.each(function(c){
21137 //Roo.log("check " +c.dateValue + '?=' + dt);
21138 if(c.dateValue == dt){
21148 findCells : function(ev) {
21149 var s = ev.start.clone().clearTime().getTime();
21151 var e= ev.end.clone().clearTime().getTime();
21154 this.cells.each(function(c){
21155 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21157 if(c.dateValue > e){
21160 if(c.dateValue < s){
21169 // findBestRow: function(cells)
21173 // for (var i =0 ; i < cells.length;i++) {
21174 // ret = Math.max(cells[i].rows || 0,ret);
21181 addItem : function(ev)
21183 // look for vertical location slot in
21184 var cells = this.findCells(ev);
21186 // ev.row = this.findBestRow(cells);
21188 // work out the location.
21192 for(var i =0; i < cells.length; i++) {
21194 cells[i].row = cells[0].row;
21197 cells[i].row = cells[i].row + 1;
21207 if (crow.start.getY() == cells[i].getY()) {
21209 crow.end = cells[i];
21226 cells[0].events.push(ev);
21228 this.calevents.push(ev);
21231 clearEvents: function() {
21233 if(!this.calevents){
21237 Roo.each(this.cells.elements, function(c){
21243 Roo.each(this.calevents, function(e) {
21244 Roo.each(e.els, function(el) {
21245 el.un('mouseenter' ,this.onEventEnter, this);
21246 el.un('mouseleave' ,this.onEventLeave, this);
21251 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21257 renderEvents: function()
21261 this.cells.each(function(c) {
21270 if(c.row != c.events.length){
21271 r = 4 - (4 - (c.row - c.events.length));
21274 c.events = ev.slice(0, r);
21275 c.more = ev.slice(r);
21277 if(c.more.length && c.more.length == 1){
21278 c.events.push(c.more.pop());
21281 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21285 this.cells.each(function(c) {
21287 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21290 for (var e = 0; e < c.events.length; e++){
21291 var ev = c.events[e];
21292 var rows = ev.rows;
21294 for(var i = 0; i < rows.length; i++) {
21296 // how many rows should it span..
21299 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21300 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21302 unselectable : "on",
21305 cls: 'fc-event-inner',
21309 // cls: 'fc-event-time',
21310 // html : cells.length > 1 ? '' : ev.time
21314 cls: 'fc-event-title',
21315 html : String.format('{0}', ev.title)
21322 cls: 'ui-resizable-handle ui-resizable-e',
21323 html : '  '
21330 cfg.cls += ' fc-event-start';
21332 if ((i+1) == rows.length) {
21333 cfg.cls += ' fc-event-end';
21336 var ctr = _this.el.select('.fc-event-container',true).first();
21337 var cg = ctr.createChild(cfg);
21339 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21340 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21342 var r = (c.more.length) ? 1 : 0;
21343 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21344 cg.setWidth(ebox.right - sbox.x -2);
21346 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21347 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21348 cg.on('click', _this.onEventClick, _this, ev);
21359 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21360 style : 'position: absolute',
21361 unselectable : "on",
21364 cls: 'fc-event-inner',
21368 cls: 'fc-event-title',
21376 cls: 'ui-resizable-handle ui-resizable-e',
21377 html : '  '
21383 var ctr = _this.el.select('.fc-event-container',true).first();
21384 var cg = ctr.createChild(cfg);
21386 var sbox = c.select('.fc-day-content',true).first().getBox();
21387 var ebox = c.select('.fc-day-content',true).first().getBox();
21389 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21390 cg.setWidth(ebox.right - sbox.x -2);
21392 cg.on('click', _this.onMoreEventClick, _this, c.more);
21402 onEventEnter: function (e, el,event,d) {
21403 this.fireEvent('evententer', this, el, event);
21406 onEventLeave: function (e, el,event,d) {
21407 this.fireEvent('eventleave', this, el, event);
21410 onEventClick: function (e, el,event,d) {
21411 this.fireEvent('eventclick', this, el, event);
21414 onMonthChange: function () {
21418 onMoreEventClick: function(e, el, more)
21422 this.calpopover.placement = 'right';
21423 this.calpopover.setTitle('More');
21425 this.calpopover.setContent('');
21427 var ctr = this.calpopover.el.select('.popover-content', true).first();
21429 Roo.each(more, function(m){
21431 cls : 'fc-event-hori fc-event-draggable',
21434 var cg = ctr.createChild(cfg);
21436 cg.on('click', _this.onEventClick, _this, m);
21439 this.calpopover.show(el);
21444 onLoad: function ()
21446 this.calevents = [];
21449 if(this.store.getCount() > 0){
21450 this.store.data.each(function(d){
21453 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21454 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21455 time : d.data.start_time,
21456 title : d.data.title,
21457 description : d.data.description,
21458 venue : d.data.venue
21463 this.renderEvents();
21465 if(this.calevents.length && this.loadMask){
21466 this.maskEl.hide();
21470 onBeforeLoad: function()
21472 this.clearEvents();
21474 this.maskEl.show();
21488 * @class Roo.bootstrap.Popover
21489 * @extends Roo.bootstrap.Component
21490 * @parent none builder
21491 * @children Roo.bootstrap.Component
21492 * Bootstrap Popover class
21493 * @cfg {String} html contents of the popover (or false to use children..)
21494 * @cfg {String} title of popover (or false to hide)
21495 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21496 * @cfg {String} trigger click || hover (or false to trigger manually)
21497 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21498 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21499 * - if false and it has a 'parent' then it will be automatically added to that element
21500 * - if string - Roo.get will be called
21501 * @cfg {Number} delay - delay before showing
21504 * Create a new Popover
21505 * @param {Object} config The config object
21508 Roo.bootstrap.Popover = function(config){
21509 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21515 * After the popover show
21517 * @param {Roo.bootstrap.Popover} this
21522 * After the popover hide
21524 * @param {Roo.bootstrap.Popover} this
21530 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21535 placement : 'right',
21536 trigger : 'hover', // hover
21542 can_build_overlaid : false,
21544 maskEl : false, // the mask element
21547 alignEl : false, // when show is called with an element - this get's stored.
21549 getChildContainer : function()
21551 return this.contentEl;
21554 getPopoverHeader : function()
21556 this.title = true; // flag not to hide it..
21557 this.headerEl.addClass('p-0');
21558 return this.headerEl
21562 getAutoCreate : function(){
21565 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21566 style: 'display:block',
21572 cls : 'popover-inner ',
21576 cls: 'popover-title popover-header',
21577 html : this.title === false ? '' : this.title
21580 cls : 'popover-content popover-body ' + (this.cls || ''),
21581 html : this.html || ''
21592 * @param {string} the title
21594 setTitle: function(str)
21598 this.headerEl.dom.innerHTML = str;
21603 * @param {string} the body content
21605 setContent: function(str)
21608 if (this.contentEl) {
21609 this.contentEl.dom.innerHTML = str;
21613 // as it get's added to the bottom of the page.
21614 onRender : function(ct, position)
21616 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21621 var cfg = Roo.apply({}, this.getAutoCreate());
21625 cfg.cls += ' ' + this.cls;
21628 cfg.style = this.style;
21630 //Roo.log("adding to ");
21631 this.el = Roo.get(document.body).createChild(cfg, position);
21632 // Roo.log(this.el);
21635 this.contentEl = this.el.select('.popover-content',true).first();
21636 this.headerEl = this.el.select('.popover-title',true).first();
21639 if(typeof(this.items) != 'undefined'){
21640 var items = this.items;
21643 for(var i =0;i < items.length;i++) {
21644 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21648 this.items = nitems;
21650 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21651 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21658 resizeMask : function()
21660 this.maskEl.setSize(
21661 Roo.lib.Dom.getViewWidth(true),
21662 Roo.lib.Dom.getViewHeight(true)
21666 initEvents : function()
21670 Roo.bootstrap.Popover.register(this);
21673 this.arrowEl = this.el.select('.arrow',true).first();
21674 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21675 this.el.enableDisplayMode('block');
21679 if (this.over === false && !this.parent()) {
21682 if (this.triggers === false) {
21687 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21688 var triggers = this.trigger ? this.trigger.split(' ') : [];
21689 Roo.each(triggers, function(trigger) {
21691 if (trigger == 'click') {
21692 on_el.on('click', this.toggle, this);
21693 } else if (trigger != 'manual') {
21694 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21695 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21697 on_el.on(eventIn ,this.enter, this);
21698 on_el.on(eventOut, this.leave, this);
21708 toggle : function () {
21709 this.hoverState == 'in' ? this.leave() : this.enter();
21712 enter : function () {
21714 clearTimeout(this.timeout);
21716 this.hoverState = 'in';
21718 if (!this.delay || !this.delay.show) {
21723 this.timeout = setTimeout(function () {
21724 if (_t.hoverState == 'in') {
21727 }, this.delay.show)
21730 leave : function() {
21731 clearTimeout(this.timeout);
21733 this.hoverState = 'out';
21735 if (!this.delay || !this.delay.hide) {
21740 this.timeout = setTimeout(function () {
21741 if (_t.hoverState == 'out') {
21744 }, this.delay.hide)
21748 * update the position of the dialog
21749 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21754 doAlign : function()
21757 if (this.alignEl) {
21758 this.updatePosition(this.placement, true);
21761 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21762 var es = this.el.getSize();
21763 var x = Roo.lib.Dom.getViewWidth()/2;
21764 var y = Roo.lib.Dom.getViewHeight()/2;
21765 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21777 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21778 * @param {string} (left|right|top|bottom) position
21780 show : function (on_el, placement)
21782 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21783 on_el = on_el || false; // default to false
21786 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21787 on_el = this.parent().el;
21788 } else if (this.over) {
21789 on_el = Roo.get(this.over);
21794 this.alignEl = Roo.get( on_el );
21797 this.render(document.body);
21803 if (this.title === false) {
21804 this.headerEl.hide();
21809 this.el.dom.style.display = 'block';
21813 //var arrow = this.el.select('.arrow',true).first();
21814 //arrow.set(align[2],
21816 this.el.addClass('in');
21820 this.hoverState = 'in';
21823 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21824 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21825 this.maskEl.dom.style.display = 'block';
21826 this.maskEl.addClass('show');
21828 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21830 this.fireEvent('show', this);
21834 * fire this manually after loading a grid in the table for example
21835 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21836 * @param {Boolean} try and move it if we cant get right position.
21838 updatePosition : function(placement, try_move)
21840 // allow for calling with no parameters
21841 placement = placement ? placement : this.placement;
21842 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21844 this.el.removeClass([
21845 'fade','top','bottom', 'left', 'right','in',
21846 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21848 this.el.addClass(placement + ' bs-popover-' + placement);
21850 if (!this.alignEl ) {
21854 switch (placement) {
21856 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21857 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21858 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21859 //normal display... or moved up/down.
21860 this.el.setXY(offset);
21861 var xy = this.alignEl.getAnchorXY('tr', false);
21863 this.arrowEl.setXY(xy);
21866 // continue through...
21867 return this.updatePosition('left', false);
21871 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21872 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21873 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21874 //normal display... or moved up/down.
21875 this.el.setXY(offset);
21876 var xy = this.alignEl.getAnchorXY('tl', false);
21877 xy[0]-=10;xy[1]+=5; // << fix me
21878 this.arrowEl.setXY(xy);
21882 return this.updatePosition('right', false);
21885 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21886 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21887 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21888 //normal display... or moved up/down.
21889 this.el.setXY(offset);
21890 var xy = this.alignEl.getAnchorXY('t', false);
21891 xy[1]-=10; // << fix me
21892 this.arrowEl.setXY(xy);
21896 return this.updatePosition('bottom', false);
21899 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21900 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21901 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21902 //normal display... or moved up/down.
21903 this.el.setXY(offset);
21904 var xy = this.alignEl.getAnchorXY('b', false);
21905 xy[1]+=2; // << fix me
21906 this.arrowEl.setXY(xy);
21910 return this.updatePosition('top', false);
21921 this.el.setXY([0,0]);
21922 this.el.removeClass('in');
21924 this.hoverState = null;
21925 this.maskEl.hide(); // always..
21926 this.fireEvent('hide', this);
21932 Roo.apply(Roo.bootstrap.Popover, {
21935 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21936 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21937 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21938 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21943 clickHander : false,
21947 onMouseDown : function(e)
21949 if (this.popups.length && !e.getTarget(".roo-popover")) {
21950 /// what is nothing is showing..
21959 register : function(popup)
21961 if (!Roo.bootstrap.Popover.clickHandler) {
21962 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21964 // hide other popups.
21965 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21966 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21967 this.hideAll(); //<< why?
21968 //this.popups.push(popup);
21970 hideAll : function()
21972 this.popups.forEach(function(p) {
21976 onShow : function() {
21977 Roo.bootstrap.Popover.popups.push(this);
21979 onHide : function() {
21980 Roo.bootstrap.Popover.popups.remove(this);
21985 * @class Roo.bootstrap.PopoverNav
21986 * @extends Roo.bootstrap.nav.Simplebar
21987 * @parent Roo.bootstrap.Popover
21988 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21990 * Bootstrap Popover header navigation class
21991 * FIXME? should this go under nav?
21995 * Create a new Popover Header Navigation
21996 * @param {Object} config The config object
21999 Roo.bootstrap.PopoverNav = function(config){
22000 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22003 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22006 container_method : 'getPopoverHeader'
22024 * @class Roo.bootstrap.Progress
22025 * @extends Roo.bootstrap.Component
22026 * @children Roo.bootstrap.ProgressBar
22027 * Bootstrap Progress class
22028 * @cfg {Boolean} striped striped of the progress bar
22029 * @cfg {Boolean} active animated of the progress bar
22033 * Create a new Progress
22034 * @param {Object} config The config object
22037 Roo.bootstrap.Progress = function(config){
22038 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22041 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22046 getAutoCreate : function(){
22054 cfg.cls += ' progress-striped';
22058 cfg.cls += ' active';
22077 * @class Roo.bootstrap.ProgressBar
22078 * @extends Roo.bootstrap.Component
22079 * Bootstrap ProgressBar class
22080 * @cfg {Number} aria_valuenow aria-value now
22081 * @cfg {Number} aria_valuemin aria-value min
22082 * @cfg {Number} aria_valuemax aria-value max
22083 * @cfg {String} label label for the progress bar
22084 * @cfg {String} panel (success | info | warning | danger )
22085 * @cfg {String} role role of the progress bar
22086 * @cfg {String} sr_only text
22090 * Create a new ProgressBar
22091 * @param {Object} config The config object
22094 Roo.bootstrap.ProgressBar = function(config){
22095 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22098 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22102 aria_valuemax : 100,
22108 getAutoCreate : function()
22113 cls: 'progress-bar',
22114 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22126 cfg.role = this.role;
22129 if(this.aria_valuenow){
22130 cfg['aria-valuenow'] = this.aria_valuenow;
22133 if(this.aria_valuemin){
22134 cfg['aria-valuemin'] = this.aria_valuemin;
22137 if(this.aria_valuemax){
22138 cfg['aria-valuemax'] = this.aria_valuemax;
22141 if(this.label && !this.sr_only){
22142 cfg.html = this.label;
22146 cfg.cls += ' progress-bar-' + this.panel;
22152 update : function(aria_valuenow)
22154 this.aria_valuenow = aria_valuenow;
22156 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22164 * @class Roo.bootstrap.TabGroup
22165 * @extends Roo.bootstrap.Column
22166 * @children Roo.bootstrap.TabPanel
22167 * Bootstrap Column class
22168 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22169 * @cfg {Boolean} carousel true to make the group behave like a carousel
22170 * @cfg {Boolean} bullets show bullets for the panels
22171 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22172 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22173 * @cfg {Boolean} showarrow (true|false) show arrow default true
22176 * Create a new TabGroup
22177 * @param {Object} config The config object
22180 Roo.bootstrap.TabGroup = function(config){
22181 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22183 this.navId = Roo.id();
22186 Roo.bootstrap.TabGroup.register(this);
22190 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22193 transition : false,
22198 slideOnTouch : false,
22201 getAutoCreate : function()
22203 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22205 cfg.cls += ' tab-content';
22207 if (this.carousel) {
22208 cfg.cls += ' carousel slide';
22211 cls : 'carousel-inner',
22215 if(this.bullets && !Roo.isTouch){
22218 cls : 'carousel-bullets',
22222 if(this.bullets_cls){
22223 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22230 cfg.cn[0].cn.push(bullets);
22233 if(this.showarrow){
22234 cfg.cn[0].cn.push({
22236 class : 'carousel-arrow',
22240 class : 'carousel-prev',
22244 class : 'fa fa-chevron-left'
22250 class : 'carousel-next',
22254 class : 'fa fa-chevron-right'
22267 initEvents: function()
22269 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22270 // this.el.on("touchstart", this.onTouchStart, this);
22273 if(this.autoslide){
22276 this.slideFn = window.setInterval(function() {
22277 _this.showPanelNext();
22281 if(this.showarrow){
22282 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22283 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22289 // onTouchStart : function(e, el, o)
22291 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22295 // this.showPanelNext();
22299 getChildContainer : function()
22301 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22305 * register a Navigation item
22306 * @param {Roo.bootstrap.nav.Item} the navitem to add
22308 register : function(item)
22310 this.tabs.push( item);
22311 item.navId = this.navId; // not really needed..
22316 getActivePanel : function()
22319 Roo.each(this.tabs, function(t) {
22329 getPanelByName : function(n)
22332 Roo.each(this.tabs, function(t) {
22333 if (t.tabId == n) {
22341 indexOfPanel : function(p)
22344 Roo.each(this.tabs, function(t,i) {
22345 if (t.tabId == p.tabId) {
22354 * show a specific panel
22355 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22356 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22358 showPanel : function (pan)
22360 if(this.transition || typeof(pan) == 'undefined'){
22361 Roo.log("waiting for the transitionend");
22365 if (typeof(pan) == 'number') {
22366 pan = this.tabs[pan];
22369 if (typeof(pan) == 'string') {
22370 pan = this.getPanelByName(pan);
22373 var cur = this.getActivePanel();
22376 Roo.log('pan or acitve pan is undefined');
22380 if (pan.tabId == this.getActivePanel().tabId) {
22384 if (false === cur.fireEvent('beforedeactivate')) {
22388 if(this.bullets > 0 && !Roo.isTouch){
22389 this.setActiveBullet(this.indexOfPanel(pan));
22392 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22394 //class="carousel-item carousel-item-next carousel-item-left"
22396 this.transition = true;
22397 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22398 var lr = dir == 'next' ? 'left' : 'right';
22399 pan.el.addClass(dir); // or prev
22400 pan.el.addClass('carousel-item-' + dir); // or prev
22401 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22402 cur.el.addClass(lr); // or right
22403 pan.el.addClass(lr);
22404 cur.el.addClass('carousel-item-' +lr); // or right
22405 pan.el.addClass('carousel-item-' +lr);
22409 cur.el.on('transitionend', function() {
22410 Roo.log("trans end?");
22412 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22413 pan.setActive(true);
22415 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22416 cur.setActive(false);
22418 _this.transition = false;
22420 }, this, { single: true } );
22425 cur.setActive(false);
22426 pan.setActive(true);
22431 showPanelNext : function()
22433 var i = this.indexOfPanel(this.getActivePanel());
22435 if (i >= this.tabs.length - 1 && !this.autoslide) {
22439 if (i >= this.tabs.length - 1 && this.autoslide) {
22443 this.showPanel(this.tabs[i+1]);
22446 showPanelPrev : function()
22448 var i = this.indexOfPanel(this.getActivePanel());
22450 if (i < 1 && !this.autoslide) {
22454 if (i < 1 && this.autoslide) {
22455 i = this.tabs.length;
22458 this.showPanel(this.tabs[i-1]);
22462 addBullet: function()
22464 if(!this.bullets || Roo.isTouch){
22467 var ctr = this.el.select('.carousel-bullets',true).first();
22468 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22469 var bullet = ctr.createChild({
22470 cls : 'bullet bullet-' + i
22471 },ctr.dom.lastChild);
22476 bullet.on('click', (function(e, el, o, ii, t){
22478 e.preventDefault();
22480 this.showPanel(ii);
22482 if(this.autoslide && this.slideFn){
22483 clearInterval(this.slideFn);
22484 this.slideFn = window.setInterval(function() {
22485 _this.showPanelNext();
22489 }).createDelegate(this, [i, bullet], true));
22494 setActiveBullet : function(i)
22500 Roo.each(this.el.select('.bullet', true).elements, function(el){
22501 el.removeClass('selected');
22504 var bullet = this.el.select('.bullet-' + i, true).first();
22510 bullet.addClass('selected');
22521 Roo.apply(Roo.bootstrap.TabGroup, {
22525 * register a Navigation Group
22526 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22528 register : function(navgrp)
22530 this.groups[navgrp.navId] = navgrp;
22534 * fetch a Navigation Group based on the navigation ID
22535 * if one does not exist , it will get created.
22536 * @param {string} the navgroup to add
22537 * @returns {Roo.bootstrap.nav.Group} the navgroup
22539 get: function(navId) {
22540 if (typeof(this.groups[navId]) == 'undefined') {
22541 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22543 return this.groups[navId] ;
22558 * @class Roo.bootstrap.TabPanel
22559 * @extends Roo.bootstrap.Component
22560 * @children Roo.bootstrap.Component
22561 * Bootstrap TabPanel class
22562 * @cfg {Boolean} active panel active
22563 * @cfg {String} html panel content
22564 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22565 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22566 * @cfg {String} href click to link..
22567 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22571 * Create a new TabPanel
22572 * @param {Object} config The config object
22575 Roo.bootstrap.TabPanel = function(config){
22576 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22580 * Fires when the active status changes
22581 * @param {Roo.bootstrap.TabPanel} this
22582 * @param {Boolean} state the new state
22587 * @event beforedeactivate
22588 * Fires before a tab is de-activated - can be used to do validation on a form.
22589 * @param {Roo.bootstrap.TabPanel} this
22590 * @return {Boolean} false if there is an error
22593 'beforedeactivate': true
22596 this.tabId = this.tabId || Roo.id();
22600 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22607 touchSlide : false,
22608 getAutoCreate : function(){
22613 // item is needed for carousel - not sure if it has any effect otherwise
22614 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22615 html: this.html || ''
22619 cfg.cls += ' active';
22623 cfg.tabId = this.tabId;
22631 initEvents: function()
22633 var p = this.parent();
22635 this.navId = this.navId || p.navId;
22637 if (typeof(this.navId) != 'undefined') {
22638 // not really needed.. but just in case.. parent should be a NavGroup.
22639 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22643 var i = tg.tabs.length - 1;
22645 if(this.active && tg.bullets > 0 && i < tg.bullets){
22646 tg.setActiveBullet(i);
22650 this.el.on('click', this.onClick, this);
22652 if(Roo.isTouch && this.touchSlide){
22653 this.el.on("touchstart", this.onTouchStart, this);
22654 this.el.on("touchmove", this.onTouchMove, this);
22655 this.el.on("touchend", this.onTouchEnd, this);
22660 onRender : function(ct, position)
22662 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22665 setActive : function(state)
22667 Roo.log("panel - set active " + this.tabId + "=" + state);
22669 this.active = state;
22671 this.el.removeClass('active');
22673 } else if (!this.el.hasClass('active')) {
22674 this.el.addClass('active');
22677 this.fireEvent('changed', this, state);
22680 onClick : function(e)
22682 e.preventDefault();
22684 if(!this.href.length){
22688 window.location.href = this.href;
22697 onTouchStart : function(e)
22699 this.swiping = false;
22701 this.startX = e.browserEvent.touches[0].clientX;
22702 this.startY = e.browserEvent.touches[0].clientY;
22705 onTouchMove : function(e)
22707 this.swiping = true;
22709 this.endX = e.browserEvent.touches[0].clientX;
22710 this.endY = e.browserEvent.touches[0].clientY;
22713 onTouchEnd : function(e)
22720 var tabGroup = this.parent();
22722 if(this.endX > this.startX){ // swiping right
22723 tabGroup.showPanelPrev();
22727 if(this.startX > this.endX){ // swiping left
22728 tabGroup.showPanelNext();
22747 * @class Roo.bootstrap.form.DateField
22748 * @extends Roo.bootstrap.form.Input
22749 * Bootstrap DateField class
22750 * @cfg {Number} weekStart default 0
22751 * @cfg {String} viewMode default empty, (months|years)
22752 * @cfg {String} minViewMode default empty, (months|years)
22753 * @cfg {Number} startDate default -Infinity
22754 * @cfg {Number} endDate default Infinity
22755 * @cfg {Boolean} todayHighlight default false
22756 * @cfg {Boolean} todayBtn default false
22757 * @cfg {Boolean} calendarWeeks default false
22758 * @cfg {Object} daysOfWeekDisabled default empty
22759 * @cfg {Boolean} singleMode default false (true | false)
22761 * @cfg {Boolean} keyboardNavigation default true
22762 * @cfg {String} language default en
22765 * Create a new DateField
22766 * @param {Object} config The config object
22769 Roo.bootstrap.form.DateField = function(config){
22770 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22774 * Fires when this field show.
22775 * @param {Roo.bootstrap.form.DateField} this
22776 * @param {Mixed} date The date value
22781 * Fires when this field hide.
22782 * @param {Roo.bootstrap.form.DateField} this
22783 * @param {Mixed} date The date value
22788 * Fires when select a date.
22789 * @param {Roo.bootstrap.form.DateField} this
22790 * @param {Mixed} date The date value
22794 * @event beforeselect
22795 * Fires when before select a date.
22796 * @param {Roo.bootstrap.form.DateField} this
22797 * @param {Mixed} date The date value
22799 beforeselect : true
22803 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22806 * @cfg {String} format
22807 * The default date format string which can be overriden for localization support. The format must be
22808 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22812 * @cfg {String} altFormats
22813 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22814 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22816 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22824 todayHighlight : false,
22830 keyboardNavigation: true,
22832 calendarWeeks: false,
22834 startDate: -Infinity,
22838 daysOfWeekDisabled: [],
22842 singleMode : false,
22844 UTCDate: function()
22846 return new Date(Date.UTC.apply(Date, arguments));
22849 UTCToday: function()
22851 var today = new Date();
22852 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22855 getDate: function() {
22856 var d = this.getUTCDate();
22857 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22860 getUTCDate: function() {
22864 setDate: function(d) {
22865 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22868 setUTCDate: function(d) {
22870 this.setValue(this.formatDate(this.date));
22873 onRender: function(ct, position)
22876 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22878 this.language = this.language || 'en';
22879 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22880 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22882 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22883 this.format = this.format || 'm/d/y';
22884 this.isInline = false;
22885 this.isInput = true;
22886 this.component = this.el.select('.add-on', true).first() || false;
22887 this.component = (this.component && this.component.length === 0) ? false : this.component;
22888 this.hasInput = this.component && this.inputEl().length;
22890 if (typeof(this.minViewMode === 'string')) {
22891 switch (this.minViewMode) {
22893 this.minViewMode = 1;
22896 this.minViewMode = 2;
22899 this.minViewMode = 0;
22904 if (typeof(this.viewMode === 'string')) {
22905 switch (this.viewMode) {
22918 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22920 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22922 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22924 this.picker().on('mousedown', this.onMousedown, this);
22925 this.picker().on('click', this.onClick, this);
22927 this.picker().addClass('datepicker-dropdown');
22929 this.startViewMode = this.viewMode;
22931 if(this.singleMode){
22932 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22933 v.setVisibilityMode(Roo.Element.DISPLAY);
22937 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22938 v.setStyle('width', '189px');
22942 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22943 if(!this.calendarWeeks){
22948 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22949 v.attr('colspan', function(i, val){
22950 return parseInt(val) + 1;
22955 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22957 this.setStartDate(this.startDate);
22958 this.setEndDate(this.endDate);
22960 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22967 if(this.isInline) {
22972 picker : function()
22974 return this.pickerEl;
22975 // return this.el.select('.datepicker', true).first();
22978 fillDow: function()
22980 var dowCnt = this.weekStart;
22989 if(this.calendarWeeks){
22997 while (dowCnt < this.weekStart + 7) {
23001 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23005 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23008 fillMonths: function()
23011 var months = this.picker().select('>.datepicker-months td', true).first();
23013 months.dom.innerHTML = '';
23019 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23022 months.createChild(month);
23029 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;
23031 if (this.date < this.startDate) {
23032 this.viewDate = new Date(this.startDate);
23033 } else if (this.date > this.endDate) {
23034 this.viewDate = new Date(this.endDate);
23036 this.viewDate = new Date(this.date);
23044 var d = new Date(this.viewDate),
23045 year = d.getUTCFullYear(),
23046 month = d.getUTCMonth(),
23047 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23048 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23049 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23050 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23051 currentDate = this.date && this.date.valueOf(),
23052 today = this.UTCToday();
23054 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23056 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23058 // this.picker.select('>tfoot th.today').
23059 // .text(dates[this.language].today)
23060 // .toggle(this.todayBtn !== false);
23062 this.updateNavArrows();
23065 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23067 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23069 prevMonth.setUTCDate(day);
23071 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23073 var nextMonth = new Date(prevMonth);
23075 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23077 nextMonth = nextMonth.valueOf();
23079 var fillMonths = false;
23081 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23083 while(prevMonth.valueOf() <= nextMonth) {
23086 if (prevMonth.getUTCDay() === this.weekStart) {
23088 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23096 if(this.calendarWeeks){
23097 // ISO 8601: First week contains first thursday.
23098 // ISO also states week starts on Monday, but we can be more abstract here.
23100 // Start of current week: based on weekstart/current date
23101 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23102 // Thursday of this week
23103 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23104 // First Thursday of year, year from thursday
23105 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23106 // Calendar week: ms between thursdays, div ms per day, div 7 days
23107 calWeek = (th - yth) / 864e5 / 7 + 1;
23109 fillMonths.cn.push({
23117 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23119 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23122 if (this.todayHighlight &&
23123 prevMonth.getUTCFullYear() == today.getFullYear() &&
23124 prevMonth.getUTCMonth() == today.getMonth() &&
23125 prevMonth.getUTCDate() == today.getDate()) {
23126 clsName += ' today';
23129 if (currentDate && prevMonth.valueOf() === currentDate) {
23130 clsName += ' active';
23133 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23134 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23135 clsName += ' disabled';
23138 fillMonths.cn.push({
23140 cls: 'day ' + clsName,
23141 html: prevMonth.getDate()
23144 prevMonth.setDate(prevMonth.getDate()+1);
23147 var currentYear = this.date && this.date.getUTCFullYear();
23148 var currentMonth = this.date && this.date.getUTCMonth();
23150 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23152 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23153 v.removeClass('active');
23155 if(currentYear === year && k === currentMonth){
23156 v.addClass('active');
23159 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23160 v.addClass('disabled');
23166 year = parseInt(year/10, 10) * 10;
23168 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23170 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23173 for (var i = -1; i < 11; i++) {
23174 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23176 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23184 showMode: function(dir)
23187 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23190 Roo.each(this.picker().select('>div',true).elements, function(v){
23191 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23194 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23199 if(this.isInline) {
23203 this.picker().removeClass(['bottom', 'top']);
23205 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23207 * place to the top of element!
23211 this.picker().addClass('top');
23212 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23217 this.picker().addClass('bottom');
23219 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23222 parseDate : function(value)
23224 if(!value || value instanceof Date){
23227 var v = Date.parseDate(value, this.format);
23228 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23229 v = Date.parseDate(value, 'Y-m-d');
23231 if(!v && this.altFormats){
23232 if(!this.altFormatsArray){
23233 this.altFormatsArray = this.altFormats.split("|");
23235 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23236 v = Date.parseDate(value, this.altFormatsArray[i]);
23242 formatDate : function(date, fmt)
23244 return (!date || !(date instanceof Date)) ?
23245 date : date.dateFormat(fmt || this.format);
23248 onFocus : function()
23250 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23254 onBlur : function()
23256 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23258 var d = this.inputEl().getValue();
23265 showPopup : function()
23267 this.picker().show();
23271 this.fireEvent('showpopup', this, this.date);
23274 hidePopup : function()
23276 if(this.isInline) {
23279 this.picker().hide();
23280 this.viewMode = this.startViewMode;
23283 this.fireEvent('hidepopup', this, this.date);
23287 onMousedown: function(e)
23289 e.stopPropagation();
23290 e.preventDefault();
23295 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23299 setValue: function(v)
23301 if(this.fireEvent('beforeselect', this, v) !== false){
23302 var d = new Date(this.parseDate(v) ).clearTime();
23304 if(isNaN(d.getTime())){
23305 this.date = this.viewDate = '';
23306 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23310 v = this.formatDate(d);
23312 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23314 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23318 this.fireEvent('select', this, this.date);
23322 getValue: function()
23324 return this.formatDate(this.date);
23327 fireKey: function(e)
23329 if (!this.picker().isVisible()){
23330 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23336 var dateChanged = false,
23338 newDate, newViewDate;
23343 e.preventDefault();
23347 if (!this.keyboardNavigation) {
23350 dir = e.keyCode == 37 ? -1 : 1;
23353 newDate = this.moveYear(this.date, dir);
23354 newViewDate = this.moveYear(this.viewDate, dir);
23355 } else if (e.shiftKey){
23356 newDate = this.moveMonth(this.date, dir);
23357 newViewDate = this.moveMonth(this.viewDate, dir);
23359 newDate = new Date(this.date);
23360 newDate.setUTCDate(this.date.getUTCDate() + dir);
23361 newViewDate = new Date(this.viewDate);
23362 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23364 if (this.dateWithinRange(newDate)){
23365 this.date = newDate;
23366 this.viewDate = newViewDate;
23367 this.setValue(this.formatDate(this.date));
23369 e.preventDefault();
23370 dateChanged = true;
23375 if (!this.keyboardNavigation) {
23378 dir = e.keyCode == 38 ? -1 : 1;
23380 newDate = this.moveYear(this.date, dir);
23381 newViewDate = this.moveYear(this.viewDate, dir);
23382 } else if (e.shiftKey){
23383 newDate = this.moveMonth(this.date, dir);
23384 newViewDate = this.moveMonth(this.viewDate, dir);
23386 newDate = new Date(this.date);
23387 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23388 newViewDate = new Date(this.viewDate);
23389 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23391 if (this.dateWithinRange(newDate)){
23392 this.date = newDate;
23393 this.viewDate = newViewDate;
23394 this.setValue(this.formatDate(this.date));
23396 e.preventDefault();
23397 dateChanged = true;
23401 this.setValue(this.formatDate(this.date));
23403 e.preventDefault();
23406 this.setValue(this.formatDate(this.date));
23420 onClick: function(e)
23422 e.stopPropagation();
23423 e.preventDefault();
23425 var target = e.getTarget();
23427 if(target.nodeName.toLowerCase() === 'i'){
23428 target = Roo.get(target).dom.parentNode;
23431 var nodeName = target.nodeName;
23432 var className = target.className;
23433 var html = target.innerHTML;
23434 //Roo.log(nodeName);
23436 switch(nodeName.toLowerCase()) {
23438 switch(className) {
23444 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23445 switch(this.viewMode){
23447 this.viewDate = this.moveMonth(this.viewDate, dir);
23451 this.viewDate = this.moveYear(this.viewDate, dir);
23457 var date = new Date();
23458 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23460 this.setValue(this.formatDate(this.date));
23467 if (className.indexOf('disabled') < 0) {
23468 if (!this.viewDate) {
23469 this.viewDate = new Date();
23471 this.viewDate.setUTCDate(1);
23472 if (className.indexOf('month') > -1) {
23473 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23475 var year = parseInt(html, 10) || 0;
23476 this.viewDate.setUTCFullYear(year);
23480 if(this.singleMode){
23481 this.setValue(this.formatDate(this.viewDate));
23492 //Roo.log(className);
23493 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23494 var day = parseInt(html, 10) || 1;
23495 var year = (this.viewDate || new Date()).getUTCFullYear(),
23496 month = (this.viewDate || new Date()).getUTCMonth();
23498 if (className.indexOf('old') > -1) {
23505 } else if (className.indexOf('new') > -1) {
23513 //Roo.log([year,month,day]);
23514 this.date = this.UTCDate(year, month, day,0,0,0,0);
23515 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23517 //Roo.log(this.formatDate(this.date));
23518 this.setValue(this.formatDate(this.date));
23525 setStartDate: function(startDate)
23527 this.startDate = startDate || -Infinity;
23528 if (this.startDate !== -Infinity) {
23529 this.startDate = this.parseDate(this.startDate);
23532 this.updateNavArrows();
23535 setEndDate: function(endDate)
23537 this.endDate = endDate || Infinity;
23538 if (this.endDate !== Infinity) {
23539 this.endDate = this.parseDate(this.endDate);
23542 this.updateNavArrows();
23545 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23547 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23548 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23549 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23551 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23552 return parseInt(d, 10);
23555 this.updateNavArrows();
23558 updateNavArrows: function()
23560 if(this.singleMode){
23564 var d = new Date(this.viewDate),
23565 year = d.getUTCFullYear(),
23566 month = d.getUTCMonth();
23568 Roo.each(this.picker().select('.prev', true).elements, function(v){
23570 switch (this.viewMode) {
23573 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23579 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23586 Roo.each(this.picker().select('.next', true).elements, function(v){
23588 switch (this.viewMode) {
23591 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23597 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23605 moveMonth: function(date, dir)
23610 var new_date = new Date(date.valueOf()),
23611 day = new_date.getUTCDate(),
23612 month = new_date.getUTCMonth(),
23613 mag = Math.abs(dir),
23615 dir = dir > 0 ? 1 : -1;
23618 // If going back one month, make sure month is not current month
23619 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23621 return new_date.getUTCMonth() == month;
23623 // If going forward one month, make sure month is as expected
23624 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23626 return new_date.getUTCMonth() != new_month;
23628 new_month = month + dir;
23629 new_date.setUTCMonth(new_month);
23630 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23631 if (new_month < 0 || new_month > 11) {
23632 new_month = (new_month + 12) % 12;
23635 // For magnitudes >1, move one month at a time...
23636 for (var i=0; i<mag; i++) {
23637 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23638 new_date = this.moveMonth(new_date, dir);
23640 // ...then reset the day, keeping it in the new month
23641 new_month = new_date.getUTCMonth();
23642 new_date.setUTCDate(day);
23644 return new_month != new_date.getUTCMonth();
23647 // Common date-resetting loop -- if date is beyond end of month, make it
23650 new_date.setUTCDate(--day);
23651 new_date.setUTCMonth(new_month);
23656 moveYear: function(date, dir)
23658 return this.moveMonth(date, dir*12);
23661 dateWithinRange: function(date)
23663 return date >= this.startDate && date <= this.endDate;
23669 this.picker().remove();
23672 validateValue : function(value)
23674 if(this.getVisibilityEl().hasClass('hidden')){
23678 if(value.length < 1) {
23679 if(this.allowBlank){
23685 if(value.length < this.minLength){
23688 if(value.length > this.maxLength){
23692 var vt = Roo.form.VTypes;
23693 if(!vt[this.vtype](value, this)){
23697 if(typeof this.validator == "function"){
23698 var msg = this.validator(value);
23704 if(this.regex && !this.regex.test(value)){
23708 if(typeof(this.parseDate(value)) == 'undefined'){
23712 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23716 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23726 this.date = this.viewDate = '';
23728 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23733 Roo.apply(Roo.bootstrap.form.DateField, {
23744 html: '<i class="fa fa-arrow-left"/>'
23754 html: '<i class="fa fa-arrow-right"/>'
23796 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23797 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23798 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23799 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23800 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23813 navFnc: 'FullYear',
23818 navFnc: 'FullYear',
23823 Roo.apply(Roo.bootstrap.form.DateField, {
23827 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23831 cls: 'datepicker-days',
23835 cls: 'table-condensed',
23837 Roo.bootstrap.form.DateField.head,
23841 Roo.bootstrap.form.DateField.footer
23848 cls: 'datepicker-months',
23852 cls: 'table-condensed',
23854 Roo.bootstrap.form.DateField.head,
23855 Roo.bootstrap.form.DateField.content,
23856 Roo.bootstrap.form.DateField.footer
23863 cls: 'datepicker-years',
23867 cls: 'table-condensed',
23869 Roo.bootstrap.form.DateField.head,
23870 Roo.bootstrap.form.DateField.content,
23871 Roo.bootstrap.form.DateField.footer
23890 * @class Roo.bootstrap.form.TimeField
23891 * @extends Roo.bootstrap.form.Input
23892 * Bootstrap DateField class
23896 * Create a new TimeField
23897 * @param {Object} config The config object
23900 Roo.bootstrap.form.TimeField = function(config){
23901 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23905 * Fires when this field show.
23906 * @param {Roo.bootstrap.form.DateField} thisthis
23907 * @param {Mixed} date The date value
23912 * Fires when this field hide.
23913 * @param {Roo.bootstrap.form.DateField} this
23914 * @param {Mixed} date The date value
23919 * Fires when select a date.
23920 * @param {Roo.bootstrap.form.DateField} this
23921 * @param {Mixed} date The date value
23927 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23930 * @cfg {String} format
23931 * The default time format string which can be overriden for localization support. The format must be
23932 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23936 getAutoCreate : function()
23938 this.after = '<i class="fa far fa-clock"></i>';
23939 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23943 onRender: function(ct, position)
23946 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23948 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23950 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23952 this.pop = this.picker().select('>.datepicker-time',true).first();
23953 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23955 this.picker().on('mousedown', this.onMousedown, this);
23956 this.picker().on('click', this.onClick, this);
23958 this.picker().addClass('datepicker-dropdown');
23963 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23964 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23965 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23966 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23967 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23968 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23972 fireKey: function(e){
23973 if (!this.picker().isVisible()){
23974 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23980 e.preventDefault();
23988 this.onTogglePeriod();
23991 this.onIncrementMinutes();
23994 this.onDecrementMinutes();
24003 onClick: function(e) {
24004 e.stopPropagation();
24005 e.preventDefault();
24008 picker : function()
24010 return this.pickerEl;
24013 fillTime: function()
24015 var time = this.pop.select('tbody', true).first();
24017 time.dom.innerHTML = '';
24032 cls: 'hours-up fa fas fa-chevron-up'
24052 cls: 'minutes-up fa fas fa-chevron-up'
24073 cls: 'timepicker-hour',
24088 cls: 'timepicker-minute',
24103 cls: 'btn btn-primary period',
24125 cls: 'hours-down fa fas fa-chevron-down'
24145 cls: 'minutes-down fa fas fa-chevron-down'
24163 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24170 var hours = this.time.getHours();
24171 var minutes = this.time.getMinutes();
24184 hours = hours - 12;
24188 hours = '0' + hours;
24192 minutes = '0' + minutes;
24195 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24196 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24197 this.pop.select('button', true).first().dom.innerHTML = period;
24203 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24205 var cls = ['bottom'];
24207 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24214 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24218 //this.picker().setXY(20000,20000);
24219 this.picker().addClass(cls.join('-'));
24223 Roo.each(cls, function(c){
24228 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24229 //_this.picker().setTop(_this.inputEl().getHeight());
24233 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24235 //_this.picker().setTop(0 - _this.picker().getHeight());
24240 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24244 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24252 onFocus : function()
24254 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24258 onBlur : function()
24260 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24266 this.picker().show();
24271 this.fireEvent('show', this, this.date);
24276 this.picker().hide();
24279 this.fireEvent('hide', this, this.date);
24282 setTime : function()
24285 this.setValue(this.time.format(this.format));
24287 this.fireEvent('select', this, this.date);
24292 onMousedown: function(e){
24293 e.stopPropagation();
24294 e.preventDefault();
24297 onIncrementHours: function()
24299 Roo.log('onIncrementHours');
24300 this.time = this.time.add(Date.HOUR, 1);
24305 onDecrementHours: function()
24307 Roo.log('onDecrementHours');
24308 this.time = this.time.add(Date.HOUR, -1);
24312 onIncrementMinutes: function()
24314 Roo.log('onIncrementMinutes');
24315 this.time = this.time.add(Date.MINUTE, 1);
24319 onDecrementMinutes: function()
24321 Roo.log('onDecrementMinutes');
24322 this.time = this.time.add(Date.MINUTE, -1);
24326 onTogglePeriod: function()
24328 Roo.log('onTogglePeriod');
24329 this.time = this.time.add(Date.HOUR, 12);
24337 Roo.apply(Roo.bootstrap.form.TimeField, {
24341 cls: 'datepicker dropdown-menu',
24345 cls: 'datepicker-time',
24349 cls: 'table-condensed',
24378 cls: 'btn btn-info ok',
24406 * @class Roo.bootstrap.form.MonthField
24407 * @extends Roo.bootstrap.form.Input
24408 * Bootstrap MonthField class
24410 * @cfg {String} language default en
24413 * Create a new MonthField
24414 * @param {Object} config The config object
24417 Roo.bootstrap.form.MonthField = function(config){
24418 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24423 * Fires when this field show.
24424 * @param {Roo.bootstrap.form.MonthField} this
24425 * @param {Mixed} date The date value
24430 * Fires when this field hide.
24431 * @param {Roo.bootstrap.form.MonthField} this
24432 * @param {Mixed} date The date value
24437 * Fires when select a date.
24438 * @param {Roo.bootstrap.form.MonthField} this
24439 * @param {String} oldvalue The old value
24440 * @param {String} newvalue The new value
24446 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24448 onRender: function(ct, position)
24451 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24453 this.language = this.language || 'en';
24454 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24455 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24457 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24458 this.isInline = false;
24459 this.isInput = true;
24460 this.component = this.el.select('.add-on', true).first() || false;
24461 this.component = (this.component && this.component.length === 0) ? false : this.component;
24462 this.hasInput = this.component && this.inputEL().length;
24464 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24466 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24468 this.picker().on('mousedown', this.onMousedown, this);
24469 this.picker().on('click', this.onClick, this);
24471 this.picker().addClass('datepicker-dropdown');
24473 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24474 v.setStyle('width', '189px');
24481 if(this.isInline) {
24487 setValue: function(v, suppressEvent)
24489 var o = this.getValue();
24491 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24495 if(suppressEvent !== true){
24496 this.fireEvent('select', this, o, v);
24501 getValue: function()
24506 onClick: function(e)
24508 e.stopPropagation();
24509 e.preventDefault();
24511 var target = e.getTarget();
24513 if(target.nodeName.toLowerCase() === 'i'){
24514 target = Roo.get(target).dom.parentNode;
24517 var nodeName = target.nodeName;
24518 var className = target.className;
24519 var html = target.innerHTML;
24521 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24525 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24527 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24533 picker : function()
24535 return this.pickerEl;
24538 fillMonths: function()
24541 var months = this.picker().select('>.datepicker-months td', true).first();
24543 months.dom.innerHTML = '';
24549 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24552 months.createChild(month);
24561 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24562 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24565 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24566 e.removeClass('active');
24568 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24569 e.addClass('active');
24576 if(this.isInline) {
24580 this.picker().removeClass(['bottom', 'top']);
24582 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24584 * place to the top of element!
24588 this.picker().addClass('top');
24589 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24594 this.picker().addClass('bottom');
24596 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24599 onFocus : function()
24601 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24605 onBlur : function()
24607 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24609 var d = this.inputEl().getValue();
24618 this.picker().show();
24619 this.picker().select('>.datepicker-months', true).first().show();
24623 this.fireEvent('show', this, this.date);
24628 if(this.isInline) {
24631 this.picker().hide();
24632 this.fireEvent('hide', this, this.date);
24636 onMousedown: function(e)
24638 e.stopPropagation();
24639 e.preventDefault();
24644 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24648 fireKey: function(e)
24650 if (!this.picker().isVisible()){
24651 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24662 e.preventDefault();
24666 dir = e.keyCode == 37 ? -1 : 1;
24668 this.vIndex = this.vIndex + dir;
24670 if(this.vIndex < 0){
24674 if(this.vIndex > 11){
24678 if(isNaN(this.vIndex)){
24682 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24688 dir = e.keyCode == 38 ? -1 : 1;
24690 this.vIndex = this.vIndex + dir * 4;
24692 if(this.vIndex < 0){
24696 if(this.vIndex > 11){
24700 if(isNaN(this.vIndex)){
24704 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24709 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24710 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24714 e.preventDefault();
24717 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24718 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24734 this.picker().remove();
24739 Roo.apply(Roo.bootstrap.form.MonthField, {
24758 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24759 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24764 Roo.apply(Roo.bootstrap.form.MonthField, {
24768 cls: 'datepicker dropdown-menu roo-dynamic',
24772 cls: 'datepicker-months',
24776 cls: 'table-condensed',
24778 Roo.bootstrap.form.DateField.content
24798 * @class Roo.bootstrap.form.CheckBox
24799 * @extends Roo.bootstrap.form.Input
24800 * Bootstrap CheckBox class
24802 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24803 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24804 * @cfg {String} boxLabel The text that appears beside the checkbox
24805 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24806 * @cfg {Boolean} checked initnal the element
24807 * @cfg {Boolean} inline inline the element (default false)
24808 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24809 * @cfg {String} tooltip label tooltip
24812 * Create a new CheckBox
24813 * @param {Object} config The config object
24816 Roo.bootstrap.form.CheckBox = function(config){
24817 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24822 * Fires when the element is checked or unchecked.
24823 * @param {Roo.bootstrap.form.CheckBox} this This input
24824 * @param {Boolean} checked The new checked value
24829 * Fires when the element is click.
24830 * @param {Roo.bootstrap.form.CheckBox} this This input
24837 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24839 inputType: 'checkbox',
24848 // checkbox success does not make any sense really..
24853 getAutoCreate : function()
24855 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24861 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24864 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24870 type : this.inputType,
24871 value : this.inputValue,
24872 cls : 'roo-' + this.inputType, //'form-box',
24873 placeholder : this.placeholder || ''
24877 if(this.inputType != 'radio'){
24881 cls : 'roo-hidden-value',
24882 value : this.checked ? this.inputValue : this.valueOff
24887 if (this.weight) { // Validity check?
24888 cfg.cls += " " + this.inputType + "-" + this.weight;
24891 if (this.disabled) {
24892 input.disabled=true;
24896 input.checked = this.checked;
24901 input.name = this.name;
24903 if(this.inputType != 'radio'){
24904 hidden.name = this.name;
24905 input.name = '_hidden_' + this.name;
24910 input.cls += ' input-' + this.size;
24915 ['xs','sm','md','lg'].map(function(size){
24916 if (settings[size]) {
24917 cfg.cls += ' col-' + size + '-' + settings[size];
24921 var inputblock = input;
24923 if (this.before || this.after) {
24926 cls : 'input-group',
24931 inputblock.cn.push({
24933 cls : 'input-group-addon',
24938 inputblock.cn.push(input);
24940 if(this.inputType != 'radio'){
24941 inputblock.cn.push(hidden);
24945 inputblock.cn.push({
24947 cls : 'input-group-addon',
24953 var boxLabelCfg = false;
24959 //'for': id, // box label is handled by onclick - so no for...
24961 html: this.boxLabel
24964 boxLabelCfg.tooltip = this.tooltip;
24970 if (align ==='left' && this.fieldLabel.length) {
24971 // Roo.log("left and has label");
24976 cls : 'control-label',
24977 html : this.fieldLabel
24988 cfg.cn[1].cn.push(boxLabelCfg);
24991 if(this.labelWidth > 12){
24992 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24995 if(this.labelWidth < 13 && this.labelmd == 0){
24996 this.labelmd = this.labelWidth;
24999 if(this.labellg > 0){
25000 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25001 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25004 if(this.labelmd > 0){
25005 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25006 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25009 if(this.labelsm > 0){
25010 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25011 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25014 if(this.labelxs > 0){
25015 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25016 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25019 } else if ( this.fieldLabel.length) {
25020 // Roo.log(" label");
25024 tag: this.boxLabel ? 'span' : 'label',
25026 cls: 'control-label box-input-label',
25027 //cls : 'input-group-addon',
25028 html : this.fieldLabel
25035 cfg.cn.push(boxLabelCfg);
25040 // Roo.log(" no label && no align");
25041 cfg.cn = [ inputblock ] ;
25043 cfg.cn.push(boxLabelCfg);
25051 if(this.inputType != 'radio'){
25052 cfg.cn.push(hidden);
25060 * return the real input element.
25062 inputEl: function ()
25064 return this.el.select('input.roo-' + this.inputType,true).first();
25066 hiddenEl: function ()
25068 return this.el.select('input.roo-hidden-value',true).first();
25071 labelEl: function()
25073 return this.el.select('label.control-label',true).first();
25075 /* depricated... */
25079 return this.labelEl();
25082 boxLabelEl: function()
25084 return this.el.select('label.box-label',true).first();
25087 initEvents : function()
25089 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25091 this.inputEl().on('click', this.onClick, this);
25093 if (this.boxLabel) {
25094 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25097 this.startValue = this.getValue();
25100 Roo.bootstrap.form.CheckBox.register(this);
25104 onClick : function(e)
25106 if(this.fireEvent('click', this, e) !== false){
25107 this.setChecked(!this.checked);
25112 setChecked : function(state,suppressEvent)
25114 this.startValue = this.getValue();
25116 if(this.inputType == 'radio'){
25118 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25119 e.dom.checked = false;
25122 this.inputEl().dom.checked = true;
25124 this.inputEl().dom.value = this.inputValue;
25126 if(suppressEvent !== true){
25127 this.fireEvent('check', this, true);
25135 this.checked = state;
25137 this.inputEl().dom.checked = state;
25140 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25142 if(suppressEvent !== true){
25143 this.fireEvent('check', this, state);
25149 getValue : function()
25151 if(this.inputType == 'radio'){
25152 return this.getGroupValue();
25155 return this.hiddenEl().dom.value;
25159 getGroupValue : function()
25161 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25165 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25168 setValue : function(v,suppressEvent)
25170 if(this.inputType == 'radio'){
25171 this.setGroupValue(v, suppressEvent);
25175 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25180 setGroupValue : function(v, suppressEvent)
25182 this.startValue = this.getValue();
25184 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25185 e.dom.checked = false;
25187 if(e.dom.value == v){
25188 e.dom.checked = true;
25192 if(suppressEvent !== true){
25193 this.fireEvent('check', this, true);
25201 validate : function()
25203 if(this.getVisibilityEl().hasClass('hidden')){
25209 (this.inputType == 'radio' && this.validateRadio()) ||
25210 (this.inputType == 'checkbox' && this.validateCheckbox())
25216 this.markInvalid();
25220 validateRadio : function()
25222 if(this.getVisibilityEl().hasClass('hidden')){
25226 if(this.allowBlank){
25232 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25233 if(!e.dom.checked){
25245 validateCheckbox : function()
25248 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25249 //return (this.getValue() == this.inputValue) ? true : false;
25252 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25260 for(var i in group){
25261 if(group[i].el.isVisible(true)){
25269 for(var i in group){
25274 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25281 * Mark this field as valid
25283 markValid : function()
25287 this.fireEvent('valid', this);
25289 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25292 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25299 if(this.inputType == 'radio'){
25300 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25301 var fg = e.findParent('.form-group', false, true);
25302 if (Roo.bootstrap.version == 3) {
25303 fg.removeClass([_this.invalidClass, _this.validClass]);
25304 fg.addClass(_this.validClass);
25306 fg.removeClass(['is-valid', 'is-invalid']);
25307 fg.addClass('is-valid');
25315 var fg = this.el.findParent('.form-group', false, true);
25316 if (Roo.bootstrap.version == 3) {
25317 fg.removeClass([this.invalidClass, this.validClass]);
25318 fg.addClass(this.validClass);
25320 fg.removeClass(['is-valid', 'is-invalid']);
25321 fg.addClass('is-valid');
25326 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25332 for(var i in group){
25333 var fg = group[i].el.findParent('.form-group', false, true);
25334 if (Roo.bootstrap.version == 3) {
25335 fg.removeClass([this.invalidClass, this.validClass]);
25336 fg.addClass(this.validClass);
25338 fg.removeClass(['is-valid', 'is-invalid']);
25339 fg.addClass('is-valid');
25345 * Mark this field as invalid
25346 * @param {String} msg The validation message
25348 markInvalid : function(msg)
25350 if(this.allowBlank){
25356 this.fireEvent('invalid', this, msg);
25358 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25361 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25365 label.markInvalid();
25368 if(this.inputType == 'radio'){
25370 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25371 var fg = e.findParent('.form-group', false, true);
25372 if (Roo.bootstrap.version == 3) {
25373 fg.removeClass([_this.invalidClass, _this.validClass]);
25374 fg.addClass(_this.invalidClass);
25376 fg.removeClass(['is-invalid', 'is-valid']);
25377 fg.addClass('is-invalid');
25385 var fg = this.el.findParent('.form-group', false, true);
25386 if (Roo.bootstrap.version == 3) {
25387 fg.removeClass([_this.invalidClass, _this.validClass]);
25388 fg.addClass(_this.invalidClass);
25390 fg.removeClass(['is-invalid', 'is-valid']);
25391 fg.addClass('is-invalid');
25396 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25402 for(var i in group){
25403 var fg = group[i].el.findParent('.form-group', false, true);
25404 if (Roo.bootstrap.version == 3) {
25405 fg.removeClass([_this.invalidClass, _this.validClass]);
25406 fg.addClass(_this.invalidClass);
25408 fg.removeClass(['is-invalid', 'is-valid']);
25409 fg.addClass('is-invalid');
25415 clearInvalid : function()
25417 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25419 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25421 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25423 if (label && label.iconEl) {
25424 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25425 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25429 disable : function()
25431 if(this.inputType != 'radio'){
25432 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25439 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25440 _this.getActionEl().addClass(this.disabledClass);
25441 e.dom.disabled = true;
25445 this.disabled = true;
25446 this.fireEvent("disable", this);
25450 enable : function()
25452 if(this.inputType != 'radio'){
25453 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25460 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25461 _this.getActionEl().removeClass(this.disabledClass);
25462 e.dom.disabled = false;
25466 this.disabled = false;
25467 this.fireEvent("enable", this);
25471 setBoxLabel : function(v)
25476 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25482 Roo.apply(Roo.bootstrap.form.CheckBox, {
25487 * register a CheckBox Group
25488 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25490 register : function(checkbox)
25492 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25493 this.groups[checkbox.groupId] = {};
25496 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25500 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25504 * fetch a CheckBox Group based on the group ID
25505 * @param {string} the group ID
25506 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25508 get: function(groupId) {
25509 if (typeof(this.groups[groupId]) == 'undefined') {
25513 return this.groups[groupId] ;
25526 * @class Roo.bootstrap.form.Radio
25527 * @extends Roo.bootstrap.Component
25528 * Bootstrap Radio class
25529 * @cfg {String} boxLabel - the label associated
25530 * @cfg {String} value - the value of radio
25533 * Create a new Radio
25534 * @param {Object} config The config object
25536 Roo.bootstrap.form.Radio = function(config){
25537 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25541 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25547 getAutoCreate : function()
25551 cls : 'form-group radio',
25556 html : this.boxLabel
25564 initEvents : function()
25566 this.parent().register(this);
25568 this.el.on('click', this.onClick, this);
25572 onClick : function(e)
25574 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25575 this.setChecked(true);
25579 setChecked : function(state, suppressEvent)
25581 this.parent().setValue(this.value, suppressEvent);
25585 setBoxLabel : function(v)
25590 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25605 * @class Roo.bootstrap.form.SecurePass
25606 * @extends Roo.bootstrap.form.Input
25607 * Bootstrap SecurePass class
25611 * Create a new SecurePass
25612 * @param {Object} config The config object
25615 Roo.bootstrap.form.SecurePass = function (config) {
25616 // these go here, so the translation tool can replace them..
25618 PwdEmpty: "Please type a password, and then retype it to confirm.",
25619 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25620 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25621 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25622 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25623 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25624 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25625 TooWeak: "Your password is Too Weak."
25627 this.meterLabel = "Password strength:";
25628 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25629 this.meterClass = [
25630 "roo-password-meter-tooweak",
25631 "roo-password-meter-weak",
25632 "roo-password-meter-medium",
25633 "roo-password-meter-strong",
25634 "roo-password-meter-grey"
25639 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25642 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25644 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25646 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25647 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25648 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25649 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25650 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25651 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25652 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25662 * @cfg {String/Object} Label for the strength meter (defaults to
25663 * 'Password strength:')
25668 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25669 * ['Weak', 'Medium', 'Strong'])
25672 pwdStrengths: false,
25685 initEvents: function ()
25687 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25689 if (this.el.is('input[type=password]') && Roo.isSafari) {
25690 this.el.on('keydown', this.SafariOnKeyDown, this);
25693 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25696 onRender: function (ct, position)
25698 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25699 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25700 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25702 this.trigger.createChild({
25707 cls: 'roo-password-meter-grey col-xs-12',
25710 //width: this.meterWidth + 'px'
25714 cls: 'roo-password-meter-text'
25720 if (this.hideTrigger) {
25721 this.trigger.setDisplayed(false);
25723 this.setSize(this.width || '', this.height || '');
25726 onDestroy: function ()
25728 if (this.trigger) {
25729 this.trigger.removeAllListeners();
25730 this.trigger.remove();
25733 this.wrap.remove();
25735 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25738 checkStrength: function ()
25740 var pwd = this.inputEl().getValue();
25741 if (pwd == this._lastPwd) {
25746 if (this.ClientSideStrongPassword(pwd)) {
25748 } else if (this.ClientSideMediumPassword(pwd)) {
25750 } else if (this.ClientSideWeakPassword(pwd)) {
25756 Roo.log('strength1: ' + strength);
25758 //var pm = this.trigger.child('div/div/div').dom;
25759 var pm = this.trigger.child('div/div');
25760 pm.removeClass(this.meterClass);
25761 pm.addClass(this.meterClass[strength]);
25764 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25766 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25768 this._lastPwd = pwd;
25772 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25774 this._lastPwd = '';
25776 var pm = this.trigger.child('div/div');
25777 pm.removeClass(this.meterClass);
25778 pm.addClass('roo-password-meter-grey');
25781 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25784 this.inputEl().dom.type='password';
25787 validateValue: function (value)
25789 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25792 if (value.length == 0) {
25793 if (this.allowBlank) {
25794 this.clearInvalid();
25798 this.markInvalid(this.errors.PwdEmpty);
25799 this.errorMsg = this.errors.PwdEmpty;
25807 if (!value.match(/[\x21-\x7e]+/)) {
25808 this.markInvalid(this.errors.PwdBadChar);
25809 this.errorMsg = this.errors.PwdBadChar;
25812 if (value.length < 6) {
25813 this.markInvalid(this.errors.PwdShort);
25814 this.errorMsg = this.errors.PwdShort;
25817 if (value.length > 16) {
25818 this.markInvalid(this.errors.PwdLong);
25819 this.errorMsg = this.errors.PwdLong;
25823 if (this.ClientSideStrongPassword(value)) {
25825 } else if (this.ClientSideMediumPassword(value)) {
25827 } else if (this.ClientSideWeakPassword(value)) {
25834 if (strength < 2) {
25835 //this.markInvalid(this.errors.TooWeak);
25836 this.errorMsg = this.errors.TooWeak;
25841 console.log('strength2: ' + strength);
25843 //var pm = this.trigger.child('div/div/div').dom;
25845 var pm = this.trigger.child('div/div');
25846 pm.removeClass(this.meterClass);
25847 pm.addClass(this.meterClass[strength]);
25849 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25851 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25853 this.errorMsg = '';
25857 CharacterSetChecks: function (type)
25860 this.fResult = false;
25863 isctype: function (character, type)
25866 case this.kCapitalLetter:
25867 if (character >= 'A' && character <= 'Z') {
25872 case this.kSmallLetter:
25873 if (character >= 'a' && character <= 'z') {
25879 if (character >= '0' && character <= '9') {
25884 case this.kPunctuation:
25885 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25896 IsLongEnough: function (pwd, size)
25898 return !(pwd == null || isNaN(size) || pwd.length < size);
25901 SpansEnoughCharacterSets: function (word, nb)
25903 if (!this.IsLongEnough(word, nb))
25908 var characterSetChecks = new Array(
25909 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25910 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25913 for (var index = 0; index < word.length; ++index) {
25914 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25915 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25916 characterSetChecks[nCharSet].fResult = true;
25923 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25924 if (characterSetChecks[nCharSet].fResult) {
25929 if (nCharSets < nb) {
25935 ClientSideStrongPassword: function (pwd)
25937 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25940 ClientSideMediumPassword: function (pwd)
25942 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25945 ClientSideWeakPassword: function (pwd)
25947 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25950 })//<script type="text/javascript">
25953 * Based Ext JS Library 1.1.1
25954 * Copyright(c) 2006-2007, Ext JS, LLC.
25960 * @class Roo.HtmlEditorCore
25961 * @extends Roo.Component
25962 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25964 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25967 Roo.HtmlEditorCore = function(config){
25970 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25975 * @event initialize
25976 * Fires when the editor is fully initialized (including the iframe)
25977 * @param {Roo.HtmlEditorCore} this
25982 * Fires when the editor is first receives the focus. Any insertion must wait
25983 * until after this event.
25984 * @param {Roo.HtmlEditorCore} this
25988 * @event beforesync
25989 * Fires before the textarea is updated with content from the editor iframe. Return false
25990 * to cancel the sync.
25991 * @param {Roo.HtmlEditorCore} this
25992 * @param {String} html
25996 * @event beforepush
25997 * Fires before the iframe editor is updated with content from the textarea. Return false
25998 * to cancel the push.
25999 * @param {Roo.HtmlEditorCore} this
26000 * @param {String} html
26005 * Fires when the textarea is updated with content from the editor iframe.
26006 * @param {Roo.HtmlEditorCore} this
26007 * @param {String} html
26012 * Fires when the iframe editor is updated with content from the textarea.
26013 * @param {Roo.HtmlEditorCore} this
26014 * @param {String} html
26019 * @event editorevent
26020 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26021 * @param {Roo.HtmlEditorCore} this
26027 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26029 // defaults : white / black...
26030 this.applyBlacklists();
26037 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26041 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26047 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26052 * @cfg {Number} height (in pixels)
26056 * @cfg {Number} width (in pixels)
26061 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26064 stylesheets: false,
26067 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26069 allowComments: false,
26073 // private properties
26074 validationEvent : false,
26076 initialized : false,
26078 sourceEditMode : false,
26079 onFocus : Roo.emptyFn,
26081 hideMode:'offsets',
26085 // blacklist + whitelisted elements..
26092 * Protected method that will not generally be called directly. It
26093 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26094 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26096 getDocMarkup : function(){
26100 // inherit styels from page...??
26101 if (this.stylesheets === false) {
26103 Roo.get(document.head).select('style').each(function(node) {
26104 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26107 Roo.get(document.head).select('link').each(function(node) {
26108 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26111 } else if (!this.stylesheets.length) {
26113 st = '<style type="text/css">' +
26114 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26117 for (var i in this.stylesheets) {
26118 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26123 st += '<style type="text/css">' +
26124 'IMG { cursor: pointer } ' +
26127 var cls = 'roo-htmleditor-body';
26129 if(this.bodyCls.length){
26130 cls += ' ' + this.bodyCls;
26133 return '<html><head>' + st +
26134 //<style type="text/css">' +
26135 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26137 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26141 onRender : function(ct, position)
26144 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26145 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26148 this.el.dom.style.border = '0 none';
26149 this.el.dom.setAttribute('tabIndex', -1);
26150 this.el.addClass('x-hidden hide');
26154 if(Roo.isIE){ // fix IE 1px bogus margin
26155 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26159 this.frameId = Roo.id();
26163 var iframe = this.owner.wrap.createChild({
26165 cls: 'form-control', // bootstrap..
26167 name: this.frameId,
26168 frameBorder : 'no',
26169 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26174 this.iframe = iframe.dom;
26176 this.assignDocWin();
26178 this.doc.designMode = 'on';
26181 this.doc.write(this.getDocMarkup());
26185 var task = { // must defer to wait for browser to be ready
26187 //console.log("run task?" + this.doc.readyState);
26188 this.assignDocWin();
26189 if(this.doc.body || this.doc.readyState == 'complete'){
26191 this.doc.designMode="on";
26195 Roo.TaskMgr.stop(task);
26196 this.initEditor.defer(10, this);
26203 Roo.TaskMgr.start(task);
26208 onResize : function(w, h)
26210 Roo.log('resize: ' +w + ',' + h );
26211 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26215 if(typeof w == 'number'){
26217 this.iframe.style.width = w + 'px';
26219 if(typeof h == 'number'){
26221 this.iframe.style.height = h + 'px';
26223 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26230 * Toggles the editor between standard and source edit mode.
26231 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26233 toggleSourceEdit : function(sourceEditMode){
26235 this.sourceEditMode = sourceEditMode === true;
26237 if(this.sourceEditMode){
26239 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26242 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26243 //this.iframe.className = '';
26246 //this.setSize(this.owner.wrap.getSize());
26247 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26254 * Protected method that will not generally be called directly. If you need/want
26255 * custom HTML cleanup, this is the method you should override.
26256 * @param {String} html The HTML to be cleaned
26257 * return {String} The cleaned HTML
26259 cleanHtml : function(html){
26260 html = String(html);
26261 if(html.length > 5){
26262 if(Roo.isSafari){ // strip safari nonsense
26263 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26266 if(html == ' '){
26273 * HTML Editor -> Textarea
26274 * Protected method that will not generally be called directly. Syncs the contents
26275 * of the editor iframe with the textarea.
26277 syncValue : function(){
26278 if(this.initialized){
26279 var bd = (this.doc.body || this.doc.documentElement);
26280 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26281 var html = bd.innerHTML;
26283 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26284 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26286 html = '<div style="'+m[0]+'">' + html + '</div>';
26289 html = this.cleanHtml(html);
26290 // fix up the special chars.. normaly like back quotes in word...
26291 // however we do not want to do this with chinese..
26292 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26294 var cc = match.charCodeAt();
26296 // Get the character value, handling surrogate pairs
26297 if (match.length == 2) {
26298 // It's a surrogate pair, calculate the Unicode code point
26299 var high = match.charCodeAt(0) - 0xD800;
26300 var low = match.charCodeAt(1) - 0xDC00;
26301 cc = (high * 0x400) + low + 0x10000;
26303 (cc >= 0x4E00 && cc < 0xA000 ) ||
26304 (cc >= 0x3400 && cc < 0x4E00 ) ||
26305 (cc >= 0xf900 && cc < 0xfb00 )
26310 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26311 return "&#" + cc + ";";
26318 if(this.owner.fireEvent('beforesync', this, html) !== false){
26319 this.el.dom.value = html;
26320 this.owner.fireEvent('sync', this, html);
26326 * Protected method that will not generally be called directly. Pushes the value of the textarea
26327 * into the iframe editor.
26329 pushValue : function(){
26330 if(this.initialized){
26331 var v = this.el.dom.value.trim();
26333 // if(v.length < 1){
26337 if(this.owner.fireEvent('beforepush', this, v) !== false){
26338 var d = (this.doc.body || this.doc.documentElement);
26340 this.cleanUpPaste();
26341 this.el.dom.value = d.innerHTML;
26342 this.owner.fireEvent('push', this, v);
26348 deferFocus : function(){
26349 this.focus.defer(10, this);
26353 focus : function(){
26354 if(this.win && !this.sourceEditMode){
26361 assignDocWin: function()
26363 var iframe = this.iframe;
26366 this.doc = iframe.contentWindow.document;
26367 this.win = iframe.contentWindow;
26369 // if (!Roo.get(this.frameId)) {
26372 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26373 // this.win = Roo.get(this.frameId).dom.contentWindow;
26375 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26379 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26380 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26385 initEditor : function(){
26386 //console.log("INIT EDITOR");
26387 this.assignDocWin();
26391 this.doc.designMode="on";
26393 this.doc.write(this.getDocMarkup());
26396 var dbody = (this.doc.body || this.doc.documentElement);
26397 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26398 // this copies styles from the containing element into thsi one..
26399 // not sure why we need all of this..
26400 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26402 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26403 //ss['background-attachment'] = 'fixed'; // w3c
26404 dbody.bgProperties = 'fixed'; // ie
26405 //Roo.DomHelper.applyStyles(dbody, ss);
26406 Roo.EventManager.on(this.doc, {
26407 //'mousedown': this.onEditorEvent,
26408 'mouseup': this.onEditorEvent,
26409 'dblclick': this.onEditorEvent,
26410 'click': this.onEditorEvent,
26411 'keyup': this.onEditorEvent,
26416 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26418 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26419 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26421 this.initialized = true;
26423 this.owner.fireEvent('initialize', this);
26428 onDestroy : function(){
26434 //for (var i =0; i < this.toolbars.length;i++) {
26435 // // fixme - ask toolbars for heights?
26436 // this.toolbars[i].onDestroy();
26439 //this.wrap.dom.innerHTML = '';
26440 //this.wrap.remove();
26445 onFirstFocus : function(){
26447 this.assignDocWin();
26450 this.activated = true;
26453 if(Roo.isGecko){ // prevent silly gecko errors
26455 var s = this.win.getSelection();
26456 if(!s.focusNode || s.focusNode.nodeType != 3){
26457 var r = s.getRangeAt(0);
26458 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26463 this.execCmd('useCSS', true);
26464 this.execCmd('styleWithCSS', false);
26467 this.owner.fireEvent('activate', this);
26471 adjustFont: function(btn){
26472 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26473 //if(Roo.isSafari){ // safari
26476 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26477 if(Roo.isSafari){ // safari
26478 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26479 v = (v < 10) ? 10 : v;
26480 v = (v > 48) ? 48 : v;
26481 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26486 v = Math.max(1, v+adjust);
26488 this.execCmd('FontSize', v );
26491 onEditorEvent : function(e)
26493 this.owner.fireEvent('editorevent', this, e);
26494 // this.updateToolbar();
26495 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26498 insertTag : function(tg)
26500 // could be a bit smarter... -> wrap the current selected tRoo..
26501 if (tg.toLowerCase() == 'span' ||
26502 tg.toLowerCase() == 'code' ||
26503 tg.toLowerCase() == 'sup' ||
26504 tg.toLowerCase() == 'sub'
26507 range = this.createRange(this.getSelection());
26508 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26509 wrappingNode.appendChild(range.extractContents());
26510 range.insertNode(wrappingNode);
26517 this.execCmd("formatblock", tg);
26521 insertText : function(txt)
26525 var range = this.createRange();
26526 range.deleteContents();
26527 //alert(Sender.getAttribute('label'));
26529 range.insertNode(this.doc.createTextNode(txt));
26535 * Executes a Midas editor command on the editor document and performs necessary focus and
26536 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26537 * @param {String} cmd The Midas command
26538 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26540 relayCmd : function(cmd, value){
26542 this.execCmd(cmd, value);
26543 this.owner.fireEvent('editorevent', this);
26544 //this.updateToolbar();
26545 this.owner.deferFocus();
26549 * Executes a Midas editor command directly on the editor document.
26550 * For visual commands, you should use {@link #relayCmd} instead.
26551 * <b>This should only be called after the editor is initialized.</b>
26552 * @param {String} cmd The Midas command
26553 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26555 execCmd : function(cmd, value){
26556 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26563 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26565 * @param {String} text | dom node..
26567 insertAtCursor : function(text)
26570 if(!this.activated){
26576 var r = this.doc.selection.createRange();
26587 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26591 // from jquery ui (MIT licenced)
26593 var win = this.win;
26595 if (win.getSelection && win.getSelection().getRangeAt) {
26596 range = win.getSelection().getRangeAt(0);
26597 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26598 range.insertNode(node);
26599 } else if (win.document.selection && win.document.selection.createRange) {
26600 // no firefox support
26601 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26602 win.document.selection.createRange().pasteHTML(txt);
26604 // no firefox support
26605 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26606 this.execCmd('InsertHTML', txt);
26615 mozKeyPress : function(e){
26617 var c = e.getCharCode(), cmd;
26620 c = String.fromCharCode(c).toLowerCase();
26634 this.cleanUpPaste.defer(100, this);
26642 e.preventDefault();
26650 fixKeys : function(){ // load time branching for fastest keydown performance
26652 return function(e){
26653 var k = e.getKey(), r;
26656 r = this.doc.selection.createRange();
26659 r.pasteHTML('    ');
26666 r = this.doc.selection.createRange();
26668 var target = r.parentElement();
26669 if(!target || target.tagName.toLowerCase() != 'li'){
26671 r.pasteHTML('<br />');
26677 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26678 this.cleanUpPaste.defer(100, this);
26684 }else if(Roo.isOpera){
26685 return function(e){
26686 var k = e.getKey();
26690 this.execCmd('InsertHTML','    ');
26693 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26694 this.cleanUpPaste.defer(100, this);
26699 }else if(Roo.isSafari){
26700 return function(e){
26701 var k = e.getKey();
26705 this.execCmd('InsertText','\t');
26709 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26710 this.cleanUpPaste.defer(100, this);
26718 getAllAncestors: function()
26720 var p = this.getSelectedNode();
26723 a.push(p); // push blank onto stack..
26724 p = this.getParentElement();
26728 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26732 a.push(this.doc.body);
26736 lastSelNode : false,
26739 getSelection : function()
26741 this.assignDocWin();
26742 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26745 getSelectedNode: function()
26747 // this may only work on Gecko!!!
26749 // should we cache this!!!!
26754 var range = this.createRange(this.getSelection()).cloneRange();
26757 var parent = range.parentElement();
26759 var testRange = range.duplicate();
26760 testRange.moveToElementText(parent);
26761 if (testRange.inRange(range)) {
26764 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26767 parent = parent.parentElement;
26772 // is ancestor a text element.
26773 var ac = range.commonAncestorContainer;
26774 if (ac.nodeType == 3) {
26775 ac = ac.parentNode;
26778 var ar = ac.childNodes;
26781 var other_nodes = [];
26782 var has_other_nodes = false;
26783 for (var i=0;i<ar.length;i++) {
26784 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26787 // fullly contained node.
26789 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26794 // probably selected..
26795 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26796 other_nodes.push(ar[i]);
26800 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26805 has_other_nodes = true;
26807 if (!nodes.length && other_nodes.length) {
26808 nodes= other_nodes;
26810 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26816 createRange: function(sel)
26818 // this has strange effects when using with
26819 // top toolbar - not sure if it's a great idea.
26820 //this.editor.contentWindow.focus();
26821 if (typeof sel != "undefined") {
26823 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26825 return this.doc.createRange();
26828 return this.doc.createRange();
26831 getParentElement: function()
26834 this.assignDocWin();
26835 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26837 var range = this.createRange(sel);
26840 var p = range.commonAncestorContainer;
26841 while (p.nodeType == 3) { // text node
26852 * Range intersection.. the hard stuff...
26856 * [ -- selected range --- ]
26860 * if end is before start or hits it. fail.
26861 * if start is after end or hits it fail.
26863 * if either hits (but other is outside. - then it's not
26869 // @see http://www.thismuchiknow.co.uk/?p=64.
26870 rangeIntersectsNode : function(range, node)
26872 var nodeRange = node.ownerDocument.createRange();
26874 nodeRange.selectNode(node);
26876 nodeRange.selectNodeContents(node);
26879 var rangeStartRange = range.cloneRange();
26880 rangeStartRange.collapse(true);
26882 var rangeEndRange = range.cloneRange();
26883 rangeEndRange.collapse(false);
26885 var nodeStartRange = nodeRange.cloneRange();
26886 nodeStartRange.collapse(true);
26888 var nodeEndRange = nodeRange.cloneRange();
26889 nodeEndRange.collapse(false);
26891 return rangeStartRange.compareBoundaryPoints(
26892 Range.START_TO_START, nodeEndRange) == -1 &&
26893 rangeEndRange.compareBoundaryPoints(
26894 Range.START_TO_START, nodeStartRange) == 1;
26898 rangeCompareNode : function(range, node)
26900 var nodeRange = node.ownerDocument.createRange();
26902 nodeRange.selectNode(node);
26904 nodeRange.selectNodeContents(node);
26908 range.collapse(true);
26910 nodeRange.collapse(true);
26912 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26913 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26915 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26917 var nodeIsBefore = ss == 1;
26918 var nodeIsAfter = ee == -1;
26920 if (nodeIsBefore && nodeIsAfter) {
26923 if (!nodeIsBefore && nodeIsAfter) {
26924 return 1; //right trailed.
26927 if (nodeIsBefore && !nodeIsAfter) {
26928 return 2; // left trailed.
26934 // private? - in a new class?
26935 cleanUpPaste : function()
26937 // cleans up the whole document..
26938 Roo.log('cleanuppaste');
26940 this.cleanUpChildren(this.doc.body);
26941 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26942 if (clean != this.doc.body.innerHTML) {
26943 this.doc.body.innerHTML = clean;
26948 cleanWordChars : function(input) {// change the chars to hex code
26949 var he = Roo.HtmlEditorCore;
26951 var output = input;
26952 Roo.each(he.swapCodes, function(sw) {
26953 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26955 output = output.replace(swapper, sw[1]);
26962 cleanUpChildren : function (n)
26964 if (!n.childNodes.length) {
26967 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26968 this.cleanUpChild(n.childNodes[i]);
26975 cleanUpChild : function (node)
26978 //console.log(node);
26979 if (node.nodeName == "#text") {
26980 // clean up silly Windows -- stuff?
26983 if (node.nodeName == "#comment") {
26984 if (!this.allowComments) {
26985 node.parentNode.removeChild(node);
26987 // clean up silly Windows -- stuff?
26990 var lcname = node.tagName.toLowerCase();
26991 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26992 // whitelist of tags..
26994 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26996 node.parentNode.removeChild(node);
27001 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27003 // spans with no attributes - just remove them..
27004 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27005 remove_keep_children = true;
27008 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27009 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27011 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27012 // remove_keep_children = true;
27015 if (remove_keep_children) {
27016 this.cleanUpChildren(node);
27017 // inserts everything just before this node...
27018 while (node.childNodes.length) {
27019 var cn = node.childNodes[0];
27020 node.removeChild(cn);
27021 node.parentNode.insertBefore(cn, node);
27023 node.parentNode.removeChild(node);
27027 if (!node.attributes || !node.attributes.length) {
27032 this.cleanUpChildren(node);
27036 function cleanAttr(n,v)
27039 if (v.match(/^\./) || v.match(/^\//)) {
27042 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27045 if (v.match(/^#/)) {
27048 if (v.match(/^\{/)) { // allow template editing.
27051 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27052 node.removeAttribute(n);
27056 var cwhite = this.cwhite;
27057 var cblack = this.cblack;
27059 function cleanStyle(n,v)
27061 if (v.match(/expression/)) { //XSS?? should we even bother..
27062 node.removeAttribute(n);
27066 var parts = v.split(/;/);
27069 Roo.each(parts, function(p) {
27070 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27074 var l = p.split(':').shift().replace(/\s+/g,'');
27075 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27077 if ( cwhite.length && cblack.indexOf(l) > -1) {
27078 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27079 //node.removeAttribute(n);
27083 // only allow 'c whitelisted system attributes'
27084 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27085 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27086 //node.removeAttribute(n);
27096 if (clean.length) {
27097 node.setAttribute(n, clean.join(';'));
27099 node.removeAttribute(n);
27105 for (var i = node.attributes.length-1; i > -1 ; i--) {
27106 var a = node.attributes[i];
27109 if (a.name.toLowerCase().substr(0,2)=='on') {
27110 node.removeAttribute(a.name);
27113 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27114 node.removeAttribute(a.name);
27117 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27118 cleanAttr(a.name,a.value); // fixme..
27121 if (a.name == 'style') {
27122 cleanStyle(a.name,a.value);
27125 /// clean up MS crap..
27126 // tecnically this should be a list of valid class'es..
27129 if (a.name == 'class') {
27130 if (a.value.match(/^Mso/)) {
27131 node.removeAttribute('class');
27134 if (a.value.match(/^body$/)) {
27135 node.removeAttribute('class');
27146 this.cleanUpChildren(node);
27152 * Clean up MS wordisms...
27154 cleanWord : function(node)
27157 this.cleanWord(this.doc.body);
27162 node.nodeName == 'SPAN' &&
27163 !node.hasAttributes() &&
27164 node.childNodes.length == 1 &&
27165 node.firstChild.nodeName == "#text"
27167 var textNode = node.firstChild;
27168 node.removeChild(textNode);
27169 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27170 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27172 node.parentNode.insertBefore(textNode, node);
27173 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27174 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27176 node.parentNode.removeChild(node);
27179 if (node.nodeName == "#text") {
27180 // clean up silly Windows -- stuff?
27183 if (node.nodeName == "#comment") {
27184 node.parentNode.removeChild(node);
27185 // clean up silly Windows -- stuff?
27189 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27190 node.parentNode.removeChild(node);
27193 //Roo.log(node.tagName);
27194 // remove - but keep children..
27195 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27196 //Roo.log('-- removed');
27197 while (node.childNodes.length) {
27198 var cn = node.childNodes[0];
27199 node.removeChild(cn);
27200 node.parentNode.insertBefore(cn, node);
27201 // move node to parent - and clean it..
27202 this.cleanWord(cn);
27204 node.parentNode.removeChild(node);
27205 /// no need to iterate chidlren = it's got none..
27206 //this.iterateChildren(node, this.cleanWord);
27210 if (node.className.length) {
27212 var cn = node.className.split(/\W+/);
27214 Roo.each(cn, function(cls) {
27215 if (cls.match(/Mso[a-zA-Z]+/)) {
27220 node.className = cna.length ? cna.join(' ') : '';
27222 node.removeAttribute("class");
27226 if (node.hasAttribute("lang")) {
27227 node.removeAttribute("lang");
27230 if (node.hasAttribute("style")) {
27232 var styles = node.getAttribute("style").split(";");
27234 Roo.each(styles, function(s) {
27235 if (!s.match(/:/)) {
27238 var kv = s.split(":");
27239 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27242 // what ever is left... we allow.
27245 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27246 if (!nstyle.length) {
27247 node.removeAttribute('style');
27250 this.iterateChildren(node, this.cleanWord);
27256 * iterateChildren of a Node, calling fn each time, using this as the scole..
27257 * @param {DomNode} node node to iterate children of.
27258 * @param {Function} fn method of this class to call on each item.
27260 iterateChildren : function(node, fn)
27262 if (!node.childNodes.length) {
27265 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27266 fn.call(this, node.childNodes[i])
27272 * cleanTableWidths.
27274 * Quite often pasting from word etc.. results in tables with column and widths.
27275 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27278 cleanTableWidths : function(node)
27283 this.cleanTableWidths(this.doc.body);
27288 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27291 Roo.log(node.tagName);
27292 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27293 this.iterateChildren(node, this.cleanTableWidths);
27296 if (node.hasAttribute('width')) {
27297 node.removeAttribute('width');
27301 if (node.hasAttribute("style")) {
27304 var styles = node.getAttribute("style").split(";");
27306 Roo.each(styles, function(s) {
27307 if (!s.match(/:/)) {
27310 var kv = s.split(":");
27311 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27314 // what ever is left... we allow.
27317 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27318 if (!nstyle.length) {
27319 node.removeAttribute('style');
27323 this.iterateChildren(node, this.cleanTableWidths);
27331 domToHTML : function(currentElement, depth, nopadtext) {
27333 depth = depth || 0;
27334 nopadtext = nopadtext || false;
27336 if (!currentElement) {
27337 return this.domToHTML(this.doc.body);
27340 //Roo.log(currentElement);
27342 var allText = false;
27343 var nodeName = currentElement.nodeName;
27344 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27346 if (nodeName == '#text') {
27348 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27353 if (nodeName != 'BODY') {
27356 // Prints the node tagName, such as <A>, <IMG>, etc
27359 for(i = 0; i < currentElement.attributes.length;i++) {
27361 var aname = currentElement.attributes.item(i).name;
27362 if (!currentElement.attributes.item(i).value.length) {
27365 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27368 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27377 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27380 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27385 // Traverse the tree
27387 var currentElementChild = currentElement.childNodes.item(i);
27388 var allText = true;
27389 var innerHTML = '';
27391 while (currentElementChild) {
27392 // Formatting code (indent the tree so it looks nice on the screen)
27393 var nopad = nopadtext;
27394 if (lastnode == 'SPAN') {
27398 if (currentElementChild.nodeName == '#text') {
27399 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27400 toadd = nopadtext ? toadd : toadd.trim();
27401 if (!nopad && toadd.length > 80) {
27402 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27404 innerHTML += toadd;
27407 currentElementChild = currentElement.childNodes.item(i);
27413 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27415 // Recursively traverse the tree structure of the child node
27416 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27417 lastnode = currentElementChild.nodeName;
27419 currentElementChild=currentElement.childNodes.item(i);
27425 // The remaining code is mostly for formatting the tree
27426 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27431 ret+= "</"+tagName+">";
27437 applyBlacklists : function()
27439 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27440 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27444 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27445 if (b.indexOf(tag) > -1) {
27448 this.white.push(tag);
27452 Roo.each(w, function(tag) {
27453 if (b.indexOf(tag) > -1) {
27456 if (this.white.indexOf(tag) > -1) {
27459 this.white.push(tag);
27464 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27465 if (w.indexOf(tag) > -1) {
27468 this.black.push(tag);
27472 Roo.each(b, function(tag) {
27473 if (w.indexOf(tag) > -1) {
27476 if (this.black.indexOf(tag) > -1) {
27479 this.black.push(tag);
27484 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27485 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27489 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27490 if (b.indexOf(tag) > -1) {
27493 this.cwhite.push(tag);
27497 Roo.each(w, function(tag) {
27498 if (b.indexOf(tag) > -1) {
27501 if (this.cwhite.indexOf(tag) > -1) {
27504 this.cwhite.push(tag);
27509 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27510 if (w.indexOf(tag) > -1) {
27513 this.cblack.push(tag);
27517 Roo.each(b, function(tag) {
27518 if (w.indexOf(tag) > -1) {
27521 if (this.cblack.indexOf(tag) > -1) {
27524 this.cblack.push(tag);
27529 setStylesheets : function(stylesheets)
27531 if(typeof(stylesheets) == 'string'){
27532 Roo.get(this.iframe.contentDocument.head).createChild({
27534 rel : 'stylesheet',
27543 Roo.each(stylesheets, function(s) {
27548 Roo.get(_this.iframe.contentDocument.head).createChild({
27550 rel : 'stylesheet',
27559 removeStylesheets : function()
27563 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27568 setStyle : function(style)
27570 Roo.get(this.iframe.contentDocument.head).createChild({
27579 // hide stuff that is not compatible
27593 * @event specialkey
27597 * @cfg {String} fieldClass @hide
27600 * @cfg {String} focusClass @hide
27603 * @cfg {String} autoCreate @hide
27606 * @cfg {String} inputType @hide
27609 * @cfg {String} invalidClass @hide
27612 * @cfg {String} invalidText @hide
27615 * @cfg {String} msgFx @hide
27618 * @cfg {String} validateOnBlur @hide
27622 Roo.HtmlEditorCore.white = [
27623 'area', 'br', 'img', 'input', 'hr', 'wbr',
27625 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27626 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27627 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27628 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27629 'table', 'ul', 'xmp',
27631 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27634 'dir', 'menu', 'ol', 'ul', 'dl',
27640 Roo.HtmlEditorCore.black = [
27641 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27643 'base', 'basefont', 'bgsound', 'blink', 'body',
27644 'frame', 'frameset', 'head', 'html', 'ilayer',
27645 'iframe', 'layer', 'link', 'meta', 'object',
27646 'script', 'style' ,'title', 'xml' // clean later..
27648 Roo.HtmlEditorCore.clean = [
27649 'script', 'style', 'title', 'xml'
27651 Roo.HtmlEditorCore.remove = [
27656 Roo.HtmlEditorCore.ablack = [
27660 Roo.HtmlEditorCore.aclean = [
27661 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27665 Roo.HtmlEditorCore.pwhite= [
27666 'http', 'https', 'mailto'
27669 // white listed style attributes.
27670 Roo.HtmlEditorCore.cwhite= [
27671 // 'text-align', /// default is to allow most things..
27677 // black listed style attributes.
27678 Roo.HtmlEditorCore.cblack= [
27679 // 'font-size' -- this can be set by the project
27683 Roo.HtmlEditorCore.swapCodes =[
27684 [ 8211, "–" ],
27685 [ 8212, "—" ],
27702 * @class Roo.bootstrap.form.HtmlEditor
27703 * @extends Roo.bootstrap.form.TextArea
27704 * Bootstrap HtmlEditor class
27707 * Create a new HtmlEditor
27708 * @param {Object} config The config object
27711 Roo.bootstrap.form.HtmlEditor = function(config){
27712 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27713 if (!this.toolbars) {
27714 this.toolbars = [];
27717 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27720 * @event initialize
27721 * Fires when the editor is fully initialized (including the iframe)
27722 * @param {HtmlEditor} this
27727 * Fires when the editor is first receives the focus. Any insertion must wait
27728 * until after this event.
27729 * @param {HtmlEditor} this
27733 * @event beforesync
27734 * Fires before the textarea is updated with content from the editor iframe. Return false
27735 * to cancel the sync.
27736 * @param {HtmlEditor} this
27737 * @param {String} html
27741 * @event beforepush
27742 * Fires before the iframe editor is updated with content from the textarea. Return false
27743 * to cancel the push.
27744 * @param {HtmlEditor} this
27745 * @param {String} html
27750 * Fires when the textarea is updated with content from the editor iframe.
27751 * @param {HtmlEditor} this
27752 * @param {String} html
27757 * Fires when the iframe editor is updated with content from the textarea.
27758 * @param {HtmlEditor} this
27759 * @param {String} html
27763 * @event editmodechange
27764 * Fires when the editor switches edit modes
27765 * @param {HtmlEditor} this
27766 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27768 editmodechange: true,
27770 * @event editorevent
27771 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27772 * @param {HtmlEditor} this
27776 * @event firstfocus
27777 * Fires when on first focus - needed by toolbars..
27778 * @param {HtmlEditor} this
27783 * Auto save the htmlEditor value as a file into Events
27784 * @param {HtmlEditor} this
27788 * @event savedpreview
27789 * preview the saved version of htmlEditor
27790 * @param {HtmlEditor} this
27797 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27801 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27806 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27811 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27816 * @cfg {Number} height (in pixels)
27820 * @cfg {Number} width (in pixels)
27825 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27828 stylesheets: false,
27833 // private properties
27834 validationEvent : false,
27836 initialized : false,
27839 onFocus : Roo.emptyFn,
27841 hideMode:'offsets',
27843 tbContainer : false,
27847 toolbarContainer :function() {
27848 return this.wrap.select('.x-html-editor-tb',true).first();
27852 * Protected method that will not generally be called directly. It
27853 * is called when the editor creates its toolbar. Override this method if you need to
27854 * add custom toolbar buttons.
27855 * @param {HtmlEditor} editor
27857 createToolbar : function(){
27858 Roo.log('renewing');
27859 Roo.log("create toolbars");
27861 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27862 this.toolbars[0].render(this.toolbarContainer());
27866 // if (!editor.toolbars || !editor.toolbars.length) {
27867 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27870 // for (var i =0 ; i < editor.toolbars.length;i++) {
27871 // editor.toolbars[i] = Roo.factory(
27872 // typeof(editor.toolbars[i]) == 'string' ?
27873 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27874 // Roo.bootstrap.form.HtmlEditor);
27875 // editor.toolbars[i].init(editor);
27881 onRender : function(ct, position)
27883 // Roo.log("Call onRender: " + this.xtype);
27885 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27887 this.wrap = this.inputEl().wrap({
27888 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27891 this.editorcore.onRender(ct, position);
27893 if (this.resizable) {
27894 this.resizeEl = new Roo.Resizable(this.wrap, {
27898 minHeight : this.height,
27899 height: this.height,
27900 handles : this.resizable,
27903 resize : function(r, w, h) {
27904 _t.onResize(w,h); // -something
27910 this.createToolbar(this);
27913 if(!this.width && this.resizable){
27914 this.setSize(this.wrap.getSize());
27916 if (this.resizeEl) {
27917 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27918 // should trigger onReize..
27924 onResize : function(w, h)
27926 Roo.log('resize: ' +w + ',' + h );
27927 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27931 if(this.inputEl() ){
27932 if(typeof w == 'number'){
27933 var aw = w - this.wrap.getFrameWidth('lr');
27934 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27937 if(typeof h == 'number'){
27938 var tbh = -11; // fixme it needs to tool bar size!
27939 for (var i =0; i < this.toolbars.length;i++) {
27940 // fixme - ask toolbars for heights?
27941 tbh += this.toolbars[i].el.getHeight();
27942 //if (this.toolbars[i].footer) {
27943 // tbh += this.toolbars[i].footer.el.getHeight();
27951 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27952 ah -= 5; // knock a few pixes off for look..
27953 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27957 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27958 this.editorcore.onResize(ew,eh);
27963 * Toggles the editor between standard and source edit mode.
27964 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27966 toggleSourceEdit : function(sourceEditMode)
27968 this.editorcore.toggleSourceEdit(sourceEditMode);
27970 if(this.editorcore.sourceEditMode){
27971 Roo.log('editor - showing textarea');
27974 // Roo.log(this.syncValue());
27976 this.inputEl().removeClass(['hide', 'x-hidden']);
27977 this.inputEl().dom.removeAttribute('tabIndex');
27978 this.inputEl().focus();
27980 Roo.log('editor - hiding textarea');
27982 // Roo.log(this.pushValue());
27985 this.inputEl().addClass(['hide', 'x-hidden']);
27986 this.inputEl().dom.setAttribute('tabIndex', -1);
27987 //this.deferFocus();
27990 if(this.resizable){
27991 this.setSize(this.wrap.getSize());
27994 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27997 // private (for BoxComponent)
27998 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28000 // private (for BoxComponent)
28001 getResizeEl : function(){
28005 // private (for BoxComponent)
28006 getPositionEl : function(){
28011 initEvents : function(){
28012 this.originalValue = this.getValue();
28016 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28019 // markInvalid : Roo.emptyFn,
28021 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28024 // clearInvalid : Roo.emptyFn,
28026 setValue : function(v){
28027 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28028 this.editorcore.pushValue();
28033 deferFocus : function(){
28034 this.focus.defer(10, this);
28038 focus : function(){
28039 this.editorcore.focus();
28045 onDestroy : function(){
28051 for (var i =0; i < this.toolbars.length;i++) {
28052 // fixme - ask toolbars for heights?
28053 this.toolbars[i].onDestroy();
28056 this.wrap.dom.innerHTML = '';
28057 this.wrap.remove();
28062 onFirstFocus : function(){
28063 //Roo.log("onFirstFocus");
28064 this.editorcore.onFirstFocus();
28065 for (var i =0; i < this.toolbars.length;i++) {
28066 this.toolbars[i].onFirstFocus();
28072 syncValue : function()
28074 this.editorcore.syncValue();
28077 pushValue : function()
28079 this.editorcore.pushValue();
28083 // hide stuff that is not compatible
28097 * @event specialkey
28101 * @cfg {String} fieldClass @hide
28104 * @cfg {String} focusClass @hide
28107 * @cfg {String} autoCreate @hide
28110 * @cfg {String} inputType @hide
28114 * @cfg {String} invalidText @hide
28117 * @cfg {String} msgFx @hide
28120 * @cfg {String} validateOnBlur @hide
28129 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28131 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28132 * @parent Roo.bootstrap.form.HtmlEditor
28133 * @extends Roo.bootstrap.nav.Simplebar
28139 new Roo.bootstrap.form.HtmlEditor({
28142 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28143 disable : { fonts: 1 , format: 1, ..., ... , ...],
28149 * @cfg {Object} disable List of elements to disable..
28150 * @cfg {Array} btns List of additional buttons.
28154 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28157 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28160 Roo.apply(this, config);
28162 // default disabled, based on 'good practice'..
28163 this.disable = this.disable || {};
28164 Roo.applyIf(this.disable, {
28167 specialElements : true
28169 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28171 this.editor = config.editor;
28172 this.editorcore = config.editor.editorcore;
28174 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28176 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28177 // dont call parent... till later.
28179 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28184 editorcore : false,
28189 "h1","h2","h3","h4","h5","h6",
28191 "abbr", "acronym", "address", "cite", "samp", "var",
28195 onRender : function(ct, position)
28197 // Roo.log("Call onRender: " + this.xtype);
28199 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28201 this.el.dom.style.marginBottom = '0';
28203 var editorcore = this.editorcore;
28204 var editor= this.editor;
28207 var btn = function(id,cmd , toggle, handler, html){
28209 var event = toggle ? 'toggle' : 'click';
28214 xns: Roo.bootstrap,
28218 enableToggle:toggle !== false,
28220 pressed : toggle ? false : null,
28223 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28224 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28230 // var cb_box = function...
28235 xns: Roo.bootstrap,
28240 xns: Roo.bootstrap,
28244 Roo.each(this.formats, function(f) {
28245 style.menu.items.push({
28247 xns: Roo.bootstrap,
28248 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28253 editorcore.insertTag(this.tagname);
28260 children.push(style);
28262 btn('bold',false,true);
28263 btn('italic',false,true);
28264 btn('align-left', 'justifyleft',true);
28265 btn('align-center', 'justifycenter',true);
28266 btn('align-right' , 'justifyright',true);
28267 btn('link', false, false, function(btn) {
28268 //Roo.log("create link?");
28269 var url = prompt(this.createLinkText, this.defaultLinkValue);
28270 if(url && url != 'http:/'+'/'){
28271 this.editorcore.relayCmd('createlink', url);
28274 btn('list','insertunorderedlist',true);
28275 btn('pencil', false,true, function(btn){
28277 this.toggleSourceEdit(btn.pressed);
28280 if (this.editor.btns.length > 0) {
28281 for (var i = 0; i<this.editor.btns.length; i++) {
28282 children.push(this.editor.btns[i]);
28290 xns: Roo.bootstrap,
28295 xns: Roo.bootstrap,
28300 cog.menu.items.push({
28302 xns: Roo.bootstrap,
28303 html : Clean styles,
28308 editorcore.insertTag(this.tagname);
28317 this.xtype = 'NavSimplebar';
28319 for(var i=0;i< children.length;i++) {
28321 this.buttons.add(this.addxtypeChild(children[i]));
28325 editor.on('editorevent', this.updateToolbar, this);
28327 onBtnClick : function(id)
28329 this.editorcore.relayCmd(id);
28330 this.editorcore.focus();
28334 * Protected method that will not generally be called directly. It triggers
28335 * a toolbar update by reading the markup state of the current selection in the editor.
28337 updateToolbar: function(){
28339 if(!this.editorcore.activated){
28340 this.editor.onFirstFocus(); // is this neeed?
28344 var btns = this.buttons;
28345 var doc = this.editorcore.doc;
28346 btns.get('bold').setActive(doc.queryCommandState('bold'));
28347 btns.get('italic').setActive(doc.queryCommandState('italic'));
28348 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28350 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28351 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28352 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28354 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28355 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28358 var ans = this.editorcore.getAllAncestors();
28359 if (this.formatCombo) {
28362 var store = this.formatCombo.store;
28363 this.formatCombo.setValue("");
28364 for (var i =0; i < ans.length;i++) {
28365 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28367 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28375 // hides menus... - so this cant be on a menu...
28376 Roo.bootstrap.MenuMgr.hideAll();
28378 Roo.bootstrap.menu.Manager.hideAll();
28379 //this.editorsyncValue();
28381 onFirstFocus: function() {
28382 this.buttons.each(function(item){
28386 toggleSourceEdit : function(sourceEditMode){
28389 if(sourceEditMode){
28390 Roo.log("disabling buttons");
28391 this.buttons.each( function(item){
28392 if(item.cmd != 'pencil'){
28398 Roo.log("enabling buttons");
28399 if(this.editorcore.initialized){
28400 this.buttons.each( function(item){
28406 Roo.log("calling toggole on editor");
28407 // tell the editor that it's been pressed..
28408 this.editor.toggleSourceEdit(sourceEditMode);
28422 * @class Roo.bootstrap.form.Markdown
28423 * @extends Roo.bootstrap.form.TextArea
28424 * Bootstrap Showdown editable area
28425 * @cfg {string} content
28428 * Create a new Showdown
28431 Roo.bootstrap.form.Markdown = function(config){
28432 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28436 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28440 initEvents : function()
28443 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28444 this.markdownEl = this.el.createChild({
28445 cls : 'roo-markdown-area'
28447 this.inputEl().addClass('d-none');
28448 if (this.getValue() == '') {
28449 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28452 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28454 this.markdownEl.on('click', this.toggleTextEdit, this);
28455 this.on('blur', this.toggleTextEdit, this);
28456 this.on('specialkey', this.resizeTextArea, this);
28459 toggleTextEdit : function()
28461 var sh = this.markdownEl.getHeight();
28462 this.inputEl().addClass('d-none');
28463 this.markdownEl.addClass('d-none');
28464 if (!this.editing) {
28466 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28467 this.inputEl().removeClass('d-none');
28468 this.inputEl().focus();
28469 this.editing = true;
28472 // show showdown...
28473 this.updateMarkdown();
28474 this.markdownEl.removeClass('d-none');
28475 this.editing = false;
28478 updateMarkdown : function()
28480 if (this.getValue() == '') {
28481 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28485 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28488 resizeTextArea: function () {
28491 Roo.log([sh, this.getValue().split("\n").length * 30]);
28492 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28494 setValue : function(val)
28496 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28497 if (!this.editing) {
28498 this.updateMarkdown();
28504 if (!this.editing) {
28505 this.toggleTextEdit();
28513 * Ext JS Library 1.1.1
28514 * Copyright(c) 2006-2007, Ext JS, LLC.
28516 * Originally Released Under LGPL - original licence link has changed is not relivant.
28519 * <script type="text/javascript">
28523 * @class Roo.bootstrap.PagingToolbar
28524 * @extends Roo.bootstrap.nav.Simplebar
28525 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28527 * Create a new PagingToolbar
28528 * @param {Object} config The config object
28529 * @param {Roo.data.Store} store
28531 Roo.bootstrap.PagingToolbar = function(config)
28533 // old args format still supported... - xtype is prefered..
28534 // created from xtype...
28536 this.ds = config.dataSource;
28538 if (config.store && !this.ds) {
28539 this.store= Roo.factory(config.store, Roo.data);
28540 this.ds = this.store;
28541 this.ds.xmodule = this.xmodule || false;
28544 this.toolbarItems = [];
28545 if (config.items) {
28546 this.toolbarItems = config.items;
28549 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28554 this.bind(this.ds);
28557 if (Roo.bootstrap.version == 4) {
28558 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28560 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28565 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28567 * @cfg {Roo.bootstrap.Button} buttons[]
28568 * Buttons for the toolbar
28571 * @cfg {Roo.data.Store} store
28572 * The underlying data store providing the paged data
28575 * @cfg {String/HTMLElement/Element} container
28576 * container The id or element that will contain the toolbar
28579 * @cfg {Boolean} displayInfo
28580 * True to display the displayMsg (defaults to false)
28583 * @cfg {Number} pageSize
28584 * The number of records to display per page (defaults to 20)
28588 * @cfg {String} displayMsg
28589 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28591 displayMsg : 'Displaying {0} - {1} of {2}',
28593 * @cfg {String} emptyMsg
28594 * The message to display when no records are found (defaults to "No data to display")
28596 emptyMsg : 'No data to display',
28598 * Customizable piece of the default paging text (defaults to "Page")
28601 beforePageText : "Page",
28603 * Customizable piece of the default paging text (defaults to "of %0")
28606 afterPageText : "of {0}",
28608 * Customizable piece of the default paging text (defaults to "First Page")
28611 firstText : "First Page",
28613 * Customizable piece of the default paging text (defaults to "Previous Page")
28616 prevText : "Previous Page",
28618 * Customizable piece of the default paging text (defaults to "Next Page")
28621 nextText : "Next Page",
28623 * Customizable piece of the default paging text (defaults to "Last Page")
28626 lastText : "Last Page",
28628 * Customizable piece of the default paging text (defaults to "Refresh")
28631 refreshText : "Refresh",
28635 onRender : function(ct, position)
28637 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28638 this.navgroup.parentId = this.id;
28639 this.navgroup.onRender(this.el, null);
28640 // add the buttons to the navgroup
28642 if(this.displayInfo){
28643 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28644 this.displayEl = this.el.select('.x-paging-info', true).first();
28645 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28646 // this.displayEl = navel.el.select('span',true).first();
28652 Roo.each(_this.buttons, function(e){ // this might need to use render????
28653 Roo.factory(e).render(_this.el);
28657 Roo.each(_this.toolbarItems, function(e) {
28658 _this.navgroup.addItem(e);
28662 this.first = this.navgroup.addItem({
28663 tooltip: this.firstText,
28664 cls: "prev btn-outline-secondary",
28665 html : ' <i class="fa fa-step-backward"></i>',
28667 preventDefault: true,
28668 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28671 this.prev = this.navgroup.addItem({
28672 tooltip: this.prevText,
28673 cls: "prev btn-outline-secondary",
28674 html : ' <i class="fa fa-backward"></i>',
28676 preventDefault: true,
28677 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28679 //this.addSeparator();
28682 var field = this.navgroup.addItem( {
28684 cls : 'x-paging-position btn-outline-secondary',
28686 html : this.beforePageText +
28687 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28688 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28691 this.field = field.el.select('input', true).first();
28692 this.field.on("keydown", this.onPagingKeydown, this);
28693 this.field.on("focus", function(){this.dom.select();});
28696 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28697 //this.field.setHeight(18);
28698 //this.addSeparator();
28699 this.next = this.navgroup.addItem({
28700 tooltip: this.nextText,
28701 cls: "next btn-outline-secondary",
28702 html : ' <i class="fa fa-forward"></i>',
28704 preventDefault: true,
28705 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28707 this.last = this.navgroup.addItem({
28708 tooltip: this.lastText,
28709 html : ' <i class="fa fa-step-forward"></i>',
28710 cls: "next btn-outline-secondary",
28712 preventDefault: true,
28713 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28715 //this.addSeparator();
28716 this.loading = this.navgroup.addItem({
28717 tooltip: this.refreshText,
28718 cls: "btn-outline-secondary",
28719 html : ' <i class="fa fa-refresh"></i>',
28720 preventDefault: true,
28721 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28727 updateInfo : function(){
28728 if(this.displayEl){
28729 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28730 var msg = count == 0 ?
28734 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28736 this.displayEl.update(msg);
28741 onLoad : function(ds, r, o)
28743 this.cursor = o.params && o.params.start ? o.params.start : 0;
28745 var d = this.getPageData(),
28750 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28751 this.field.dom.value = ap;
28752 this.first.setDisabled(ap == 1);
28753 this.prev.setDisabled(ap == 1);
28754 this.next.setDisabled(ap == ps);
28755 this.last.setDisabled(ap == ps);
28756 this.loading.enable();
28761 getPageData : function(){
28762 var total = this.ds.getTotalCount();
28765 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28766 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28771 onLoadError : function(){
28772 this.loading.enable();
28776 onPagingKeydown : function(e){
28777 var k = e.getKey();
28778 var d = this.getPageData();
28780 var v = this.field.dom.value, pageNum;
28781 if(!v || isNaN(pageNum = parseInt(v, 10))){
28782 this.field.dom.value = d.activePage;
28785 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28786 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28789 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))
28791 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28792 this.field.dom.value = pageNum;
28793 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28796 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28798 var v = this.field.dom.value, pageNum;
28799 var increment = (e.shiftKey) ? 10 : 1;
28800 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28803 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28804 this.field.dom.value = d.activePage;
28807 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28809 this.field.dom.value = parseInt(v, 10) + increment;
28810 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28811 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28818 beforeLoad : function(){
28820 this.loading.disable();
28825 onClick : function(which){
28834 ds.load({params:{start: 0, limit: this.pageSize}});
28837 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28840 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28843 var total = ds.getTotalCount();
28844 var extra = total % this.pageSize;
28845 var lastStart = extra ? (total - extra) : total-this.pageSize;
28846 ds.load({params:{start: lastStart, limit: this.pageSize}});
28849 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28855 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28856 * @param {Roo.data.Store} store The data store to unbind
28858 unbind : function(ds){
28859 ds.un("beforeload", this.beforeLoad, this);
28860 ds.un("load", this.onLoad, this);
28861 ds.un("loadexception", this.onLoadError, this);
28862 ds.un("remove", this.updateInfo, this);
28863 ds.un("add", this.updateInfo, this);
28864 this.ds = undefined;
28868 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28869 * @param {Roo.data.Store} store The data store to bind
28871 bind : function(ds){
28872 ds.on("beforeload", this.beforeLoad, this);
28873 ds.on("load", this.onLoad, this);
28874 ds.on("loadexception", this.onLoadError, this);
28875 ds.on("remove", this.updateInfo, this);
28876 ds.on("add", this.updateInfo, this);
28887 * @class Roo.bootstrap.MessageBar
28888 * @extends Roo.bootstrap.Component
28889 * Bootstrap MessageBar class
28890 * @cfg {String} html contents of the MessageBar
28891 * @cfg {String} weight (info | success | warning | danger) default info
28892 * @cfg {String} beforeClass insert the bar before the given class
28893 * @cfg {Boolean} closable (true | false) default false
28894 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28897 * Create a new Element
28898 * @param {Object} config The config object
28901 Roo.bootstrap.MessageBar = function(config){
28902 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28905 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28911 beforeClass: 'bootstrap-sticky-wrap',
28913 getAutoCreate : function(){
28917 cls: 'alert alert-dismissable alert-' + this.weight,
28922 html: this.html || ''
28928 cfg.cls += ' alert-messages-fixed';
28942 onRender : function(ct, position)
28944 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28947 var cfg = Roo.apply({}, this.getAutoCreate());
28951 cfg.cls += ' ' + this.cls;
28954 cfg.style = this.style;
28956 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28958 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28961 this.el.select('>button.close').on('click', this.hide, this);
28967 if (!this.rendered) {
28973 this.fireEvent('show', this);
28979 if (!this.rendered) {
28985 this.fireEvent('hide', this);
28988 update : function()
28990 // var e = this.el.dom.firstChild;
28992 // if(this.closable){
28993 // e = e.nextSibling;
28996 // e.data = this.html || '';
28998 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29014 * @class Roo.bootstrap.Graph
29015 * @extends Roo.bootstrap.Component
29016 * Bootstrap Graph class
29020 @cfg {String} graphtype bar | vbar | pie
29021 @cfg {number} g_x coodinator | centre x (pie)
29022 @cfg {number} g_y coodinator | centre y (pie)
29023 @cfg {number} g_r radius (pie)
29024 @cfg {number} g_height height of the chart (respected by all elements in the set)
29025 @cfg {number} g_width width of the chart (respected by all elements in the set)
29026 @cfg {Object} title The title of the chart
29029 -opts (object) options for the chart
29031 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29032 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29034 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.
29035 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29037 o stretch (boolean)
29039 -opts (object) options for the pie
29042 o startAngle (number)
29043 o endAngle (number)
29047 * Create a new Input
29048 * @param {Object} config The config object
29051 Roo.bootstrap.Graph = function(config){
29052 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29058 * The img click event for the img.
29059 * @param {Roo.EventObject} e
29065 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29076 //g_colors: this.colors,
29083 getAutoCreate : function(){
29094 onRender : function(ct,position){
29097 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29099 if (typeof(Raphael) == 'undefined') {
29100 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29104 this.raphael = Raphael(this.el.dom);
29106 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29107 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29108 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29109 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29111 r.text(160, 10, "Single Series Chart").attr(txtattr);
29112 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29113 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29114 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29116 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29117 r.barchart(330, 10, 300, 220, data1);
29118 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29119 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29122 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29123 // r.barchart(30, 30, 560, 250, xdata, {
29124 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29125 // axis : "0 0 1 1",
29126 // axisxlabels : xdata
29127 // //yvalues : cols,
29130 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29132 // this.load(null,xdata,{
29133 // axis : "0 0 1 1",
29134 // axisxlabels : xdata
29139 load : function(graphtype,xdata,opts)
29141 this.raphael.clear();
29143 graphtype = this.graphtype;
29148 var r = this.raphael,
29149 fin = function () {
29150 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29152 fout = function () {
29153 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29155 pfin = function() {
29156 this.sector.stop();
29157 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29160 this.label[0].stop();
29161 this.label[0].attr({ r: 7.5 });
29162 this.label[1].attr({ "font-weight": 800 });
29165 pfout = function() {
29166 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29169 this.label[0].animate({ r: 5 }, 500, "bounce");
29170 this.label[1].attr({ "font-weight": 400 });
29176 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29179 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29182 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29183 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29185 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29192 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29197 setTitle: function(o)
29202 initEvents: function() {
29205 this.el.on('click', this.onClick, this);
29209 onClick : function(e)
29211 Roo.log('img onclick');
29212 this.fireEvent('click', this, e);
29224 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29227 * @class Roo.bootstrap.dash.NumberBox
29228 * @extends Roo.bootstrap.Component
29229 * Bootstrap NumberBox class
29230 * @cfg {String} headline Box headline
29231 * @cfg {String} content Box content
29232 * @cfg {String} icon Box icon
29233 * @cfg {String} footer Footer text
29234 * @cfg {String} fhref Footer href
29237 * Create a new NumberBox
29238 * @param {Object} config The config object
29242 Roo.bootstrap.dash.NumberBox = function(config){
29243 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29247 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29256 getAutoCreate : function(){
29260 cls : 'small-box ',
29268 cls : 'roo-headline',
29269 html : this.headline
29273 cls : 'roo-content',
29274 html : this.content
29288 cls : 'ion ' + this.icon
29297 cls : 'small-box-footer',
29298 href : this.fhref || '#',
29302 cfg.cn.push(footer);
29309 onRender : function(ct,position){
29310 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29317 setHeadline: function (value)
29319 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29322 setFooter: function (value, href)
29324 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29327 this.el.select('a.small-box-footer',true).first().attr('href', href);
29332 setContent: function (value)
29334 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29337 initEvents: function()
29351 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29354 * @class Roo.bootstrap.dash.TabBox
29355 * @extends Roo.bootstrap.Component
29356 * @children Roo.bootstrap.dash.TabPane
29357 * Bootstrap TabBox class
29358 * @cfg {String} title Title of the TabBox
29359 * @cfg {String} icon Icon of the TabBox
29360 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29361 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29364 * Create a new TabBox
29365 * @param {Object} config The config object
29369 Roo.bootstrap.dash.TabBox = function(config){
29370 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29375 * When a pane is added
29376 * @param {Roo.bootstrap.dash.TabPane} pane
29380 * @event activatepane
29381 * When a pane is activated
29382 * @param {Roo.bootstrap.dash.TabPane} pane
29384 "activatepane" : true
29392 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29397 tabScrollable : false,
29399 getChildContainer : function()
29401 return this.el.select('.tab-content', true).first();
29404 getAutoCreate : function(){
29408 cls: 'pull-left header',
29416 cls: 'fa ' + this.icon
29422 cls: 'nav nav-tabs pull-right',
29428 if(this.tabScrollable){
29435 cls: 'nav nav-tabs pull-right',
29446 cls: 'nav-tabs-custom',
29451 cls: 'tab-content no-padding',
29459 initEvents : function()
29461 //Roo.log('add add pane handler');
29462 this.on('addpane', this.onAddPane, this);
29465 * Updates the box title
29466 * @param {String} html to set the title to.
29468 setTitle : function(value)
29470 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29472 onAddPane : function(pane)
29474 this.panes.push(pane);
29475 //Roo.log('addpane');
29477 // tabs are rendere left to right..
29478 if(!this.showtabs){
29482 var ctr = this.el.select('.nav-tabs', true).first();
29485 var existing = ctr.select('.nav-tab',true);
29486 var qty = existing.getCount();;
29489 var tab = ctr.createChild({
29491 cls : 'nav-tab' + (qty ? '' : ' active'),
29499 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29502 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29504 pane.el.addClass('active');
29509 onTabClick : function(ev,un,ob,pane)
29511 //Roo.log('tab - prev default');
29512 ev.preventDefault();
29515 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29516 pane.tab.addClass('active');
29517 //Roo.log(pane.title);
29518 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29519 // technically we should have a deactivate event.. but maybe add later.
29520 // and it should not de-activate the selected tab...
29521 this.fireEvent('activatepane', pane);
29522 pane.el.addClass('active');
29523 pane.fireEvent('activate');
29528 getActivePane : function()
29531 Roo.each(this.panes, function(p) {
29532 if(p.el.hasClass('active')){
29553 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29555 * @class Roo.bootstrap.TabPane
29556 * @extends Roo.bootstrap.Component
29557 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29558 * Bootstrap TabPane class
29559 * @cfg {Boolean} active (false | true) Default false
29560 * @cfg {String} title title of panel
29564 * Create a new TabPane
29565 * @param {Object} config The config object
29568 Roo.bootstrap.dash.TabPane = function(config){
29569 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29575 * When a pane is activated
29576 * @param {Roo.bootstrap.dash.TabPane} pane
29583 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29588 // the tabBox that this is attached to.
29591 getAutoCreate : function()
29599 cfg.cls += ' active';
29604 initEvents : function()
29606 //Roo.log('trigger add pane handler');
29607 this.parent().fireEvent('addpane', this)
29611 * Updates the tab title
29612 * @param {String} html to set the title to.
29614 setTitle: function(str)
29620 this.tab.select('a', true).first().dom.innerHTML = str;
29639 * @class Roo.bootstrap.Tooltip
29640 * Bootstrap Tooltip class
29641 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29642 * to determine which dom element triggers the tooltip.
29644 * It needs to add support for additional attributes like tooltip-position
29647 * Create a new Toolti
29648 * @param {Object} config The config object
29651 Roo.bootstrap.Tooltip = function(config){
29652 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29654 this.alignment = Roo.bootstrap.Tooltip.alignment;
29656 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29657 this.alignment = config.alignment;
29662 Roo.apply(Roo.bootstrap.Tooltip, {
29664 * @function init initialize tooltip monitoring.
29668 currentTip : false,
29669 currentRegion : false,
29675 Roo.get(document).on('mouseover', this.enter ,this);
29676 Roo.get(document).on('mouseout', this.leave, this);
29679 this.currentTip = new Roo.bootstrap.Tooltip();
29682 enter : function(ev)
29684 var dom = ev.getTarget();
29686 //Roo.log(['enter',dom]);
29687 var el = Roo.fly(dom);
29688 if (this.currentEl) {
29690 //Roo.log(this.currentEl);
29691 //Roo.log(this.currentEl.contains(dom));
29692 if (this.currentEl == el) {
29695 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29701 if (this.currentTip.el) {
29702 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29706 if(!el || el.dom == document){
29712 if (!el.attr('tooltip')) {
29713 pel = el.findParent("[tooltip]");
29715 bindEl = Roo.get(pel);
29721 // you can not look for children, as if el is the body.. then everythign is the child..
29722 if (!pel && !el.attr('tooltip')) { //
29723 if (!el.select("[tooltip]").elements.length) {
29726 // is the mouse over this child...?
29727 bindEl = el.select("[tooltip]").first();
29728 var xy = ev.getXY();
29729 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29730 //Roo.log("not in region.");
29733 //Roo.log("child element over..");
29736 this.currentEl = el;
29737 this.currentTip.bind(bindEl);
29738 this.currentRegion = Roo.lib.Region.getRegion(dom);
29739 this.currentTip.enter();
29742 leave : function(ev)
29744 var dom = ev.getTarget();
29745 //Roo.log(['leave',dom]);
29746 if (!this.currentEl) {
29751 if (dom != this.currentEl.dom) {
29754 var xy = ev.getXY();
29755 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29758 // only activate leave if mouse cursor is outside... bounding box..
29763 if (this.currentTip) {
29764 this.currentTip.leave();
29766 //Roo.log('clear currentEl');
29767 this.currentEl = false;
29772 'left' : ['r-l', [-2,0], 'right'],
29773 'right' : ['l-r', [2,0], 'left'],
29774 'bottom' : ['t-b', [0,2], 'top'],
29775 'top' : [ 'b-t', [0,-2], 'bottom']
29781 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29786 delay : null, // can be { show : 300 , hide: 500}
29790 hoverState : null, //???
29792 placement : 'bottom',
29796 getAutoCreate : function(){
29803 cls : 'tooltip-arrow arrow'
29806 cls : 'tooltip-inner'
29813 bind : function(el)
29818 initEvents : function()
29820 this.arrowEl = this.el.select('.arrow', true).first();
29821 this.innerEl = this.el.select('.tooltip-inner', true).first();
29824 enter : function () {
29826 if (this.timeout != null) {
29827 clearTimeout(this.timeout);
29830 this.hoverState = 'in';
29831 //Roo.log("enter - show");
29832 if (!this.delay || !this.delay.show) {
29837 this.timeout = setTimeout(function () {
29838 if (_t.hoverState == 'in') {
29841 }, this.delay.show);
29845 clearTimeout(this.timeout);
29847 this.hoverState = 'out';
29848 if (!this.delay || !this.delay.hide) {
29854 this.timeout = setTimeout(function () {
29855 //Roo.log("leave - timeout");
29857 if (_t.hoverState == 'out') {
29859 Roo.bootstrap.Tooltip.currentEl = false;
29864 show : function (msg)
29867 this.render(document.body);
29870 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29872 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29874 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29876 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29877 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29879 var placement = typeof this.placement == 'function' ?
29880 this.placement.call(this, this.el, on_el) :
29883 var autoToken = /\s?auto?\s?/i;
29884 var autoPlace = autoToken.test(placement);
29886 placement = placement.replace(autoToken, '') || 'top';
29890 //this.el.setXY([0,0]);
29892 //this.el.dom.style.display='block';
29894 //this.el.appendTo(on_el);
29896 var p = this.getPosition();
29897 var box = this.el.getBox();
29903 var align = this.alignment[placement];
29905 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29907 if(placement == 'top' || placement == 'bottom'){
29909 placement = 'right';
29912 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29913 placement = 'left';
29916 var scroll = Roo.select('body', true).first().getScroll();
29918 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29922 align = this.alignment[placement];
29924 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29928 var elems = document.getElementsByTagName('div');
29929 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29930 for (var i = 0; i < elems.length; i++) {
29931 var zindex = Number.parseInt(
29932 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29935 if (zindex > highest) {
29942 this.el.dom.style.zIndex = highest;
29944 this.el.alignTo(this.bindEl, align[0],align[1]);
29945 //var arrow = this.el.select('.arrow',true).first();
29946 //arrow.set(align[2],
29948 this.el.addClass(placement);
29949 this.el.addClass("bs-tooltip-"+ placement);
29951 this.el.addClass('in fade show');
29953 this.hoverState = null;
29955 if (this.el.hasClass('fade')) {
29970 //this.el.setXY([0,0]);
29971 this.el.removeClass(['show', 'in']);
29987 * @class Roo.bootstrap.LocationPicker
29988 * @extends Roo.bootstrap.Component
29989 * Bootstrap LocationPicker class
29990 * @cfg {Number} latitude Position when init default 0
29991 * @cfg {Number} longitude Position when init default 0
29992 * @cfg {Number} zoom default 15
29993 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29994 * @cfg {Boolean} mapTypeControl default false
29995 * @cfg {Boolean} disableDoubleClickZoom default false
29996 * @cfg {Boolean} scrollwheel default true
29997 * @cfg {Boolean} streetViewControl default false
29998 * @cfg {Number} radius default 0
29999 * @cfg {String} locationName
30000 * @cfg {Boolean} draggable default true
30001 * @cfg {Boolean} enableAutocomplete default false
30002 * @cfg {Boolean} enableReverseGeocode default true
30003 * @cfg {String} markerTitle
30006 * Create a new LocationPicker
30007 * @param {Object} config The config object
30011 Roo.bootstrap.LocationPicker = function(config){
30013 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30018 * Fires when the picker initialized.
30019 * @param {Roo.bootstrap.LocationPicker} this
30020 * @param {Google Location} location
30024 * @event positionchanged
30025 * Fires when the picker position changed.
30026 * @param {Roo.bootstrap.LocationPicker} this
30027 * @param {Google Location} location
30029 positionchanged : true,
30032 * Fires when the map resize.
30033 * @param {Roo.bootstrap.LocationPicker} this
30038 * Fires when the map show.
30039 * @param {Roo.bootstrap.LocationPicker} this
30044 * Fires when the map hide.
30045 * @param {Roo.bootstrap.LocationPicker} this
30050 * Fires when click the map.
30051 * @param {Roo.bootstrap.LocationPicker} this
30052 * @param {Map event} e
30056 * @event mapRightClick
30057 * Fires when right click the map.
30058 * @param {Roo.bootstrap.LocationPicker} this
30059 * @param {Map event} e
30061 mapRightClick : true,
30063 * @event markerClick
30064 * Fires when click the marker.
30065 * @param {Roo.bootstrap.LocationPicker} this
30066 * @param {Map event} e
30068 markerClick : true,
30070 * @event markerRightClick
30071 * Fires when right click the marker.
30072 * @param {Roo.bootstrap.LocationPicker} this
30073 * @param {Map event} e
30075 markerRightClick : true,
30077 * @event OverlayViewDraw
30078 * Fires when OverlayView Draw
30079 * @param {Roo.bootstrap.LocationPicker} this
30081 OverlayViewDraw : true,
30083 * @event OverlayViewOnAdd
30084 * Fires when OverlayView Draw
30085 * @param {Roo.bootstrap.LocationPicker} this
30087 OverlayViewOnAdd : true,
30089 * @event OverlayViewOnRemove
30090 * Fires when OverlayView Draw
30091 * @param {Roo.bootstrap.LocationPicker} this
30093 OverlayViewOnRemove : true,
30095 * @event OverlayViewShow
30096 * Fires when OverlayView Draw
30097 * @param {Roo.bootstrap.LocationPicker} this
30098 * @param {Pixel} cpx
30100 OverlayViewShow : true,
30102 * @event OverlayViewHide
30103 * Fires when OverlayView Draw
30104 * @param {Roo.bootstrap.LocationPicker} this
30106 OverlayViewHide : true,
30108 * @event loadexception
30109 * Fires when load google lib failed.
30110 * @param {Roo.bootstrap.LocationPicker} this
30112 loadexception : true
30117 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30119 gMapContext: false,
30125 mapTypeControl: false,
30126 disableDoubleClickZoom: false,
30128 streetViewControl: false,
30132 enableAutocomplete: false,
30133 enableReverseGeocode: true,
30136 getAutoCreate: function()
30141 cls: 'roo-location-picker'
30147 initEvents: function(ct, position)
30149 if(!this.el.getWidth() || this.isApplied()){
30153 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30158 initial: function()
30160 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30161 this.fireEvent('loadexception', this);
30165 if(!this.mapTypeId){
30166 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30169 this.gMapContext = this.GMapContext();
30171 this.initOverlayView();
30173 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30177 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30178 _this.setPosition(_this.gMapContext.marker.position);
30181 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30182 _this.fireEvent('mapClick', this, event);
30186 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30187 _this.fireEvent('mapRightClick', this, event);
30191 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30192 _this.fireEvent('markerClick', this, event);
30196 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30197 _this.fireEvent('markerRightClick', this, event);
30201 this.setPosition(this.gMapContext.location);
30203 this.fireEvent('initial', this, this.gMapContext.location);
30206 initOverlayView: function()
30210 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30214 _this.fireEvent('OverlayViewDraw', _this);
30219 _this.fireEvent('OverlayViewOnAdd', _this);
30222 onRemove: function()
30224 _this.fireEvent('OverlayViewOnRemove', _this);
30227 show: function(cpx)
30229 _this.fireEvent('OverlayViewShow', _this, cpx);
30234 _this.fireEvent('OverlayViewHide', _this);
30240 fromLatLngToContainerPixel: function(event)
30242 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30245 isApplied: function()
30247 return this.getGmapContext() == false ? false : true;
30250 getGmapContext: function()
30252 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30255 GMapContext: function()
30257 var position = new google.maps.LatLng(this.latitude, this.longitude);
30259 var _map = new google.maps.Map(this.el.dom, {
30262 mapTypeId: this.mapTypeId,
30263 mapTypeControl: this.mapTypeControl,
30264 disableDoubleClickZoom: this.disableDoubleClickZoom,
30265 scrollwheel: this.scrollwheel,
30266 streetViewControl: this.streetViewControl,
30267 locationName: this.locationName,
30268 draggable: this.draggable,
30269 enableAutocomplete: this.enableAutocomplete,
30270 enableReverseGeocode: this.enableReverseGeocode
30273 var _marker = new google.maps.Marker({
30274 position: position,
30276 title: this.markerTitle,
30277 draggable: this.draggable
30284 location: position,
30285 radius: this.radius,
30286 locationName: this.locationName,
30287 addressComponents: {
30288 formatted_address: null,
30289 addressLine1: null,
30290 addressLine2: null,
30292 streetNumber: null,
30296 stateOrProvince: null
30299 domContainer: this.el.dom,
30300 geodecoder: new google.maps.Geocoder()
30304 drawCircle: function(center, radius, options)
30306 if (this.gMapContext.circle != null) {
30307 this.gMapContext.circle.setMap(null);
30311 options = Roo.apply({}, options, {
30312 strokeColor: "#0000FF",
30313 strokeOpacity: .35,
30315 fillColor: "#0000FF",
30319 options.map = this.gMapContext.map;
30320 options.radius = radius;
30321 options.center = center;
30322 this.gMapContext.circle = new google.maps.Circle(options);
30323 return this.gMapContext.circle;
30329 setPosition: function(location)
30331 this.gMapContext.location = location;
30332 this.gMapContext.marker.setPosition(location);
30333 this.gMapContext.map.panTo(location);
30334 this.drawCircle(location, this.gMapContext.radius, {});
30338 if (this.gMapContext.settings.enableReverseGeocode) {
30339 this.gMapContext.geodecoder.geocode({
30340 latLng: this.gMapContext.location
30341 }, function(results, status) {
30343 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30344 _this.gMapContext.locationName = results[0].formatted_address;
30345 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30347 _this.fireEvent('positionchanged', this, location);
30354 this.fireEvent('positionchanged', this, location);
30359 google.maps.event.trigger(this.gMapContext.map, "resize");
30361 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30363 this.fireEvent('resize', this);
30366 setPositionByLatLng: function(latitude, longitude)
30368 this.setPosition(new google.maps.LatLng(latitude, longitude));
30371 getCurrentPosition: function()
30374 latitude: this.gMapContext.location.lat(),
30375 longitude: this.gMapContext.location.lng()
30379 getAddressName: function()
30381 return this.gMapContext.locationName;
30384 getAddressComponents: function()
30386 return this.gMapContext.addressComponents;
30389 address_component_from_google_geocode: function(address_components)
30393 for (var i = 0; i < address_components.length; i++) {
30394 var component = address_components[i];
30395 if (component.types.indexOf("postal_code") >= 0) {
30396 result.postalCode = component.short_name;
30397 } else if (component.types.indexOf("street_number") >= 0) {
30398 result.streetNumber = component.short_name;
30399 } else if (component.types.indexOf("route") >= 0) {
30400 result.streetName = component.short_name;
30401 } else if (component.types.indexOf("neighborhood") >= 0) {
30402 result.city = component.short_name;
30403 } else if (component.types.indexOf("locality") >= 0) {
30404 result.city = component.short_name;
30405 } else if (component.types.indexOf("sublocality") >= 0) {
30406 result.district = component.short_name;
30407 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30408 result.stateOrProvince = component.short_name;
30409 } else if (component.types.indexOf("country") >= 0) {
30410 result.country = component.short_name;
30414 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30415 result.addressLine2 = "";
30419 setZoomLevel: function(zoom)
30421 this.gMapContext.map.setZoom(zoom);
30434 this.fireEvent('show', this);
30445 this.fireEvent('hide', this);
30450 Roo.apply(Roo.bootstrap.LocationPicker, {
30452 OverlayView : function(map, options)
30454 options = options || {};
30461 * @class Roo.bootstrap.Alert
30462 * @extends Roo.bootstrap.Component
30463 * Bootstrap Alert class - shows an alert area box
30465 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30466 Enter a valid email address
30469 * @cfg {String} title The title of alert
30470 * @cfg {String} html The content of alert
30471 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30472 * @cfg {String} fa font-awesomeicon
30473 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30474 * @cfg {Boolean} close true to show a x closer
30478 * Create a new alert
30479 * @param {Object} config The config object
30483 Roo.bootstrap.Alert = function(config){
30484 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30488 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30494 faicon: false, // BC
30498 getAutoCreate : function()
30510 style : this.close ? '' : 'display:none'
30514 cls : 'roo-alert-icon'
30519 cls : 'roo-alert-title',
30524 cls : 'roo-alert-text',
30531 cfg.cn[0].cls += ' fa ' + this.faicon;
30534 cfg.cn[0].cls += ' fa ' + this.fa;
30538 cfg.cls += ' alert-' + this.weight;
30544 initEvents: function()
30546 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30547 this.titleEl = this.el.select('.roo-alert-title',true).first();
30548 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30549 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30550 if (this.seconds > 0) {
30551 this.hide.defer(this.seconds, this);
30555 * Set the Title Message HTML
30556 * @param {String} html
30558 setTitle : function(str)
30560 this.titleEl.dom.innerHTML = str;
30564 * Set the Body Message HTML
30565 * @param {String} html
30567 setHtml : function(str)
30569 this.htmlEl.dom.innerHTML = str;
30572 * Set the Weight of the alert
30573 * @param {String} (success|info|warning|danger) weight
30576 setWeight : function(weight)
30579 this.el.removeClass('alert-' + this.weight);
30582 this.weight = weight;
30584 this.el.addClass('alert-' + this.weight);
30587 * Set the Icon of the alert
30588 * @param {String} see fontawsome names (name without the 'fa-' bit)
30590 setIcon : function(icon)
30593 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30596 this.faicon = icon;
30598 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30623 * @class Roo.bootstrap.UploadCropbox
30624 * @extends Roo.bootstrap.Component
30625 * Bootstrap UploadCropbox class
30626 * @cfg {String} emptyText show when image has been loaded
30627 * @cfg {String} rotateNotify show when image too small to rotate
30628 * @cfg {Number} errorTimeout default 3000
30629 * @cfg {Number} minWidth default 300
30630 * @cfg {Number} minHeight default 300
30631 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30632 * @cfg {Boolean} isDocument (true|false) default false
30633 * @cfg {String} url action url
30634 * @cfg {String} paramName default 'imageUpload'
30635 * @cfg {String} method default POST
30636 * @cfg {Boolean} loadMask (true|false) default true
30637 * @cfg {Boolean} loadingText default 'Loading...'
30640 * Create a new UploadCropbox
30641 * @param {Object} config The config object
30644 Roo.bootstrap.UploadCropbox = function(config){
30645 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30649 * @event beforeselectfile
30650 * Fire before select file
30651 * @param {Roo.bootstrap.UploadCropbox} this
30653 "beforeselectfile" : true,
30656 * Fire after initEvent
30657 * @param {Roo.bootstrap.UploadCropbox} this
30662 * Fire after initEvent
30663 * @param {Roo.bootstrap.UploadCropbox} this
30664 * @param {String} data
30669 * Fire when preparing the file data
30670 * @param {Roo.bootstrap.UploadCropbox} this
30671 * @param {Object} file
30676 * Fire when get exception
30677 * @param {Roo.bootstrap.UploadCropbox} this
30678 * @param {XMLHttpRequest} xhr
30680 "exception" : true,
30682 * @event beforeloadcanvas
30683 * Fire before load the canvas
30684 * @param {Roo.bootstrap.UploadCropbox} this
30685 * @param {String} src
30687 "beforeloadcanvas" : true,
30690 * Fire when trash image
30691 * @param {Roo.bootstrap.UploadCropbox} this
30696 * Fire when download the image
30697 * @param {Roo.bootstrap.UploadCropbox} this
30701 * @event footerbuttonclick
30702 * Fire when footerbuttonclick
30703 * @param {Roo.bootstrap.UploadCropbox} this
30704 * @param {String} type
30706 "footerbuttonclick" : true,
30710 * @param {Roo.bootstrap.UploadCropbox} this
30715 * Fire when rotate the image
30716 * @param {Roo.bootstrap.UploadCropbox} this
30717 * @param {String} pos
30722 * Fire when inspect the file
30723 * @param {Roo.bootstrap.UploadCropbox} this
30724 * @param {Object} file
30729 * Fire when xhr upload the file
30730 * @param {Roo.bootstrap.UploadCropbox} this
30731 * @param {Object} data
30736 * Fire when arrange the file data
30737 * @param {Roo.bootstrap.UploadCropbox} this
30738 * @param {Object} formData
30743 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30746 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30748 emptyText : 'Click to upload image',
30749 rotateNotify : 'Image is too small to rotate',
30750 errorTimeout : 3000,
30764 cropType : 'image/jpeg',
30766 canvasLoaded : false,
30767 isDocument : false,
30769 paramName : 'imageUpload',
30771 loadingText : 'Loading...',
30774 getAutoCreate : function()
30778 cls : 'roo-upload-cropbox',
30782 cls : 'roo-upload-cropbox-selector',
30787 cls : 'roo-upload-cropbox-body',
30788 style : 'cursor:pointer',
30792 cls : 'roo-upload-cropbox-preview'
30796 cls : 'roo-upload-cropbox-thumb'
30800 cls : 'roo-upload-cropbox-empty-notify',
30801 html : this.emptyText
30805 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30806 html : this.rotateNotify
30812 cls : 'roo-upload-cropbox-footer',
30815 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30825 onRender : function(ct, position)
30827 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30829 if (this.buttons.length) {
30831 Roo.each(this.buttons, function(bb) {
30833 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30835 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30841 this.maskEl = this.el;
30845 initEvents : function()
30847 this.urlAPI = (window.createObjectURL && window) ||
30848 (window.URL && URL.revokeObjectURL && URL) ||
30849 (window.webkitURL && webkitURL);
30851 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30852 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30854 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30855 this.selectorEl.hide();
30857 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30858 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30860 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30861 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30862 this.thumbEl.hide();
30864 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30865 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30867 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30868 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30869 this.errorEl.hide();
30871 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30872 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30873 this.footerEl.hide();
30875 this.setThumbBoxSize();
30881 this.fireEvent('initial', this);
30888 window.addEventListener("resize", function() { _this.resize(); } );
30890 this.bodyEl.on('click', this.beforeSelectFile, this);
30893 this.bodyEl.on('touchstart', this.onTouchStart, this);
30894 this.bodyEl.on('touchmove', this.onTouchMove, this);
30895 this.bodyEl.on('touchend', this.onTouchEnd, this);
30899 this.bodyEl.on('mousedown', this.onMouseDown, this);
30900 this.bodyEl.on('mousemove', this.onMouseMove, this);
30901 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30902 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30903 Roo.get(document).on('mouseup', this.onMouseUp, this);
30906 this.selectorEl.on('change', this.onFileSelected, this);
30912 this.baseScale = 1;
30914 this.baseRotate = 1;
30915 this.dragable = false;
30916 this.pinching = false;
30919 this.cropData = false;
30920 this.notifyEl.dom.innerHTML = this.emptyText;
30922 this.selectorEl.dom.value = '';
30926 resize : function()
30928 if(this.fireEvent('resize', this) != false){
30929 this.setThumbBoxPosition();
30930 this.setCanvasPosition();
30934 onFooterButtonClick : function(e, el, o, type)
30937 case 'rotate-left' :
30938 this.onRotateLeft(e);
30940 case 'rotate-right' :
30941 this.onRotateRight(e);
30944 this.beforeSelectFile(e);
30959 this.fireEvent('footerbuttonclick', this, type);
30962 beforeSelectFile : function(e)
30964 e.preventDefault();
30966 if(this.fireEvent('beforeselectfile', this) != false){
30967 this.selectorEl.dom.click();
30971 onFileSelected : function(e)
30973 e.preventDefault();
30975 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30979 var file = this.selectorEl.dom.files[0];
30981 if(this.fireEvent('inspect', this, file) != false){
30982 this.prepare(file);
30987 trash : function(e)
30989 this.fireEvent('trash', this);
30992 download : function(e)
30994 this.fireEvent('download', this);
30997 loadCanvas : function(src)
30999 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31003 this.imageEl = document.createElement('img');
31007 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31009 this.imageEl.src = src;
31013 onLoadCanvas : function()
31015 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31016 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31018 this.bodyEl.un('click', this.beforeSelectFile, this);
31020 this.notifyEl.hide();
31021 this.thumbEl.show();
31022 this.footerEl.show();
31024 this.baseRotateLevel();
31026 if(this.isDocument){
31027 this.setThumbBoxSize();
31030 this.setThumbBoxPosition();
31032 this.baseScaleLevel();
31038 this.canvasLoaded = true;
31041 this.maskEl.unmask();
31046 setCanvasPosition : function()
31048 if(!this.canvasEl){
31052 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31053 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31055 this.previewEl.setLeft(pw);
31056 this.previewEl.setTop(ph);
31060 onMouseDown : function(e)
31064 this.dragable = true;
31065 this.pinching = false;
31067 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31068 this.dragable = false;
31072 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31073 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31077 onMouseMove : function(e)
31081 if(!this.canvasLoaded){
31085 if (!this.dragable){
31089 var minX = Math.ceil(this.thumbEl.getLeft(true));
31090 var minY = Math.ceil(this.thumbEl.getTop(true));
31092 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31093 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31095 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31096 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31098 x = x - this.mouseX;
31099 y = y - this.mouseY;
31101 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31102 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31104 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31105 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31107 this.previewEl.setLeft(bgX);
31108 this.previewEl.setTop(bgY);
31110 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31111 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31114 onMouseUp : function(e)
31118 this.dragable = false;
31121 onMouseWheel : function(e)
31125 this.startScale = this.scale;
31127 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31129 if(!this.zoomable()){
31130 this.scale = this.startScale;
31139 zoomable : function()
31141 var minScale = this.thumbEl.getWidth() / this.minWidth;
31143 if(this.minWidth < this.minHeight){
31144 minScale = this.thumbEl.getHeight() / this.minHeight;
31147 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31148 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31152 (this.rotate == 0 || this.rotate == 180) &&
31154 width > this.imageEl.OriginWidth ||
31155 height > this.imageEl.OriginHeight ||
31156 (width < this.minWidth && height < this.minHeight)
31164 (this.rotate == 90 || this.rotate == 270) &&
31166 width > this.imageEl.OriginWidth ||
31167 height > this.imageEl.OriginHeight ||
31168 (width < this.minHeight && height < this.minWidth)
31175 !this.isDocument &&
31176 (this.rotate == 0 || this.rotate == 180) &&
31178 width < this.minWidth ||
31179 width > this.imageEl.OriginWidth ||
31180 height < this.minHeight ||
31181 height > this.imageEl.OriginHeight
31188 !this.isDocument &&
31189 (this.rotate == 90 || this.rotate == 270) &&
31191 width < this.minHeight ||
31192 width > this.imageEl.OriginWidth ||
31193 height < this.minWidth ||
31194 height > this.imageEl.OriginHeight
31204 onRotateLeft : function(e)
31206 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31208 var minScale = this.thumbEl.getWidth() / this.minWidth;
31210 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31211 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31213 this.startScale = this.scale;
31215 while (this.getScaleLevel() < minScale){
31217 this.scale = this.scale + 1;
31219 if(!this.zoomable()){
31224 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31225 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31230 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31237 this.scale = this.startScale;
31239 this.onRotateFail();
31244 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31246 if(this.isDocument){
31247 this.setThumbBoxSize();
31248 this.setThumbBoxPosition();
31249 this.setCanvasPosition();
31254 this.fireEvent('rotate', this, 'left');
31258 onRotateRight : function(e)
31260 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31262 var minScale = this.thumbEl.getWidth() / this.minWidth;
31264 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31265 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31267 this.startScale = this.scale;
31269 while (this.getScaleLevel() < minScale){
31271 this.scale = this.scale + 1;
31273 if(!this.zoomable()){
31278 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31279 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31284 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31291 this.scale = this.startScale;
31293 this.onRotateFail();
31298 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31300 if(this.isDocument){
31301 this.setThumbBoxSize();
31302 this.setThumbBoxPosition();
31303 this.setCanvasPosition();
31308 this.fireEvent('rotate', this, 'right');
31311 onRotateFail : function()
31313 this.errorEl.show(true);
31317 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31322 this.previewEl.dom.innerHTML = '';
31324 var canvasEl = document.createElement("canvas");
31326 var contextEl = canvasEl.getContext("2d");
31328 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31329 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31330 var center = this.imageEl.OriginWidth / 2;
31332 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31333 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31334 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31335 center = this.imageEl.OriginHeight / 2;
31338 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31340 contextEl.translate(center, center);
31341 contextEl.rotate(this.rotate * Math.PI / 180);
31343 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31345 this.canvasEl = document.createElement("canvas");
31347 this.contextEl = this.canvasEl.getContext("2d");
31349 switch (this.rotate) {
31352 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31353 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31355 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31360 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31361 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31363 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31364 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);
31368 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31373 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31374 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31376 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31377 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);
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);
31386 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31387 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31389 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31390 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
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);
31401 this.previewEl.appendChild(this.canvasEl);
31403 this.setCanvasPosition();
31408 if(!this.canvasLoaded){
31412 var imageCanvas = document.createElement("canvas");
31414 var imageContext = imageCanvas.getContext("2d");
31416 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31417 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31419 var center = imageCanvas.width / 2;
31421 imageContext.translate(center, center);
31423 imageContext.rotate(this.rotate * Math.PI / 180);
31425 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31427 var canvas = document.createElement("canvas");
31429 var context = canvas.getContext("2d");
31431 canvas.width = this.minWidth;
31432 canvas.height = this.minHeight;
31434 switch (this.rotate) {
31437 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31438 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31440 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31441 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31443 var targetWidth = this.minWidth - 2 * x;
31444 var targetHeight = this.minHeight - 2 * y;
31448 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31449 scale = targetWidth / width;
31452 if(x > 0 && y == 0){
31453 scale = targetHeight / height;
31456 if(x > 0 && y > 0){
31457 scale = targetWidth / width;
31459 if(width < height){
31460 scale = targetHeight / height;
31464 context.scale(scale, scale);
31466 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31467 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31469 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31470 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31472 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31477 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31478 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31480 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31481 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31483 var targetWidth = this.minWidth - 2 * x;
31484 var targetHeight = this.minHeight - 2 * y;
31488 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31489 scale = targetWidth / width;
31492 if(x > 0 && y == 0){
31493 scale = targetHeight / height;
31496 if(x > 0 && y > 0){
31497 scale = targetWidth / width;
31499 if(width < height){
31500 scale = targetHeight / height;
31504 context.scale(scale, scale);
31506 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31507 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31509 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31510 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31512 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31514 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31519 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31520 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31522 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31523 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31525 var targetWidth = this.minWidth - 2 * x;
31526 var targetHeight = this.minHeight - 2 * y;
31530 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31531 scale = targetWidth / width;
31534 if(x > 0 && y == 0){
31535 scale = targetHeight / height;
31538 if(x > 0 && y > 0){
31539 scale = targetWidth / width;
31541 if(width < height){
31542 scale = targetHeight / height;
31546 context.scale(scale, scale);
31548 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31549 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31551 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31552 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31554 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31555 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31557 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31562 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31563 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31565 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31566 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31568 var targetWidth = this.minWidth - 2 * x;
31569 var targetHeight = this.minHeight - 2 * y;
31573 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31574 scale = targetWidth / width;
31577 if(x > 0 && y == 0){
31578 scale = targetHeight / height;
31581 if(x > 0 && y > 0){
31582 scale = targetWidth / width;
31584 if(width < height){
31585 scale = targetHeight / height;
31589 context.scale(scale, scale);
31591 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31592 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31594 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31595 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31597 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31599 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31606 this.cropData = canvas.toDataURL(this.cropType);
31608 if(this.fireEvent('crop', this, this.cropData) !== false){
31609 this.process(this.file, this.cropData);
31616 setThumbBoxSize : function()
31620 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31621 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31622 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31624 this.minWidth = width;
31625 this.minHeight = height;
31627 if(this.rotate == 90 || this.rotate == 270){
31628 this.minWidth = height;
31629 this.minHeight = width;
31634 width = Math.ceil(this.minWidth * height / this.minHeight);
31636 if(this.minWidth > this.minHeight){
31638 height = Math.ceil(this.minHeight * width / this.minWidth);
31641 this.thumbEl.setStyle({
31642 width : width + 'px',
31643 height : height + 'px'
31650 setThumbBoxPosition : function()
31652 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31653 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31655 this.thumbEl.setLeft(x);
31656 this.thumbEl.setTop(y);
31660 baseRotateLevel : function()
31662 this.baseRotate = 1;
31665 typeof(this.exif) != 'undefined' &&
31666 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31667 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31669 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31672 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31676 baseScaleLevel : function()
31680 if(this.isDocument){
31682 if(this.baseRotate == 6 || this.baseRotate == 8){
31684 height = this.thumbEl.getHeight();
31685 this.baseScale = height / this.imageEl.OriginWidth;
31687 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31688 width = this.thumbEl.getWidth();
31689 this.baseScale = width / this.imageEl.OriginHeight;
31695 height = this.thumbEl.getHeight();
31696 this.baseScale = height / this.imageEl.OriginHeight;
31698 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31699 width = this.thumbEl.getWidth();
31700 this.baseScale = width / this.imageEl.OriginWidth;
31706 if(this.baseRotate == 6 || this.baseRotate == 8){
31708 width = this.thumbEl.getHeight();
31709 this.baseScale = width / this.imageEl.OriginHeight;
31711 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31712 height = this.thumbEl.getWidth();
31713 this.baseScale = height / this.imageEl.OriginHeight;
31716 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31717 height = this.thumbEl.getWidth();
31718 this.baseScale = height / this.imageEl.OriginHeight;
31720 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31721 width = this.thumbEl.getHeight();
31722 this.baseScale = width / this.imageEl.OriginWidth;
31729 width = this.thumbEl.getWidth();
31730 this.baseScale = width / this.imageEl.OriginWidth;
31732 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31733 height = this.thumbEl.getHeight();
31734 this.baseScale = height / this.imageEl.OriginHeight;
31737 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31739 height = this.thumbEl.getHeight();
31740 this.baseScale = height / this.imageEl.OriginHeight;
31742 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31743 width = this.thumbEl.getWidth();
31744 this.baseScale = width / this.imageEl.OriginWidth;
31752 getScaleLevel : function()
31754 return this.baseScale * Math.pow(1.1, this.scale);
31757 onTouchStart : function(e)
31759 if(!this.canvasLoaded){
31760 this.beforeSelectFile(e);
31764 var touches = e.browserEvent.touches;
31770 if(touches.length == 1){
31771 this.onMouseDown(e);
31775 if(touches.length != 2){
31781 for(var i = 0, finger; finger = touches[i]; i++){
31782 coords.push(finger.pageX, finger.pageY);
31785 var x = Math.pow(coords[0] - coords[2], 2);
31786 var y = Math.pow(coords[1] - coords[3], 2);
31788 this.startDistance = Math.sqrt(x + y);
31790 this.startScale = this.scale;
31792 this.pinching = true;
31793 this.dragable = false;
31797 onTouchMove : function(e)
31799 if(!this.pinching && !this.dragable){
31803 var touches = e.browserEvent.touches;
31810 this.onMouseMove(e);
31816 for(var i = 0, finger; finger = touches[i]; i++){
31817 coords.push(finger.pageX, finger.pageY);
31820 var x = Math.pow(coords[0] - coords[2], 2);
31821 var y = Math.pow(coords[1] - coords[3], 2);
31823 this.endDistance = Math.sqrt(x + y);
31825 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31827 if(!this.zoomable()){
31828 this.scale = this.startScale;
31836 onTouchEnd : function(e)
31838 this.pinching = false;
31839 this.dragable = false;
31843 process : function(file, crop)
31846 this.maskEl.mask(this.loadingText);
31849 this.xhr = new XMLHttpRequest();
31851 file.xhr = this.xhr;
31853 this.xhr.open(this.method, this.url, true);
31856 "Accept": "application/json",
31857 "Cache-Control": "no-cache",
31858 "X-Requested-With": "XMLHttpRequest"
31861 for (var headerName in headers) {
31862 var headerValue = headers[headerName];
31864 this.xhr.setRequestHeader(headerName, headerValue);
31870 this.xhr.onload = function()
31872 _this.xhrOnLoad(_this.xhr);
31875 this.xhr.onerror = function()
31877 _this.xhrOnError(_this.xhr);
31880 var formData = new FormData();
31882 formData.append('returnHTML', 'NO');
31885 formData.append('crop', crop);
31888 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31889 formData.append(this.paramName, file, file.name);
31892 if(typeof(file.filename) != 'undefined'){
31893 formData.append('filename', file.filename);
31896 if(typeof(file.mimetype) != 'undefined'){
31897 formData.append('mimetype', file.mimetype);
31900 if(this.fireEvent('arrange', this, formData) != false){
31901 this.xhr.send(formData);
31905 xhrOnLoad : function(xhr)
31908 this.maskEl.unmask();
31911 if (xhr.readyState !== 4) {
31912 this.fireEvent('exception', this, xhr);
31916 var response = Roo.decode(xhr.responseText);
31918 if(!response.success){
31919 this.fireEvent('exception', this, xhr);
31923 var response = Roo.decode(xhr.responseText);
31925 this.fireEvent('upload', this, response);
31929 xhrOnError : function()
31932 this.maskEl.unmask();
31935 Roo.log('xhr on error');
31937 var response = Roo.decode(xhr.responseText);
31943 prepare : function(file)
31946 this.maskEl.mask(this.loadingText);
31952 if(typeof(file) === 'string'){
31953 this.loadCanvas(file);
31957 if(!file || !this.urlAPI){
31962 this.cropType = file.type;
31966 if(this.fireEvent('prepare', this, this.file) != false){
31968 var reader = new FileReader();
31970 reader.onload = function (e) {
31971 if (e.target.error) {
31972 Roo.log(e.target.error);
31976 var buffer = e.target.result,
31977 dataView = new DataView(buffer),
31979 maxOffset = dataView.byteLength - 4,
31983 if (dataView.getUint16(0) === 0xffd8) {
31984 while (offset < maxOffset) {
31985 markerBytes = dataView.getUint16(offset);
31987 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31988 markerLength = dataView.getUint16(offset + 2) + 2;
31989 if (offset + markerLength > dataView.byteLength) {
31990 Roo.log('Invalid meta data: Invalid segment size.');
31994 if(markerBytes == 0xffe1){
31995 _this.parseExifData(
32002 offset += markerLength;
32012 var url = _this.urlAPI.createObjectURL(_this.file);
32014 _this.loadCanvas(url);
32019 reader.readAsArrayBuffer(this.file);
32025 parseExifData : function(dataView, offset, length)
32027 var tiffOffset = offset + 10,
32031 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32032 // No Exif data, might be XMP data instead
32036 // Check for the ASCII code for "Exif" (0x45786966):
32037 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32038 // No Exif data, might be XMP data instead
32041 if (tiffOffset + 8 > dataView.byteLength) {
32042 Roo.log('Invalid Exif data: Invalid segment size.');
32045 // Check for the two null bytes:
32046 if (dataView.getUint16(offset + 8) !== 0x0000) {
32047 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32050 // Check the byte alignment:
32051 switch (dataView.getUint16(tiffOffset)) {
32053 littleEndian = true;
32056 littleEndian = false;
32059 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32062 // Check for the TIFF tag marker (0x002A):
32063 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32064 Roo.log('Invalid Exif data: Missing TIFF marker.');
32067 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32068 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32070 this.parseExifTags(
32073 tiffOffset + dirOffset,
32078 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32083 if (dirOffset + 6 > dataView.byteLength) {
32084 Roo.log('Invalid Exif data: Invalid directory offset.');
32087 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32088 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32089 if (dirEndOffset + 4 > dataView.byteLength) {
32090 Roo.log('Invalid Exif data: Invalid directory size.');
32093 for (i = 0; i < tagsNumber; i += 1) {
32097 dirOffset + 2 + 12 * i, // tag offset
32101 // Return the offset to the next directory:
32102 return dataView.getUint32(dirEndOffset, littleEndian);
32105 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32107 var tag = dataView.getUint16(offset, littleEndian);
32109 this.exif[tag] = this.getExifValue(
32113 dataView.getUint16(offset + 2, littleEndian), // tag type
32114 dataView.getUint32(offset + 4, littleEndian), // tag length
32119 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32121 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32130 Roo.log('Invalid Exif data: Invalid tag type.');
32134 tagSize = tagType.size * length;
32135 // Determine if the value is contained in the dataOffset bytes,
32136 // or if the value at the dataOffset is a pointer to the actual data:
32137 dataOffset = tagSize > 4 ?
32138 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32139 if (dataOffset + tagSize > dataView.byteLength) {
32140 Roo.log('Invalid Exif data: Invalid data offset.');
32143 if (length === 1) {
32144 return tagType.getValue(dataView, dataOffset, littleEndian);
32147 for (i = 0; i < length; i += 1) {
32148 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32151 if (tagType.ascii) {
32153 // Concatenate the chars:
32154 for (i = 0; i < values.length; i += 1) {
32156 // Ignore the terminating NULL byte(s):
32157 if (c === '\u0000') {
32169 Roo.apply(Roo.bootstrap.UploadCropbox, {
32171 'Orientation': 0x0112
32175 1: 0, //'top-left',
32177 3: 180, //'bottom-right',
32178 // 4: 'bottom-left',
32180 6: 90, //'right-top',
32181 // 7: 'right-bottom',
32182 8: 270 //'left-bottom'
32186 // byte, 8-bit unsigned int:
32188 getValue: function (dataView, dataOffset) {
32189 return dataView.getUint8(dataOffset);
32193 // ascii, 8-bit byte:
32195 getValue: function (dataView, dataOffset) {
32196 return String.fromCharCode(dataView.getUint8(dataOffset));
32201 // short, 16 bit int:
32203 getValue: function (dataView, dataOffset, littleEndian) {
32204 return dataView.getUint16(dataOffset, littleEndian);
32208 // long, 32 bit int:
32210 getValue: function (dataView, dataOffset, littleEndian) {
32211 return dataView.getUint32(dataOffset, littleEndian);
32215 // rational = two long values, first is numerator, second is denominator:
32217 getValue: function (dataView, dataOffset, littleEndian) {
32218 return dataView.getUint32(dataOffset, littleEndian) /
32219 dataView.getUint32(dataOffset + 4, littleEndian);
32223 // slong, 32 bit signed int:
32225 getValue: function (dataView, dataOffset, littleEndian) {
32226 return dataView.getInt32(dataOffset, littleEndian);
32230 // srational, two slongs, first is numerator, second is denominator:
32232 getValue: function (dataView, dataOffset, littleEndian) {
32233 return dataView.getInt32(dataOffset, littleEndian) /
32234 dataView.getInt32(dataOffset + 4, littleEndian);
32244 cls : 'btn-group roo-upload-cropbox-rotate-left',
32245 action : 'rotate-left',
32249 cls : 'btn btn-default',
32250 html : '<i class="fa fa-undo"></i>'
32256 cls : 'btn-group roo-upload-cropbox-picture',
32257 action : 'picture',
32261 cls : 'btn btn-default',
32262 html : '<i class="fa fa-picture-o"></i>'
32268 cls : 'btn-group roo-upload-cropbox-rotate-right',
32269 action : 'rotate-right',
32273 cls : 'btn btn-default',
32274 html : '<i class="fa fa-repeat"></i>'
32282 cls : 'btn-group roo-upload-cropbox-rotate-left',
32283 action : 'rotate-left',
32287 cls : 'btn btn-default',
32288 html : '<i class="fa fa-undo"></i>'
32294 cls : 'btn-group roo-upload-cropbox-download',
32295 action : 'download',
32299 cls : 'btn btn-default',
32300 html : '<i class="fa fa-download"></i>'
32306 cls : 'btn-group roo-upload-cropbox-crop',
32311 cls : 'btn btn-default',
32312 html : '<i class="fa fa-crop"></i>'
32318 cls : 'btn-group roo-upload-cropbox-trash',
32323 cls : 'btn btn-default',
32324 html : '<i class="fa fa-trash"></i>'
32330 cls : 'btn-group roo-upload-cropbox-rotate-right',
32331 action : 'rotate-right',
32335 cls : 'btn btn-default',
32336 html : '<i class="fa fa-repeat"></i>'
32344 cls : 'btn-group roo-upload-cropbox-rotate-left',
32345 action : 'rotate-left',
32349 cls : 'btn btn-default',
32350 html : '<i class="fa fa-undo"></i>'
32356 cls : 'btn-group roo-upload-cropbox-rotate-right',
32357 action : 'rotate-right',
32361 cls : 'btn btn-default',
32362 html : '<i class="fa fa-repeat"></i>'
32375 * @class Roo.bootstrap.DocumentManager
32376 * @extends Roo.bootstrap.Component
32377 * Bootstrap DocumentManager class
32378 * @cfg {String} paramName default 'imageUpload'
32379 * @cfg {String} toolTipName default 'filename'
32380 * @cfg {String} method default POST
32381 * @cfg {String} url action url
32382 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32383 * @cfg {Boolean} multiple multiple upload default true
32384 * @cfg {Number} thumbSize default 300
32385 * @cfg {String} fieldLabel
32386 * @cfg {Number} labelWidth default 4
32387 * @cfg {String} labelAlign (left|top) default left
32388 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32389 * @cfg {Number} labellg set the width of label (1-12)
32390 * @cfg {Number} labelmd set the width of label (1-12)
32391 * @cfg {Number} labelsm set the width of label (1-12)
32392 * @cfg {Number} labelxs set the width of label (1-12)
32395 * Create a new DocumentManager
32396 * @param {Object} config The config object
32399 Roo.bootstrap.DocumentManager = function(config){
32400 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32403 this.delegates = [];
32408 * Fire when initial the DocumentManager
32409 * @param {Roo.bootstrap.DocumentManager} this
32414 * inspect selected file
32415 * @param {Roo.bootstrap.DocumentManager} this
32416 * @param {File} file
32421 * Fire when xhr load exception
32422 * @param {Roo.bootstrap.DocumentManager} this
32423 * @param {XMLHttpRequest} xhr
32425 "exception" : true,
32427 * @event afterupload
32428 * Fire when xhr load exception
32429 * @param {Roo.bootstrap.DocumentManager} this
32430 * @param {XMLHttpRequest} xhr
32432 "afterupload" : true,
32435 * prepare the form data
32436 * @param {Roo.bootstrap.DocumentManager} this
32437 * @param {Object} formData
32442 * Fire when remove the file
32443 * @param {Roo.bootstrap.DocumentManager} this
32444 * @param {Object} file
32449 * Fire after refresh the file
32450 * @param {Roo.bootstrap.DocumentManager} this
32455 * Fire after click the image
32456 * @param {Roo.bootstrap.DocumentManager} this
32457 * @param {Object} file
32462 * Fire when upload a image and editable set to true
32463 * @param {Roo.bootstrap.DocumentManager} this
32464 * @param {Object} file
32468 * @event beforeselectfile
32469 * Fire before select file
32470 * @param {Roo.bootstrap.DocumentManager} this
32472 "beforeselectfile" : true,
32475 * Fire before process file
32476 * @param {Roo.bootstrap.DocumentManager} this
32477 * @param {Object} file
32481 * @event previewrendered
32482 * Fire when preview rendered
32483 * @param {Roo.bootstrap.DocumentManager} this
32484 * @param {Object} file
32486 "previewrendered" : true,
32489 "previewResize" : true
32494 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32503 paramName : 'imageUpload',
32504 toolTipName : 'filename',
32507 labelAlign : 'left',
32517 getAutoCreate : function()
32519 var managerWidget = {
32521 cls : 'roo-document-manager',
32525 cls : 'roo-document-manager-selector',
32530 cls : 'roo-document-manager-uploader',
32534 cls : 'roo-document-manager-upload-btn',
32535 html : '<i class="fa fa-plus"></i>'
32546 cls : 'column col-md-12',
32551 if(this.fieldLabel.length){
32556 cls : 'column col-md-12',
32557 html : this.fieldLabel
32561 cls : 'column col-md-12',
32566 if(this.labelAlign == 'left'){
32571 html : this.fieldLabel
32580 if(this.labelWidth > 12){
32581 content[0].style = "width: " + this.labelWidth + 'px';
32584 if(this.labelWidth < 13 && this.labelmd == 0){
32585 this.labelmd = this.labelWidth;
32588 if(this.labellg > 0){
32589 content[0].cls += ' col-lg-' + this.labellg;
32590 content[1].cls += ' col-lg-' + (12 - this.labellg);
32593 if(this.labelmd > 0){
32594 content[0].cls += ' col-md-' + this.labelmd;
32595 content[1].cls += ' col-md-' + (12 - this.labelmd);
32598 if(this.labelsm > 0){
32599 content[0].cls += ' col-sm-' + this.labelsm;
32600 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32603 if(this.labelxs > 0){
32604 content[0].cls += ' col-xs-' + this.labelxs;
32605 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32613 cls : 'row clearfix',
32621 initEvents : function()
32623 this.managerEl = this.el.select('.roo-document-manager', true).first();
32624 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32626 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32627 this.selectorEl.hide();
32630 this.selectorEl.attr('multiple', 'multiple');
32633 this.selectorEl.on('change', this.onFileSelected, this);
32635 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32636 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32638 this.uploader.on('click', this.onUploaderClick, this);
32640 this.renderProgressDialog();
32644 window.addEventListener("resize", function() { _this.refresh(); } );
32646 this.fireEvent('initial', this);
32649 renderProgressDialog : function()
32653 this.progressDialog = new Roo.bootstrap.Modal({
32654 cls : 'roo-document-manager-progress-dialog',
32655 allow_close : false,
32666 btnclick : function() {
32667 _this.uploadCancel();
32673 this.progressDialog.render(Roo.get(document.body));
32675 this.progress = new Roo.bootstrap.Progress({
32676 cls : 'roo-document-manager-progress',
32681 this.progress.render(this.progressDialog.getChildContainer());
32683 this.progressBar = new Roo.bootstrap.ProgressBar({
32684 cls : 'roo-document-manager-progress-bar',
32687 aria_valuemax : 12,
32691 this.progressBar.render(this.progress.getChildContainer());
32694 onUploaderClick : function(e)
32696 e.preventDefault();
32698 if(this.fireEvent('beforeselectfile', this) != false){
32699 this.selectorEl.dom.click();
32704 onFileSelected : function(e)
32706 e.preventDefault();
32708 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32712 Roo.each(this.selectorEl.dom.files, function(file){
32713 if(this.fireEvent('inspect', this, file) != false){
32714 this.files.push(file);
32724 this.selectorEl.dom.value = '';
32726 if(!this.files || !this.files.length){
32730 if(this.boxes > 0 && this.files.length > this.boxes){
32731 this.files = this.files.slice(0, this.boxes);
32734 this.uploader.show();
32736 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32737 this.uploader.hide();
32746 Roo.each(this.files, function(file){
32748 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32749 var f = this.renderPreview(file);
32754 if(file.type.indexOf('image') != -1){
32755 this.delegates.push(
32757 _this.process(file);
32758 }).createDelegate(this)
32766 _this.process(file);
32767 }).createDelegate(this)
32772 this.files = files;
32774 this.delegates = this.delegates.concat(docs);
32776 if(!this.delegates.length){
32781 this.progressBar.aria_valuemax = this.delegates.length;
32788 arrange : function()
32790 if(!this.delegates.length){
32791 this.progressDialog.hide();
32796 var delegate = this.delegates.shift();
32798 this.progressDialog.show();
32800 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32802 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32807 refresh : function()
32809 this.uploader.show();
32811 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32812 this.uploader.hide();
32815 Roo.isTouch ? this.closable(false) : this.closable(true);
32817 this.fireEvent('refresh', this);
32820 onRemove : function(e, el, o)
32822 e.preventDefault();
32824 this.fireEvent('remove', this, o);
32828 remove : function(o)
32832 Roo.each(this.files, function(file){
32833 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32842 this.files = files;
32849 Roo.each(this.files, function(file){
32854 file.target.remove();
32863 onClick : function(e, el, o)
32865 e.preventDefault();
32867 this.fireEvent('click', this, o);
32871 closable : function(closable)
32873 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32875 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32887 xhrOnLoad : function(xhr)
32889 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32893 if (xhr.readyState !== 4) {
32895 this.fireEvent('exception', this, xhr);
32899 var response = Roo.decode(xhr.responseText);
32901 if(!response.success){
32903 this.fireEvent('exception', this, xhr);
32907 var file = this.renderPreview(response.data);
32909 this.files.push(file);
32913 this.fireEvent('afterupload', this, xhr);
32917 xhrOnError : function(xhr)
32919 Roo.log('xhr on error');
32921 var response = Roo.decode(xhr.responseText);
32928 process : function(file)
32930 if(this.fireEvent('process', this, file) !== false){
32931 if(this.editable && file.type.indexOf('image') != -1){
32932 this.fireEvent('edit', this, file);
32936 this.uploadStart(file, false);
32943 uploadStart : function(file, crop)
32945 this.xhr = new XMLHttpRequest();
32947 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32952 file.xhr = this.xhr;
32954 this.managerEl.createChild({
32956 cls : 'roo-document-manager-loading',
32960 tooltip : file.name,
32961 cls : 'roo-document-manager-thumb',
32962 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32968 this.xhr.open(this.method, this.url, true);
32971 "Accept": "application/json",
32972 "Cache-Control": "no-cache",
32973 "X-Requested-With": "XMLHttpRequest"
32976 for (var headerName in headers) {
32977 var headerValue = headers[headerName];
32979 this.xhr.setRequestHeader(headerName, headerValue);
32985 this.xhr.onload = function()
32987 _this.xhrOnLoad(_this.xhr);
32990 this.xhr.onerror = function()
32992 _this.xhrOnError(_this.xhr);
32995 var formData = new FormData();
32997 formData.append('returnHTML', 'NO');
33000 formData.append('crop', crop);
33003 formData.append(this.paramName, file, file.name);
33010 if(this.fireEvent('prepare', this, formData, options) != false){
33012 if(options.manually){
33016 this.xhr.send(formData);
33020 this.uploadCancel();
33023 uploadCancel : function()
33029 this.delegates = [];
33031 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33038 renderPreview : function(file)
33040 if(typeof(file.target) != 'undefined' && file.target){
33044 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33046 var previewEl = this.managerEl.createChild({
33048 cls : 'roo-document-manager-preview',
33052 tooltip : file[this.toolTipName],
33053 cls : 'roo-document-manager-thumb',
33054 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33059 html : '<i class="fa fa-times-circle"></i>'
33064 var close = previewEl.select('button.close', true).first();
33066 close.on('click', this.onRemove, this, file);
33068 file.target = previewEl;
33070 var image = previewEl.select('img', true).first();
33074 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33076 image.on('click', this.onClick, this, file);
33078 this.fireEvent('previewrendered', this, file);
33084 onPreviewLoad : function(file, image)
33086 if(typeof(file.target) == 'undefined' || !file.target){
33090 var width = image.dom.naturalWidth || image.dom.width;
33091 var height = image.dom.naturalHeight || image.dom.height;
33093 if(!this.previewResize) {
33097 if(width > height){
33098 file.target.addClass('wide');
33102 file.target.addClass('tall');
33107 uploadFromSource : function(file, crop)
33109 this.xhr = new XMLHttpRequest();
33111 this.managerEl.createChild({
33113 cls : 'roo-document-manager-loading',
33117 tooltip : file.name,
33118 cls : 'roo-document-manager-thumb',
33119 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33125 this.xhr.open(this.method, this.url, true);
33128 "Accept": "application/json",
33129 "Cache-Control": "no-cache",
33130 "X-Requested-With": "XMLHttpRequest"
33133 for (var headerName in headers) {
33134 var headerValue = headers[headerName];
33136 this.xhr.setRequestHeader(headerName, headerValue);
33142 this.xhr.onload = function()
33144 _this.xhrOnLoad(_this.xhr);
33147 this.xhr.onerror = function()
33149 _this.xhrOnError(_this.xhr);
33152 var formData = new FormData();
33154 formData.append('returnHTML', 'NO');
33156 formData.append('crop', crop);
33158 if(typeof(file.filename) != 'undefined'){
33159 formData.append('filename', file.filename);
33162 if(typeof(file.mimetype) != 'undefined'){
33163 formData.append('mimetype', file.mimetype);
33168 if(this.fireEvent('prepare', this, formData) != false){
33169 this.xhr.send(formData);
33179 * @class Roo.bootstrap.DocumentViewer
33180 * @extends Roo.bootstrap.Component
33181 * Bootstrap DocumentViewer class
33182 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33183 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33186 * Create a new DocumentViewer
33187 * @param {Object} config The config object
33190 Roo.bootstrap.DocumentViewer = function(config){
33191 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33196 * Fire after initEvent
33197 * @param {Roo.bootstrap.DocumentViewer} this
33203 * @param {Roo.bootstrap.DocumentViewer} this
33208 * Fire after download button
33209 * @param {Roo.bootstrap.DocumentViewer} this
33214 * Fire after trash button
33215 * @param {Roo.bootstrap.DocumentViewer} this
33222 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33224 showDownload : true,
33228 getAutoCreate : function()
33232 cls : 'roo-document-viewer',
33236 cls : 'roo-document-viewer-body',
33240 cls : 'roo-document-viewer-thumb',
33244 cls : 'roo-document-viewer-image'
33252 cls : 'roo-document-viewer-footer',
33255 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33259 cls : 'btn-group roo-document-viewer-download',
33263 cls : 'btn btn-default',
33264 html : '<i class="fa fa-download"></i>'
33270 cls : 'btn-group roo-document-viewer-trash',
33274 cls : 'btn btn-default',
33275 html : '<i class="fa fa-trash"></i>'
33288 initEvents : function()
33290 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33291 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33293 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33294 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33296 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33297 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33299 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33300 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33302 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33303 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33305 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33306 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33308 this.bodyEl.on('click', this.onClick, this);
33309 this.downloadBtn.on('click', this.onDownload, this);
33310 this.trashBtn.on('click', this.onTrash, this);
33312 this.downloadBtn.hide();
33313 this.trashBtn.hide();
33315 if(this.showDownload){
33316 this.downloadBtn.show();
33319 if(this.showTrash){
33320 this.trashBtn.show();
33323 if(!this.showDownload && !this.showTrash) {
33324 this.footerEl.hide();
33329 initial : function()
33331 this.fireEvent('initial', this);
33335 onClick : function(e)
33337 e.preventDefault();
33339 this.fireEvent('click', this);
33342 onDownload : function(e)
33344 e.preventDefault();
33346 this.fireEvent('download', this);
33349 onTrash : function(e)
33351 e.preventDefault();
33353 this.fireEvent('trash', this);
33365 * @class Roo.bootstrap.form.FieldLabel
33366 * @extends Roo.bootstrap.Component
33367 * Bootstrap FieldLabel class
33368 * @cfg {String} html contents of the element
33369 * @cfg {String} tag tag of the element default label
33370 * @cfg {String} cls class of the element
33371 * @cfg {String} target label target
33372 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33373 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33374 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33375 * @cfg {String} iconTooltip default "This field is required"
33376 * @cfg {String} indicatorpos (left|right) default left
33379 * Create a new FieldLabel
33380 * @param {Object} config The config object
33383 Roo.bootstrap.form.FieldLabel = function(config){
33384 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33389 * Fires after the field has been marked as invalid.
33390 * @param {Roo.form.FieldLabel} this
33391 * @param {String} msg The validation message
33396 * Fires after the field has been validated with no errors.
33397 * @param {Roo.form.FieldLabel} this
33403 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33410 invalidClass : 'has-warning',
33411 validClass : 'has-success',
33412 iconTooltip : 'This field is required',
33413 indicatorpos : 'left',
33415 getAutoCreate : function(){
33418 if (!this.allowBlank) {
33424 cls : 'roo-bootstrap-field-label ' + this.cls,
33429 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33430 tooltip : this.iconTooltip
33439 if(this.indicatorpos == 'right'){
33442 cls : 'roo-bootstrap-field-label ' + this.cls,
33451 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33452 tooltip : this.iconTooltip
33461 initEvents: function()
33463 Roo.bootstrap.Element.superclass.initEvents.call(this);
33465 this.indicator = this.indicatorEl();
33467 if(this.indicator){
33468 this.indicator.removeClass('visible');
33469 this.indicator.addClass('invisible');
33472 Roo.bootstrap.form.FieldLabel.register(this);
33475 indicatorEl : function()
33477 var indicator = this.el.select('i.roo-required-indicator',true).first();
33488 * Mark this field as valid
33490 markValid : function()
33492 if(this.indicator){
33493 this.indicator.removeClass('visible');
33494 this.indicator.addClass('invisible');
33496 if (Roo.bootstrap.version == 3) {
33497 this.el.removeClass(this.invalidClass);
33498 this.el.addClass(this.validClass);
33500 this.el.removeClass('is-invalid');
33501 this.el.addClass('is-valid');
33505 this.fireEvent('valid', this);
33509 * Mark this field as invalid
33510 * @param {String} msg The validation message
33512 markInvalid : function(msg)
33514 if(this.indicator){
33515 this.indicator.removeClass('invisible');
33516 this.indicator.addClass('visible');
33518 if (Roo.bootstrap.version == 3) {
33519 this.el.removeClass(this.validClass);
33520 this.el.addClass(this.invalidClass);
33522 this.el.removeClass('is-valid');
33523 this.el.addClass('is-invalid');
33527 this.fireEvent('invalid', this, msg);
33533 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33538 * register a FieldLabel Group
33539 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33541 register : function(label)
33543 if(this.groups.hasOwnProperty(label.target)){
33547 this.groups[label.target] = label;
33551 * fetch a FieldLabel Group based on the target
33552 * @param {string} target
33553 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33555 get: function(target) {
33556 if (typeof(this.groups[target]) == 'undefined') {
33560 return this.groups[target] ;
33569 * page DateSplitField.
33575 * @class Roo.bootstrap.form.DateSplitField
33576 * @extends Roo.bootstrap.Component
33577 * Bootstrap DateSplitField class
33578 * @cfg {string} fieldLabel - the label associated
33579 * @cfg {Number} labelWidth set the width of label (0-12)
33580 * @cfg {String} labelAlign (top|left)
33581 * @cfg {Boolean} dayAllowBlank (true|false) default false
33582 * @cfg {Boolean} monthAllowBlank (true|false) default false
33583 * @cfg {Boolean} yearAllowBlank (true|false) default false
33584 * @cfg {string} dayPlaceholder
33585 * @cfg {string} monthPlaceholder
33586 * @cfg {string} yearPlaceholder
33587 * @cfg {string} dayFormat default 'd'
33588 * @cfg {string} monthFormat default 'm'
33589 * @cfg {string} yearFormat default 'Y'
33590 * @cfg {Number} labellg set the width of label (1-12)
33591 * @cfg {Number} labelmd set the width of label (1-12)
33592 * @cfg {Number} labelsm set the width of label (1-12)
33593 * @cfg {Number} labelxs set the width of label (1-12)
33597 * Create a new DateSplitField
33598 * @param {Object} config The config object
33601 Roo.bootstrap.form.DateSplitField = function(config){
33602 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33608 * getting the data of years
33609 * @param {Roo.bootstrap.form.DateSplitField} this
33610 * @param {Object} years
33615 * getting the data of days
33616 * @param {Roo.bootstrap.form.DateSplitField} this
33617 * @param {Object} days
33622 * Fires after the field has been marked as invalid.
33623 * @param {Roo.form.Field} this
33624 * @param {String} msg The validation message
33629 * Fires after the field has been validated with no errors.
33630 * @param {Roo.form.Field} this
33636 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33639 labelAlign : 'top',
33641 dayAllowBlank : false,
33642 monthAllowBlank : false,
33643 yearAllowBlank : false,
33644 dayPlaceholder : '',
33645 monthPlaceholder : '',
33646 yearPlaceholder : '',
33650 isFormField : true,
33656 getAutoCreate : function()
33660 cls : 'row roo-date-split-field-group',
33665 cls : 'form-hidden-field roo-date-split-field-group-value',
33671 var labelCls = 'col-md-12';
33672 var contentCls = 'col-md-4';
33674 if(this.fieldLabel){
33678 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33682 html : this.fieldLabel
33687 if(this.labelAlign == 'left'){
33689 if(this.labelWidth > 12){
33690 label.style = "width: " + this.labelWidth + 'px';
33693 if(this.labelWidth < 13 && this.labelmd == 0){
33694 this.labelmd = this.labelWidth;
33697 if(this.labellg > 0){
33698 labelCls = ' col-lg-' + this.labellg;
33699 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33702 if(this.labelmd > 0){
33703 labelCls = ' col-md-' + this.labelmd;
33704 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33707 if(this.labelsm > 0){
33708 labelCls = ' col-sm-' + this.labelsm;
33709 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33712 if(this.labelxs > 0){
33713 labelCls = ' col-xs-' + this.labelxs;
33714 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33718 label.cls += ' ' + labelCls;
33720 cfg.cn.push(label);
33723 Roo.each(['day', 'month', 'year'], function(t){
33726 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33733 inputEl: function ()
33735 return this.el.select('.roo-date-split-field-group-value', true).first();
33738 onRender : function(ct, position)
33742 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33744 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33746 this.dayField = new Roo.bootstrap.form.ComboBox({
33747 allowBlank : this.dayAllowBlank,
33748 alwaysQuery : true,
33749 displayField : 'value',
33752 forceSelection : true,
33754 placeholder : this.dayPlaceholder,
33755 selectOnFocus : true,
33756 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33757 triggerAction : 'all',
33759 valueField : 'value',
33760 store : new Roo.data.SimpleStore({
33761 data : (function() {
33763 _this.fireEvent('days', _this, days);
33766 fields : [ 'value' ]
33769 select : function (_self, record, index)
33771 _this.setValue(_this.getValue());
33776 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33778 this.monthField = new Roo.bootstrap.form.MonthField({
33779 after : '<i class=\"fa fa-calendar\"></i>',
33780 allowBlank : this.monthAllowBlank,
33781 placeholder : this.monthPlaceholder,
33784 render : function (_self)
33786 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33787 e.preventDefault();
33791 select : function (_self, oldvalue, newvalue)
33793 _this.setValue(_this.getValue());
33798 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33800 this.yearField = new Roo.bootstrap.form.ComboBox({
33801 allowBlank : this.yearAllowBlank,
33802 alwaysQuery : true,
33803 displayField : 'value',
33806 forceSelection : true,
33808 placeholder : this.yearPlaceholder,
33809 selectOnFocus : true,
33810 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33811 triggerAction : 'all',
33813 valueField : 'value',
33814 store : new Roo.data.SimpleStore({
33815 data : (function() {
33817 _this.fireEvent('years', _this, years);
33820 fields : [ 'value' ]
33823 select : function (_self, record, index)
33825 _this.setValue(_this.getValue());
33830 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33833 setValue : function(v, format)
33835 this.inputEl.dom.value = v;
33837 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33839 var d = Date.parseDate(v, f);
33846 this.setDay(d.format(this.dayFormat));
33847 this.setMonth(d.format(this.monthFormat));
33848 this.setYear(d.format(this.yearFormat));
33855 setDay : function(v)
33857 this.dayField.setValue(v);
33858 this.inputEl.dom.value = this.getValue();
33863 setMonth : function(v)
33865 this.monthField.setValue(v, true);
33866 this.inputEl.dom.value = this.getValue();
33871 setYear : function(v)
33873 this.yearField.setValue(v);
33874 this.inputEl.dom.value = this.getValue();
33879 getDay : function()
33881 return this.dayField.getValue();
33884 getMonth : function()
33886 return this.monthField.getValue();
33889 getYear : function()
33891 return this.yearField.getValue();
33894 getValue : function()
33896 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33898 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33908 this.inputEl.dom.value = '';
33913 validate : function()
33915 var d = this.dayField.validate();
33916 var m = this.monthField.validate();
33917 var y = this.yearField.validate();
33922 (!this.dayAllowBlank && !d) ||
33923 (!this.monthAllowBlank && !m) ||
33924 (!this.yearAllowBlank && !y)
33929 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33938 this.markInvalid();
33943 markValid : function()
33946 var label = this.el.select('label', true).first();
33947 var icon = this.el.select('i.fa-star', true).first();
33953 this.fireEvent('valid', this);
33957 * Mark this field as invalid
33958 * @param {String} msg The validation message
33960 markInvalid : function(msg)
33963 var label = this.el.select('label', true).first();
33964 var icon = this.el.select('i.fa-star', true).first();
33966 if(label && !icon){
33967 this.el.select('.roo-date-split-field-label', true).createChild({
33969 cls : 'text-danger fa fa-lg fa-star',
33970 tooltip : 'This field is required',
33971 style : 'margin-right:5px;'
33975 this.fireEvent('invalid', this, msg);
33978 clearInvalid : function()
33980 var label = this.el.select('label', true).first();
33981 var icon = this.el.select('i.fa-star', true).first();
33987 this.fireEvent('valid', this);
33990 getName: function()
34000 * @class Roo.bootstrap.LayoutMasonry
34001 * @extends Roo.bootstrap.Component
34002 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34003 * Bootstrap Layout Masonry class
34006 * http://masonry.desandro.com
34008 * The idea is to render all the bricks based on vertical width...
34010 * The original code extends 'outlayer' - we might need to use that....
34013 * Create a new Element
34014 * @param {Object} config The config object
34017 Roo.bootstrap.LayoutMasonry = function(config){
34019 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34023 Roo.bootstrap.LayoutMasonry.register(this);
34029 * Fire after layout the items
34030 * @param {Roo.bootstrap.LayoutMasonry} this
34031 * @param {Roo.EventObject} e
34038 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34041 * @cfg {Boolean} isLayoutInstant = no animation?
34043 isLayoutInstant : false, // needed?
34046 * @cfg {Number} boxWidth width of the columns
34051 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34056 * @cfg {Number} padWidth padding below box..
34061 * @cfg {Number} gutter gutter width..
34066 * @cfg {Number} maxCols maximum number of columns
34072 * @cfg {Boolean} isAutoInitial defalut true
34074 isAutoInitial : true,
34079 * @cfg {Boolean} isHorizontal defalut false
34081 isHorizontal : false,
34083 currentSize : null,
34089 bricks: null, //CompositeElement
34093 _isLayoutInited : false,
34095 // isAlternative : false, // only use for vertical layout...
34098 * @cfg {Number} alternativePadWidth padding below box..
34100 alternativePadWidth : 50,
34102 selectedBrick : [],
34104 getAutoCreate : function(){
34106 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34110 cls: 'blog-masonary-wrapper ' + this.cls,
34112 cls : 'mas-boxes masonary'
34119 getChildContainer: function( )
34121 if (this.boxesEl) {
34122 return this.boxesEl;
34125 this.boxesEl = this.el.select('.mas-boxes').first();
34127 return this.boxesEl;
34131 initEvents : function()
34135 if(this.isAutoInitial){
34136 Roo.log('hook children rendered');
34137 this.on('childrenrendered', function() {
34138 Roo.log('children rendered');
34144 initial : function()
34146 this.selectedBrick = [];
34148 this.currentSize = this.el.getBox(true);
34150 Roo.EventManager.onWindowResize(this.resize, this);
34152 if(!this.isAutoInitial){
34160 //this.layout.defer(500,this);
34164 resize : function()
34166 var cs = this.el.getBox(true);
34169 this.currentSize.width == cs.width &&
34170 this.currentSize.x == cs.x &&
34171 this.currentSize.height == cs.height &&
34172 this.currentSize.y == cs.y
34174 Roo.log("no change in with or X or Y");
34178 this.currentSize = cs;
34184 layout : function()
34186 this._resetLayout();
34188 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34190 this.layoutItems( isInstant );
34192 this._isLayoutInited = true;
34194 this.fireEvent('layout', this);
34198 _resetLayout : function()
34200 if(this.isHorizontal){
34201 this.horizontalMeasureColumns();
34205 this.verticalMeasureColumns();
34209 verticalMeasureColumns : function()
34211 this.getContainerWidth();
34213 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34214 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34218 var boxWidth = this.boxWidth + this.padWidth;
34220 if(this.containerWidth < this.boxWidth){
34221 boxWidth = this.containerWidth
34224 var containerWidth = this.containerWidth;
34226 var cols = Math.floor(containerWidth / boxWidth);
34228 this.cols = Math.max( cols, 1 );
34230 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34232 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34234 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34236 this.colWidth = boxWidth + avail - this.padWidth;
34238 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34239 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34242 horizontalMeasureColumns : function()
34244 this.getContainerWidth();
34246 var boxWidth = this.boxWidth;
34248 if(this.containerWidth < boxWidth){
34249 boxWidth = this.containerWidth;
34252 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34254 this.el.setHeight(boxWidth);
34258 getContainerWidth : function()
34260 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34263 layoutItems : function( isInstant )
34265 Roo.log(this.bricks);
34267 var items = Roo.apply([], this.bricks);
34269 if(this.isHorizontal){
34270 this._horizontalLayoutItems( items , isInstant );
34274 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34275 // this._verticalAlternativeLayoutItems( items , isInstant );
34279 this._verticalLayoutItems( items , isInstant );
34283 _verticalLayoutItems : function ( items , isInstant)
34285 if ( !items || !items.length ) {
34290 ['xs', 'xs', 'xs', 'tall'],
34291 ['xs', 'xs', 'tall'],
34292 ['xs', 'xs', 'sm'],
34293 ['xs', 'xs', 'xs'],
34299 ['sm', 'xs', 'xs'],
34303 ['tall', 'xs', 'xs', 'xs'],
34304 ['tall', 'xs', 'xs'],
34316 Roo.each(items, function(item, k){
34318 switch (item.size) {
34319 // these layouts take up a full box,
34330 boxes.push([item]);
34353 var filterPattern = function(box, length)
34361 var pattern = box.slice(0, length);
34365 Roo.each(pattern, function(i){
34366 format.push(i.size);
34369 Roo.each(standard, function(s){
34371 if(String(s) != String(format)){
34380 if(!match && length == 1){
34385 filterPattern(box, length - 1);
34389 queue.push(pattern);
34391 box = box.slice(length, box.length);
34393 filterPattern(box, 4);
34399 Roo.each(boxes, function(box, k){
34405 if(box.length == 1){
34410 filterPattern(box, 4);
34414 this._processVerticalLayoutQueue( queue, isInstant );
34418 // _verticalAlternativeLayoutItems : function( items , isInstant )
34420 // if ( !items || !items.length ) {
34424 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34428 _horizontalLayoutItems : function ( items , isInstant)
34430 if ( !items || !items.length || items.length < 3) {
34436 var eItems = items.slice(0, 3);
34438 items = items.slice(3, items.length);
34441 ['xs', 'xs', 'xs', 'wide'],
34442 ['xs', 'xs', 'wide'],
34443 ['xs', 'xs', 'sm'],
34444 ['xs', 'xs', 'xs'],
34450 ['sm', 'xs', 'xs'],
34454 ['wide', 'xs', 'xs', 'xs'],
34455 ['wide', 'xs', 'xs'],
34468 Roo.each(items, function(item, k){
34470 switch (item.size) {
34481 boxes.push([item]);
34505 var filterPattern = function(box, length)
34513 var pattern = box.slice(0, length);
34517 Roo.each(pattern, function(i){
34518 format.push(i.size);
34521 Roo.each(standard, function(s){
34523 if(String(s) != String(format)){
34532 if(!match && length == 1){
34537 filterPattern(box, length - 1);
34541 queue.push(pattern);
34543 box = box.slice(length, box.length);
34545 filterPattern(box, 4);
34551 Roo.each(boxes, function(box, k){
34557 if(box.length == 1){
34562 filterPattern(box, 4);
34569 var pos = this.el.getBox(true);
34573 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34575 var hit_end = false;
34577 Roo.each(queue, function(box){
34581 Roo.each(box, function(b){
34583 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34593 Roo.each(box, function(b){
34595 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34598 mx = Math.max(mx, b.x);
34602 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34606 Roo.each(box, function(b){
34608 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34622 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34625 /** Sets position of item in DOM
34626 * @param {Element} item
34627 * @param {Number} x - horizontal position
34628 * @param {Number} y - vertical position
34629 * @param {Boolean} isInstant - disables transitions
34631 _processVerticalLayoutQueue : function( queue, isInstant )
34633 var pos = this.el.getBox(true);
34638 for (var i = 0; i < this.cols; i++){
34642 Roo.each(queue, function(box, k){
34644 var col = k % this.cols;
34646 Roo.each(box, function(b,kk){
34648 b.el.position('absolute');
34650 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34651 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34653 if(b.size == 'md-left' || b.size == 'md-right'){
34654 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34655 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34658 b.el.setWidth(width);
34659 b.el.setHeight(height);
34661 b.el.select('iframe',true).setSize(width,height);
34665 for (var i = 0; i < this.cols; i++){
34667 if(maxY[i] < maxY[col]){
34672 col = Math.min(col, i);
34676 x = pos.x + col * (this.colWidth + this.padWidth);
34680 var positions = [];
34682 switch (box.length){
34684 positions = this.getVerticalOneBoxColPositions(x, y, box);
34687 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34690 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34693 positions = this.getVerticalFourBoxColPositions(x, y, box);
34699 Roo.each(box, function(b,kk){
34701 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34703 var sz = b.el.getSize();
34705 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34713 for (var i = 0; i < this.cols; i++){
34714 mY = Math.max(mY, maxY[i]);
34717 this.el.setHeight(mY - pos.y);
34721 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34723 // var pos = this.el.getBox(true);
34726 // var maxX = pos.right;
34728 // var maxHeight = 0;
34730 // Roo.each(items, function(item, k){
34734 // item.el.position('absolute');
34736 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34738 // item.el.setWidth(width);
34740 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34742 // item.el.setHeight(height);
34745 // item.el.setXY([x, y], isInstant ? false : true);
34747 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34750 // y = y + height + this.alternativePadWidth;
34752 // maxHeight = maxHeight + height + this.alternativePadWidth;
34756 // this.el.setHeight(maxHeight);
34760 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34762 var pos = this.el.getBox(true);
34767 var maxX = pos.right;
34769 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34771 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34773 Roo.each(queue, function(box, k){
34775 Roo.each(box, function(b, kk){
34777 b.el.position('absolute');
34779 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34780 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34782 if(b.size == 'md-left' || b.size == 'md-right'){
34783 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34784 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34787 b.el.setWidth(width);
34788 b.el.setHeight(height);
34796 var positions = [];
34798 switch (box.length){
34800 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34803 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34806 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34809 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34815 Roo.each(box, function(b,kk){
34817 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34819 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34827 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34829 Roo.each(eItems, function(b,k){
34831 b.size = (k == 0) ? 'sm' : 'xs';
34832 b.x = (k == 0) ? 2 : 1;
34833 b.y = (k == 0) ? 2 : 1;
34835 b.el.position('absolute');
34837 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34839 b.el.setWidth(width);
34841 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34843 b.el.setHeight(height);
34847 var positions = [];
34850 x : maxX - this.unitWidth * 2 - this.gutter,
34855 x : maxX - this.unitWidth,
34856 y : minY + (this.unitWidth + this.gutter) * 2
34860 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34864 Roo.each(eItems, function(b,k){
34866 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34872 getVerticalOneBoxColPositions : function(x, y, box)
34876 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34878 if(box[0].size == 'md-left'){
34882 if(box[0].size == 'md-right'){
34887 x : x + (this.unitWidth + this.gutter) * rand,
34894 getVerticalTwoBoxColPositions : function(x, y, box)
34898 if(box[0].size == 'xs'){
34902 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34906 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34920 x : x + (this.unitWidth + this.gutter) * 2,
34921 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34928 getVerticalThreeBoxColPositions : function(x, y, box)
34932 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34940 x : x + (this.unitWidth + this.gutter) * 1,
34945 x : x + (this.unitWidth + this.gutter) * 2,
34953 if(box[0].size == 'xs' && box[1].size == 'xs'){
34962 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34966 x : x + (this.unitWidth + this.gutter) * 1,
34980 x : x + (this.unitWidth + this.gutter) * 2,
34985 x : x + (this.unitWidth + this.gutter) * 2,
34986 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34993 getVerticalFourBoxColPositions : function(x, y, box)
34997 if(box[0].size == 'xs'){
35006 y : y + (this.unitHeight + this.gutter) * 1
35011 y : y + (this.unitHeight + this.gutter) * 2
35015 x : x + (this.unitWidth + this.gutter) * 1,
35029 x : x + (this.unitWidth + this.gutter) * 2,
35034 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35035 y : y + (this.unitHeight + this.gutter) * 1
35039 x : x + (this.unitWidth + this.gutter) * 2,
35040 y : y + (this.unitWidth + this.gutter) * 2
35047 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35051 if(box[0].size == 'md-left'){
35053 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35060 if(box[0].size == 'md-right'){
35062 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35063 y : minY + (this.unitWidth + this.gutter) * 1
35069 var rand = Math.floor(Math.random() * (4 - box[0].y));
35072 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35073 y : minY + (this.unitWidth + this.gutter) * rand
35080 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35084 if(box[0].size == 'xs'){
35087 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35092 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35093 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35101 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35106 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35107 y : minY + (this.unitWidth + this.gutter) * 2
35114 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35118 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35121 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35126 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35127 y : minY + (this.unitWidth + this.gutter) * 1
35131 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35132 y : minY + (this.unitWidth + this.gutter) * 2
35139 if(box[0].size == 'xs' && box[1].size == 'xs'){
35142 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35147 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35152 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35153 y : minY + (this.unitWidth + this.gutter) * 1
35161 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35166 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35167 y : minY + (this.unitWidth + this.gutter) * 2
35171 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35172 y : minY + (this.unitWidth + this.gutter) * 2
35179 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35183 if(box[0].size == 'xs'){
35186 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35191 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35196 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),
35201 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35202 y : minY + (this.unitWidth + this.gutter) * 1
35210 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35215 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35216 y : minY + (this.unitWidth + this.gutter) * 2
35220 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35221 y : minY + (this.unitWidth + this.gutter) * 2
35225 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),
35226 y : minY + (this.unitWidth + this.gutter) * 2
35234 * remove a Masonry Brick
35235 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35237 removeBrick : function(brick_id)
35243 for (var i = 0; i<this.bricks.length; i++) {
35244 if (this.bricks[i].id == brick_id) {
35245 this.bricks.splice(i,1);
35246 this.el.dom.removeChild(Roo.get(brick_id).dom);
35253 * adds a Masonry Brick
35254 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35256 addBrick : function(cfg)
35258 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35259 //this.register(cn);
35260 cn.parentId = this.id;
35261 cn.render(this.el);
35266 * register a Masonry Brick
35267 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35270 register : function(brick)
35272 this.bricks.push(brick);
35273 brick.masonryId = this.id;
35277 * clear all the Masonry Brick
35279 clearAll : function()
35282 //this.getChildContainer().dom.innerHTML = "";
35283 this.el.dom.innerHTML = '';
35286 getSelected : function()
35288 if (!this.selectedBrick) {
35292 return this.selectedBrick;
35296 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35300 * register a Masonry Layout
35301 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35304 register : function(layout)
35306 this.groups[layout.id] = layout;
35309 * fetch a Masonry Layout based on the masonry layout ID
35310 * @param {string} the masonry layout to add
35311 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35314 get: function(layout_id) {
35315 if (typeof(this.groups[layout_id]) == 'undefined') {
35318 return this.groups[layout_id] ;
35330 * http://masonry.desandro.com
35332 * The idea is to render all the bricks based on vertical width...
35334 * The original code extends 'outlayer' - we might need to use that....
35340 * @class Roo.bootstrap.LayoutMasonryAuto
35341 * @extends Roo.bootstrap.Component
35342 * Bootstrap Layout Masonry class
35345 * Create a new Element
35346 * @param {Object} config The config object
35349 Roo.bootstrap.LayoutMasonryAuto = function(config){
35350 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35353 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35356 * @cfg {Boolean} isFitWidth - resize the width..
35358 isFitWidth : false, // options..
35360 * @cfg {Boolean} isOriginLeft = left align?
35362 isOriginLeft : true,
35364 * @cfg {Boolean} isOriginTop = top align?
35366 isOriginTop : false,
35368 * @cfg {Boolean} isLayoutInstant = no animation?
35370 isLayoutInstant : false, // needed?
35372 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35374 isResizingContainer : true,
35376 * @cfg {Number} columnWidth width of the columns
35382 * @cfg {Number} maxCols maximum number of columns
35387 * @cfg {Number} padHeight padding below box..
35393 * @cfg {Boolean} isAutoInitial defalut true
35396 isAutoInitial : true,
35402 initialColumnWidth : 0,
35403 currentSize : null,
35405 colYs : null, // array.
35412 bricks: null, //CompositeElement
35413 cols : 0, // array?
35414 // element : null, // wrapped now this.el
35415 _isLayoutInited : null,
35418 getAutoCreate : function(){
35422 cls: 'blog-masonary-wrapper ' + this.cls,
35424 cls : 'mas-boxes masonary'
35431 getChildContainer: function( )
35433 if (this.boxesEl) {
35434 return this.boxesEl;
35437 this.boxesEl = this.el.select('.mas-boxes').first();
35439 return this.boxesEl;
35443 initEvents : function()
35447 if(this.isAutoInitial){
35448 Roo.log('hook children rendered');
35449 this.on('childrenrendered', function() {
35450 Roo.log('children rendered');
35457 initial : function()
35459 this.reloadItems();
35461 this.currentSize = this.el.getBox(true);
35463 /// was window resize... - let's see if this works..
35464 Roo.EventManager.onWindowResize(this.resize, this);
35466 if(!this.isAutoInitial){
35471 this.layout.defer(500,this);
35474 reloadItems: function()
35476 this.bricks = this.el.select('.masonry-brick', true);
35478 this.bricks.each(function(b) {
35479 //Roo.log(b.getSize());
35480 if (!b.attr('originalwidth')) {
35481 b.attr('originalwidth', b.getSize().width);
35486 Roo.log(this.bricks.elements.length);
35489 resize : function()
35492 var cs = this.el.getBox(true);
35494 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35495 Roo.log("no change in with or X");
35498 this.currentSize = cs;
35502 layout : function()
35505 this._resetLayout();
35506 //this._manageStamps();
35508 // don't animate first layout
35509 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35510 this.layoutItems( isInstant );
35512 // flag for initalized
35513 this._isLayoutInited = true;
35516 layoutItems : function( isInstant )
35518 //var items = this._getItemsForLayout( this.items );
35519 // original code supports filtering layout items.. we just ignore it..
35521 this._layoutItems( this.bricks , isInstant );
35523 this._postLayout();
35525 _layoutItems : function ( items , isInstant)
35527 //this.fireEvent( 'layout', this, items );
35530 if ( !items || !items.elements.length ) {
35531 // no items, emit event with empty array
35536 items.each(function(item) {
35537 Roo.log("layout item");
35539 // get x/y object from method
35540 var position = this._getItemLayoutPosition( item );
35542 position.item = item;
35543 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35544 queue.push( position );
35547 this._processLayoutQueue( queue );
35549 /** Sets position of item in DOM
35550 * @param {Element} item
35551 * @param {Number} x - horizontal position
35552 * @param {Number} y - vertical position
35553 * @param {Boolean} isInstant - disables transitions
35555 _processLayoutQueue : function( queue )
35557 for ( var i=0, len = queue.length; i < len; i++ ) {
35558 var obj = queue[i];
35559 obj.item.position('absolute');
35560 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35566 * Any logic you want to do after each layout,
35567 * i.e. size the container
35569 _postLayout : function()
35571 this.resizeContainer();
35574 resizeContainer : function()
35576 if ( !this.isResizingContainer ) {
35579 var size = this._getContainerSize();
35581 this.el.setSize(size.width,size.height);
35582 this.boxesEl.setSize(size.width,size.height);
35588 _resetLayout : function()
35590 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35591 this.colWidth = this.el.getWidth();
35592 //this.gutter = this.el.getWidth();
35594 this.measureColumns();
35600 this.colYs.push( 0 );
35606 measureColumns : function()
35608 this.getContainerWidth();
35609 // if columnWidth is 0, default to outerWidth of first item
35610 if ( !this.columnWidth ) {
35611 var firstItem = this.bricks.first();
35612 Roo.log(firstItem);
35613 this.columnWidth = this.containerWidth;
35614 if (firstItem && firstItem.attr('originalwidth') ) {
35615 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35617 // columnWidth fall back to item of first element
35618 Roo.log("set column width?");
35619 this.initialColumnWidth = this.columnWidth ;
35621 // if first elem has no width, default to size of container
35626 if (this.initialColumnWidth) {
35627 this.columnWidth = this.initialColumnWidth;
35632 // column width is fixed at the top - however if container width get's smaller we should
35635 // this bit calcs how man columns..
35637 var columnWidth = this.columnWidth += this.gutter;
35639 // calculate columns
35640 var containerWidth = this.containerWidth + this.gutter;
35642 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35643 // fix rounding errors, typically with gutters
35644 var excess = columnWidth - containerWidth % columnWidth;
35647 // if overshoot is less than a pixel, round up, otherwise floor it
35648 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35649 cols = Math[ mathMethod ]( cols );
35650 this.cols = Math.max( cols, 1 );
35651 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35653 // padding positioning..
35654 var totalColWidth = this.cols * this.columnWidth;
35655 var padavail = this.containerWidth - totalColWidth;
35656 // so for 2 columns - we need 3 'pads'
35658 var padNeeded = (1+this.cols) * this.padWidth;
35660 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35662 this.columnWidth += padExtra
35663 //this.padWidth = Math.floor(padavail / ( this.cols));
35665 // adjust colum width so that padding is fixed??
35667 // we have 3 columns ... total = width * 3
35668 // we have X left over... that should be used by
35670 //if (this.expandC) {
35678 getContainerWidth : function()
35680 /* // container is parent if fit width
35681 var container = this.isFitWidth ? this.element.parentNode : this.element;
35682 // check that this.size and size are there
35683 // IE8 triggers resize on body size change, so they might not be
35685 var size = getSize( container ); //FIXME
35686 this.containerWidth = size && size.innerWidth; //FIXME
35689 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35693 _getItemLayoutPosition : function( item ) // what is item?
35695 // we resize the item to our columnWidth..
35697 item.setWidth(this.columnWidth);
35698 item.autoBoxAdjust = false;
35700 var sz = item.getSize();
35702 // how many columns does this brick span
35703 var remainder = this.containerWidth % this.columnWidth;
35705 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35706 // round if off by 1 pixel, otherwise use ceil
35707 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35708 colSpan = Math.min( colSpan, this.cols );
35710 // normally this should be '1' as we dont' currently allow multi width columns..
35712 var colGroup = this._getColGroup( colSpan );
35713 // get the minimum Y value from the columns
35714 var minimumY = Math.min.apply( Math, colGroup );
35715 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35717 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35719 // position the brick
35721 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35722 y: this.currentSize.y + minimumY + this.padHeight
35726 // apply setHeight to necessary columns
35727 var setHeight = minimumY + sz.height + this.padHeight;
35728 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35730 var setSpan = this.cols + 1 - colGroup.length;
35731 for ( var i = 0; i < setSpan; i++ ) {
35732 this.colYs[ shortColIndex + i ] = setHeight ;
35739 * @param {Number} colSpan - number of columns the element spans
35740 * @returns {Array} colGroup
35742 _getColGroup : function( colSpan )
35744 if ( colSpan < 2 ) {
35745 // if brick spans only one column, use all the column Ys
35750 // how many different places could this brick fit horizontally
35751 var groupCount = this.cols + 1 - colSpan;
35752 // for each group potential horizontal position
35753 for ( var i = 0; i < groupCount; i++ ) {
35754 // make an array of colY values for that one group
35755 var groupColYs = this.colYs.slice( i, i + colSpan );
35756 // and get the max value of the array
35757 colGroup[i] = Math.max.apply( Math, groupColYs );
35762 _manageStamp : function( stamp )
35764 var stampSize = stamp.getSize();
35765 var offset = stamp.getBox();
35766 // get the columns that this stamp affects
35767 var firstX = this.isOriginLeft ? offset.x : offset.right;
35768 var lastX = firstX + stampSize.width;
35769 var firstCol = Math.floor( firstX / this.columnWidth );
35770 firstCol = Math.max( 0, firstCol );
35772 var lastCol = Math.floor( lastX / this.columnWidth );
35773 // lastCol should not go over if multiple of columnWidth #425
35774 lastCol -= lastX % this.columnWidth ? 0 : 1;
35775 lastCol = Math.min( this.cols - 1, lastCol );
35777 // set colYs to bottom of the stamp
35778 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35781 for ( var i = firstCol; i <= lastCol; i++ ) {
35782 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35787 _getContainerSize : function()
35789 this.maxY = Math.max.apply( Math, this.colYs );
35794 if ( this.isFitWidth ) {
35795 size.width = this._getContainerFitWidth();
35801 _getContainerFitWidth : function()
35803 var unusedCols = 0;
35804 // count unused columns
35807 if ( this.colYs[i] !== 0 ) {
35812 // fit container to columns that have been used
35813 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35816 needsResizeLayout : function()
35818 var previousWidth = this.containerWidth;
35819 this.getContainerWidth();
35820 return previousWidth !== this.containerWidth;
35835 * @class Roo.bootstrap.MasonryBrick
35836 * @extends Roo.bootstrap.Component
35837 * Bootstrap MasonryBrick class
35840 * Create a new MasonryBrick
35841 * @param {Object} config The config object
35844 Roo.bootstrap.MasonryBrick = function(config){
35846 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35848 Roo.bootstrap.MasonryBrick.register(this);
35854 * When a MasonryBrick is clcik
35855 * @param {Roo.bootstrap.MasonryBrick} this
35856 * @param {Roo.EventObject} e
35862 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35865 * @cfg {String} title
35869 * @cfg {String} html
35873 * @cfg {String} bgimage
35877 * @cfg {String} videourl
35881 * @cfg {String} cls
35885 * @cfg {String} href
35889 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35894 * @cfg {String} placetitle (center|bottom)
35899 * @cfg {Boolean} isFitContainer defalut true
35901 isFitContainer : true,
35904 * @cfg {Boolean} preventDefault defalut false
35906 preventDefault : false,
35909 * @cfg {Boolean} inverse defalut false
35911 maskInverse : false,
35913 getAutoCreate : function()
35915 if(!this.isFitContainer){
35916 return this.getSplitAutoCreate();
35919 var cls = 'masonry-brick masonry-brick-full';
35921 if(this.href.length){
35922 cls += ' masonry-brick-link';
35925 if(this.bgimage.length){
35926 cls += ' masonry-brick-image';
35929 if(this.maskInverse){
35930 cls += ' mask-inverse';
35933 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35934 cls += ' enable-mask';
35938 cls += ' masonry-' + this.size + '-brick';
35941 if(this.placetitle.length){
35943 switch (this.placetitle) {
35945 cls += ' masonry-center-title';
35948 cls += ' masonry-bottom-title';
35955 if(!this.html.length && !this.bgimage.length){
35956 cls += ' masonry-center-title';
35959 if(!this.html.length && this.bgimage.length){
35960 cls += ' masonry-bottom-title';
35965 cls += ' ' + this.cls;
35969 tag: (this.href.length) ? 'a' : 'div',
35974 cls: 'masonry-brick-mask'
35978 cls: 'masonry-brick-paragraph',
35984 if(this.href.length){
35985 cfg.href = this.href;
35988 var cn = cfg.cn[1].cn;
35990 if(this.title.length){
35993 cls: 'masonry-brick-title',
35998 if(this.html.length){
36001 cls: 'masonry-brick-text',
36006 if (!this.title.length && !this.html.length) {
36007 cfg.cn[1].cls += ' hide';
36010 if(this.bgimage.length){
36013 cls: 'masonry-brick-image-view',
36018 if(this.videourl.length){
36019 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36020 // youtube support only?
36023 cls: 'masonry-brick-image-view',
36026 allowfullscreen : true
36034 getSplitAutoCreate : function()
36036 var cls = 'masonry-brick masonry-brick-split';
36038 if(this.href.length){
36039 cls += ' masonry-brick-link';
36042 if(this.bgimage.length){
36043 cls += ' masonry-brick-image';
36047 cls += ' masonry-' + this.size + '-brick';
36050 switch (this.placetitle) {
36052 cls += ' masonry-center-title';
36055 cls += ' masonry-bottom-title';
36058 if(!this.bgimage.length){
36059 cls += ' masonry-center-title';
36062 if(this.bgimage.length){
36063 cls += ' masonry-bottom-title';
36069 cls += ' ' + this.cls;
36073 tag: (this.href.length) ? 'a' : 'div',
36078 cls: 'masonry-brick-split-head',
36082 cls: 'masonry-brick-paragraph',
36089 cls: 'masonry-brick-split-body',
36095 if(this.href.length){
36096 cfg.href = this.href;
36099 if(this.title.length){
36100 cfg.cn[0].cn[0].cn.push({
36102 cls: 'masonry-brick-title',
36107 if(this.html.length){
36108 cfg.cn[1].cn.push({
36110 cls: 'masonry-brick-text',
36115 if(this.bgimage.length){
36116 cfg.cn[0].cn.push({
36118 cls: 'masonry-brick-image-view',
36123 if(this.videourl.length){
36124 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36125 // youtube support only?
36126 cfg.cn[0].cn.cn.push({
36128 cls: 'masonry-brick-image-view',
36131 allowfullscreen : true
36138 initEvents: function()
36140 switch (this.size) {
36173 this.el.on('touchstart', this.onTouchStart, this);
36174 this.el.on('touchmove', this.onTouchMove, this);
36175 this.el.on('touchend', this.onTouchEnd, this);
36176 this.el.on('contextmenu', this.onContextMenu, this);
36178 this.el.on('mouseenter' ,this.enter, this);
36179 this.el.on('mouseleave', this.leave, this);
36180 this.el.on('click', this.onClick, this);
36183 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36184 this.parent().bricks.push(this);
36189 onClick: function(e, el)
36191 var time = this.endTimer - this.startTimer;
36192 // Roo.log(e.preventDefault());
36195 e.preventDefault();
36200 if(!this.preventDefault){
36204 e.preventDefault();
36206 if (this.activeClass != '') {
36207 this.selectBrick();
36210 this.fireEvent('click', this, e);
36213 enter: function(e, el)
36215 e.preventDefault();
36217 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36221 if(this.bgimage.length && this.html.length){
36222 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36226 leave: function(e, el)
36228 e.preventDefault();
36230 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36234 if(this.bgimage.length && this.html.length){
36235 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36239 onTouchStart: function(e, el)
36241 // e.preventDefault();
36243 this.touchmoved = false;
36245 if(!this.isFitContainer){
36249 if(!this.bgimage.length || !this.html.length){
36253 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36255 this.timer = new Date().getTime();
36259 onTouchMove: function(e, el)
36261 this.touchmoved = true;
36264 onContextMenu : function(e,el)
36266 e.preventDefault();
36267 e.stopPropagation();
36271 onTouchEnd: function(e, el)
36273 // e.preventDefault();
36275 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36282 if(!this.bgimage.length || !this.html.length){
36284 if(this.href.length){
36285 window.location.href = this.href;
36291 if(!this.isFitContainer){
36295 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36297 window.location.href = this.href;
36300 //selection on single brick only
36301 selectBrick : function() {
36303 if (!this.parentId) {
36307 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36308 var index = m.selectedBrick.indexOf(this.id);
36311 m.selectedBrick.splice(index,1);
36312 this.el.removeClass(this.activeClass);
36316 for(var i = 0; i < m.selectedBrick.length; i++) {
36317 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36318 b.el.removeClass(b.activeClass);
36321 m.selectedBrick = [];
36323 m.selectedBrick.push(this.id);
36324 this.el.addClass(this.activeClass);
36328 isSelected : function(){
36329 return this.el.hasClass(this.activeClass);
36334 Roo.apply(Roo.bootstrap.MasonryBrick, {
36337 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36339 * register a Masonry Brick
36340 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36343 register : function(brick)
36345 //this.groups[brick.id] = brick;
36346 this.groups.add(brick.id, brick);
36349 * fetch a masonry brick based on the masonry brick ID
36350 * @param {string} the masonry brick to add
36351 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36354 get: function(brick_id)
36356 // if (typeof(this.groups[brick_id]) == 'undefined') {
36359 // return this.groups[brick_id] ;
36361 if(this.groups.key(brick_id)) {
36362 return this.groups.key(brick_id);
36380 * @class Roo.bootstrap.Brick
36381 * @extends Roo.bootstrap.Component
36382 * Bootstrap Brick class
36385 * Create a new Brick
36386 * @param {Object} config The config object
36389 Roo.bootstrap.Brick = function(config){
36390 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36396 * When a Brick is click
36397 * @param {Roo.bootstrap.Brick} this
36398 * @param {Roo.EventObject} e
36404 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36407 * @cfg {String} title
36411 * @cfg {String} html
36415 * @cfg {String} bgimage
36419 * @cfg {String} cls
36423 * @cfg {String} href
36427 * @cfg {String} video
36431 * @cfg {Boolean} square
36435 getAutoCreate : function()
36437 var cls = 'roo-brick';
36439 if(this.href.length){
36440 cls += ' roo-brick-link';
36443 if(this.bgimage.length){
36444 cls += ' roo-brick-image';
36447 if(!this.html.length && !this.bgimage.length){
36448 cls += ' roo-brick-center-title';
36451 if(!this.html.length && this.bgimage.length){
36452 cls += ' roo-brick-bottom-title';
36456 cls += ' ' + this.cls;
36460 tag: (this.href.length) ? 'a' : 'div',
36465 cls: 'roo-brick-paragraph',
36471 if(this.href.length){
36472 cfg.href = this.href;
36475 var cn = cfg.cn[0].cn;
36477 if(this.title.length){
36480 cls: 'roo-brick-title',
36485 if(this.html.length){
36488 cls: 'roo-brick-text',
36495 if(this.bgimage.length){
36498 cls: 'roo-brick-image-view',
36506 initEvents: function()
36508 if(this.title.length || this.html.length){
36509 this.el.on('mouseenter' ,this.enter, this);
36510 this.el.on('mouseleave', this.leave, this);
36513 Roo.EventManager.onWindowResize(this.resize, this);
36515 if(this.bgimage.length){
36516 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36517 this.imageEl.on('load', this.onImageLoad, this);
36524 onImageLoad : function()
36529 resize : function()
36531 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36533 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36535 if(this.bgimage.length){
36536 var image = this.el.select('.roo-brick-image-view', true).first();
36538 image.setWidth(paragraph.getWidth());
36541 image.setHeight(paragraph.getWidth());
36544 this.el.setHeight(image.getHeight());
36545 paragraph.setHeight(image.getHeight());
36551 enter: function(e, el)
36553 e.preventDefault();
36555 if(this.bgimage.length){
36556 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36557 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36561 leave: function(e, el)
36563 e.preventDefault();
36565 if(this.bgimage.length){
36566 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36567 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36582 * @class Roo.bootstrap.form.NumberField
36583 * @extends Roo.bootstrap.form.Input
36584 * Bootstrap NumberField class
36590 * Create a new NumberField
36591 * @param {Object} config The config object
36594 Roo.bootstrap.form.NumberField = function(config){
36595 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36598 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36601 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36603 allowDecimals : true,
36605 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36607 decimalSeparator : ".",
36609 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36611 decimalPrecision : 2,
36613 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36615 allowNegative : true,
36618 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36622 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36624 minValue : Number.NEGATIVE_INFINITY,
36626 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36628 maxValue : Number.MAX_VALUE,
36630 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36632 minText : "The minimum value for this field is {0}",
36634 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36636 maxText : "The maximum value for this field is {0}",
36638 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36639 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36641 nanText : "{0} is not a valid number",
36643 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36645 thousandsDelimiter : false,
36647 * @cfg {String} valueAlign alignment of value
36649 valueAlign : "left",
36651 getAutoCreate : function()
36653 var hiddenInput = {
36657 cls: 'hidden-number-input'
36661 hiddenInput.name = this.name;
36666 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36668 this.name = hiddenInput.name;
36670 if(cfg.cn.length > 0) {
36671 cfg.cn.push(hiddenInput);
36678 initEvents : function()
36680 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36682 var allowed = "0123456789";
36684 if(this.allowDecimals){
36685 allowed += this.decimalSeparator;
36688 if(this.allowNegative){
36692 if(this.thousandsDelimiter) {
36696 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36698 var keyPress = function(e){
36700 var k = e.getKey();
36702 var c = e.getCharCode();
36705 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36706 allowed.indexOf(String.fromCharCode(c)) === -1
36712 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36716 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36721 this.el.on("keypress", keyPress, this);
36724 validateValue : function(value)
36727 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36731 var num = this.parseValue(value);
36734 this.markInvalid(String.format(this.nanText, value));
36738 if(num < this.minValue){
36739 this.markInvalid(String.format(this.minText, this.minValue));
36743 if(num > this.maxValue){
36744 this.markInvalid(String.format(this.maxText, this.maxValue));
36751 getValue : function()
36753 var v = this.hiddenEl().getValue();
36755 return this.fixPrecision(this.parseValue(v));
36758 parseValue : function(value)
36760 if(this.thousandsDelimiter) {
36762 r = new RegExp(",", "g");
36763 value = value.replace(r, "");
36766 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36767 return isNaN(value) ? '' : value;
36770 fixPrecision : function(value)
36772 if(this.thousandsDelimiter) {
36774 r = new RegExp(",", "g");
36775 value = value.replace(r, "");
36778 var nan = isNaN(value);
36780 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36781 return nan ? '' : value;
36783 return parseFloat(value).toFixed(this.decimalPrecision);
36786 setValue : function(v)
36788 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36794 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36796 this.inputEl().dom.value = (v == '') ? '' :
36797 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36799 if(!this.allowZero && v === '0') {
36800 this.hiddenEl().dom.value = '';
36801 this.inputEl().dom.value = '';
36808 decimalPrecisionFcn : function(v)
36810 return Math.floor(v);
36813 beforeBlur : function()
36815 var v = this.parseValue(this.getRawValue());
36817 if(v || v === 0 || v === ''){
36822 hiddenEl : function()
36824 return this.el.select('input.hidden-number-input',true).first();
36836 * @class Roo.bootstrap.DocumentSlider
36837 * @extends Roo.bootstrap.Component
36838 * Bootstrap DocumentSlider class
36841 * Create a new DocumentViewer
36842 * @param {Object} config The config object
36845 Roo.bootstrap.DocumentSlider = function(config){
36846 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36853 * Fire after initEvent
36854 * @param {Roo.bootstrap.DocumentSlider} this
36859 * Fire after update
36860 * @param {Roo.bootstrap.DocumentSlider} this
36866 * @param {Roo.bootstrap.DocumentSlider} this
36872 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36878 getAutoCreate : function()
36882 cls : 'roo-document-slider',
36886 cls : 'roo-document-slider-header',
36890 cls : 'roo-document-slider-header-title'
36896 cls : 'roo-document-slider-body',
36900 cls : 'roo-document-slider-prev',
36904 cls : 'fa fa-chevron-left'
36910 cls : 'roo-document-slider-thumb',
36914 cls : 'roo-document-slider-image'
36920 cls : 'roo-document-slider-next',
36924 cls : 'fa fa-chevron-right'
36936 initEvents : function()
36938 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36939 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36941 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36942 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36944 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36945 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36947 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36948 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36950 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36951 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36953 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36954 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36956 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36957 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36959 this.thumbEl.on('click', this.onClick, this);
36961 this.prevIndicator.on('click', this.prev, this);
36963 this.nextIndicator.on('click', this.next, this);
36967 initial : function()
36969 if(this.files.length){
36970 this.indicator = 1;
36974 this.fireEvent('initial', this);
36977 update : function()
36979 this.imageEl.attr('src', this.files[this.indicator - 1]);
36981 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36983 this.prevIndicator.show();
36985 if(this.indicator == 1){
36986 this.prevIndicator.hide();
36989 this.nextIndicator.show();
36991 if(this.indicator == this.files.length){
36992 this.nextIndicator.hide();
36995 this.thumbEl.scrollTo('top');
36997 this.fireEvent('update', this);
37000 onClick : function(e)
37002 e.preventDefault();
37004 this.fireEvent('click', this);
37009 e.preventDefault();
37011 this.indicator = Math.max(1, this.indicator - 1);
37018 e.preventDefault();
37020 this.indicator = Math.min(this.files.length, this.indicator + 1);
37034 * @class Roo.bootstrap.form.RadioSet
37035 * @extends Roo.bootstrap.form.Input
37036 * @children Roo.bootstrap.form.Radio
37037 * Bootstrap RadioSet class
37038 * @cfg {String} indicatorpos (left|right) default left
37039 * @cfg {Boolean} inline (true|false) inline the element (default true)
37040 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37042 * Create a new RadioSet
37043 * @param {Object} config The config object
37046 Roo.bootstrap.form.RadioSet = function(config){
37048 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37052 Roo.bootstrap.form.RadioSet.register(this);
37057 * Fires when the element is checked or unchecked.
37058 * @param {Roo.bootstrap.form.RadioSet} this This radio
37059 * @param {Roo.bootstrap.form.Radio} item The checked item
37064 * Fires when the element is click.
37065 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37066 * @param {Roo.bootstrap.form.Radio} item The checked item
37067 * @param {Roo.EventObject} e The event object
37074 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37082 indicatorpos : 'left',
37084 getAutoCreate : function()
37088 cls : 'roo-radio-set-label',
37092 html : this.fieldLabel
37096 if (Roo.bootstrap.version == 3) {
37099 if(this.indicatorpos == 'left'){
37102 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37103 tooltip : 'This field is required'
37108 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37109 tooltip : 'This field is required'
37115 cls : 'roo-radio-set-items'
37118 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37120 if (align === 'left' && this.fieldLabel.length) {
37123 cls : "roo-radio-set-right",
37129 if(this.labelWidth > 12){
37130 label.style = "width: " + this.labelWidth + 'px';
37133 if(this.labelWidth < 13 && this.labelmd == 0){
37134 this.labelmd = this.labelWidth;
37137 if(this.labellg > 0){
37138 label.cls += ' col-lg-' + this.labellg;
37139 items.cls += ' col-lg-' + (12 - this.labellg);
37142 if(this.labelmd > 0){
37143 label.cls += ' col-md-' + this.labelmd;
37144 items.cls += ' col-md-' + (12 - this.labelmd);
37147 if(this.labelsm > 0){
37148 label.cls += ' col-sm-' + this.labelsm;
37149 items.cls += ' col-sm-' + (12 - this.labelsm);
37152 if(this.labelxs > 0){
37153 label.cls += ' col-xs-' + this.labelxs;
37154 items.cls += ' col-xs-' + (12 - this.labelxs);
37160 cls : 'roo-radio-set',
37164 cls : 'roo-radio-set-input',
37167 value : this.value ? this.value : ''
37174 if(this.weight.length){
37175 cfg.cls += ' roo-radio-' + this.weight;
37179 cfg.cls += ' roo-radio-set-inline';
37183 ['xs','sm','md','lg'].map(function(size){
37184 if (settings[size]) {
37185 cfg.cls += ' col-' + size + '-' + settings[size];
37193 initEvents : function()
37195 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37196 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37198 if(!this.fieldLabel.length){
37199 this.labelEl.hide();
37202 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37203 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37205 this.indicator = this.indicatorEl();
37207 if(this.indicator){
37208 this.indicator.addClass('invisible');
37211 this.originalValue = this.getValue();
37215 inputEl: function ()
37217 return this.el.select('.roo-radio-set-input', true).first();
37220 getChildContainer : function()
37222 return this.itemsEl;
37225 register : function(item)
37227 this.radioes.push(item);
37231 validate : function()
37233 if(this.getVisibilityEl().hasClass('hidden')){
37239 Roo.each(this.radioes, function(i){
37248 if(this.allowBlank) {
37252 if(this.disabled || valid){
37257 this.markInvalid();
37262 markValid : function()
37264 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37265 this.indicatorEl().removeClass('visible');
37266 this.indicatorEl().addClass('invisible');
37270 if (Roo.bootstrap.version == 3) {
37271 this.el.removeClass([this.invalidClass, this.validClass]);
37272 this.el.addClass(this.validClass);
37274 this.el.removeClass(['is-invalid','is-valid']);
37275 this.el.addClass(['is-valid']);
37277 this.fireEvent('valid', this);
37280 markInvalid : function(msg)
37282 if(this.allowBlank || this.disabled){
37286 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37287 this.indicatorEl().removeClass('invisible');
37288 this.indicatorEl().addClass('visible');
37290 if (Roo.bootstrap.version == 3) {
37291 this.el.removeClass([this.invalidClass, this.validClass]);
37292 this.el.addClass(this.invalidClass);
37294 this.el.removeClass(['is-invalid','is-valid']);
37295 this.el.addClass(['is-invalid']);
37298 this.fireEvent('invalid', this, msg);
37302 setValue : function(v, suppressEvent)
37304 if(this.value === v){
37311 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37314 Roo.each(this.radioes, function(i){
37316 i.el.removeClass('checked');
37319 Roo.each(this.radioes, function(i){
37321 if(i.value === v || i.value.toString() === v.toString()){
37323 i.el.addClass('checked');
37325 if(suppressEvent !== true){
37326 this.fireEvent('check', this, i);
37337 clearInvalid : function(){
37339 if(!this.el || this.preventMark){
37343 this.el.removeClass([this.invalidClass]);
37345 this.fireEvent('valid', this);
37350 Roo.apply(Roo.bootstrap.form.RadioSet, {
37354 register : function(set)
37356 this.groups[set.name] = set;
37359 get: function(name)
37361 if (typeof(this.groups[name]) == 'undefined') {
37365 return this.groups[name] ;
37371 * Ext JS Library 1.1.1
37372 * Copyright(c) 2006-2007, Ext JS, LLC.
37374 * Originally Released Under LGPL - original licence link has changed is not relivant.
37377 * <script type="text/javascript">
37382 * @class Roo.bootstrap.SplitBar
37383 * @extends Roo.util.Observable
37384 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37388 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37389 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37390 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37391 split.minSize = 100;
37392 split.maxSize = 600;
37393 split.animate = true;
37394 split.on('moved', splitterMoved);
37397 * Create a new SplitBar
37398 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37399 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37400 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37401 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37402 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37403 position of the SplitBar).
37405 Roo.bootstrap.SplitBar = function(cfg){
37410 // dragElement : elm
37411 // resizingElement: el,
37413 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37414 // placement : Roo.bootstrap.SplitBar.LEFT ,
37415 // existingProxy ???
37418 this.el = Roo.get(cfg.dragElement, true);
37419 this.el.dom.unselectable = "on";
37421 this.resizingEl = Roo.get(cfg.resizingElement, true);
37425 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37426 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37429 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37432 * The minimum size of the resizing element. (Defaults to 0)
37438 * The maximum size of the resizing element. (Defaults to 2000)
37441 this.maxSize = 2000;
37444 * Whether to animate the transition to the new size
37447 this.animate = false;
37450 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37453 this.useShim = false;
37458 if(!cfg.existingProxy){
37460 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37462 this.proxy = Roo.get(cfg.existingProxy).dom;
37465 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37468 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37471 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37474 this.dragSpecs = {};
37477 * @private The adapter to use to positon and resize elements
37479 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37480 this.adapter.init(this);
37482 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37484 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37485 this.el.addClass("roo-splitbar-h");
37488 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37489 this.el.addClass("roo-splitbar-v");
37495 * Fires when the splitter is moved (alias for {@link #event-moved})
37496 * @param {Roo.bootstrap.SplitBar} this
37497 * @param {Number} newSize the new width or height
37502 * Fires when the splitter is moved
37503 * @param {Roo.bootstrap.SplitBar} this
37504 * @param {Number} newSize the new width or height
37508 * @event beforeresize
37509 * Fires before the splitter is dragged
37510 * @param {Roo.bootstrap.SplitBar} this
37512 "beforeresize" : true,
37514 "beforeapply" : true
37517 Roo.util.Observable.call(this);
37520 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37521 onStartProxyDrag : function(x, y){
37522 this.fireEvent("beforeresize", this);
37524 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37526 o.enableDisplayMode("block");
37527 // all splitbars share the same overlay
37528 Roo.bootstrap.SplitBar.prototype.overlay = o;
37530 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37531 this.overlay.show();
37532 Roo.get(this.proxy).setDisplayed("block");
37533 var size = this.adapter.getElementSize(this);
37534 this.activeMinSize = this.getMinimumSize();;
37535 this.activeMaxSize = this.getMaximumSize();;
37536 var c1 = size - this.activeMinSize;
37537 var c2 = Math.max(this.activeMaxSize - size, 0);
37538 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37539 this.dd.resetConstraints();
37540 this.dd.setXConstraint(
37541 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37542 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37544 this.dd.setYConstraint(0, 0);
37546 this.dd.resetConstraints();
37547 this.dd.setXConstraint(0, 0);
37548 this.dd.setYConstraint(
37549 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37550 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37553 this.dragSpecs.startSize = size;
37554 this.dragSpecs.startPoint = [x, y];
37555 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37559 * @private Called after the drag operation by the DDProxy
37561 onEndProxyDrag : function(e){
37562 Roo.get(this.proxy).setDisplayed(false);
37563 var endPoint = Roo.lib.Event.getXY(e);
37565 this.overlay.hide();
37568 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37569 newSize = this.dragSpecs.startSize +
37570 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37571 endPoint[0] - this.dragSpecs.startPoint[0] :
37572 this.dragSpecs.startPoint[0] - endPoint[0]
37575 newSize = this.dragSpecs.startSize +
37576 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37577 endPoint[1] - this.dragSpecs.startPoint[1] :
37578 this.dragSpecs.startPoint[1] - endPoint[1]
37581 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37582 if(newSize != this.dragSpecs.startSize){
37583 if(this.fireEvent('beforeapply', this, newSize) !== false){
37584 this.adapter.setElementSize(this, newSize);
37585 this.fireEvent("moved", this, newSize);
37586 this.fireEvent("resize", this, newSize);
37592 * Get the adapter this SplitBar uses
37593 * @return The adapter object
37595 getAdapter : function(){
37596 return this.adapter;
37600 * Set the adapter this SplitBar uses
37601 * @param {Object} adapter A SplitBar adapter object
37603 setAdapter : function(adapter){
37604 this.adapter = adapter;
37605 this.adapter.init(this);
37609 * Gets the minimum size for the resizing element
37610 * @return {Number} The minimum size
37612 getMinimumSize : function(){
37613 return this.minSize;
37617 * Sets the minimum size for the resizing element
37618 * @param {Number} minSize The minimum size
37620 setMinimumSize : function(minSize){
37621 this.minSize = minSize;
37625 * Gets the maximum size for the resizing element
37626 * @return {Number} The maximum size
37628 getMaximumSize : function(){
37629 return this.maxSize;
37633 * Sets the maximum size for the resizing element
37634 * @param {Number} maxSize The maximum size
37636 setMaximumSize : function(maxSize){
37637 this.maxSize = maxSize;
37641 * Sets the initialize size for the resizing element
37642 * @param {Number} size The initial size
37644 setCurrentSize : function(size){
37645 var oldAnimate = this.animate;
37646 this.animate = false;
37647 this.adapter.setElementSize(this, size);
37648 this.animate = oldAnimate;
37652 * Destroy this splitbar.
37653 * @param {Boolean} removeEl True to remove the element
37655 destroy : function(removeEl){
37657 this.shim.remove();
37660 this.proxy.parentNode.removeChild(this.proxy);
37668 * @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.
37670 Roo.bootstrap.SplitBar.createProxy = function(dir){
37671 var proxy = new Roo.Element(document.createElement("div"));
37672 proxy.unselectable();
37673 var cls = 'roo-splitbar-proxy';
37674 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37675 document.body.appendChild(proxy.dom);
37680 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37681 * Default Adapter. It assumes the splitter and resizing element are not positioned
37682 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37684 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37687 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37688 // do nothing for now
37689 init : function(s){
37693 * Called before drag operations to get the current size of the resizing element.
37694 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37696 getElementSize : function(s){
37697 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37698 return s.resizingEl.getWidth();
37700 return s.resizingEl.getHeight();
37705 * Called after drag operations to set the size of the resizing element.
37706 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37707 * @param {Number} newSize The new size to set
37708 * @param {Function} onComplete A function to be invoked when resizing is complete
37710 setElementSize : function(s, newSize, onComplete){
37711 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37713 s.resizingEl.setWidth(newSize);
37715 onComplete(s, newSize);
37718 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37723 s.resizingEl.setHeight(newSize);
37725 onComplete(s, newSize);
37728 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37735 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37736 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37737 * Adapter that moves the splitter element to align with the resized sizing element.
37738 * Used with an absolute positioned SplitBar.
37739 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37740 * document.body, make sure you assign an id to the body element.
37742 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37743 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37744 this.container = Roo.get(container);
37747 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37748 init : function(s){
37749 this.basic.init(s);
37752 getElementSize : function(s){
37753 return this.basic.getElementSize(s);
37756 setElementSize : function(s, newSize, onComplete){
37757 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37760 moveSplitter : function(s){
37761 var yes = Roo.bootstrap.SplitBar;
37762 switch(s.placement){
37764 s.el.setX(s.resizingEl.getRight());
37767 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37770 s.el.setY(s.resizingEl.getBottom());
37773 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37780 * Orientation constant - Create a vertical SplitBar
37784 Roo.bootstrap.SplitBar.VERTICAL = 1;
37787 * Orientation constant - Create a horizontal SplitBar
37791 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37794 * Placement constant - The resizing element is to the left of the splitter element
37798 Roo.bootstrap.SplitBar.LEFT = 1;
37801 * Placement constant - The resizing element is to the right of the splitter element
37805 Roo.bootstrap.SplitBar.RIGHT = 2;
37808 * Placement constant - The resizing element is positioned above the splitter element
37812 Roo.bootstrap.SplitBar.TOP = 3;
37815 * Placement constant - The resizing element is positioned under splitter element
37819 Roo.bootstrap.SplitBar.BOTTOM = 4;
37822 * Ext JS Library 1.1.1
37823 * Copyright(c) 2006-2007, Ext JS, LLC.
37825 * Originally Released Under LGPL - original licence link has changed is not relivant.
37828 * <script type="text/javascript">
37832 * @class Roo.bootstrap.layout.Manager
37833 * @extends Roo.bootstrap.Component
37835 * Base class for layout managers.
37837 Roo.bootstrap.layout.Manager = function(config)
37839 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37845 /** false to disable window resize monitoring @type Boolean */
37846 this.monitorWindowResize = true;
37851 * Fires when a layout is performed.
37852 * @param {Roo.LayoutManager} this
37856 * @event regionresized
37857 * Fires when the user resizes a region.
37858 * @param {Roo.LayoutRegion} region The resized region
37859 * @param {Number} newSize The new size (width for east/west, height for north/south)
37861 "regionresized" : true,
37863 * @event regioncollapsed
37864 * Fires when a region is collapsed.
37865 * @param {Roo.LayoutRegion} region The collapsed region
37867 "regioncollapsed" : true,
37869 * @event regionexpanded
37870 * Fires when a region is expanded.
37871 * @param {Roo.LayoutRegion} region The expanded region
37873 "regionexpanded" : true
37875 this.updating = false;
37878 this.el = Roo.get(config.el);
37884 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37889 monitorWindowResize : true,
37895 onRender : function(ct, position)
37898 this.el = Roo.get(ct);
37901 //this.fireEvent('render',this);
37905 initEvents: function()
37909 // ie scrollbar fix
37910 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37911 document.body.scroll = "no";
37912 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37913 this.el.position('relative');
37915 this.id = this.el.id;
37916 this.el.addClass("roo-layout-container");
37917 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37918 if(this.el.dom != document.body ) {
37919 this.el.on('resize', this.layout,this);
37920 this.el.on('show', this.layout,this);
37926 * Returns true if this layout is currently being updated
37927 * @return {Boolean}
37929 isUpdating : function(){
37930 return this.updating;
37934 * Suspend the LayoutManager from doing auto-layouts while
37935 * making multiple add or remove calls
37937 beginUpdate : function(){
37938 this.updating = true;
37942 * Restore auto-layouts and optionally disable the manager from performing a layout
37943 * @param {Boolean} noLayout true to disable a layout update
37945 endUpdate : function(noLayout){
37946 this.updating = false;
37952 layout: function(){
37956 onRegionResized : function(region, newSize){
37957 this.fireEvent("regionresized", region, newSize);
37961 onRegionCollapsed : function(region){
37962 this.fireEvent("regioncollapsed", region);
37965 onRegionExpanded : function(region){
37966 this.fireEvent("regionexpanded", region);
37970 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37971 * performs box-model adjustments.
37972 * @return {Object} The size as an object {width: (the width), height: (the height)}
37974 getViewSize : function()
37977 if(this.el.dom != document.body){
37978 size = this.el.getSize();
37980 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37982 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37983 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37988 * Returns the Element this layout is bound to.
37989 * @return {Roo.Element}
37991 getEl : function(){
37996 * Returns the specified region.
37997 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37998 * @return {Roo.LayoutRegion}
38000 getRegion : function(target){
38001 return this.regions[target.toLowerCase()];
38004 onWindowResize : function(){
38005 if(this.monitorWindowResize){
38012 * Ext JS Library 1.1.1
38013 * Copyright(c) 2006-2007, Ext JS, LLC.
38015 * Originally Released Under LGPL - original licence link has changed is not relivant.
38018 * <script type="text/javascript">
38021 * @class Roo.bootstrap.layout.Border
38022 * @extends Roo.bootstrap.layout.Manager
38024 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38025 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38026 * please see: examples/bootstrap/nested.html<br><br>
38028 <b>The container the layout is rendered into can be either the body element or any other element.
38029 If it is not the body element, the container needs to either be an absolute positioned element,
38030 or you will need to add "position:relative" to the css of the container. You will also need to specify
38031 the container size if it is not the body element.</b>
38034 * Create a new Border
38035 * @param {Object} config Configuration options
38037 Roo.bootstrap.layout.Border = function(config){
38038 config = config || {};
38039 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38043 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38044 if(config[region]){
38045 config[region].region = region;
38046 this.addRegion(config[region]);
38052 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38054 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38057 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38060 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38063 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38066 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38069 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38075 parent : false, // this might point to a 'nest' or a ???
38078 * Creates and adds a new region if it doesn't already exist.
38079 * @param {String} target The target region key (north, south, east, west or center).
38080 * @param {Object} config The regions config object
38081 * @return {BorderLayoutRegion} The new region
38083 addRegion : function(config)
38085 if(!this.regions[config.region]){
38086 var r = this.factory(config);
38087 this.bindRegion(r);
38089 return this.regions[config.region];
38093 bindRegion : function(r){
38094 this.regions[r.config.region] = r;
38096 r.on("visibilitychange", this.layout, this);
38097 r.on("paneladded", this.layout, this);
38098 r.on("panelremoved", this.layout, this);
38099 r.on("invalidated", this.layout, this);
38100 r.on("resized", this.onRegionResized, this);
38101 r.on("collapsed", this.onRegionCollapsed, this);
38102 r.on("expanded", this.onRegionExpanded, this);
38106 * Performs a layout update.
38108 layout : function()
38110 if(this.updating) {
38114 // render all the rebions if they have not been done alreayd?
38115 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38116 if(this.regions[region] && !this.regions[region].bodyEl){
38117 this.regions[region].onRender(this.el)
38121 var size = this.getViewSize();
38122 var w = size.width;
38123 var h = size.height;
38128 //var x = 0, y = 0;
38130 var rs = this.regions;
38131 var north = rs["north"];
38132 var south = rs["south"];
38133 var west = rs["west"];
38134 var east = rs["east"];
38135 var center = rs["center"];
38136 //if(this.hideOnLayout){ // not supported anymore
38137 //c.el.setStyle("display", "none");
38139 if(north && north.isVisible()){
38140 var b = north.getBox();
38141 var m = north.getMargins();
38142 b.width = w - (m.left+m.right);
38145 centerY = b.height + b.y + m.bottom;
38146 centerH -= centerY;
38147 north.updateBox(this.safeBox(b));
38149 if(south && south.isVisible()){
38150 var b = south.getBox();
38151 var m = south.getMargins();
38152 b.width = w - (m.left+m.right);
38154 var totalHeight = (b.height + m.top + m.bottom);
38155 b.y = h - totalHeight + m.top;
38156 centerH -= totalHeight;
38157 south.updateBox(this.safeBox(b));
38159 if(west && west.isVisible()){
38160 var b = west.getBox();
38161 var m = west.getMargins();
38162 b.height = centerH - (m.top+m.bottom);
38164 b.y = centerY + m.top;
38165 var totalWidth = (b.width + m.left + m.right);
38166 centerX += totalWidth;
38167 centerW -= totalWidth;
38168 west.updateBox(this.safeBox(b));
38170 if(east && east.isVisible()){
38171 var b = east.getBox();
38172 var m = east.getMargins();
38173 b.height = centerH - (m.top+m.bottom);
38174 var totalWidth = (b.width + m.left + m.right);
38175 b.x = w - totalWidth + m.left;
38176 b.y = centerY + m.top;
38177 centerW -= totalWidth;
38178 east.updateBox(this.safeBox(b));
38181 var m = center.getMargins();
38183 x: centerX + m.left,
38184 y: centerY + m.top,
38185 width: centerW - (m.left+m.right),
38186 height: centerH - (m.top+m.bottom)
38188 //if(this.hideOnLayout){
38189 //center.el.setStyle("display", "block");
38191 center.updateBox(this.safeBox(centerBox));
38194 this.fireEvent("layout", this);
38198 safeBox : function(box){
38199 box.width = Math.max(0, box.width);
38200 box.height = Math.max(0, box.height);
38205 * Adds a ContentPanel (or subclass) to this layout.
38206 * @param {String} target The target region key (north, south, east, west or center).
38207 * @param {Roo.ContentPanel} panel The panel to add
38208 * @return {Roo.ContentPanel} The added panel
38210 add : function(target, panel){
38212 target = target.toLowerCase();
38213 return this.regions[target].add(panel);
38217 * Remove a ContentPanel (or subclass) to this layout.
38218 * @param {String} target The target region key (north, south, east, west or center).
38219 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38220 * @return {Roo.ContentPanel} The removed panel
38222 remove : function(target, panel){
38223 target = target.toLowerCase();
38224 return this.regions[target].remove(panel);
38228 * Searches all regions for a panel with the specified id
38229 * @param {String} panelId
38230 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38232 findPanel : function(panelId){
38233 var rs = this.regions;
38234 for(var target in rs){
38235 if(typeof rs[target] != "function"){
38236 var p = rs[target].getPanel(panelId);
38246 * Searches all regions for a panel with the specified id and activates (shows) it.
38247 * @param {String/ContentPanel} panelId The panels id or the panel itself
38248 * @return {Roo.ContentPanel} The shown panel or null
38250 showPanel : function(panelId) {
38251 var rs = this.regions;
38252 for(var target in rs){
38253 var r = rs[target];
38254 if(typeof r != "function"){
38255 if(r.hasPanel(panelId)){
38256 return r.showPanel(panelId);
38264 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38265 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38268 restoreState : function(provider){
38270 provider = Roo.state.Manager;
38272 var sm = new Roo.LayoutStateManager();
38273 sm.init(this, provider);
38279 * Adds a xtype elements to the layout.
38283 xtype : 'ContentPanel',
38290 xtype : 'NestedLayoutPanel',
38296 items : [ ... list of content panels or nested layout panels.. ]
38300 * @param {Object} cfg Xtype definition of item to add.
38302 addxtype : function(cfg)
38304 // basically accepts a pannel...
38305 // can accept a layout region..!?!?
38306 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38309 // theory? children can only be panels??
38311 //if (!cfg.xtype.match(/Panel$/)) {
38316 if (typeof(cfg.region) == 'undefined') {
38317 Roo.log("Failed to add Panel, region was not set");
38321 var region = cfg.region;
38327 xitems = cfg.items;
38332 if ( region == 'center') {
38333 Roo.log("Center: " + cfg.title);
38339 case 'Content': // ContentPanel (el, cfg)
38340 case 'Scroll': // ContentPanel (el, cfg)
38342 cfg.autoCreate = cfg.autoCreate || true;
38343 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38345 // var el = this.el.createChild();
38346 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38349 this.add(region, ret);
38353 case 'TreePanel': // our new panel!
38354 cfg.el = this.el.createChild();
38355 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38356 this.add(region, ret);
38361 // create a new Layout (which is a Border Layout...
38363 var clayout = cfg.layout;
38364 clayout.el = this.el.createChild();
38365 clayout.items = clayout.items || [];
38369 // replace this exitems with the clayout ones..
38370 xitems = clayout.items;
38372 // force background off if it's in center...
38373 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38374 cfg.background = false;
38376 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38379 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38380 //console.log('adding nested layout panel ' + cfg.toSource());
38381 this.add(region, ret);
38382 nb = {}; /// find first...
38387 // needs grid and region
38389 //var el = this.getRegion(region).el.createChild();
38391 *var el = this.el.createChild();
38392 // create the grid first...
38393 cfg.grid.container = el;
38394 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38397 if (region == 'center' && this.active ) {
38398 cfg.background = false;
38401 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38403 this.add(region, ret);
38405 if (cfg.background) {
38406 // render grid on panel activation (if panel background)
38407 ret.on('activate', function(gp) {
38408 if (!gp.grid.rendered) {
38409 // gp.grid.render(el);
38413 // cfg.grid.render(el);
38419 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38420 // it was the old xcomponent building that caused this before.
38421 // espeically if border is the top element in the tree.
38431 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38433 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38434 this.add(region, ret);
38438 throw "Can not add '" + cfg.xtype + "' to Border";
38444 this.beginUpdate();
38448 Roo.each(xitems, function(i) {
38449 region = nb && i.region ? i.region : false;
38451 var add = ret.addxtype(i);
38454 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38455 if (!i.background) {
38456 abn[region] = nb[region] ;
38463 // make the last non-background panel active..
38464 //if (nb) { Roo.log(abn); }
38467 for(var r in abn) {
38468 region = this.getRegion(r);
38470 // tried using nb[r], but it does not work..
38472 region.showPanel(abn[r]);
38483 factory : function(cfg)
38486 var validRegions = Roo.bootstrap.layout.Border.regions;
38488 var target = cfg.region;
38491 var r = Roo.bootstrap.layout;
38495 return new r.North(cfg);
38497 return new r.South(cfg);
38499 return new r.East(cfg);
38501 return new r.West(cfg);
38503 return new r.Center(cfg);
38505 throw 'Layout region "'+target+'" not supported.';
38512 * Ext JS Library 1.1.1
38513 * Copyright(c) 2006-2007, Ext JS, LLC.
38515 * Originally Released Under LGPL - original licence link has changed is not relivant.
38518 * <script type="text/javascript">
38522 * @class Roo.bootstrap.layout.Basic
38523 * @extends Roo.util.Observable
38524 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38525 * and does not have a titlebar, tabs or any other features. All it does is size and position
38526 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38527 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38528 * @cfg {string} region the region that it inhabits..
38529 * @cfg {bool} skipConfig skip config?
38533 Roo.bootstrap.layout.Basic = function(config){
38535 this.mgr = config.mgr;
38537 this.position = config.region;
38539 var skipConfig = config.skipConfig;
38543 * @scope Roo.BasicLayoutRegion
38547 * @event beforeremove
38548 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38549 * @param {Roo.LayoutRegion} this
38550 * @param {Roo.ContentPanel} panel The panel
38551 * @param {Object} e The cancel event object
38553 "beforeremove" : true,
38555 * @event invalidated
38556 * Fires when the layout for this region is changed.
38557 * @param {Roo.LayoutRegion} this
38559 "invalidated" : true,
38561 * @event visibilitychange
38562 * Fires when this region is shown or hidden
38563 * @param {Roo.LayoutRegion} this
38564 * @param {Boolean} visibility true or false
38566 "visibilitychange" : true,
38568 * @event paneladded
38569 * Fires when a panel is added.
38570 * @param {Roo.LayoutRegion} this
38571 * @param {Roo.ContentPanel} panel The panel
38573 "paneladded" : true,
38575 * @event panelremoved
38576 * Fires when a panel is removed.
38577 * @param {Roo.LayoutRegion} this
38578 * @param {Roo.ContentPanel} panel The panel
38580 "panelremoved" : true,
38582 * @event beforecollapse
38583 * Fires when this region before collapse.
38584 * @param {Roo.LayoutRegion} this
38586 "beforecollapse" : true,
38589 * Fires when this region is collapsed.
38590 * @param {Roo.LayoutRegion} this
38592 "collapsed" : true,
38595 * Fires when this region is expanded.
38596 * @param {Roo.LayoutRegion} this
38601 * Fires when this region is slid into view.
38602 * @param {Roo.LayoutRegion} this
38604 "slideshow" : true,
38607 * Fires when this region slides out of view.
38608 * @param {Roo.LayoutRegion} this
38610 "slidehide" : true,
38612 * @event panelactivated
38613 * Fires when a panel is activated.
38614 * @param {Roo.LayoutRegion} this
38615 * @param {Roo.ContentPanel} panel The activated panel
38617 "panelactivated" : true,
38620 * Fires when the user resizes this region.
38621 * @param {Roo.LayoutRegion} this
38622 * @param {Number} newSize The new size (width for east/west, height for north/south)
38626 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38627 this.panels = new Roo.util.MixedCollection();
38628 this.panels.getKey = this.getPanelId.createDelegate(this);
38630 this.activePanel = null;
38631 // ensure listeners are added...
38633 if (config.listeners || config.events) {
38634 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38635 listeners : config.listeners || {},
38636 events : config.events || {}
38640 if(skipConfig !== true){
38641 this.applyConfig(config);
38645 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38647 getPanelId : function(p){
38651 applyConfig : function(config){
38652 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38653 this.config = config;
38658 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38659 * the width, for horizontal (north, south) the height.
38660 * @param {Number} newSize The new width or height
38662 resizeTo : function(newSize){
38663 var el = this.el ? this.el :
38664 (this.activePanel ? this.activePanel.getEl() : null);
38666 switch(this.position){
38669 el.setWidth(newSize);
38670 this.fireEvent("resized", this, newSize);
38674 el.setHeight(newSize);
38675 this.fireEvent("resized", this, newSize);
38681 getBox : function(){
38682 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38685 getMargins : function(){
38686 return this.margins;
38689 updateBox : function(box){
38691 var el = this.activePanel.getEl();
38692 el.dom.style.left = box.x + "px";
38693 el.dom.style.top = box.y + "px";
38694 this.activePanel.setSize(box.width, box.height);
38698 * Returns the container element for this region.
38699 * @return {Roo.Element}
38701 getEl : function(){
38702 return this.activePanel;
38706 * Returns true if this region is currently visible.
38707 * @return {Boolean}
38709 isVisible : function(){
38710 return this.activePanel ? true : false;
38713 setActivePanel : function(panel){
38714 panel = this.getPanel(panel);
38715 if(this.activePanel && this.activePanel != panel){
38716 this.activePanel.setActiveState(false);
38717 this.activePanel.getEl().setLeftTop(-10000,-10000);
38719 this.activePanel = panel;
38720 panel.setActiveState(true);
38722 panel.setSize(this.box.width, this.box.height);
38724 this.fireEvent("panelactivated", this, panel);
38725 this.fireEvent("invalidated");
38729 * Show the specified panel.
38730 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38731 * @return {Roo.ContentPanel} The shown panel or null
38733 showPanel : function(panel){
38734 panel = this.getPanel(panel);
38736 this.setActivePanel(panel);
38742 * Get the active panel for this region.
38743 * @return {Roo.ContentPanel} The active panel or null
38745 getActivePanel : function(){
38746 return this.activePanel;
38750 * Add the passed ContentPanel(s)
38751 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38752 * @return {Roo.ContentPanel} The panel added (if only one was added)
38754 add : function(panel){
38755 if(arguments.length > 1){
38756 for(var i = 0, len = arguments.length; i < len; i++) {
38757 this.add(arguments[i]);
38761 if(this.hasPanel(panel)){
38762 this.showPanel(panel);
38765 var el = panel.getEl();
38766 if(el.dom.parentNode != this.mgr.el.dom){
38767 this.mgr.el.dom.appendChild(el.dom);
38769 if(panel.setRegion){
38770 panel.setRegion(this);
38772 this.panels.add(panel);
38773 el.setStyle("position", "absolute");
38774 if(!panel.background){
38775 this.setActivePanel(panel);
38776 if(this.config.initialSize && this.panels.getCount()==1){
38777 this.resizeTo(this.config.initialSize);
38780 this.fireEvent("paneladded", this, panel);
38785 * Returns true if the panel is in this region.
38786 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38787 * @return {Boolean}
38789 hasPanel : function(panel){
38790 if(typeof panel == "object"){ // must be panel obj
38791 panel = panel.getId();
38793 return this.getPanel(panel) ? true : false;
38797 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38798 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38799 * @param {Boolean} preservePanel Overrides the config preservePanel option
38800 * @return {Roo.ContentPanel} The panel that was removed
38802 remove : function(panel, preservePanel){
38803 panel = this.getPanel(panel);
38808 this.fireEvent("beforeremove", this, panel, e);
38809 if(e.cancel === true){
38812 var panelId = panel.getId();
38813 this.panels.removeKey(panelId);
38818 * Returns the panel specified or null if it's not in this region.
38819 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38820 * @return {Roo.ContentPanel}
38822 getPanel : function(id){
38823 if(typeof id == "object"){ // must be panel obj
38826 return this.panels.get(id);
38830 * Returns this regions position (north/south/east/west/center).
38833 getPosition: function(){
38834 return this.position;
38838 * Ext JS Library 1.1.1
38839 * Copyright(c) 2006-2007, Ext JS, LLC.
38841 * Originally Released Under LGPL - original licence link has changed is not relivant.
38844 * <script type="text/javascript">
38848 * @class Roo.bootstrap.layout.Region
38849 * @extends Roo.bootstrap.layout.Basic
38850 * This class represents a region in a layout manager.
38852 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38853 * @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})
38854 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38855 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38856 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38857 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38858 * @cfg {String} title The title for the region (overrides panel titles)
38859 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38860 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38861 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38862 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38863 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38864 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38865 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38866 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38867 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38868 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38870 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38871 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38872 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38873 * @cfg {Number} width For East/West panels
38874 * @cfg {Number} height For North/South panels
38875 * @cfg {Boolean} split To show the splitter
38876 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38878 * @cfg {string} cls Extra CSS classes to add to region
38880 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38881 * @cfg {string} region the region that it inhabits..
38884 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38885 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38887 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38888 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38889 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38891 Roo.bootstrap.layout.Region = function(config)
38893 this.applyConfig(config);
38895 var mgr = config.mgr;
38896 var pos = config.region;
38897 config.skipConfig = true;
38898 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38901 this.onRender(mgr.el);
38904 this.visible = true;
38905 this.collapsed = false;
38906 this.unrendered_panels = [];
38909 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38911 position: '', // set by wrapper (eg. north/south etc..)
38912 unrendered_panels : null, // unrendered panels.
38914 tabPosition : false,
38916 mgr: false, // points to 'Border'
38919 createBody : function(){
38920 /** This region's body element
38921 * @type Roo.Element */
38922 this.bodyEl = this.el.createChild({
38924 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38928 onRender: function(ctr, pos)
38930 var dh = Roo.DomHelper;
38931 /** This region's container element
38932 * @type Roo.Element */
38933 this.el = dh.append(ctr.dom, {
38935 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38937 /** This region's title element
38938 * @type Roo.Element */
38940 this.titleEl = dh.append(this.el.dom, {
38942 unselectable: "on",
38943 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38945 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38946 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38950 this.titleEl.enableDisplayMode();
38951 /** This region's title text element
38952 * @type HTMLElement */
38953 this.titleTextEl = this.titleEl.dom.firstChild;
38954 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38956 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38957 this.closeBtn.enableDisplayMode();
38958 this.closeBtn.on("click", this.closeClicked, this);
38959 this.closeBtn.hide();
38961 this.createBody(this.config);
38962 if(this.config.hideWhenEmpty){
38964 this.on("paneladded", this.validateVisibility, this);
38965 this.on("panelremoved", this.validateVisibility, this);
38967 if(this.autoScroll){
38968 this.bodyEl.setStyle("overflow", "auto");
38970 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38972 //if(c.titlebar !== false){
38973 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38974 this.titleEl.hide();
38976 this.titleEl.show();
38977 if(this.config.title){
38978 this.titleTextEl.innerHTML = this.config.title;
38982 if(this.config.collapsed){
38983 this.collapse(true);
38985 if(this.config.hidden){
38989 if (this.unrendered_panels && this.unrendered_panels.length) {
38990 for (var i =0;i< this.unrendered_panels.length; i++) {
38991 this.add(this.unrendered_panels[i]);
38993 this.unrendered_panels = null;
38999 applyConfig : function(c)
39002 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39003 var dh = Roo.DomHelper;
39004 if(c.titlebar !== false){
39005 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39006 this.collapseBtn.on("click", this.collapse, this);
39007 this.collapseBtn.enableDisplayMode();
39009 if(c.showPin === true || this.showPin){
39010 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39011 this.stickBtn.enableDisplayMode();
39012 this.stickBtn.on("click", this.expand, this);
39013 this.stickBtn.hide();
39018 /** This region's collapsed element
39019 * @type Roo.Element */
39022 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39023 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39026 if(c.floatable !== false){
39027 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39028 this.collapsedEl.on("click", this.collapseClick, this);
39031 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39032 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39033 id: "message", unselectable: "on", style:{"float":"left"}});
39034 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39036 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39037 this.expandBtn.on("click", this.expand, this);
39041 if(this.collapseBtn){
39042 this.collapseBtn.setVisible(c.collapsible == true);
39045 this.cmargins = c.cmargins || this.cmargins ||
39046 (this.position == "west" || this.position == "east" ?
39047 {top: 0, left: 2, right:2, bottom: 0} :
39048 {top: 2, left: 0, right:0, bottom: 2});
39050 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39053 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39055 this.autoScroll = c.autoScroll || false;
39060 this.duration = c.duration || .30;
39061 this.slideDuration = c.slideDuration || .45;
39066 * Returns true if this region is currently visible.
39067 * @return {Boolean}
39069 isVisible : function(){
39070 return this.visible;
39074 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39075 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39077 //setCollapsedTitle : function(title){
39078 // title = title || " ";
39079 // if(this.collapsedTitleTextEl){
39080 // this.collapsedTitleTextEl.innerHTML = title;
39084 getBox : function(){
39086 // if(!this.collapsed){
39087 b = this.el.getBox(false, true);
39089 // b = this.collapsedEl.getBox(false, true);
39094 getMargins : function(){
39095 return this.margins;
39096 //return this.collapsed ? this.cmargins : this.margins;
39099 highlight : function(){
39100 this.el.addClass("x-layout-panel-dragover");
39103 unhighlight : function(){
39104 this.el.removeClass("x-layout-panel-dragover");
39107 updateBox : function(box)
39109 if (!this.bodyEl) {
39110 return; // not rendered yet..
39114 if(!this.collapsed){
39115 this.el.dom.style.left = box.x + "px";
39116 this.el.dom.style.top = box.y + "px";
39117 this.updateBody(box.width, box.height);
39119 this.collapsedEl.dom.style.left = box.x + "px";
39120 this.collapsedEl.dom.style.top = box.y + "px";
39121 this.collapsedEl.setSize(box.width, box.height);
39124 this.tabs.autoSizeTabs();
39128 updateBody : function(w, h)
39131 this.el.setWidth(w);
39132 w -= this.el.getBorderWidth("rl");
39133 if(this.config.adjustments){
39134 w += this.config.adjustments[0];
39137 if(h !== null && h > 0){
39138 this.el.setHeight(h);
39139 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39140 h -= this.el.getBorderWidth("tb");
39141 if(this.config.adjustments){
39142 h += this.config.adjustments[1];
39144 this.bodyEl.setHeight(h);
39146 h = this.tabs.syncHeight(h);
39149 if(this.panelSize){
39150 w = w !== null ? w : this.panelSize.width;
39151 h = h !== null ? h : this.panelSize.height;
39153 if(this.activePanel){
39154 var el = this.activePanel.getEl();
39155 w = w !== null ? w : el.getWidth();
39156 h = h !== null ? h : el.getHeight();
39157 this.panelSize = {width: w, height: h};
39158 this.activePanel.setSize(w, h);
39160 if(Roo.isIE && this.tabs){
39161 this.tabs.el.repaint();
39166 * Returns the container element for this region.
39167 * @return {Roo.Element}
39169 getEl : function(){
39174 * Hides this region.
39177 //if(!this.collapsed){
39178 this.el.dom.style.left = "-2000px";
39181 // this.collapsedEl.dom.style.left = "-2000px";
39182 // this.collapsedEl.hide();
39184 this.visible = false;
39185 this.fireEvent("visibilitychange", this, false);
39189 * Shows this region if it was previously hidden.
39192 //if(!this.collapsed){
39195 // this.collapsedEl.show();
39197 this.visible = true;
39198 this.fireEvent("visibilitychange", this, true);
39201 closeClicked : function(){
39202 if(this.activePanel){
39203 this.remove(this.activePanel);
39207 collapseClick : function(e){
39209 e.stopPropagation();
39212 e.stopPropagation();
39218 * Collapses this region.
39219 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39222 collapse : function(skipAnim, skipCheck = false){
39223 if(this.collapsed) {
39227 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39229 this.collapsed = true;
39231 this.split.el.hide();
39233 if(this.config.animate && skipAnim !== true){
39234 this.fireEvent("invalidated", this);
39235 this.animateCollapse();
39237 this.el.setLocation(-20000,-20000);
39239 this.collapsedEl.show();
39240 this.fireEvent("collapsed", this);
39241 this.fireEvent("invalidated", this);
39247 animateCollapse : function(){
39252 * Expands this region if it was previously collapsed.
39253 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39254 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39257 expand : function(e, skipAnim){
39259 e.stopPropagation();
39261 if(!this.collapsed || this.el.hasActiveFx()) {
39265 this.afterSlideIn();
39268 this.collapsed = false;
39269 if(this.config.animate && skipAnim !== true){
39270 this.animateExpand();
39274 this.split.el.show();
39276 this.collapsedEl.setLocation(-2000,-2000);
39277 this.collapsedEl.hide();
39278 this.fireEvent("invalidated", this);
39279 this.fireEvent("expanded", this);
39283 animateExpand : function(){
39287 initTabs : function()
39289 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39291 var ts = new Roo.bootstrap.panel.Tabs({
39292 el: this.bodyEl.dom,
39294 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39295 disableTooltips: this.config.disableTabTips,
39296 toolbar : this.config.toolbar
39299 if(this.config.hideTabs){
39300 ts.stripWrap.setDisplayed(false);
39303 ts.resizeTabs = this.config.resizeTabs === true;
39304 ts.minTabWidth = this.config.minTabWidth || 40;
39305 ts.maxTabWidth = this.config.maxTabWidth || 250;
39306 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39307 ts.monitorResize = false;
39308 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39309 ts.bodyEl.addClass('roo-layout-tabs-body');
39310 this.panels.each(this.initPanelAsTab, this);
39313 initPanelAsTab : function(panel){
39314 var ti = this.tabs.addTab(
39318 this.config.closeOnTab && panel.isClosable(),
39321 if(panel.tabTip !== undefined){
39322 ti.setTooltip(panel.tabTip);
39324 ti.on("activate", function(){
39325 this.setActivePanel(panel);
39328 if(this.config.closeOnTab){
39329 ti.on("beforeclose", function(t, e){
39331 this.remove(panel);
39335 panel.tabItem = ti;
39340 updatePanelTitle : function(panel, title)
39342 if(this.activePanel == panel){
39343 this.updateTitle(title);
39346 var ti = this.tabs.getTab(panel.getEl().id);
39348 if(panel.tabTip !== undefined){
39349 ti.setTooltip(panel.tabTip);
39354 updateTitle : function(title){
39355 if(this.titleTextEl && !this.config.title){
39356 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39360 setActivePanel : function(panel)
39362 panel = this.getPanel(panel);
39363 if(this.activePanel && this.activePanel != panel){
39364 if(this.activePanel.setActiveState(false) === false){
39368 this.activePanel = panel;
39369 panel.setActiveState(true);
39370 if(this.panelSize){
39371 panel.setSize(this.panelSize.width, this.panelSize.height);
39374 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39376 this.updateTitle(panel.getTitle());
39378 this.fireEvent("invalidated", this);
39380 this.fireEvent("panelactivated", this, panel);
39384 * Shows the specified panel.
39385 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39386 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39388 showPanel : function(panel)
39390 panel = this.getPanel(panel);
39393 var tab = this.tabs.getTab(panel.getEl().id);
39394 if(tab.isHidden()){
39395 this.tabs.unhideTab(tab.id);
39399 this.setActivePanel(panel);
39406 * Get the active panel for this region.
39407 * @return {Roo.ContentPanel} The active panel or null
39409 getActivePanel : function(){
39410 return this.activePanel;
39413 validateVisibility : function(){
39414 if(this.panels.getCount() < 1){
39415 this.updateTitle(" ");
39416 this.closeBtn.hide();
39419 if(!this.isVisible()){
39426 * Adds the passed ContentPanel(s) to this region.
39427 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39428 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39430 add : function(panel)
39432 if(arguments.length > 1){
39433 for(var i = 0, len = arguments.length; i < len; i++) {
39434 this.add(arguments[i]);
39439 // if we have not been rendered yet, then we can not really do much of this..
39440 if (!this.bodyEl) {
39441 this.unrendered_panels.push(panel);
39448 if(this.hasPanel(panel)){
39449 this.showPanel(panel);
39452 panel.setRegion(this);
39453 this.panels.add(panel);
39454 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39455 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39456 // and hide them... ???
39457 this.bodyEl.dom.appendChild(panel.getEl().dom);
39458 if(panel.background !== true){
39459 this.setActivePanel(panel);
39461 this.fireEvent("paneladded", this, panel);
39468 this.initPanelAsTab(panel);
39472 if(panel.background !== true){
39473 this.tabs.activate(panel.getEl().id);
39475 this.fireEvent("paneladded", this, panel);
39480 * Hides the tab for the specified panel.
39481 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39483 hidePanel : function(panel){
39484 if(this.tabs && (panel = this.getPanel(panel))){
39485 this.tabs.hideTab(panel.getEl().id);
39490 * Unhides the tab for a previously hidden panel.
39491 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39493 unhidePanel : function(panel){
39494 if(this.tabs && (panel = this.getPanel(panel))){
39495 this.tabs.unhideTab(panel.getEl().id);
39499 clearPanels : function(){
39500 while(this.panels.getCount() > 0){
39501 this.remove(this.panels.first());
39506 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39507 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39508 * @param {Boolean} preservePanel Overrides the config preservePanel option
39509 * @return {Roo.ContentPanel} The panel that was removed
39511 remove : function(panel, preservePanel)
39513 panel = this.getPanel(panel);
39518 this.fireEvent("beforeremove", this, panel, e);
39519 if(e.cancel === true){
39522 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39523 var panelId = panel.getId();
39524 this.panels.removeKey(panelId);
39526 document.body.appendChild(panel.getEl().dom);
39529 this.tabs.removeTab(panel.getEl().id);
39530 }else if (!preservePanel){
39531 this.bodyEl.dom.removeChild(panel.getEl().dom);
39533 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39534 var p = this.panels.first();
39535 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39536 tempEl.appendChild(p.getEl().dom);
39537 this.bodyEl.update("");
39538 this.bodyEl.dom.appendChild(p.getEl().dom);
39540 this.updateTitle(p.getTitle());
39542 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39543 this.setActivePanel(p);
39545 panel.setRegion(null);
39546 if(this.activePanel == panel){
39547 this.activePanel = null;
39549 if(this.config.autoDestroy !== false && preservePanel !== true){
39550 try{panel.destroy();}catch(e){}
39552 this.fireEvent("panelremoved", this, panel);
39557 * Returns the TabPanel component used by this region
39558 * @return {Roo.TabPanel}
39560 getTabs : function(){
39564 createTool : function(parentEl, className){
39565 var btn = Roo.DomHelper.append(parentEl, {
39567 cls: "x-layout-tools-button",
39570 cls: "roo-layout-tools-button-inner " + className,
39574 btn.addClassOnOver("roo-layout-tools-button-over");
39579 * Ext JS Library 1.1.1
39580 * Copyright(c) 2006-2007, Ext JS, LLC.
39582 * Originally Released Under LGPL - original licence link has changed is not relivant.
39585 * <script type="text/javascript">
39591 * @class Roo.SplitLayoutRegion
39592 * @extends Roo.LayoutRegion
39593 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39595 Roo.bootstrap.layout.Split = function(config){
39596 this.cursor = config.cursor;
39597 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39600 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39602 splitTip : "Drag to resize.",
39603 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39604 useSplitTips : false,
39606 applyConfig : function(config){
39607 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39610 onRender : function(ctr,pos) {
39612 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39613 if(!this.config.split){
39618 var splitEl = Roo.DomHelper.append(ctr.dom, {
39620 id: this.el.id + "-split",
39621 cls: "roo-layout-split roo-layout-split-"+this.position,
39624 /** The SplitBar for this region
39625 * @type Roo.SplitBar */
39626 // does not exist yet...
39627 Roo.log([this.position, this.orientation]);
39629 this.split = new Roo.bootstrap.SplitBar({
39630 dragElement : splitEl,
39631 resizingElement: this.el,
39632 orientation : this.orientation
39635 this.split.on("moved", this.onSplitMove, this);
39636 this.split.useShim = this.config.useShim === true;
39637 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39638 if(this.useSplitTips){
39639 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39641 //if(config.collapsible){
39642 // this.split.el.on("dblclick", this.collapse, this);
39645 if(typeof this.config.minSize != "undefined"){
39646 this.split.minSize = this.config.minSize;
39648 if(typeof this.config.maxSize != "undefined"){
39649 this.split.maxSize = this.config.maxSize;
39651 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39652 this.hideSplitter();
39657 getHMaxSize : function(){
39658 var cmax = this.config.maxSize || 10000;
39659 var center = this.mgr.getRegion("center");
39660 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39663 getVMaxSize : function(){
39664 var cmax = this.config.maxSize || 10000;
39665 var center = this.mgr.getRegion("center");
39666 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39669 onSplitMove : function(split, newSize){
39670 this.fireEvent("resized", this, newSize);
39674 * Returns the {@link Roo.SplitBar} for this region.
39675 * @return {Roo.SplitBar}
39677 getSplitBar : function(){
39682 this.hideSplitter();
39683 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39686 hideSplitter : function(){
39688 this.split.el.setLocation(-2000,-2000);
39689 this.split.el.hide();
39695 this.split.el.show();
39697 Roo.bootstrap.layout.Split.superclass.show.call(this);
39700 beforeSlide: function(){
39701 if(Roo.isGecko){// firefox overflow auto bug workaround
39702 this.bodyEl.clip();
39704 this.tabs.bodyEl.clip();
39706 if(this.activePanel){
39707 this.activePanel.getEl().clip();
39709 if(this.activePanel.beforeSlide){
39710 this.activePanel.beforeSlide();
39716 afterSlide : function(){
39717 if(Roo.isGecko){// firefox overflow auto bug workaround
39718 this.bodyEl.unclip();
39720 this.tabs.bodyEl.unclip();
39722 if(this.activePanel){
39723 this.activePanel.getEl().unclip();
39724 if(this.activePanel.afterSlide){
39725 this.activePanel.afterSlide();
39731 initAutoHide : function(){
39732 if(this.autoHide !== false){
39733 if(!this.autoHideHd){
39734 var st = new Roo.util.DelayedTask(this.slideIn, this);
39735 this.autoHideHd = {
39736 "mouseout": function(e){
39737 if(!e.within(this.el, true)){
39741 "mouseover" : function(e){
39747 this.el.on(this.autoHideHd);
39751 clearAutoHide : function(){
39752 if(this.autoHide !== false){
39753 this.el.un("mouseout", this.autoHideHd.mouseout);
39754 this.el.un("mouseover", this.autoHideHd.mouseover);
39758 clearMonitor : function(){
39759 Roo.get(document).un("click", this.slideInIf, this);
39762 // these names are backwards but not changed for compat
39763 slideOut : function(){
39764 if(this.isSlid || this.el.hasActiveFx()){
39767 this.isSlid = true;
39768 if(this.collapseBtn){
39769 this.collapseBtn.hide();
39771 this.closeBtnState = this.closeBtn.getStyle('display');
39772 this.closeBtn.hide();
39774 this.stickBtn.show();
39777 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39778 this.beforeSlide();
39779 this.el.setStyle("z-index", 10001);
39780 this.el.slideIn(this.getSlideAnchor(), {
39781 callback: function(){
39783 this.initAutoHide();
39784 Roo.get(document).on("click", this.slideInIf, this);
39785 this.fireEvent("slideshow", this);
39792 afterSlideIn : function(){
39793 this.clearAutoHide();
39794 this.isSlid = false;
39795 this.clearMonitor();
39796 this.el.setStyle("z-index", "");
39797 if(this.collapseBtn){
39798 this.collapseBtn.show();
39800 this.closeBtn.setStyle('display', this.closeBtnState);
39802 this.stickBtn.hide();
39804 this.fireEvent("slidehide", this);
39807 slideIn : function(cb){
39808 if(!this.isSlid || this.el.hasActiveFx()){
39812 this.isSlid = false;
39813 this.beforeSlide();
39814 this.el.slideOut(this.getSlideAnchor(), {
39815 callback: function(){
39816 this.el.setLeftTop(-10000, -10000);
39818 this.afterSlideIn();
39826 slideInIf : function(e){
39827 if(!e.within(this.el)){
39832 animateCollapse : function(){
39833 this.beforeSlide();
39834 this.el.setStyle("z-index", 20000);
39835 var anchor = this.getSlideAnchor();
39836 this.el.slideOut(anchor, {
39837 callback : function(){
39838 this.el.setStyle("z-index", "");
39839 this.collapsedEl.slideIn(anchor, {duration:.3});
39841 this.el.setLocation(-10000,-10000);
39843 this.fireEvent("collapsed", this);
39850 animateExpand : function(){
39851 this.beforeSlide();
39852 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39853 this.el.setStyle("z-index", 20000);
39854 this.collapsedEl.hide({
39857 this.el.slideIn(this.getSlideAnchor(), {
39858 callback : function(){
39859 this.el.setStyle("z-index", "");
39862 this.split.el.show();
39864 this.fireEvent("invalidated", this);
39865 this.fireEvent("expanded", this);
39893 getAnchor : function(){
39894 return this.anchors[this.position];
39897 getCollapseAnchor : function(){
39898 return this.canchors[this.position];
39901 getSlideAnchor : function(){
39902 return this.sanchors[this.position];
39905 getAlignAdj : function(){
39906 var cm = this.cmargins;
39907 switch(this.position){
39923 getExpandAdj : function(){
39924 var c = this.collapsedEl, cm = this.cmargins;
39925 switch(this.position){
39927 return [-(cm.right+c.getWidth()+cm.left), 0];
39930 return [cm.right+c.getWidth()+cm.left, 0];
39933 return [0, -(cm.top+cm.bottom+c.getHeight())];
39936 return [0, cm.top+cm.bottom+c.getHeight()];
39942 * Ext JS Library 1.1.1
39943 * Copyright(c) 2006-2007, Ext JS, LLC.
39945 * Originally Released Under LGPL - original licence link has changed is not relivant.
39948 * <script type="text/javascript">
39951 * These classes are private internal classes
39953 Roo.bootstrap.layout.Center = function(config){
39954 config.region = "center";
39955 Roo.bootstrap.layout.Region.call(this, config);
39956 this.visible = true;
39957 this.minWidth = config.minWidth || 20;
39958 this.minHeight = config.minHeight || 20;
39961 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39963 // center panel can't be hidden
39967 // center panel can't be hidden
39970 getMinWidth: function(){
39971 return this.minWidth;
39974 getMinHeight: function(){
39975 return this.minHeight;
39989 Roo.bootstrap.layout.North = function(config)
39991 config.region = 'north';
39992 config.cursor = 'n-resize';
39994 Roo.bootstrap.layout.Split.call(this, config);
39998 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39999 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40000 this.split.el.addClass("roo-layout-split-v");
40002 //var size = config.initialSize || config.height;
40003 //if(this.el && typeof size != "undefined"){
40004 // this.el.setHeight(size);
40007 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40009 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40012 onRender : function(ctr, pos)
40014 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40015 var size = this.config.initialSize || this.config.height;
40016 if(this.el && typeof size != "undefined"){
40017 this.el.setHeight(size);
40022 getBox : function(){
40023 if(this.collapsed){
40024 return this.collapsedEl.getBox();
40026 var box = this.el.getBox();
40028 box.height += this.split.el.getHeight();
40033 updateBox : function(box){
40034 if(this.split && !this.collapsed){
40035 box.height -= this.split.el.getHeight();
40036 this.split.el.setLeft(box.x);
40037 this.split.el.setTop(box.y+box.height);
40038 this.split.el.setWidth(box.width);
40040 if(this.collapsed){
40041 this.updateBody(box.width, null);
40043 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40051 Roo.bootstrap.layout.South = function(config){
40052 config.region = 'south';
40053 config.cursor = 's-resize';
40054 Roo.bootstrap.layout.Split.call(this, config);
40056 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40057 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40058 this.split.el.addClass("roo-layout-split-v");
40063 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40064 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40066 onRender : function(ctr, pos)
40068 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40069 var size = this.config.initialSize || this.config.height;
40070 if(this.el && typeof size != "undefined"){
40071 this.el.setHeight(size);
40076 getBox : function(){
40077 if(this.collapsed){
40078 return this.collapsedEl.getBox();
40080 var box = this.el.getBox();
40082 var sh = this.split.el.getHeight();
40089 updateBox : function(box){
40090 if(this.split && !this.collapsed){
40091 var sh = this.split.el.getHeight();
40094 this.split.el.setLeft(box.x);
40095 this.split.el.setTop(box.y-sh);
40096 this.split.el.setWidth(box.width);
40098 if(this.collapsed){
40099 this.updateBody(box.width, null);
40101 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40105 Roo.bootstrap.layout.East = function(config){
40106 config.region = "east";
40107 config.cursor = "e-resize";
40108 Roo.bootstrap.layout.Split.call(this, config);
40110 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40111 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40112 this.split.el.addClass("roo-layout-split-h");
40116 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40117 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40119 onRender : function(ctr, pos)
40121 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40122 var size = this.config.initialSize || this.config.width;
40123 if(this.el && typeof size != "undefined"){
40124 this.el.setWidth(size);
40129 getBox : function(){
40130 if(this.collapsed){
40131 return this.collapsedEl.getBox();
40133 var box = this.el.getBox();
40135 var sw = this.split.el.getWidth();
40142 updateBox : function(box){
40143 if(this.split && !this.collapsed){
40144 var sw = this.split.el.getWidth();
40146 this.split.el.setLeft(box.x);
40147 this.split.el.setTop(box.y);
40148 this.split.el.setHeight(box.height);
40151 if(this.collapsed){
40152 this.updateBody(null, box.height);
40154 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40158 Roo.bootstrap.layout.West = function(config){
40159 config.region = "west";
40160 config.cursor = "w-resize";
40162 Roo.bootstrap.layout.Split.call(this, config);
40164 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40165 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40166 this.split.el.addClass("roo-layout-split-h");
40170 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40171 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40173 onRender: function(ctr, pos)
40175 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40176 var size = this.config.initialSize || this.config.width;
40177 if(typeof size != "undefined"){
40178 this.el.setWidth(size);
40182 getBox : function(){
40183 if(this.collapsed){
40184 return this.collapsedEl.getBox();
40186 var box = this.el.getBox();
40187 if (box.width == 0) {
40188 box.width = this.config.width; // kludge?
40191 box.width += this.split.el.getWidth();
40196 updateBox : function(box){
40197 if(this.split && !this.collapsed){
40198 var sw = this.split.el.getWidth();
40200 this.split.el.setLeft(box.x+box.width);
40201 this.split.el.setTop(box.y);
40202 this.split.el.setHeight(box.height);
40204 if(this.collapsed){
40205 this.updateBody(null, box.height);
40207 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40211 * Ext JS Library 1.1.1
40212 * Copyright(c) 2006-2007, Ext JS, LLC.
40214 * Originally Released Under LGPL - original licence link has changed is not relivant.
40217 * <script type="text/javascript">
40220 * @class Roo.bootstrap.paenl.Content
40221 * @extends Roo.util.Observable
40223 * @children Roo.bootstrap.Component
40224 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40225 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40226 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40227 * @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
40228 * @cfg {Boolean} closable True if the panel can be closed/removed
40229 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40230 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40231 * @cfg {Toolbar} toolbar A toolbar for this panel
40232 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40233 * @cfg {String} title The title for this panel
40234 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40235 * @cfg {String} url Calls {@link #setUrl} with this value
40236 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40237 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40238 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40239 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40240 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40241 * @cfg {Boolean} badges render the badges
40242 * @cfg {String} cls extra classes to use
40243 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40246 * Create a new ContentPanel.
40247 * @param {String/Object} config A string to set only the title or a config object
40250 Roo.bootstrap.panel.Content = function( config){
40252 this.tpl = config.tpl || false;
40254 var el = config.el;
40255 var content = config.content;
40257 if(config.autoCreate){ // xtype is available if this is called from factory
40260 this.el = Roo.get(el);
40261 if(!this.el && config && config.autoCreate){
40262 if(typeof config.autoCreate == "object"){
40263 if(!config.autoCreate.id){
40264 config.autoCreate.id = config.id||el;
40266 this.el = Roo.DomHelper.append(document.body,
40267 config.autoCreate, true);
40271 cls: (config.cls || '') +
40272 (config.background ? ' bg-' + config.background : '') +
40273 " roo-layout-inactive-content",
40276 if (config.iframe) {
40280 style : 'border: 0px',
40281 src : 'about:blank'
40287 elcfg.html = config.html;
40291 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40292 if (config.iframe) {
40293 this.iframeEl = this.el.select('iframe',true).first();
40298 this.closable = false;
40299 this.loaded = false;
40300 this.active = false;
40303 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40305 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40307 this.wrapEl = this.el; //this.el.wrap();
40309 if (config.toolbar.items) {
40310 ti = config.toolbar.items ;
40311 delete config.toolbar.items ;
40315 this.toolbar.render(this.wrapEl, 'before');
40316 for(var i =0;i < ti.length;i++) {
40317 // Roo.log(['add child', items[i]]);
40318 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40320 this.toolbar.items = nitems;
40321 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40322 delete config.toolbar;
40326 // xtype created footer. - not sure if will work as we normally have to render first..
40327 if (this.footer && !this.footer.el && this.footer.xtype) {
40328 if (!this.wrapEl) {
40329 this.wrapEl = this.el.wrap();
40332 this.footer.container = this.wrapEl.createChild();
40334 this.footer = Roo.factory(this.footer, Roo);
40339 if(typeof config == "string"){
40340 this.title = config;
40342 Roo.apply(this, config);
40346 this.resizeEl = Roo.get(this.resizeEl, true);
40348 this.resizeEl = this.el;
40350 // handle view.xtype
40358 * Fires when this panel is activated.
40359 * @param {Roo.ContentPanel} this
40363 * @event deactivate
40364 * Fires when this panel is activated.
40365 * @param {Roo.ContentPanel} this
40367 "deactivate" : true,
40371 * Fires when this panel is resized if fitToFrame is true.
40372 * @param {Roo.ContentPanel} this
40373 * @param {Number} width The width after any component adjustments
40374 * @param {Number} height The height after any component adjustments
40380 * Fires when this tab is created
40381 * @param {Roo.ContentPanel} this
40387 * Fires when this content is scrolled
40388 * @param {Roo.ContentPanel} this
40389 * @param {Event} scrollEvent
40400 if(this.autoScroll && !this.iframe){
40401 this.resizeEl.setStyle("overflow", "auto");
40402 this.resizeEl.on('scroll', this.onScroll, this);
40404 // fix randome scrolling
40405 //this.el.on('scroll', function() {
40406 // Roo.log('fix random scolling');
40407 // this.scrollTo('top',0);
40410 content = content || this.content;
40412 this.setContent(content);
40414 if(config && config.url){
40415 this.setUrl(this.url, this.params, this.loadOnce);
40420 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40422 if (this.view && typeof(this.view.xtype) != 'undefined') {
40423 this.view.el = this.el.appendChild(document.createElement("div"));
40424 this.view = Roo.factory(this.view);
40425 this.view.render && this.view.render(false, '');
40429 this.fireEvent('render', this);
40432 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40442 /* Resize Element - use this to work out scroll etc. */
40445 setRegion : function(region){
40446 this.region = region;
40447 this.setActiveClass(region && !this.background);
40451 setActiveClass: function(state)
40454 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40455 this.el.setStyle('position','relative');
40457 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40458 this.el.setStyle('position', 'absolute');
40463 * Returns the toolbar for this Panel if one was configured.
40464 * @return {Roo.Toolbar}
40466 getToolbar : function(){
40467 return this.toolbar;
40470 setActiveState : function(active)
40472 this.active = active;
40473 this.setActiveClass(active);
40475 if(this.fireEvent("deactivate", this) === false){
40480 this.fireEvent("activate", this);
40484 * Updates this panel's element (not for iframe)
40485 * @param {String} content The new content
40486 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40488 setContent : function(content, loadScripts){
40493 this.el.update(content, loadScripts);
40496 ignoreResize : function(w, h){
40497 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40500 this.lastSize = {width: w, height: h};
40505 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40506 * @return {Roo.UpdateManager} The UpdateManager
40508 getUpdateManager : function(){
40512 return this.el.getUpdateManager();
40515 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40516 * Does not work with IFRAME contents
40517 * @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:
40520 url: "your-url.php",
40521 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40522 callback: yourFunction,
40523 scope: yourObject, //(optional scope)
40526 text: "Loading...",
40532 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40533 * 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.
40534 * @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}
40535 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40536 * @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.
40537 * @return {Roo.ContentPanel} this
40545 var um = this.el.getUpdateManager();
40546 um.update.apply(um, arguments);
40552 * 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.
40553 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40554 * @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)
40555 * @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)
40556 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40558 setUrl : function(url, params, loadOnce){
40560 this.iframeEl.dom.src = url;
40564 if(this.refreshDelegate){
40565 this.removeListener("activate", this.refreshDelegate);
40567 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40568 this.on("activate", this.refreshDelegate);
40569 return this.el.getUpdateManager();
40572 _handleRefresh : function(url, params, loadOnce){
40573 if(!loadOnce || !this.loaded){
40574 var updater = this.el.getUpdateManager();
40575 updater.update(url, params, this._setLoaded.createDelegate(this));
40579 _setLoaded : function(){
40580 this.loaded = true;
40584 * Returns this panel's id
40587 getId : function(){
40592 * Returns this panel's element - used by regiosn to add.
40593 * @return {Roo.Element}
40595 getEl : function(){
40596 return this.wrapEl || this.el;
40601 adjustForComponents : function(width, height)
40603 //Roo.log('adjustForComponents ');
40604 if(this.resizeEl != this.el){
40605 width -= this.el.getFrameWidth('lr');
40606 height -= this.el.getFrameWidth('tb');
40609 var te = this.toolbar.getEl();
40610 te.setWidth(width);
40611 height -= te.getHeight();
40614 var te = this.footer.getEl();
40615 te.setWidth(width);
40616 height -= te.getHeight();
40620 if(this.adjustments){
40621 width += this.adjustments[0];
40622 height += this.adjustments[1];
40624 return {"width": width, "height": height};
40627 setSize : function(width, height){
40628 if(this.fitToFrame && !this.ignoreResize(width, height)){
40629 if(this.fitContainer && this.resizeEl != this.el){
40630 this.el.setSize(width, height);
40632 var size = this.adjustForComponents(width, height);
40634 this.iframeEl.setSize(width,height);
40637 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40638 this.fireEvent('resize', this, size.width, size.height);
40645 * Returns this panel's title
40648 getTitle : function(){
40650 if (typeof(this.title) != 'object') {
40655 for (var k in this.title) {
40656 if (!this.title.hasOwnProperty(k)) {
40660 if (k.indexOf('-') >= 0) {
40661 var s = k.split('-');
40662 for (var i = 0; i<s.length; i++) {
40663 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40666 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40673 * Set this panel's title
40674 * @param {String} title
40676 setTitle : function(title){
40677 this.title = title;
40679 this.region.updatePanelTitle(this, title);
40684 * Returns true is this panel was configured to be closable
40685 * @return {Boolean}
40687 isClosable : function(){
40688 return this.closable;
40691 beforeSlide : function(){
40693 this.resizeEl.clip();
40696 afterSlide : function(){
40698 this.resizeEl.unclip();
40702 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40703 * Will fail silently if the {@link #setUrl} method has not been called.
40704 * This does not activate the panel, just updates its content.
40706 refresh : function(){
40707 if(this.refreshDelegate){
40708 this.loaded = false;
40709 this.refreshDelegate();
40714 * Destroys this panel
40716 destroy : function(){
40717 this.el.removeAllListeners();
40718 var tempEl = document.createElement("span");
40719 tempEl.appendChild(this.el.dom);
40720 tempEl.innerHTML = "";
40726 * form - if the content panel contains a form - this is a reference to it.
40727 * @type {Roo.form.Form}
40731 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40732 * This contains a reference to it.
40738 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40748 * @param {Object} cfg Xtype definition of item to add.
40752 getChildContainer: function () {
40753 return this.getEl();
40757 onScroll : function(e)
40759 this.fireEvent('scroll', this, e);
40764 var ret = new Roo.factory(cfg);
40769 if (cfg.xtype.match(/^Form$/)) {
40772 //if (this.footer) {
40773 // el = this.footer.container.insertSibling(false, 'before');
40775 el = this.el.createChild();
40778 this.form = new Roo.form.Form(cfg);
40781 if ( this.form.allItems.length) {
40782 this.form.render(el.dom);
40786 // should only have one of theses..
40787 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40788 // views.. should not be just added - used named prop 'view''
40790 cfg.el = this.el.appendChild(document.createElement("div"));
40793 var ret = new Roo.factory(cfg);
40795 ret.render && ret.render(false, ''); // render blank..
40805 * @class Roo.bootstrap.panel.Grid
40806 * @extends Roo.bootstrap.panel.Content
40808 * Create a new GridPanel.
40809 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40810 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40811 * @param {Object} config A the config object
40817 Roo.bootstrap.panel.Grid = function(config)
40821 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40822 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40824 config.el = this.wrapper;
40825 //this.el = this.wrapper;
40827 if (config.container) {
40828 // ctor'ed from a Border/panel.grid
40831 this.wrapper.setStyle("overflow", "hidden");
40832 this.wrapper.addClass('roo-grid-container');
40837 if(config.toolbar){
40838 var tool_el = this.wrapper.createChild();
40839 this.toolbar = Roo.factory(config.toolbar);
40841 if (config.toolbar.items) {
40842 ti = config.toolbar.items ;
40843 delete config.toolbar.items ;
40847 this.toolbar.render(tool_el);
40848 for(var i =0;i < ti.length;i++) {
40849 // Roo.log(['add child', items[i]]);
40850 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40852 this.toolbar.items = nitems;
40854 delete config.toolbar;
40857 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40858 config.grid.scrollBody = true;;
40859 config.grid.monitorWindowResize = false; // turn off autosizing
40860 config.grid.autoHeight = false;
40861 config.grid.autoWidth = false;
40863 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40865 if (config.background) {
40866 // render grid on panel activation (if panel background)
40867 this.on('activate', function(gp) {
40868 if (!gp.grid.rendered) {
40869 gp.grid.render(this.wrapper);
40870 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40875 this.grid.render(this.wrapper);
40876 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40879 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40880 // ??? needed ??? config.el = this.wrapper;
40885 // xtype created footer. - not sure if will work as we normally have to render first..
40886 if (this.footer && !this.footer.el && this.footer.xtype) {
40888 var ctr = this.grid.getView().getFooterPanel(true);
40889 this.footer.dataSource = this.grid.dataSource;
40890 this.footer = Roo.factory(this.footer, Roo);
40891 this.footer.render(ctr);
40901 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40902 getId : function(){
40903 return this.grid.id;
40907 * Returns the grid for this panel
40908 * @return {Roo.bootstrap.Table}
40910 getGrid : function(){
40914 setSize : function(width, height){
40915 if(!this.ignoreResize(width, height)){
40916 var grid = this.grid;
40917 var size = this.adjustForComponents(width, height);
40918 // tfoot is not a footer?
40921 var gridel = grid.getGridEl();
40922 gridel.setSize(size.width, size.height);
40924 var tbd = grid.getGridEl().select('tbody', true).first();
40925 var thd = grid.getGridEl().select('thead',true).first();
40926 var tbf= grid.getGridEl().select('tfoot', true).first();
40929 size.height -= tbf.getHeight();
40932 size.height -= thd.getHeight();
40935 tbd.setSize(size.width, size.height );
40936 // this is for the account management tab -seems to work there.
40937 var thd = grid.getGridEl().select('thead',true).first();
40939 // tbd.setSize(size.width, size.height - thd.getHeight());
40948 beforeSlide : function(){
40949 this.grid.getView().scroller.clip();
40952 afterSlide : function(){
40953 this.grid.getView().scroller.unclip();
40956 destroy : function(){
40957 this.grid.destroy();
40959 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40964 * @class Roo.bootstrap.panel.Nest
40965 * @extends Roo.bootstrap.panel.Content
40967 * Create a new Panel, that can contain a layout.Border.
40970 * @param {String/Object} config A string to set only the title or a config object
40972 Roo.bootstrap.panel.Nest = function(config)
40974 // construct with only one argument..
40975 /* FIXME - implement nicer consturctors
40976 if (layout.layout) {
40978 layout = config.layout;
40979 delete config.layout;
40981 if (layout.xtype && !layout.getEl) {
40982 // then layout needs constructing..
40983 layout = Roo.factory(layout, Roo);
40987 config.el = config.layout.getEl();
40989 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40991 config.layout.monitorWindowResize = false; // turn off autosizing
40992 this.layout = config.layout;
40993 this.layout.getEl().addClass("roo-layout-nested-layout");
40994 this.layout.parent = this;
41001 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41003 * @cfg {Roo.BorderLayout} layout The layout for this panel
41007 setSize : function(width, height){
41008 if(!this.ignoreResize(width, height)){
41009 var size = this.adjustForComponents(width, height);
41010 var el = this.layout.getEl();
41011 if (size.height < 1) {
41012 el.setWidth(size.width);
41014 el.setSize(size.width, size.height);
41016 var touch = el.dom.offsetWidth;
41017 this.layout.layout();
41018 // ie requires a double layout on the first pass
41019 if(Roo.isIE && !this.initialized){
41020 this.initialized = true;
41021 this.layout.layout();
41026 // activate all subpanels if not currently active..
41028 setActiveState : function(active){
41029 this.active = active;
41030 this.setActiveClass(active);
41033 this.fireEvent("deactivate", this);
41037 this.fireEvent("activate", this);
41038 // not sure if this should happen before or after..
41039 if (!this.layout) {
41040 return; // should not happen..
41043 for (var r in this.layout.regions) {
41044 reg = this.layout.getRegion(r);
41045 if (reg.getActivePanel()) {
41046 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41047 reg.setActivePanel(reg.getActivePanel());
41050 if (!reg.panels.length) {
41053 reg.showPanel(reg.getPanel(0));
41062 * Returns the nested BorderLayout for this panel
41063 * @return {Roo.BorderLayout}
41065 getLayout : function(){
41066 return this.layout;
41070 * Adds a xtype elements to the layout of the nested panel
41074 xtype : 'ContentPanel',
41081 xtype : 'NestedLayoutPanel',
41087 items : [ ... list of content panels or nested layout panels.. ]
41091 * @param {Object} cfg Xtype definition of item to add.
41093 addxtype : function(cfg) {
41094 return this.layout.addxtype(cfg);
41099 * Ext JS Library 1.1.1
41100 * Copyright(c) 2006-2007, Ext JS, LLC.
41102 * Originally Released Under LGPL - original licence link has changed is not relivant.
41105 * <script type="text/javascript">
41108 * @class Roo.TabPanel
41109 * @extends Roo.util.Observable
41110 * A lightweight tab container.
41114 // basic tabs 1, built from existing content
41115 var tabs = new Roo.TabPanel("tabs1");
41116 tabs.addTab("script", "View Script");
41117 tabs.addTab("markup", "View Markup");
41118 tabs.activate("script");
41120 // more advanced tabs, built from javascript
41121 var jtabs = new Roo.TabPanel("jtabs");
41122 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41124 // set up the UpdateManager
41125 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41126 var updater = tab2.getUpdateManager();
41127 updater.setDefaultUrl("ajax1.htm");
41128 tab2.on('activate', updater.refresh, updater, true);
41130 // Use setUrl for Ajax loading
41131 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41132 tab3.setUrl("ajax2.htm", null, true);
41135 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41138 jtabs.activate("jtabs-1");
41141 * Create a new TabPanel.
41142 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41143 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41145 Roo.bootstrap.panel.Tabs = function(config){
41147 * The container element for this TabPanel.
41148 * @type Roo.Element
41150 this.el = Roo.get(config.el);
41153 if(typeof config == "boolean"){
41154 this.tabPosition = config ? "bottom" : "top";
41156 Roo.apply(this, config);
41160 if(this.tabPosition == "bottom"){
41161 // if tabs are at the bottom = create the body first.
41162 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41163 this.el.addClass("roo-tabs-bottom");
41165 // next create the tabs holders
41167 if (this.tabPosition == "west"){
41169 var reg = this.region; // fake it..
41171 if (!reg.mgr.parent) {
41174 reg = reg.mgr.parent.region;
41176 Roo.log("got nest?");
41178 if (reg.mgr.getRegion('west')) {
41179 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41180 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41181 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41182 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41183 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41191 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41192 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41193 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41194 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41199 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41202 // finally - if tabs are at the top, then create the body last..
41203 if(this.tabPosition != "bottom"){
41204 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41205 * @type Roo.Element
41207 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41208 this.el.addClass("roo-tabs-top");
41212 this.bodyEl.setStyle("position", "relative");
41214 this.active = null;
41215 this.activateDelegate = this.activate.createDelegate(this);
41220 * Fires when the active tab changes
41221 * @param {Roo.TabPanel} this
41222 * @param {Roo.TabPanelItem} activePanel The new active tab
41226 * @event beforetabchange
41227 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41228 * @param {Roo.TabPanel} this
41229 * @param {Object} e Set cancel to true on this object to cancel the tab change
41230 * @param {Roo.TabPanelItem} tab The tab being changed to
41232 "beforetabchange" : true
41235 Roo.EventManager.onWindowResize(this.onResize, this);
41236 this.cpad = this.el.getPadding("lr");
41237 this.hiddenCount = 0;
41240 // toolbar on the tabbar support...
41241 if (this.toolbar) {
41242 alert("no toolbar support yet");
41243 this.toolbar = false;
41245 var tcfg = this.toolbar;
41246 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41247 this.toolbar = new Roo.Toolbar(tcfg);
41248 if (Roo.isSafari) {
41249 var tbl = tcfg.container.child('table', true);
41250 tbl.setAttribute('width', '100%');
41258 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41261 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41263 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41265 tabPosition : "top",
41267 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41269 currentTabWidth : 0,
41271 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41275 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41279 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41281 preferredTabWidth : 175,
41283 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41285 resizeTabs : false,
41287 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41289 monitorResize : true,
41291 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41293 toolbar : false, // set by caller..
41295 region : false, /// set by caller
41297 disableTooltips : true, // not used yet...
41300 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41301 * @param {String} id The id of the div to use <b>or create</b>
41302 * @param {String} text The text for the tab
41303 * @param {String} content (optional) Content to put in the TabPanelItem body
41304 * @param {Boolean} closable (optional) True to create a close icon on the tab
41305 * @return {Roo.TabPanelItem} The created TabPanelItem
41307 addTab : function(id, text, content, closable, tpl)
41309 var item = new Roo.bootstrap.panel.TabItem({
41313 closable : closable,
41316 this.addTabItem(item);
41318 item.setContent(content);
41324 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41325 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41326 * @return {Roo.TabPanelItem}
41328 getTab : function(id){
41329 return this.items[id];
41333 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41334 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41336 hideTab : function(id){
41337 var t = this.items[id];
41340 this.hiddenCount++;
41341 this.autoSizeTabs();
41346 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41347 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41349 unhideTab : function(id){
41350 var t = this.items[id];
41352 t.setHidden(false);
41353 this.hiddenCount--;
41354 this.autoSizeTabs();
41359 * Adds an existing {@link Roo.TabPanelItem}.
41360 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41362 addTabItem : function(item)
41364 this.items[item.id] = item;
41365 this.items.push(item);
41366 this.autoSizeTabs();
41367 // if(this.resizeTabs){
41368 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41369 // this.autoSizeTabs();
41371 // item.autoSize();
41376 * Removes a {@link Roo.TabPanelItem}.
41377 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41379 removeTab : function(id){
41380 var items = this.items;
41381 var tab = items[id];
41382 if(!tab) { return; }
41383 var index = items.indexOf(tab);
41384 if(this.active == tab && items.length > 1){
41385 var newTab = this.getNextAvailable(index);
41390 this.stripEl.dom.removeChild(tab.pnode.dom);
41391 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41392 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41394 items.splice(index, 1);
41395 delete this.items[tab.id];
41396 tab.fireEvent("close", tab);
41397 tab.purgeListeners();
41398 this.autoSizeTabs();
41401 getNextAvailable : function(start){
41402 var items = this.items;
41404 // look for a next tab that will slide over to
41405 // replace the one being removed
41406 while(index < items.length){
41407 var item = items[++index];
41408 if(item && !item.isHidden()){
41412 // if one isn't found select the previous tab (on the left)
41415 var item = items[--index];
41416 if(item && !item.isHidden()){
41424 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41425 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41427 disableTab : function(id){
41428 var tab = this.items[id];
41429 if(tab && this.active != tab){
41435 * Enables a {@link Roo.TabPanelItem} that is disabled.
41436 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41438 enableTab : function(id){
41439 var tab = this.items[id];
41444 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41445 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41446 * @return {Roo.TabPanelItem} The TabPanelItem.
41448 activate : function(id)
41450 //Roo.log('activite:' + id);
41452 var tab = this.items[id];
41456 if(tab == this.active || tab.disabled){
41460 this.fireEvent("beforetabchange", this, e, tab);
41461 if(e.cancel !== true && !tab.disabled){
41463 this.active.hide();
41465 this.active = this.items[id];
41466 this.active.show();
41467 this.fireEvent("tabchange", this, this.active);
41473 * Gets the active {@link Roo.TabPanelItem}.
41474 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41476 getActiveTab : function(){
41477 return this.active;
41481 * Updates the tab body element to fit the height of the container element
41482 * for overflow scrolling
41483 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41485 syncHeight : function(targetHeight){
41486 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41487 var bm = this.bodyEl.getMargins();
41488 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41489 this.bodyEl.setHeight(newHeight);
41493 onResize : function(){
41494 if(this.monitorResize){
41495 this.autoSizeTabs();
41500 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41502 beginUpdate : function(){
41503 this.updating = true;
41507 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41509 endUpdate : function(){
41510 this.updating = false;
41511 this.autoSizeTabs();
41515 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41517 autoSizeTabs : function()
41519 var count = this.items.length;
41520 var vcount = count - this.hiddenCount;
41523 this.stripEl.hide();
41525 this.stripEl.show();
41528 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41533 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41534 var availWidth = Math.floor(w / vcount);
41535 var b = this.stripBody;
41536 if(b.getWidth() > w){
41537 var tabs = this.items;
41538 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41539 if(availWidth < this.minTabWidth){
41540 /*if(!this.sleft){ // incomplete scrolling code
41541 this.createScrollButtons();
41544 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41547 if(this.currentTabWidth < this.preferredTabWidth){
41548 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41554 * Returns the number of tabs in this TabPanel.
41557 getCount : function(){
41558 return this.items.length;
41562 * Resizes all the tabs to the passed width
41563 * @param {Number} The new width
41565 setTabWidth : function(width){
41566 this.currentTabWidth = width;
41567 for(var i = 0, len = this.items.length; i < len; i++) {
41568 if(!this.items[i].isHidden()) {
41569 this.items[i].setWidth(width);
41575 * Destroys this TabPanel
41576 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41578 destroy : function(removeEl){
41579 Roo.EventManager.removeResizeListener(this.onResize, this);
41580 for(var i = 0, len = this.items.length; i < len; i++){
41581 this.items[i].purgeListeners();
41583 if(removeEl === true){
41584 this.el.update("");
41589 createStrip : function(container)
41591 var strip = document.createElement("nav");
41592 strip.className = Roo.bootstrap.version == 4 ?
41593 "navbar-light bg-light" :
41594 "navbar navbar-default"; //"x-tabs-wrap";
41595 container.appendChild(strip);
41599 createStripList : function(strip)
41601 // div wrapper for retard IE
41602 // returns the "tr" element.
41603 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41604 //'<div class="x-tabs-strip-wrap">'+
41605 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41606 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41607 return strip.firstChild; //.firstChild.firstChild.firstChild;
41609 createBody : function(container)
41611 var body = document.createElement("div");
41612 Roo.id(body, "tab-body");
41613 //Roo.fly(body).addClass("x-tabs-body");
41614 Roo.fly(body).addClass("tab-content");
41615 container.appendChild(body);
41618 createItemBody :function(bodyEl, id){
41619 var body = Roo.getDom(id);
41621 body = document.createElement("div");
41624 //Roo.fly(body).addClass("x-tabs-item-body");
41625 Roo.fly(body).addClass("tab-pane");
41626 bodyEl.insertBefore(body, bodyEl.firstChild);
41630 createStripElements : function(stripEl, text, closable, tpl)
41632 var td = document.createElement("li"); // was td..
41633 td.className = 'nav-item';
41635 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41638 stripEl.appendChild(td);
41640 td.className = "x-tabs-closable";
41641 if(!this.closeTpl){
41642 this.closeTpl = new Roo.Template(
41643 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41644 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41645 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41648 var el = this.closeTpl.overwrite(td, {"text": text});
41649 var close = el.getElementsByTagName("div")[0];
41650 var inner = el.getElementsByTagName("em")[0];
41651 return {"el": el, "close": close, "inner": inner};
41654 // not sure what this is..
41655 // if(!this.tabTpl){
41656 //this.tabTpl = new Roo.Template(
41657 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41658 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41660 // this.tabTpl = new Roo.Template(
41661 // '<a href="#">' +
41662 // '<span unselectable="on"' +
41663 // (this.disableTooltips ? '' : ' title="{text}"') +
41664 // ' >{text}</span></a>'
41670 var template = tpl || this.tabTpl || false;
41673 template = new Roo.Template(
41674 Roo.bootstrap.version == 4 ?
41676 '<a class="nav-link" href="#" unselectable="on"' +
41677 (this.disableTooltips ? '' : ' title="{text}"') +
41680 '<a class="nav-link" href="#">' +
41681 '<span unselectable="on"' +
41682 (this.disableTooltips ? '' : ' title="{text}"') +
41683 ' >{text}</span></a>'
41688 switch (typeof(template)) {
41692 template = new Roo.Template(template);
41698 var el = template.overwrite(td, {"text": text});
41700 var inner = el.getElementsByTagName("span")[0];
41702 return {"el": el, "inner": inner};
41710 * @class Roo.TabPanelItem
41711 * @extends Roo.util.Observable
41712 * Represents an individual item (tab plus body) in a TabPanel.
41713 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41714 * @param {String} id The id of this TabPanelItem
41715 * @param {String} text The text for the tab of this TabPanelItem
41716 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41718 Roo.bootstrap.panel.TabItem = function(config){
41720 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41721 * @type Roo.TabPanel
41723 this.tabPanel = config.panel;
41725 * The id for this TabPanelItem
41728 this.id = config.id;
41730 this.disabled = false;
41732 this.text = config.text;
41734 this.loaded = false;
41735 this.closable = config.closable;
41738 * The body element for this TabPanelItem.
41739 * @type Roo.Element
41741 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41742 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41743 this.bodyEl.setStyle("display", "block");
41744 this.bodyEl.setStyle("zoom", "1");
41745 //this.hideAction();
41747 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41749 this.el = Roo.get(els.el);
41750 this.inner = Roo.get(els.inner, true);
41751 this.textEl = Roo.bootstrap.version == 4 ?
41752 this.el : Roo.get(this.el.dom.firstChild, true);
41754 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41755 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41758 // this.el.on("mousedown", this.onTabMouseDown, this);
41759 this.el.on("click", this.onTabClick, this);
41761 if(config.closable){
41762 var c = Roo.get(els.close, true);
41763 c.dom.title = this.closeText;
41764 c.addClassOnOver("close-over");
41765 c.on("click", this.closeClick, this);
41771 * Fires when this tab becomes the active tab.
41772 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41773 * @param {Roo.TabPanelItem} this
41777 * @event beforeclose
41778 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41779 * @param {Roo.TabPanelItem} this
41780 * @param {Object} e Set cancel to true on this object to cancel the close.
41782 "beforeclose": true,
41785 * Fires when this tab is closed.
41786 * @param {Roo.TabPanelItem} this
41790 * @event deactivate
41791 * Fires when this tab is no longer the active tab.
41792 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41793 * @param {Roo.TabPanelItem} this
41795 "deactivate" : true
41797 this.hidden = false;
41799 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41802 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41804 purgeListeners : function(){
41805 Roo.util.Observable.prototype.purgeListeners.call(this);
41806 this.el.removeAllListeners();
41809 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41812 this.status_node.addClass("active");
41815 this.tabPanel.stripWrap.repaint();
41817 this.fireEvent("activate", this.tabPanel, this);
41821 * Returns true if this tab is the active tab.
41822 * @return {Boolean}
41824 isActive : function(){
41825 return this.tabPanel.getActiveTab() == this;
41829 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41832 this.status_node.removeClass("active");
41834 this.fireEvent("deactivate", this.tabPanel, this);
41837 hideAction : function(){
41838 this.bodyEl.hide();
41839 this.bodyEl.setStyle("position", "absolute");
41840 this.bodyEl.setLeft("-20000px");
41841 this.bodyEl.setTop("-20000px");
41844 showAction : function(){
41845 this.bodyEl.setStyle("position", "relative");
41846 this.bodyEl.setTop("");
41847 this.bodyEl.setLeft("");
41848 this.bodyEl.show();
41852 * Set the tooltip for the tab.
41853 * @param {String} tooltip The tab's tooltip
41855 setTooltip : function(text){
41856 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41857 this.textEl.dom.qtip = text;
41858 this.textEl.dom.removeAttribute('title');
41860 this.textEl.dom.title = text;
41864 onTabClick : function(e){
41865 e.preventDefault();
41866 this.tabPanel.activate(this.id);
41869 onTabMouseDown : function(e){
41870 e.preventDefault();
41871 this.tabPanel.activate(this.id);
41874 getWidth : function(){
41875 return this.inner.getWidth();
41878 setWidth : function(width){
41879 var iwidth = width - this.linode.getPadding("lr");
41880 this.inner.setWidth(iwidth);
41881 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41882 this.linode.setWidth(width);
41886 * Show or hide the tab
41887 * @param {Boolean} hidden True to hide or false to show.
41889 setHidden : function(hidden){
41890 this.hidden = hidden;
41891 this.linode.setStyle("display", hidden ? "none" : "");
41895 * Returns true if this tab is "hidden"
41896 * @return {Boolean}
41898 isHidden : function(){
41899 return this.hidden;
41903 * Returns the text for this tab
41906 getText : function(){
41910 autoSize : function(){
41911 //this.el.beginMeasure();
41912 this.textEl.setWidth(1);
41914 * #2804 [new] Tabs in Roojs
41915 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41917 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41918 //this.el.endMeasure();
41922 * Sets the text for the tab (Note: this also sets the tooltip text)
41923 * @param {String} text The tab's text and tooltip
41925 setText : function(text){
41927 this.textEl.update(text);
41928 this.setTooltip(text);
41929 //if(!this.tabPanel.resizeTabs){
41930 // this.autoSize();
41934 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41936 activate : function(){
41937 this.tabPanel.activate(this.id);
41941 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41943 disable : function(){
41944 if(this.tabPanel.active != this){
41945 this.disabled = true;
41946 this.status_node.addClass("disabled");
41951 * Enables this TabPanelItem if it was previously disabled.
41953 enable : function(){
41954 this.disabled = false;
41955 this.status_node.removeClass("disabled");
41959 * Sets the content for this TabPanelItem.
41960 * @param {String} content The content
41961 * @param {Boolean} loadScripts true to look for and load scripts
41963 setContent : function(content, loadScripts){
41964 this.bodyEl.update(content, loadScripts);
41968 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41969 * @return {Roo.UpdateManager} The UpdateManager
41971 getUpdateManager : function(){
41972 return this.bodyEl.getUpdateManager();
41976 * Set a URL to be used to load the content for this TabPanelItem.
41977 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41978 * @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)
41979 * @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)
41980 * @return {Roo.UpdateManager} The UpdateManager
41982 setUrl : function(url, params, loadOnce){
41983 if(this.refreshDelegate){
41984 this.un('activate', this.refreshDelegate);
41986 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41987 this.on("activate", this.refreshDelegate);
41988 return this.bodyEl.getUpdateManager();
41992 _handleRefresh : function(url, params, loadOnce){
41993 if(!loadOnce || !this.loaded){
41994 var updater = this.bodyEl.getUpdateManager();
41995 updater.update(url, params, this._setLoaded.createDelegate(this));
42000 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42001 * Will fail silently if the setUrl method has not been called.
42002 * This does not activate the panel, just updates its content.
42004 refresh : function(){
42005 if(this.refreshDelegate){
42006 this.loaded = false;
42007 this.refreshDelegate();
42012 _setLoaded : function(){
42013 this.loaded = true;
42017 closeClick : function(e){
42020 this.fireEvent("beforeclose", this, o);
42021 if(o.cancel !== true){
42022 this.tabPanel.removeTab(this.id);
42026 * The text displayed in the tooltip for the close icon.
42029 closeText : "Close this tab"
42032 * This script refer to:
42033 * Title: International Telephone Input
42034 * Author: Jack O'Connor
42035 * Code version: v12.1.12
42036 * Availability: https://github.com/jackocnr/intl-tel-input.git
42039 Roo.bootstrap.form.PhoneInputData = function() {
42042 "Afghanistan (افغانستان)",
42047 "Albania (Shqipëri)",
42052 "Algeria (الجزائر)",
42077 "Antigua and Barbuda",
42087 "Armenia (Հայաստան)",
42103 "Austria (Österreich)",
42108 "Azerbaijan (Azərbaycan)",
42118 "Bahrain (البحرين)",
42123 "Bangladesh (বাংলাদেশ)",
42133 "Belarus (Беларусь)",
42138 "Belgium (België)",
42168 "Bosnia and Herzegovina (Босна и Херцеговина)",
42183 "British Indian Ocean Territory",
42188 "British Virgin Islands",
42198 "Bulgaria (България)",
42208 "Burundi (Uburundi)",
42213 "Cambodia (កម្ពុជា)",
42218 "Cameroon (Cameroun)",
42227 ["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"]
42230 "Cape Verde (Kabu Verdi)",
42235 "Caribbean Netherlands",
42246 "Central African Republic (République centrafricaine)",
42266 "Christmas Island",
42272 "Cocos (Keeling) Islands",
42283 "Comoros (جزر القمر)",
42288 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42293 "Congo (Republic) (Congo-Brazzaville)",
42313 "Croatia (Hrvatska)",
42334 "Czech Republic (Česká republika)",
42339 "Denmark (Danmark)",
42354 "Dominican Republic (República Dominicana)",
42358 ["809", "829", "849"]
42376 "Equatorial Guinea (Guinea Ecuatorial)",
42396 "Falkland Islands (Islas Malvinas)",
42401 "Faroe Islands (Føroyar)",
42422 "French Guiana (Guyane française)",
42427 "French Polynesia (Polynésie française)",
42442 "Georgia (საქართველო)",
42447 "Germany (Deutschland)",
42467 "Greenland (Kalaallit Nunaat)",
42504 "Guinea-Bissau (Guiné Bissau)",
42529 "Hungary (Magyarország)",
42534 "Iceland (Ísland)",
42554 "Iraq (العراق)",
42570 "Israel (ישראל)",
42597 "Jordan (الأردن)",
42602 "Kazakhstan (Казахстан)",
42623 "Kuwait (الكويت)",
42628 "Kyrgyzstan (Кыргызстан)",
42638 "Latvia (Latvija)",
42643 "Lebanon (لبنان)",
42658 "Libya (ليبيا)",
42668 "Lithuania (Lietuva)",
42683 "Macedonia (FYROM) (Македонија)",
42688 "Madagascar (Madagasikara)",
42718 "Marshall Islands",
42728 "Mauritania (موريتانيا)",
42733 "Mauritius (Moris)",
42754 "Moldova (Republica Moldova)",
42764 "Mongolia (Монгол)",
42769 "Montenegro (Crna Gora)",
42779 "Morocco (المغرب)",
42785 "Mozambique (Moçambique)",
42790 "Myanmar (Burma) (မြန်မာ)",
42795 "Namibia (Namibië)",
42810 "Netherlands (Nederland)",
42815 "New Caledonia (Nouvelle-Calédonie)",
42850 "North Korea (조선 민주주의 인민 공화국)",
42855 "Northern Mariana Islands",
42871 "Pakistan (پاکستان)",
42881 "Palestine (فلسطين)",
42891 "Papua New Guinea",
42933 "Réunion (La Réunion)",
42939 "Romania (România)",
42955 "Saint Barthélemy",
42966 "Saint Kitts and Nevis",
42976 "Saint Martin (Saint-Martin (partie française))",
42982 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42987 "Saint Vincent and the Grenadines",
43002 "São Tomé and Príncipe (São Tomé e Príncipe)",
43007 "Saudi Arabia (المملكة العربية السعودية)",
43012 "Senegal (Sénégal)",
43042 "Slovakia (Slovensko)",
43047 "Slovenia (Slovenija)",
43057 "Somalia (Soomaaliya)",
43067 "South Korea (대한민국)",
43072 "South Sudan (جنوب السودان)",
43082 "Sri Lanka (ශ්රී ලංකාව)",
43087 "Sudan (السودان)",
43097 "Svalbard and Jan Mayen",
43108 "Sweden (Sverige)",
43113 "Switzerland (Schweiz)",
43118 "Syria (سوريا)",
43163 "Trinidad and Tobago",
43168 "Tunisia (تونس)",
43173 "Turkey (Türkiye)",
43183 "Turks and Caicos Islands",
43193 "U.S. Virgin Islands",
43203 "Ukraine (Україна)",
43208 "United Arab Emirates (الإمارات العربية المتحدة)",
43230 "Uzbekistan (Oʻzbekiston)",
43240 "Vatican City (Città del Vaticano)",
43251 "Vietnam (Việt Nam)",
43256 "Wallis and Futuna (Wallis-et-Futuna)",
43261 "Western Sahara (الصحراء الغربية)",
43267 "Yemen (اليمن)",
43291 * This script refer to:
43292 * Title: International Telephone Input
43293 * Author: Jack O'Connor
43294 * Code version: v12.1.12
43295 * Availability: https://github.com/jackocnr/intl-tel-input.git
43299 * @class Roo.bootstrap.form.PhoneInput
43300 * @extends Roo.bootstrap.form.TriggerField
43301 * An input with International dial-code selection
43303 * @cfg {String} defaultDialCode default '+852'
43304 * @cfg {Array} preferedCountries default []
43307 * Create a new PhoneInput.
43308 * @param {Object} config Configuration options
43311 Roo.bootstrap.form.PhoneInput = function(config) {
43312 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43315 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43317 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43319 listWidth: undefined,
43321 selectedClass: 'active',
43323 invalidClass : "has-warning",
43325 validClass: 'has-success',
43327 allowed: '0123456789',
43332 * @cfg {String} defaultDialCode The default dial code when initializing the input
43334 defaultDialCode: '+852',
43337 * @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
43339 preferedCountries: false,
43341 getAutoCreate : function()
43343 var data = Roo.bootstrap.form.PhoneInputData();
43344 var align = this.labelAlign || this.parentLabelAlign();
43347 this.allCountries = [];
43348 this.dialCodeMapping = [];
43350 for (var i = 0; i < data.length; i++) {
43352 this.allCountries[i] = {
43356 priority: c[3] || 0,
43357 areaCodes: c[4] || null
43359 this.dialCodeMapping[c[2]] = {
43362 priority: c[3] || 0,
43363 areaCodes: c[4] || null
43375 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43376 maxlength: this.max_length,
43377 cls : 'form-control tel-input',
43378 autocomplete: 'new-password'
43381 var hiddenInput = {
43384 cls: 'hidden-tel-input'
43388 hiddenInput.name = this.name;
43391 if (this.disabled) {
43392 input.disabled = true;
43395 var flag_container = {
43412 cls: this.hasFeedback ? 'has-feedback' : '',
43418 cls: 'dial-code-holder',
43425 cls: 'roo-select2-container input-group',
43432 if (this.fieldLabel.length) {
43435 tooltip: 'This field is required'
43441 cls: 'control-label',
43447 html: this.fieldLabel
43450 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43456 if(this.indicatorpos == 'right') {
43457 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43464 if(align == 'left') {
43472 if(this.labelWidth > 12){
43473 label.style = "width: " + this.labelWidth + 'px';
43475 if(this.labelWidth < 13 && this.labelmd == 0){
43476 this.labelmd = this.labelWidth;
43478 if(this.labellg > 0){
43479 label.cls += ' col-lg-' + this.labellg;
43480 input.cls += ' col-lg-' + (12 - this.labellg);
43482 if(this.labelmd > 0){
43483 label.cls += ' col-md-' + this.labelmd;
43484 container.cls += ' col-md-' + (12 - this.labelmd);
43486 if(this.labelsm > 0){
43487 label.cls += ' col-sm-' + this.labelsm;
43488 container.cls += ' col-sm-' + (12 - this.labelsm);
43490 if(this.labelxs > 0){
43491 label.cls += ' col-xs-' + this.labelxs;
43492 container.cls += ' col-xs-' + (12 - this.labelxs);
43502 var settings = this;
43504 ['xs','sm','md','lg'].map(function(size){
43505 if (settings[size]) {
43506 cfg.cls += ' col-' + size + '-' + settings[size];
43510 this.store = new Roo.data.Store({
43511 proxy : new Roo.data.MemoryProxy({}),
43512 reader : new Roo.data.JsonReader({
43523 'name' : 'dialCode',
43527 'name' : 'priority',
43531 'name' : 'areaCodes',
43538 if(!this.preferedCountries) {
43539 this.preferedCountries = [
43546 var p = this.preferedCountries.reverse();
43549 for (var i = 0; i < p.length; i++) {
43550 for (var j = 0; j < this.allCountries.length; j++) {
43551 if(this.allCountries[j].iso2 == p[i]) {
43552 var t = this.allCountries[j];
43553 this.allCountries.splice(j,1);
43554 this.allCountries.unshift(t);
43560 this.store.proxy.data = {
43562 data: this.allCountries
43568 initEvents : function()
43571 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43573 this.indicator = this.indicatorEl();
43574 this.flag = this.flagEl();
43575 this.dialCodeHolder = this.dialCodeHolderEl();
43577 this.trigger = this.el.select('div.flag-box',true).first();
43578 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43583 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43584 _this.list.setWidth(lw);
43587 this.list.on('mouseover', this.onViewOver, this);
43588 this.list.on('mousemove', this.onViewMove, this);
43589 this.inputEl().on("keyup", this.onKeyUp, this);
43590 this.inputEl().on("keypress", this.onKeyPress, this);
43592 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43594 this.view = new Roo.View(this.list, this.tpl, {
43595 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43598 this.view.on('click', this.onViewClick, this);
43599 this.setValue(this.defaultDialCode);
43602 onTriggerClick : function(e)
43604 Roo.log('trigger click');
43609 if(this.isExpanded()){
43611 this.hasFocus = false;
43613 this.store.load({});
43614 this.hasFocus = true;
43619 isExpanded : function()
43621 return this.list.isVisible();
43624 collapse : function()
43626 if(!this.isExpanded()){
43630 Roo.get(document).un('mousedown', this.collapseIf, this);
43631 Roo.get(document).un('mousewheel', this.collapseIf, this);
43632 this.fireEvent('collapse', this);
43636 expand : function()
43640 if(this.isExpanded() || !this.hasFocus){
43644 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43645 this.list.setWidth(lw);
43648 this.restrictHeight();
43650 Roo.get(document).on('mousedown', this.collapseIf, this);
43651 Roo.get(document).on('mousewheel', this.collapseIf, this);
43653 this.fireEvent('expand', this);
43656 restrictHeight : function()
43658 this.list.alignTo(this.inputEl(), this.listAlign);
43659 this.list.alignTo(this.inputEl(), this.listAlign);
43662 onViewOver : function(e, t)
43664 if(this.inKeyMode){
43667 var item = this.view.findItemFromChild(t);
43670 var index = this.view.indexOf(item);
43671 this.select(index, false);
43676 onViewClick : function(view, doFocus, el, e)
43678 var index = this.view.getSelectedIndexes()[0];
43680 var r = this.store.getAt(index);
43683 this.onSelect(r, index);
43685 if(doFocus !== false && !this.blockFocus){
43686 this.inputEl().focus();
43690 onViewMove : function(e, t)
43692 this.inKeyMode = false;
43695 select : function(index, scrollIntoView)
43697 this.selectedIndex = index;
43698 this.view.select(index);
43699 if(scrollIntoView !== false){
43700 var el = this.view.getNode(index);
43702 this.list.scrollChildIntoView(el, false);
43707 createList : function()
43709 this.list = Roo.get(document.body).createChild({
43711 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43712 style: 'display:none'
43715 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43718 collapseIf : function(e)
43720 var in_combo = e.within(this.el);
43721 var in_list = e.within(this.list);
43722 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43724 if (in_combo || in_list || is_list) {
43730 onSelect : function(record, index)
43732 if(this.fireEvent('beforeselect', this, record, index) !== false){
43734 this.setFlagClass(record.data.iso2);
43735 this.setDialCode(record.data.dialCode);
43736 this.hasFocus = false;
43738 this.fireEvent('select', this, record, index);
43742 flagEl : function()
43744 var flag = this.el.select('div.flag',true).first();
43751 dialCodeHolderEl : function()
43753 var d = this.el.select('input.dial-code-holder',true).first();
43760 setDialCode : function(v)
43762 this.dialCodeHolder.dom.value = '+'+v;
43765 setFlagClass : function(n)
43767 this.flag.dom.className = 'flag '+n;
43770 getValue : function()
43772 var v = this.inputEl().getValue();
43773 if(this.dialCodeHolder) {
43774 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43779 setValue : function(v)
43781 var d = this.getDialCode(v);
43783 //invalid dial code
43784 if(v.length == 0 || !d || d.length == 0) {
43786 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43787 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43793 this.setFlagClass(this.dialCodeMapping[d].iso2);
43794 this.setDialCode(d);
43795 this.inputEl().dom.value = v.replace('+'+d,'');
43796 this.hiddenEl().dom.value = this.getValue();
43801 getDialCode : function(v)
43805 if (v.length == 0) {
43806 return this.dialCodeHolder.dom.value;
43810 if (v.charAt(0) != "+") {
43813 var numericChars = "";
43814 for (var i = 1; i < v.length; i++) {
43815 var c = v.charAt(i);
43818 if (this.dialCodeMapping[numericChars]) {
43819 dialCode = v.substr(1, i);
43821 if (numericChars.length == 4) {
43831 this.setValue(this.defaultDialCode);
43835 hiddenEl : function()
43837 return this.el.select('input.hidden-tel-input',true).first();
43840 // after setting val
43841 onKeyUp : function(e){
43842 this.setValue(this.getValue());
43845 onKeyPress : function(e){
43846 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43853 * @class Roo.bootstrap.form.MoneyField
43854 * @extends Roo.bootstrap.form.ComboBox
43855 * Bootstrap MoneyField class
43858 * Create a new MoneyField.
43859 * @param {Object} config Configuration options
43862 Roo.bootstrap.form.MoneyField = function(config) {
43864 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43868 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43871 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43873 allowDecimals : true,
43875 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43877 decimalSeparator : ".",
43879 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43881 decimalPrecision : 0,
43883 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43885 allowNegative : true,
43887 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43891 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43893 minValue : Number.NEGATIVE_INFINITY,
43895 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43897 maxValue : Number.MAX_VALUE,
43899 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43901 minText : "The minimum value for this field is {0}",
43903 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43905 maxText : "The maximum value for this field is {0}",
43907 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43908 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43910 nanText : "{0} is not a valid number",
43912 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43916 * @cfg {String} defaults currency of the MoneyField
43917 * value should be in lkey
43919 defaultCurrency : false,
43921 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43923 thousandsDelimiter : false,
43925 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43934 * @cfg {Roo.data.Store} store Store to lookup currency??
43938 getAutoCreate : function()
43940 var align = this.labelAlign || this.parentLabelAlign();
43952 cls : 'form-control roo-money-amount-input',
43953 autocomplete: 'new-password'
43956 var hiddenInput = {
43960 cls: 'hidden-number-input'
43963 if(this.max_length) {
43964 input.maxlength = this.max_length;
43968 hiddenInput.name = this.name;
43971 if (this.disabled) {
43972 input.disabled = true;
43975 var clg = 12 - this.inputlg;
43976 var cmd = 12 - this.inputmd;
43977 var csm = 12 - this.inputsm;
43978 var cxs = 12 - this.inputxs;
43982 cls : 'row roo-money-field',
43986 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43990 cls: 'roo-select2-container input-group',
43994 cls : 'form-control roo-money-currency-input',
43995 autocomplete: 'new-password',
43997 name : this.currencyName
44001 cls : 'input-group-addon',
44015 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44019 cls: this.hasFeedback ? 'has-feedback' : '',
44030 if (this.fieldLabel.length) {
44033 tooltip: 'This field is required'
44039 cls: 'control-label',
44045 html: this.fieldLabel
44048 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44054 if(this.indicatorpos == 'right') {
44055 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44062 if(align == 'left') {
44070 if(this.labelWidth > 12){
44071 label.style = "width: " + this.labelWidth + 'px';
44073 if(this.labelWidth < 13 && this.labelmd == 0){
44074 this.labelmd = this.labelWidth;
44076 if(this.labellg > 0){
44077 label.cls += ' col-lg-' + this.labellg;
44078 input.cls += ' col-lg-' + (12 - this.labellg);
44080 if(this.labelmd > 0){
44081 label.cls += ' col-md-' + this.labelmd;
44082 container.cls += ' col-md-' + (12 - this.labelmd);
44084 if(this.labelsm > 0){
44085 label.cls += ' col-sm-' + this.labelsm;
44086 container.cls += ' col-sm-' + (12 - this.labelsm);
44088 if(this.labelxs > 0){
44089 label.cls += ' col-xs-' + this.labelxs;
44090 container.cls += ' col-xs-' + (12 - this.labelxs);
44101 var settings = this;
44103 ['xs','sm','md','lg'].map(function(size){
44104 if (settings[size]) {
44105 cfg.cls += ' col-' + size + '-' + settings[size];
44112 initEvents : function()
44114 this.indicator = this.indicatorEl();
44116 this.initCurrencyEvent();
44118 this.initNumberEvent();
44121 initCurrencyEvent : function()
44124 throw "can not find store for combo";
44127 this.store = Roo.factory(this.store, Roo.data);
44128 this.store.parent = this;
44132 this.triggerEl = this.el.select('.input-group-addon', true).first();
44134 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44139 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44140 _this.list.setWidth(lw);
44143 this.list.on('mouseover', this.onViewOver, this);
44144 this.list.on('mousemove', this.onViewMove, this);
44145 this.list.on('scroll', this.onViewScroll, this);
44148 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44151 this.view = new Roo.View(this.list, this.tpl, {
44152 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44155 this.view.on('click', this.onViewClick, this);
44157 this.store.on('beforeload', this.onBeforeLoad, this);
44158 this.store.on('load', this.onLoad, this);
44159 this.store.on('loadexception', this.onLoadException, this);
44161 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44162 "up" : function(e){
44163 this.inKeyMode = true;
44167 "down" : function(e){
44168 if(!this.isExpanded()){
44169 this.onTriggerClick();
44171 this.inKeyMode = true;
44176 "enter" : function(e){
44179 if(this.fireEvent("specialkey", this, e)){
44180 this.onViewClick(false);
44186 "esc" : function(e){
44190 "tab" : function(e){
44193 if(this.fireEvent("specialkey", this, e)){
44194 this.onViewClick(false);
44202 doRelay : function(foo, bar, hname){
44203 if(hname == 'down' || this.scope.isExpanded()){
44204 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44212 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44216 initNumberEvent : function(e)
44218 this.inputEl().on("keydown" , this.fireKey, this);
44219 this.inputEl().on("focus", this.onFocus, this);
44220 this.inputEl().on("blur", this.onBlur, this);
44222 this.inputEl().relayEvent('keyup', this);
44224 if(this.indicator){
44225 this.indicator.addClass('invisible');
44228 this.originalValue = this.getValue();
44230 if(this.validationEvent == 'keyup'){
44231 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44232 this.inputEl().on('keyup', this.filterValidation, this);
44234 else if(this.validationEvent !== false){
44235 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44238 if(this.selectOnFocus){
44239 this.on("focus", this.preFocus, this);
44242 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44243 this.inputEl().on("keypress", this.filterKeys, this);
44245 this.inputEl().relayEvent('keypress', this);
44248 var allowed = "0123456789";
44250 if(this.allowDecimals){
44251 allowed += this.decimalSeparator;
44254 if(this.allowNegative){
44258 if(this.thousandsDelimiter) {
44262 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44264 var keyPress = function(e){
44266 var k = e.getKey();
44268 var c = e.getCharCode();
44271 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44272 allowed.indexOf(String.fromCharCode(c)) === -1
44278 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44282 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44287 this.inputEl().on("keypress", keyPress, this);
44291 onTriggerClick : function(e)
44298 this.loadNext = false;
44300 if(this.isExpanded()){
44305 this.hasFocus = true;
44307 if(this.triggerAction == 'all') {
44308 this.doQuery(this.allQuery, true);
44312 this.doQuery(this.getRawValue());
44315 getCurrency : function()
44317 var v = this.currencyEl().getValue();
44322 restrictHeight : function()
44324 this.list.alignTo(this.currencyEl(), this.listAlign);
44325 this.list.alignTo(this.currencyEl(), this.listAlign);
44328 onViewClick : function(view, doFocus, el, e)
44330 var index = this.view.getSelectedIndexes()[0];
44332 var r = this.store.getAt(index);
44335 this.onSelect(r, index);
44339 onSelect : function(record, index){
44341 if(this.fireEvent('beforeselect', this, record, index) !== false){
44343 this.setFromCurrencyData(index > -1 ? record.data : false);
44347 this.fireEvent('select', this, record, index);
44351 setFromCurrencyData : function(o)
44355 this.lastCurrency = o;
44357 if (this.currencyField) {
44358 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44360 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44363 this.lastSelectionText = currency;
44365 //setting default currency
44366 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44367 this.setCurrency(this.defaultCurrency);
44371 this.setCurrency(currency);
44374 setFromData : function(o)
44378 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44380 this.setFromCurrencyData(c);
44385 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44387 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44390 this.setValue(value);
44394 setCurrency : function(v)
44396 this.currencyValue = v;
44399 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44404 setValue : function(v)
44406 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44412 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44414 this.inputEl().dom.value = (v == '') ? '' :
44415 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44417 if(!this.allowZero && v === '0') {
44418 this.hiddenEl().dom.value = '';
44419 this.inputEl().dom.value = '';
44426 getRawValue : function()
44428 var v = this.inputEl().getValue();
44433 getValue : function()
44435 return this.fixPrecision(this.parseValue(this.getRawValue()));
44438 parseValue : function(value)
44440 if(this.thousandsDelimiter) {
44442 r = new RegExp(",", "g");
44443 value = value.replace(r, "");
44446 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44447 return isNaN(value) ? '' : value;
44451 fixPrecision : function(value)
44453 if(this.thousandsDelimiter) {
44455 r = new RegExp(",", "g");
44456 value = value.replace(r, "");
44459 var nan = isNaN(value);
44461 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44462 return nan ? '' : value;
44464 return parseFloat(value).toFixed(this.decimalPrecision);
44467 decimalPrecisionFcn : function(v)
44469 return Math.floor(v);
44472 validateValue : function(value)
44474 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44478 var num = this.parseValue(value);
44481 this.markInvalid(String.format(this.nanText, value));
44485 if(num < this.minValue){
44486 this.markInvalid(String.format(this.minText, this.minValue));
44490 if(num > this.maxValue){
44491 this.markInvalid(String.format(this.maxText, this.maxValue));
44498 validate : function()
44500 if(this.disabled || this.allowBlank){
44505 var currency = this.getCurrency();
44507 if(this.validateValue(this.getRawValue()) && currency.length){
44512 this.markInvalid();
44516 getName: function()
44521 beforeBlur : function()
44527 var v = this.parseValue(this.getRawValue());
44534 onBlur : function()
44538 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44539 //this.el.removeClass(this.focusClass);
44542 this.hasFocus = false;
44544 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44548 var v = this.getValue();
44550 if(String(v) !== String(this.startValue)){
44551 this.fireEvent('change', this, v, this.startValue);
44554 this.fireEvent("blur", this);
44557 inputEl : function()
44559 return this.el.select('.roo-money-amount-input', true).first();
44562 currencyEl : function()
44564 return this.el.select('.roo-money-currency-input', true).first();
44567 hiddenEl : function()
44569 return this.el.select('input.hidden-number-input',true).first();
44573 * @class Roo.bootstrap.BezierSignature
44574 * @extends Roo.bootstrap.Component
44575 * Bootstrap BezierSignature class
44576 * This script refer to:
44577 * Title: Signature Pad
44579 * Availability: https://github.com/szimek/signature_pad
44582 * Create a new BezierSignature
44583 * @param {Object} config The config object
44586 Roo.bootstrap.BezierSignature = function(config){
44587 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44593 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44600 mouse_btn_down: true,
44603 * @cfg {int} canvas height
44605 canvas_height: '200px',
44608 * @cfg {float|function} Radius of a single dot.
44613 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44618 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44623 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44628 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44633 * @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.
44635 bg_color: 'rgba(0, 0, 0, 0)',
44638 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44640 dot_color: 'black',
44643 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44645 velocity_filter_weight: 0.7,
44648 * @cfg {function} Callback when stroke begin.
44653 * @cfg {function} Callback when stroke end.
44657 getAutoCreate : function()
44659 var cls = 'roo-signature column';
44662 cls += ' ' + this.cls;
44672 for(var i = 0; i < col_sizes.length; i++) {
44673 if(this[col_sizes[i]]) {
44674 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44684 cls: 'roo-signature-body',
44688 cls: 'roo-signature-body-canvas',
44689 height: this.canvas_height,
44690 width: this.canvas_width
44697 style: 'display: none'
44705 initEvents: function()
44707 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44709 var canvas = this.canvasEl();
44711 // mouse && touch event swapping...
44712 canvas.dom.style.touchAction = 'none';
44713 canvas.dom.style.msTouchAction = 'none';
44715 this.mouse_btn_down = false;
44716 canvas.on('mousedown', this._handleMouseDown, this);
44717 canvas.on('mousemove', this._handleMouseMove, this);
44718 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44720 if (window.PointerEvent) {
44721 canvas.on('pointerdown', this._handleMouseDown, this);
44722 canvas.on('pointermove', this._handleMouseMove, this);
44723 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44726 if ('ontouchstart' in window) {
44727 canvas.on('touchstart', this._handleTouchStart, this);
44728 canvas.on('touchmove', this._handleTouchMove, this);
44729 canvas.on('touchend', this._handleTouchEnd, this);
44732 Roo.EventManager.onWindowResize(this.resize, this, true);
44734 // file input event
44735 this.fileEl().on('change', this.uploadImage, this);
44742 resize: function(){
44744 var canvas = this.canvasEl().dom;
44745 var ctx = this.canvasElCtx();
44746 var img_data = false;
44748 if(canvas.width > 0) {
44749 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44751 // setting canvas width will clean img data
44754 var style = window.getComputedStyle ?
44755 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44757 var padding_left = parseInt(style.paddingLeft) || 0;
44758 var padding_right = parseInt(style.paddingRight) || 0;
44760 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44763 ctx.putImageData(img_data, 0, 0);
44767 _handleMouseDown: function(e)
44769 if (e.browserEvent.which === 1) {
44770 this.mouse_btn_down = true;
44771 this.strokeBegin(e);
44775 _handleMouseMove: function (e)
44777 if (this.mouse_btn_down) {
44778 this.strokeMoveUpdate(e);
44782 _handleMouseUp: function (e)
44784 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44785 this.mouse_btn_down = false;
44790 _handleTouchStart: function (e) {
44792 e.preventDefault();
44793 if (e.browserEvent.targetTouches.length === 1) {
44794 // var touch = e.browserEvent.changedTouches[0];
44795 // this.strokeBegin(touch);
44797 this.strokeBegin(e); // assume e catching the correct xy...
44801 _handleTouchMove: function (e) {
44802 e.preventDefault();
44803 // var touch = event.targetTouches[0];
44804 // _this._strokeMoveUpdate(touch);
44805 this.strokeMoveUpdate(e);
44808 _handleTouchEnd: function (e) {
44809 var wasCanvasTouched = e.target === this.canvasEl().dom;
44810 if (wasCanvasTouched) {
44811 e.preventDefault();
44812 // var touch = event.changedTouches[0];
44813 // _this._strokeEnd(touch);
44818 reset: function () {
44819 this._lastPoints = [];
44820 this._lastVelocity = 0;
44821 this._lastWidth = (this.min_width + this.max_width) / 2;
44822 this.canvasElCtx().fillStyle = this.dot_color;
44825 strokeMoveUpdate: function(e)
44827 this.strokeUpdate(e);
44829 if (this.throttle) {
44830 this.throttleStroke(this.strokeUpdate, this.throttle);
44833 this.strokeUpdate(e);
44837 strokeBegin: function(e)
44839 var newPointGroup = {
44840 color: this.dot_color,
44844 if (typeof this.onBegin === 'function') {
44848 this.curve_data.push(newPointGroup);
44850 this.strokeUpdate(e);
44853 strokeUpdate: function(e)
44855 var rect = this.canvasEl().dom.getBoundingClientRect();
44856 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44857 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44858 var lastPoints = lastPointGroup.points;
44859 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44860 var isLastPointTooClose = lastPoint
44861 ? point.distanceTo(lastPoint) <= this.min_distance
44863 var color = lastPointGroup.color;
44864 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44865 var curve = this.addPoint(point);
44867 this.drawDot({color: color, point: point});
44870 this.drawCurve({color: color, curve: curve});
44880 strokeEnd: function(e)
44882 this.strokeUpdate(e);
44883 if (typeof this.onEnd === 'function') {
44888 addPoint: function (point) {
44889 var _lastPoints = this._lastPoints;
44890 _lastPoints.push(point);
44891 if (_lastPoints.length > 2) {
44892 if (_lastPoints.length === 3) {
44893 _lastPoints.unshift(_lastPoints[0]);
44895 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44896 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44897 _lastPoints.shift();
44903 calculateCurveWidths: function (startPoint, endPoint) {
44904 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44905 (1 - this.velocity_filter_weight) * this._lastVelocity;
44907 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44910 start: this._lastWidth
44913 this._lastVelocity = velocity;
44914 this._lastWidth = newWidth;
44918 drawDot: function (_a) {
44919 var color = _a.color, point = _a.point;
44920 var ctx = this.canvasElCtx();
44921 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44923 this.drawCurveSegment(point.x, point.y, width);
44925 ctx.fillStyle = color;
44929 drawCurve: function (_a) {
44930 var color = _a.color, curve = _a.curve;
44931 var ctx = this.canvasElCtx();
44932 var widthDelta = curve.endWidth - curve.startWidth;
44933 var drawSteps = Math.floor(curve.length()) * 2;
44935 ctx.fillStyle = color;
44936 for (var i = 0; i < drawSteps; i += 1) {
44937 var t = i / drawSteps;
44943 var x = uuu * curve.startPoint.x;
44944 x += 3 * uu * t * curve.control1.x;
44945 x += 3 * u * tt * curve.control2.x;
44946 x += ttt * curve.endPoint.x;
44947 var y = uuu * curve.startPoint.y;
44948 y += 3 * uu * t * curve.control1.y;
44949 y += 3 * u * tt * curve.control2.y;
44950 y += ttt * curve.endPoint.y;
44951 var width = curve.startWidth + ttt * widthDelta;
44952 this.drawCurveSegment(x, y, width);
44958 drawCurveSegment: function (x, y, width) {
44959 var ctx = this.canvasElCtx();
44961 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44962 this.is_empty = false;
44967 var ctx = this.canvasElCtx();
44968 var canvas = this.canvasEl().dom;
44969 ctx.fillStyle = this.bg_color;
44970 ctx.clearRect(0, 0, canvas.width, canvas.height);
44971 ctx.fillRect(0, 0, canvas.width, canvas.height);
44972 this.curve_data = [];
44974 this.is_empty = true;
44979 return this.el.select('input',true).first();
44982 canvasEl: function()
44984 return this.el.select('canvas',true).first();
44987 canvasElCtx: function()
44989 return this.el.select('canvas',true).first().dom.getContext('2d');
44992 getImage: function(type)
44994 if(this.is_empty) {
44999 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45002 drawFromImage: function(img_src)
45004 var img = new Image();
45006 img.onload = function(){
45007 this.canvasElCtx().drawImage(img, 0, 0);
45012 this.is_empty = false;
45015 selectImage: function()
45017 this.fileEl().dom.click();
45020 uploadImage: function(e)
45022 var reader = new FileReader();
45024 reader.onload = function(e){
45025 var img = new Image();
45026 img.onload = function(){
45028 this.canvasElCtx().drawImage(img, 0, 0);
45030 img.src = e.target.result;
45033 reader.readAsDataURL(e.target.files[0]);
45036 // Bezier Point Constructor
45037 Point: (function () {
45038 function Point(x, y, time) {
45041 this.time = time || Date.now();
45043 Point.prototype.distanceTo = function (start) {
45044 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45046 Point.prototype.equals = function (other) {
45047 return this.x === other.x && this.y === other.y && this.time === other.time;
45049 Point.prototype.velocityFrom = function (start) {
45050 return this.time !== start.time
45051 ? this.distanceTo(start) / (this.time - start.time)
45058 // Bezier Constructor
45059 Bezier: (function () {
45060 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45061 this.startPoint = startPoint;
45062 this.control2 = control2;
45063 this.control1 = control1;
45064 this.endPoint = endPoint;
45065 this.startWidth = startWidth;
45066 this.endWidth = endWidth;
45068 Bezier.fromPoints = function (points, widths, scope) {
45069 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45070 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45071 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45073 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45074 var dx1 = s1.x - s2.x;
45075 var dy1 = s1.y - s2.y;
45076 var dx2 = s2.x - s3.x;
45077 var dy2 = s2.y - s3.y;
45078 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45079 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45080 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45081 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45082 var dxm = m1.x - m2.x;
45083 var dym = m1.y - m2.y;
45084 var k = l2 / (l1 + l2);
45085 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45086 var tx = s2.x - cm.x;
45087 var ty = s2.y - cm.y;
45089 c1: new scope.Point(m1.x + tx, m1.y + ty),
45090 c2: new scope.Point(m2.x + tx, m2.y + ty)
45093 Bezier.prototype.length = function () {
45098 for (var i = 0; i <= steps; i += 1) {
45100 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45101 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45103 var xdiff = cx - px;
45104 var ydiff = cy - py;
45105 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45112 Bezier.prototype.point = function (t, start, c1, c2, end) {
45113 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45114 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45115 + (3.0 * c2 * (1.0 - t) * t * t)
45116 + (end * t * t * t);
45121 throttleStroke: function(fn, wait) {
45122 if (wait === void 0) { wait = 250; }
45124 var timeout = null;
45128 var later = function () {
45129 previous = Date.now();
45131 result = fn.apply(storedContext, storedArgs);
45133 storedContext = null;
45137 return function wrapper() {
45139 for (var _i = 0; _i < arguments.length; _i++) {
45140 args[_i] = arguments[_i];
45142 var now = Date.now();
45143 var remaining = wait - (now - previous);
45144 storedContext = this;
45146 if (remaining <= 0 || remaining > wait) {
45148 clearTimeout(timeout);
45152 result = fn.apply(storedContext, storedArgs);
45154 storedContext = null;
45158 else if (!timeout) {
45159 timeout = window.setTimeout(later, remaining);
45169 // old names for form elements
45170 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45171 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45172 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45173 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45174 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45175 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45176 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45177 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45178 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45179 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45180 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45181 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45182 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45183 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45184 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45185 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45186 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45187 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45188 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45189 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45190 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45191 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45192 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45193 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45194 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45195 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45197 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45198 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45200 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45201 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45203 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45204 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45205 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45206 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator