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){
15390 if(success !== false){
15391 this.fireEvent("load", this, [], options, o);
15393 if(options.callback){
15394 options.callback.call(options.scope || this, [], options, false);
15398 // if data returned failure - throw an exception.
15399 if (o.success === false) {
15400 // show a message if no listener is registered.
15401 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15402 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15404 // loadmask wil be hooked into this..
15405 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15408 var r = o.records, t = o.totalRecords || r.length;
15410 this.fireEvent("beforeloadadd", this, r, options, o);
15412 if(!options || options.add !== true){
15413 if(this.pruneModifiedRecords){
15414 this.modified = [];
15416 for(var i = 0, len = r.length; i < len; i++){
15420 this.data = this.snapshot;
15421 delete this.snapshot;
15424 this.data.addAll(r);
15425 this.totalLength = t;
15427 this.fireEvent("datachanged", this);
15429 this.totalLength = Math.max(t, this.data.length+r.length);
15433 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15435 var e = new Roo.data.Record({});
15437 e.set(this.parent.displayField, this.parent.emptyTitle);
15438 e.set(this.parent.valueField, '');
15443 this.fireEvent("load", this, r, options, o);
15444 if(options.callback){
15445 options.callback.call(options.scope || this, r, options, true);
15451 * Loads data from a passed data block. A Reader which understands the format of the data
15452 * must have been configured in the constructor.
15453 * @param {Object} data The data block from which to read the Records. The format of the data expected
15454 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15455 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15457 loadData : function(o, append){
15458 var r = this.reader.readRecords(o);
15459 this.loadRecords(r, {add: append}, true);
15463 * using 'cn' the nested child reader read the child array into it's child stores.
15464 * @param {Object} rec The record with a 'children array
15466 loadDataFromChildren : function(rec)
15468 this.loadData(this.reader.toLoadData(rec));
15473 * Gets the number of cached records.
15475 * <em>If using paging, this may not be the total size of the dataset. If the data object
15476 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15477 * the data set size</em>
15479 getCount : function(){
15480 return this.data.length || 0;
15484 * Gets the total number of records in the dataset as returned by the server.
15486 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15487 * the dataset size</em>
15489 getTotalCount : function(){
15490 return this.totalLength || 0;
15494 * Returns the sort state of the Store as an object with two properties:
15496 field {String} The name of the field by which the Records are sorted
15497 direction {String} The sort order, "ASC" or "DESC"
15500 getSortState : function(){
15501 return this.sortInfo;
15505 applySort : function(){
15506 if(this.sortInfo && !this.remoteSort){
15507 var s = this.sortInfo, f = s.field;
15508 var st = this.fields.get(f).sortType;
15509 var fn = function(r1, r2){
15510 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15511 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15513 this.data.sort(s.direction, fn);
15514 if(this.snapshot && this.snapshot != this.data){
15515 this.snapshot.sort(s.direction, fn);
15521 * Sets the default sort column and order to be used by the next load operation.
15522 * @param {String} fieldName The name of the field to sort by.
15523 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15525 setDefaultSort : function(field, dir){
15526 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15530 * Sort the Records.
15531 * If remote sorting is used, the sort is performed on the server, and the cache is
15532 * reloaded. If local sorting is used, the cache is sorted internally.
15533 * @param {String} fieldName The name of the field to sort by.
15534 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15536 sort : function(fieldName, dir){
15537 var f = this.fields.get(fieldName);
15539 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15541 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15542 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15547 this.sortToggle[f.name] = dir;
15548 this.sortInfo = {field: f.name, direction: dir};
15549 if(!this.remoteSort){
15551 this.fireEvent("datachanged", this);
15553 this.load(this.lastOptions);
15558 * Calls the specified function for each of the Records in the cache.
15559 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15560 * Returning <em>false</em> aborts and exits the iteration.
15561 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15563 each : function(fn, scope){
15564 this.data.each(fn, scope);
15568 * Gets all records modified since the last commit. Modified records are persisted across load operations
15569 * (e.g., during paging).
15570 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15572 getModifiedRecords : function(){
15573 return this.modified;
15577 createFilterFn : function(property, value, anyMatch){
15578 if(!value.exec){ // not a regex
15579 value = String(value);
15580 if(value.length == 0){
15583 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15585 return function(r){
15586 return value.test(r.data[property]);
15591 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15592 * @param {String} property A field on your records
15593 * @param {Number} start The record index to start at (defaults to 0)
15594 * @param {Number} end The last record index to include (defaults to length - 1)
15595 * @return {Number} The sum
15597 sum : function(property, start, end){
15598 var rs = this.data.items, v = 0;
15599 start = start || 0;
15600 end = (end || end === 0) ? end : rs.length-1;
15602 for(var i = start; i <= end; i++){
15603 v += (rs[i].data[property] || 0);
15609 * Filter the records by a specified property.
15610 * @param {String} field A field on your records
15611 * @param {String/RegExp} value Either a string that the field
15612 * should start with or a RegExp to test against the field
15613 * @param {Boolean} anyMatch True to match any part not just the beginning
15615 filter : function(property, value, anyMatch){
15616 var fn = this.createFilterFn(property, value, anyMatch);
15617 return fn ? this.filterBy(fn) : this.clearFilter();
15621 * Filter by a function. The specified function will be called with each
15622 * record in this data source. If the function returns true the record is included,
15623 * otherwise it is filtered.
15624 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15625 * @param {Object} scope (optional) The scope of the function (defaults to this)
15627 filterBy : function(fn, scope){
15628 this.snapshot = this.snapshot || this.data;
15629 this.data = this.queryBy(fn, scope||this);
15630 this.fireEvent("datachanged", this);
15634 * Query the records by a specified property.
15635 * @param {String} field A field on your records
15636 * @param {String/RegExp} value Either a string that the field
15637 * should start with or a RegExp to test against the field
15638 * @param {Boolean} anyMatch True to match any part not just the beginning
15639 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15641 query : function(property, value, anyMatch){
15642 var fn = this.createFilterFn(property, value, anyMatch);
15643 return fn ? this.queryBy(fn) : this.data.clone();
15647 * Query by a function. The specified function will be called with each
15648 * record in this data source. If the function returns true the record is included
15650 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15651 * @param {Object} scope (optional) The scope of the function (defaults to this)
15652 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15654 queryBy : function(fn, scope){
15655 var data = this.snapshot || this.data;
15656 return data.filterBy(fn, scope||this);
15660 * Collects unique values for a particular dataIndex from this store.
15661 * @param {String} dataIndex The property to collect
15662 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15663 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15664 * @return {Array} An array of the unique values
15666 collect : function(dataIndex, allowNull, bypassFilter){
15667 var d = (bypassFilter === true && this.snapshot) ?
15668 this.snapshot.items : this.data.items;
15669 var v, sv, r = [], l = {};
15670 for(var i = 0, len = d.length; i < len; i++){
15671 v = d[i].data[dataIndex];
15673 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15682 * Revert to a view of the Record cache with no filtering applied.
15683 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15685 clearFilter : function(suppressEvent){
15686 if(this.snapshot && this.snapshot != this.data){
15687 this.data = this.snapshot;
15688 delete this.snapshot;
15689 if(suppressEvent !== true){
15690 this.fireEvent("datachanged", this);
15696 afterEdit : function(record){
15697 if(this.modified.indexOf(record) == -1){
15698 this.modified.push(record);
15700 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15704 afterReject : function(record){
15705 this.modified.remove(record);
15706 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15710 afterCommit : function(record){
15711 this.modified.remove(record);
15712 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15716 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15717 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15719 commitChanges : function(){
15720 var m = this.modified.slice(0);
15721 this.modified = [];
15722 for(var i = 0, len = m.length; i < len; i++){
15728 * Cancel outstanding changes on all changed records.
15730 rejectChanges : function(){
15731 var m = this.modified.slice(0);
15732 this.modified = [];
15733 for(var i = 0, len = m.length; i < len; i++){
15738 onMetaChange : function(meta, rtype, o){
15739 this.recordType = rtype;
15740 this.fields = rtype.prototype.fields;
15741 delete this.snapshot;
15742 this.sortInfo = meta.sortInfo || this.sortInfo;
15743 this.modified = [];
15744 this.fireEvent('metachange', this, this.reader.meta);
15747 moveIndex : function(data, type)
15749 var index = this.indexOf(data);
15751 var newIndex = index + type;
15755 this.insert(newIndex, data);
15760 * Ext JS Library 1.1.1
15761 * Copyright(c) 2006-2007, Ext JS, LLC.
15763 * Originally Released Under LGPL - original licence link has changed is not relivant.
15766 * <script type="text/javascript">
15770 * @class Roo.data.SimpleStore
15771 * @extends Roo.data.Store
15772 * Small helper class to make creating Stores from Array data easier.
15773 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15774 * @cfg {Array} fields An array of field definition objects, or field name strings.
15775 * @cfg {Object} an existing reader (eg. copied from another store)
15776 * @cfg {Array} data The multi-dimensional array of data
15777 * @cfg {Roo.data.DataProxy} proxy [not-required]
15778 * @cfg {Roo.data.Reader} reader [not-required]
15780 * @param {Object} config
15782 Roo.data.SimpleStore = function(config)
15784 Roo.data.SimpleStore.superclass.constructor.call(this, {
15786 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15789 Roo.data.Record.create(config.fields)
15791 proxy : new Roo.data.MemoryProxy(config.data)
15795 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15797 * Ext JS Library 1.1.1
15798 * Copyright(c) 2006-2007, Ext JS, LLC.
15800 * Originally Released Under LGPL - original licence link has changed is not relivant.
15803 * <script type="text/javascript">
15808 * @extends Roo.data.Store
15809 * @class Roo.data.JsonStore
15810 * Small helper class to make creating Stores for JSON data easier. <br/>
15812 var store = new Roo.data.JsonStore({
15813 url: 'get-images.php',
15815 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15818 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15819 * JsonReader and HttpProxy (unless inline data is provided).</b>
15820 * @cfg {Array} fields An array of field definition objects, or field name strings.
15822 * @param {Object} config
15824 Roo.data.JsonStore = function(c){
15825 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15826 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15827 reader: new Roo.data.JsonReader(c, c.fields)
15830 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15832 * Ext JS Library 1.1.1
15833 * Copyright(c) 2006-2007, Ext JS, LLC.
15835 * Originally Released Under LGPL - original licence link has changed is not relivant.
15838 * <script type="text/javascript">
15842 Roo.data.Field = function(config){
15843 if(typeof config == "string"){
15844 config = {name: config};
15846 Roo.apply(this, config);
15849 this.type = "auto";
15852 var st = Roo.data.SortTypes;
15853 // named sortTypes are supported, here we look them up
15854 if(typeof this.sortType == "string"){
15855 this.sortType = st[this.sortType];
15858 // set default sortType for strings and dates
15859 if(!this.sortType){
15862 this.sortType = st.asUCString;
15865 this.sortType = st.asDate;
15868 this.sortType = st.none;
15873 var stripRe = /[\$,%]/g;
15875 // prebuilt conversion function for this field, instead of
15876 // switching every time we're reading a value
15878 var cv, dateFormat = this.dateFormat;
15883 cv = function(v){ return v; };
15886 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15890 return v !== undefined && v !== null && v !== '' ?
15891 parseInt(String(v).replace(stripRe, ""), 10) : '';
15896 return v !== undefined && v !== null && v !== '' ?
15897 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15902 cv = function(v){ return v === true || v === "true" || v == 1; };
15909 if(v instanceof Date){
15913 if(dateFormat == "timestamp"){
15914 return new Date(v*1000);
15916 return Date.parseDate(v, dateFormat);
15918 var parsed = Date.parse(v);
15919 return parsed ? new Date(parsed) : null;
15928 Roo.data.Field.prototype = {
15936 * Ext JS Library 1.1.1
15937 * Copyright(c) 2006-2007, Ext JS, LLC.
15939 * Originally Released Under LGPL - original licence link has changed is not relivant.
15942 * <script type="text/javascript">
15945 // Base class for reading structured data from a data source. This class is intended to be
15946 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15949 * @class Roo.data.DataReader
15951 * Base class for reading structured data from a data source. This class is intended to be
15952 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15955 Roo.data.DataReader = function(meta, recordType){
15959 this.recordType = recordType instanceof Array ?
15960 Roo.data.Record.create(recordType) : recordType;
15963 Roo.data.DataReader.prototype = {
15966 readerType : 'Data',
15968 * Create an empty record
15969 * @param {Object} data (optional) - overlay some values
15970 * @return {Roo.data.Record} record created.
15972 newRow : function(d) {
15974 this.recordType.prototype.fields.each(function(c) {
15976 case 'int' : da[c.name] = 0; break;
15977 case 'date' : da[c.name] = new Date(); break;
15978 case 'float' : da[c.name] = 0.0; break;
15979 case 'boolean' : da[c.name] = false; break;
15980 default : da[c.name] = ""; break;
15984 return new this.recordType(Roo.apply(da, d));
15990 * Ext JS Library 1.1.1
15991 * Copyright(c) 2006-2007, Ext JS, LLC.
15993 * Originally Released Under LGPL - original licence link has changed is not relivant.
15996 * <script type="text/javascript">
16000 * @class Roo.data.DataProxy
16001 * @extends Roo.util.Observable
16003 * This class is an abstract base class for implementations which provide retrieval of
16004 * unformatted data objects.<br>
16006 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16007 * (of the appropriate type which knows how to parse the data object) to provide a block of
16008 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16010 * Custom implementations must implement the load method as described in
16011 * {@link Roo.data.HttpProxy#load}.
16013 Roo.data.DataProxy = function(){
16016 * @event beforeload
16017 * Fires before a network request is made to retrieve a data object.
16018 * @param {Object} This DataProxy object.
16019 * @param {Object} params The params parameter to the load function.
16024 * Fires before the load method's callback is called.
16025 * @param {Object} This DataProxy object.
16026 * @param {Object} o The data object.
16027 * @param {Object} arg The callback argument object passed to the load function.
16031 * @event loadexception
16032 * Fires if an Exception occurs during data retrieval.
16033 * @param {Object} This DataProxy object.
16034 * @param {Object} o The data object.
16035 * @param {Object} arg The callback argument object passed to the load function.
16036 * @param {Object} e The Exception.
16038 loadexception : true
16040 Roo.data.DataProxy.superclass.constructor.call(this);
16043 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16046 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16050 * Ext JS Library 1.1.1
16051 * Copyright(c) 2006-2007, Ext JS, LLC.
16053 * Originally Released Under LGPL - original licence link has changed is not relivant.
16056 * <script type="text/javascript">
16059 * @class Roo.data.MemoryProxy
16060 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16061 * to the Reader when its load method is called.
16063 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16065 Roo.data.MemoryProxy = function(data){
16069 Roo.data.MemoryProxy.superclass.constructor.call(this);
16073 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16076 * Load data from the requested source (in this case an in-memory
16077 * data object passed to the constructor), read the data object into
16078 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16079 * process that block using the passed callback.
16080 * @param {Object} params This parameter is not used by the MemoryProxy class.
16081 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16082 * object into a block of Roo.data.Records.
16083 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16084 * The function must be passed <ul>
16085 * <li>The Record block object</li>
16086 * <li>The "arg" argument from the load function</li>
16087 * <li>A boolean success indicator</li>
16089 * @param {Object} scope The scope in which to call the callback
16090 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16092 load : function(params, reader, callback, scope, arg){
16093 params = params || {};
16096 result = reader.readRecords(params.data ? params.data :this.data);
16098 this.fireEvent("loadexception", this, arg, null, e);
16099 callback.call(scope, null, arg, false);
16102 callback.call(scope, result, arg, true);
16106 update : function(params, records){
16111 * Ext JS Library 1.1.1
16112 * Copyright(c) 2006-2007, Ext JS, LLC.
16114 * Originally Released Under LGPL - original licence link has changed is not relivant.
16117 * <script type="text/javascript">
16120 * @class Roo.data.HttpProxy
16121 * @extends Roo.data.DataProxy
16122 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16123 * configured to reference a certain URL.<br><br>
16125 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16126 * from which the running page was served.<br><br>
16128 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16130 * Be aware that to enable the browser to parse an XML document, the server must set
16131 * the Content-Type header in the HTTP response to "text/xml".
16133 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16134 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16135 * will be used to make the request.
16137 Roo.data.HttpProxy = function(conn){
16138 Roo.data.HttpProxy.superclass.constructor.call(this);
16139 // is conn a conn config or a real conn?
16141 this.useAjax = !conn || !conn.events;
16145 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16146 // thse are take from connection...
16149 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16152 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16153 * extra parameters to each request made by this object. (defaults to undefined)
16156 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16157 * to each request made by this object. (defaults to undefined)
16160 * @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)
16163 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16166 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16172 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16176 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16177 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16178 * a finer-grained basis than the DataProxy events.
16180 getConnection : function(){
16181 return this.useAjax ? Roo.Ajax : this.conn;
16185 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16186 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16187 * process that block using the passed callback.
16188 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16189 * for the request to the remote server.
16190 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16191 * object into a block of Roo.data.Records.
16192 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16193 * The function must be passed <ul>
16194 * <li>The Record block object</li>
16195 * <li>The "arg" argument from the load function</li>
16196 * <li>A boolean success indicator</li>
16198 * @param {Object} scope The scope in which to call the callback
16199 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16201 load : function(params, reader, callback, scope, arg){
16202 if(this.fireEvent("beforeload", this, params) !== false){
16204 params : params || {},
16206 callback : callback,
16211 callback : this.loadResponse,
16215 Roo.applyIf(o, this.conn);
16216 if(this.activeRequest){
16217 Roo.Ajax.abort(this.activeRequest);
16219 this.activeRequest = Roo.Ajax.request(o);
16221 this.conn.request(o);
16224 callback.call(scope||this, null, arg, false);
16229 loadResponse : function(o, success, response){
16230 delete this.activeRequest;
16232 this.fireEvent("loadexception", this, o, response);
16233 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16238 result = o.reader.read(response);
16240 this.fireEvent("loadexception", this, o, response, e);
16241 o.request.callback.call(o.request.scope, {
16244 errorMsg : response
16247 }, o.request.arg, false);
16251 this.fireEvent("load", this, o, o.request.arg);
16252 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16256 update : function(dataSet){
16261 updateResponse : function(dataSet){
16266 * Ext JS Library 1.1.1
16267 * Copyright(c) 2006-2007, Ext JS, LLC.
16269 * Originally Released Under LGPL - original licence link has changed is not relivant.
16272 * <script type="text/javascript">
16276 * @class Roo.data.ScriptTagProxy
16277 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16278 * other than the originating domain of the running page.<br><br>
16280 * <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
16281 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16283 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16284 * source code that is used as the source inside a <script> tag.<br><br>
16286 * In order for the browser to process the returned data, the server must wrap the data object
16287 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16288 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16289 * depending on whether the callback name was passed:
16292 boolean scriptTag = false;
16293 String cb = request.getParameter("callback");
16296 response.setContentType("text/javascript");
16298 response.setContentType("application/x-json");
16300 Writer out = response.getWriter();
16302 out.write(cb + "(");
16304 out.print(dataBlock.toJsonString());
16311 * @param {Object} config A configuration object.
16313 Roo.data.ScriptTagProxy = function(config){
16314 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16315 Roo.apply(this, config);
16316 this.head = document.getElementsByTagName("head")[0];
16319 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16321 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16323 * @cfg {String} url The URL from which to request the data object.
16326 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16330 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16331 * the server the name of the callback function set up by the load call to process the returned data object.
16332 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16333 * javascript output which calls this named function passing the data object as its only parameter.
16335 callbackParam : "callback",
16337 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16338 * name to the request.
16343 * Load data from the configured URL, read the data object into
16344 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16345 * process that block using the passed callback.
16346 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16347 * for the request to the remote server.
16348 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16349 * object into a block of Roo.data.Records.
16350 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16351 * The function must be passed <ul>
16352 * <li>The Record block object</li>
16353 * <li>The "arg" argument from the load function</li>
16354 * <li>A boolean success indicator</li>
16356 * @param {Object} scope The scope in which to call the callback
16357 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16359 load : function(params, reader, callback, scope, arg){
16360 if(this.fireEvent("beforeload", this, params) !== false){
16362 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16364 var url = this.url;
16365 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16367 url += "&_dc=" + (new Date().getTime());
16369 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16372 cb : "stcCallback"+transId,
16373 scriptId : "stcScript"+transId,
16377 callback : callback,
16383 window[trans.cb] = function(o){
16384 conn.handleResponse(o, trans);
16387 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16389 if(this.autoAbort !== false){
16393 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16395 var script = document.createElement("script");
16396 script.setAttribute("src", url);
16397 script.setAttribute("type", "text/javascript");
16398 script.setAttribute("id", trans.scriptId);
16399 this.head.appendChild(script);
16401 this.trans = trans;
16403 callback.call(scope||this, null, arg, false);
16408 isLoading : function(){
16409 return this.trans ? true : false;
16413 * Abort the current server request.
16415 abort : function(){
16416 if(this.isLoading()){
16417 this.destroyTrans(this.trans);
16422 destroyTrans : function(trans, isLoaded){
16423 this.head.removeChild(document.getElementById(trans.scriptId));
16424 clearTimeout(trans.timeoutId);
16426 window[trans.cb] = undefined;
16428 delete window[trans.cb];
16431 // if hasn't been loaded, wait for load to remove it to prevent script error
16432 window[trans.cb] = function(){
16433 window[trans.cb] = undefined;
16435 delete window[trans.cb];
16442 handleResponse : function(o, trans){
16443 this.trans = false;
16444 this.destroyTrans(trans, true);
16447 result = trans.reader.readRecords(o);
16449 this.fireEvent("loadexception", this, o, trans.arg, e);
16450 trans.callback.call(trans.scope||window, null, trans.arg, false);
16453 this.fireEvent("load", this, o, trans.arg);
16454 trans.callback.call(trans.scope||window, result, trans.arg, true);
16458 handleFailure : function(trans){
16459 this.trans = false;
16460 this.destroyTrans(trans, false);
16461 this.fireEvent("loadexception", this, null, trans.arg);
16462 trans.callback.call(trans.scope||window, null, trans.arg, false);
16466 * Ext JS Library 1.1.1
16467 * Copyright(c) 2006-2007, Ext JS, LLC.
16469 * Originally Released Under LGPL - original licence link has changed is not relivant.
16472 * <script type="text/javascript">
16476 * @class Roo.data.JsonReader
16477 * @extends Roo.data.DataReader
16478 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16479 * based on mappings in a provided Roo.data.Record constructor.
16481 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16482 * in the reply previously.
16487 var RecordDef = Roo.data.Record.create([
16488 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16489 {name: 'occupation'} // This field will use "occupation" as the mapping.
16491 var myReader = new Roo.data.JsonReader({
16492 totalProperty: "results", // The property which contains the total dataset size (optional)
16493 root: "rows", // The property which contains an Array of row objects
16494 id: "id" // The property within each row object that provides an ID for the record (optional)
16498 * This would consume a JSON file like this:
16500 { 'results': 2, 'rows': [
16501 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16502 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16505 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16506 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16507 * paged from the remote server.
16508 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16509 * @cfg {String} root name of the property which contains the Array of row objects.
16510 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16511 * @cfg {Array} fields Array of field definition objects
16513 * Create a new JsonReader
16514 * @param {Object} meta Metadata configuration options
16515 * @param {Object} recordType Either an Array of field definition objects,
16516 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16518 Roo.data.JsonReader = function(meta, recordType){
16521 // set some defaults:
16522 Roo.applyIf(meta, {
16523 totalProperty: 'total',
16524 successProperty : 'success',
16529 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16531 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16533 readerType : 'Json',
16536 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16537 * Used by Store query builder to append _requestMeta to params.
16540 metaFromRemote : false,
16542 * This method is only used by a DataProxy which has retrieved data from a remote server.
16543 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16544 * @return {Object} data A data block which is used by an Roo.data.Store object as
16545 * a cache of Roo.data.Records.
16547 read : function(response){
16548 var json = response.responseText;
16550 var o = /* eval:var:o */ eval("("+json+")");
16552 throw {message: "JsonReader.read: Json object not found"};
16558 this.metaFromRemote = true;
16559 this.meta = o.metaData;
16560 this.recordType = Roo.data.Record.create(o.metaData.fields);
16561 this.onMetaChange(this.meta, this.recordType, o);
16563 return this.readRecords(o);
16566 // private function a store will implement
16567 onMetaChange : function(meta, recordType, o){
16574 simpleAccess: function(obj, subsc) {
16581 getJsonAccessor: function(){
16583 return function(expr) {
16585 return(re.test(expr))
16586 ? new Function("obj", "return obj." + expr)
16591 return Roo.emptyFn;
16596 * Create a data block containing Roo.data.Records from an XML document.
16597 * @param {Object} o An object which contains an Array of row objects in the property specified
16598 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16599 * which contains the total size of the dataset.
16600 * @return {Object} data A data block which is used by an Roo.data.Store object as
16601 * a cache of Roo.data.Records.
16603 readRecords : function(o){
16605 * After any data loads, the raw JSON data is available for further custom processing.
16609 var s = this.meta, Record = this.recordType,
16610 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16612 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16614 if(s.totalProperty) {
16615 this.getTotal = this.getJsonAccessor(s.totalProperty);
16617 if(s.successProperty) {
16618 this.getSuccess = this.getJsonAccessor(s.successProperty);
16620 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16622 var g = this.getJsonAccessor(s.id);
16623 this.getId = function(rec) {
16625 return (r === undefined || r === "") ? null : r;
16628 this.getId = function(){return null;};
16631 for(var jj = 0; jj < fl; jj++){
16633 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16634 this.ef[jj] = this.getJsonAccessor(map);
16638 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16639 if(s.totalProperty){
16640 var vt = parseInt(this.getTotal(o), 10);
16645 if(s.successProperty){
16646 var vs = this.getSuccess(o);
16647 if(vs === false || vs === 'false'){
16652 for(var i = 0; i < c; i++){
16655 var id = this.getId(n);
16656 for(var j = 0; j < fl; j++){
16658 var v = this.ef[j](n);
16660 Roo.log('missing convert for ' + f.name);
16664 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16666 var record = new Record(values, id);
16668 records[i] = record;
16674 totalRecords : totalRecords
16677 // used when loading children.. @see loadDataFromChildren
16678 toLoadData: function(rec)
16680 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16681 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16682 return { data : data, total : data.length };
16687 * Ext JS Library 1.1.1
16688 * Copyright(c) 2006-2007, Ext JS, LLC.
16690 * Originally Released Under LGPL - original licence link has changed is not relivant.
16693 * <script type="text/javascript">
16697 * @class Roo.data.ArrayReader
16698 * @extends Roo.data.DataReader
16699 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16700 * Each element of that Array represents a row of data fields. The
16701 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16702 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16706 var RecordDef = Roo.data.Record.create([
16707 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16708 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16710 var myReader = new Roo.data.ArrayReader({
16711 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16715 * This would consume an Array like this:
16717 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16721 * Create a new JsonReader
16722 * @param {Object} meta Metadata configuration options.
16723 * @param {Object|Array} recordType Either an Array of field definition objects
16725 * @cfg {Array} fields Array of field definition objects
16726 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16727 * as specified to {@link Roo.data.Record#create},
16728 * or an {@link Roo.data.Record} object
16731 * created using {@link Roo.data.Record#create}.
16733 Roo.data.ArrayReader = function(meta, recordType)
16735 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16738 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16741 * Create a data block containing Roo.data.Records from an XML document.
16742 * @param {Object} o An Array of row objects which represents the dataset.
16743 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16744 * a cache of Roo.data.Records.
16746 readRecords : function(o)
16748 var sid = this.meta ? this.meta.id : null;
16749 var recordType = this.recordType, fields = recordType.prototype.fields;
16752 for(var i = 0; i < root.length; i++){
16755 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16756 for(var j = 0, jlen = fields.length; j < jlen; j++){
16757 var f = fields.items[j];
16758 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16759 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16761 values[f.name] = v;
16763 var record = new recordType(values, id);
16765 records[records.length] = record;
16769 totalRecords : records.length
16772 // used when loading children.. @see loadDataFromChildren
16773 toLoadData: function(rec)
16775 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16776 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16787 * @class Roo.bootstrap.form.ComboBox
16788 * @extends Roo.bootstrap.form.TriggerField
16789 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16790 * @cfg {Boolean} append (true|false) default false
16791 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16792 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16793 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16794 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16795 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16796 * @cfg {Boolean} animate default true
16797 * @cfg {Boolean} emptyResultText only for touch device
16798 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16799 * @cfg {String} emptyTitle default ''
16800 * @cfg {Number} width fixed with? experimental
16802 * Create a new ComboBox.
16803 * @param {Object} config Configuration options
16805 Roo.bootstrap.form.ComboBox = function(config){
16806 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16810 * Fires when the dropdown list is expanded
16811 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16816 * Fires when the dropdown list is collapsed
16817 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16821 * @event beforeselect
16822 * Fires before a list item is selected. Return false to cancel the selection.
16823 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16824 * @param {Roo.data.Record} record The data record returned from the underlying store
16825 * @param {Number} index The index of the selected item in the dropdown list
16827 'beforeselect' : true,
16830 * Fires when a list item is selected
16831 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16832 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16833 * @param {Number} index The index of the selected item in the dropdown list
16837 * @event beforequery
16838 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16839 * The event object passed has these properties:
16840 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16841 * @param {String} query The query
16842 * @param {Boolean} forceAll true to force "all" query
16843 * @param {Boolean} cancel true to cancel the query
16844 * @param {Object} e The query event object
16846 'beforequery': true,
16849 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16850 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16855 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16856 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16862 * Fires when the remove value from the combobox array
16863 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16867 * @event afterremove
16868 * Fires when the remove value from the combobox array
16869 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871 'afterremove' : true,
16873 * @event specialfilter
16874 * Fires when specialfilter
16875 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16877 'specialfilter' : true,
16880 * Fires when tick the element
16881 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16885 * @event touchviewdisplay
16886 * Fires when touch view require special display (default is using displayField)
16887 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16888 * @param {Object} cfg set html .
16890 'touchviewdisplay' : true
16895 this.tickItems = [];
16897 this.selectedIndex = -1;
16898 if(this.mode == 'local'){
16899 if(config.queryDelay === undefined){
16900 this.queryDelay = 10;
16902 if(config.minChars === undefined){
16908 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16911 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16912 * rendering into an Roo.Editor, defaults to false)
16915 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16916 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16919 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16922 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16923 * the dropdown list (defaults to undefined, with no header element)
16927 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16931 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16933 listWidth: undefined,
16935 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16936 * mode = 'remote' or 'text' if mode = 'local')
16938 displayField: undefined,
16941 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16942 * mode = 'remote' or 'value' if mode = 'local').
16943 * Note: use of a valueField requires the user make a selection
16944 * in order for a value to be mapped.
16946 valueField: undefined,
16948 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16953 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16954 * field's data value (defaults to the underlying DOM element's name)
16956 hiddenName: undefined,
16958 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16962 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16964 selectedClass: 'active',
16967 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16971 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16972 * anchor positions (defaults to 'tl-bl')
16974 listAlign: 'tl-bl?',
16976 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16980 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16981 * query specified by the allQuery config option (defaults to 'query')
16983 triggerAction: 'query',
16985 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16986 * (defaults to 4, does not apply if editable = false)
16990 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16991 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16995 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16996 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17000 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17001 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17005 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17006 * when editable = true (defaults to false)
17008 selectOnFocus:false,
17010 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17012 queryParam: 'query',
17014 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17015 * when mode = 'remote' (defaults to 'Loading...')
17017 loadingText: 'Loading...',
17019 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17023 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17027 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17028 * traditional select (defaults to true)
17032 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17036 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17040 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17041 * listWidth has a higher value)
17045 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17046 * allow the user to set arbitrary text into the field (defaults to false)
17048 forceSelection:false,
17050 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17051 * if typeAhead = true (defaults to 250)
17053 typeAheadDelay : 250,
17055 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17056 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17058 valueNotFoundText : undefined,
17060 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17062 blockFocus : false,
17065 * @cfg {Boolean} disableClear Disable showing of clear button.
17067 disableClear : false,
17069 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17071 alwaysQuery : false,
17074 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17079 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17081 invalidClass : "has-warning",
17084 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17086 validClass : "has-success",
17089 * @cfg {Boolean} specialFilter (true|false) special filter default false
17091 specialFilter : false,
17094 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17096 mobileTouchView : true,
17099 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17101 useNativeIOS : false,
17104 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17106 mobile_restrict_height : false,
17108 ios_options : false,
17120 btnPosition : 'right',
17121 triggerList : true,
17122 showToggleBtn : true,
17124 emptyResultText: 'Empty',
17125 triggerText : 'Select',
17129 // element that contains real text value.. (when hidden is used..)
17131 getAutoCreate : function()
17136 * Render classic select for iso
17139 if(Roo.isIOS && this.useNativeIOS){
17140 cfg = this.getAutoCreateNativeIOS();
17148 if(Roo.isTouch && this.mobileTouchView){
17149 cfg = this.getAutoCreateTouchView();
17156 if(!this.tickable){
17157 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17162 * ComboBox with tickable selections
17165 var align = this.labelAlign || this.parentLabelAlign();
17168 cls : 'form-group roo-combobox-tickable' //input-group
17171 var btn_text_select = '';
17172 var btn_text_done = '';
17173 var btn_text_cancel = '';
17175 if (this.btn_text_show) {
17176 btn_text_select = 'Select';
17177 btn_text_done = 'Done';
17178 btn_text_cancel = 'Cancel';
17183 cls : 'tickable-buttons',
17188 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17189 //html : this.triggerText
17190 html: btn_text_select
17196 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17198 html: btn_text_done
17204 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17206 html: btn_text_cancel
17212 buttons.cn.unshift({
17214 cls: 'roo-select2-search-field-input'
17220 Roo.each(buttons.cn, function(c){
17222 c.cls += ' btn-' + _this.size;
17225 if (_this.disabled) {
17232 style : 'display: contents',
17237 cls: 'form-hidden-field'
17241 cls: 'roo-select2-choices',
17245 cls: 'roo-select2-search-field',
17256 cls: 'roo-select2-container input-group roo-select2-container-multi',
17262 // cls: 'typeahead typeahead-long dropdown-menu',
17263 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17268 if(this.hasFeedback && !this.allowBlank){
17272 cls: 'glyphicon form-control-feedback'
17275 combobox.cn.push(feedback);
17282 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17283 tooltip : 'This field is required'
17285 if (Roo.bootstrap.version == 4) {
17288 style : 'display:none'
17291 if (align ==='left' && this.fieldLabel.length) {
17293 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17300 cls : 'control-label col-form-label',
17301 html : this.fieldLabel
17313 var labelCfg = cfg.cn[1];
17314 var contentCfg = cfg.cn[2];
17317 if(this.indicatorpos == 'right'){
17323 cls : 'control-label col-form-label',
17327 html : this.fieldLabel
17343 labelCfg = cfg.cn[0];
17344 contentCfg = cfg.cn[1];
17348 if(this.labelWidth > 12){
17349 labelCfg.style = "width: " + this.labelWidth + 'px';
17351 if(this.width * 1 > 0){
17352 contentCfg.style = "width: " + this.width + 'px';
17354 if(this.labelWidth < 13 && this.labelmd == 0){
17355 this.labelmd = this.labelWidth;
17358 if(this.labellg > 0){
17359 labelCfg.cls += ' col-lg-' + this.labellg;
17360 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17363 if(this.labelmd > 0){
17364 labelCfg.cls += ' col-md-' + this.labelmd;
17365 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17368 if(this.labelsm > 0){
17369 labelCfg.cls += ' col-sm-' + this.labelsm;
17370 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17373 if(this.labelxs > 0){
17374 labelCfg.cls += ' col-xs-' + this.labelxs;
17375 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17379 } else if ( this.fieldLabel.length) {
17380 // Roo.log(" label");
17385 //cls : 'input-group-addon',
17386 html : this.fieldLabel
17391 if(this.indicatorpos == 'right'){
17395 //cls : 'input-group-addon',
17396 html : this.fieldLabel
17406 // Roo.log(" no label && no align");
17413 ['xs','sm','md','lg'].map(function(size){
17414 if (settings[size]) {
17415 cfg.cls += ' col-' + size + '-' + settings[size];
17423 _initEventsCalled : false,
17426 initEvents: function()
17428 if (this._initEventsCalled) { // as we call render... prevent looping...
17431 this._initEventsCalled = true;
17434 throw "can not find store for combo";
17437 this.indicator = this.indicatorEl();
17439 this.store = Roo.factory(this.store, Roo.data);
17440 this.store.parent = this;
17442 // if we are building from html. then this element is so complex, that we can not really
17443 // use the rendered HTML.
17444 // so we have to trash and replace the previous code.
17445 if (Roo.XComponent.build_from_html) {
17446 // remove this element....
17447 var e = this.el.dom, k=0;
17448 while (e ) { e = e.previousSibling; ++k;}
17453 this.rendered = false;
17455 this.render(this.parent().getChildContainer(true), k);
17458 if(Roo.isIOS && this.useNativeIOS){
17459 this.initIOSView();
17467 if(Roo.isTouch && this.mobileTouchView){
17468 this.initTouchView();
17473 this.initTickableEvents();
17477 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17479 if(this.hiddenName){
17481 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17483 this.hiddenField.dom.value =
17484 this.hiddenValue !== undefined ? this.hiddenValue :
17485 this.value !== undefined ? this.value : '';
17487 // prevent input submission
17488 this.el.dom.removeAttribute('name');
17489 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17494 // this.el.dom.setAttribute('autocomplete', 'off');
17497 var cls = 'x-combo-list';
17499 //this.list = new Roo.Layer({
17500 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17506 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17507 _this.list.setWidth(lw);
17510 this.list.on('mouseover', this.onViewOver, this);
17511 this.list.on('mousemove', this.onViewMove, this);
17512 this.list.on('scroll', this.onViewScroll, this);
17515 this.list.swallowEvent('mousewheel');
17516 this.assetHeight = 0;
17519 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17520 this.assetHeight += this.header.getHeight();
17523 this.innerList = this.list.createChild({cls:cls+'-inner'});
17524 this.innerList.on('mouseover', this.onViewOver, this);
17525 this.innerList.on('mousemove', this.onViewMove, this);
17526 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17528 if(this.allowBlank && !this.pageSize && !this.disableClear){
17529 this.footer = this.list.createChild({cls:cls+'-ft'});
17530 this.pageTb = new Roo.Toolbar(this.footer);
17534 this.footer = this.list.createChild({cls:cls+'-ft'});
17535 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17536 {pageSize: this.pageSize});
17540 if (this.pageTb && this.allowBlank && !this.disableClear) {
17542 this.pageTb.add(new Roo.Toolbar.Fill(), {
17543 cls: 'x-btn-icon x-btn-clear',
17545 handler: function()
17548 _this.clearValue();
17549 _this.onSelect(false, -1);
17554 this.assetHeight += this.footer.getHeight();
17559 this.tpl = Roo.bootstrap.version == 4 ?
17560 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17561 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17564 this.view = new Roo.View(this.list, this.tpl, {
17565 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17567 //this.view.wrapEl.setDisplayed(false);
17568 this.view.on('click', this.onViewClick, this);
17571 this.store.on('beforeload', this.onBeforeLoad, this);
17572 this.store.on('load', this.onLoad, this);
17573 this.store.on('loadexception', this.onLoadException, this);
17575 if(this.resizable){
17576 this.resizer = new Roo.Resizable(this.list, {
17577 pinned:true, handles:'se'
17579 this.resizer.on('resize', function(r, w, h){
17580 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17581 this.listWidth = w;
17582 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17583 this.restrictHeight();
17585 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17588 if(!this.editable){
17589 this.editable = true;
17590 this.setEditable(false);
17595 if (typeof(this.events.add.listeners) != 'undefined') {
17597 this.addicon = this.wrap.createChild(
17598 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17600 this.addicon.on('click', function(e) {
17601 this.fireEvent('add', this);
17604 if (typeof(this.events.edit.listeners) != 'undefined') {
17606 this.editicon = this.wrap.createChild(
17607 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17608 if (this.addicon) {
17609 this.editicon.setStyle('margin-left', '40px');
17611 this.editicon.on('click', function(e) {
17613 // we fire even if inothing is selected..
17614 this.fireEvent('edit', this, this.lastData );
17620 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17621 "up" : function(e){
17622 this.inKeyMode = true;
17626 "down" : function(e){
17627 if(!this.isExpanded()){
17628 this.onTriggerClick();
17630 this.inKeyMode = true;
17635 "enter" : function(e){
17636 // this.onViewClick();
17640 if(this.fireEvent("specialkey", this, e)){
17641 this.onViewClick(false);
17647 "esc" : function(e){
17651 "tab" : function(e){
17654 if(this.fireEvent("specialkey", this, e)){
17655 this.onViewClick(false);
17663 doRelay : function(foo, bar, hname){
17664 if(hname == 'down' || this.scope.isExpanded()){
17665 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17674 this.queryDelay = Math.max(this.queryDelay || 10,
17675 this.mode == 'local' ? 10 : 250);
17678 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17680 if(this.typeAhead){
17681 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17683 if(this.editable !== false){
17684 this.inputEl().on("keyup", this.onKeyUp, this);
17686 if(this.forceSelection){
17687 this.inputEl().on('blur', this.doForce, this);
17691 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17692 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17696 initTickableEvents: function()
17700 if(this.hiddenName){
17702 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17704 this.hiddenField.dom.value =
17705 this.hiddenValue !== undefined ? this.hiddenValue :
17706 this.value !== undefined ? this.value : '';
17708 // prevent input submission
17709 this.el.dom.removeAttribute('name');
17710 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17715 // this.list = this.el.select('ul.dropdown-menu',true).first();
17717 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17718 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17719 if(this.triggerList){
17720 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17723 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17724 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17726 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17727 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17729 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17730 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17732 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17733 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17734 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17737 this.cancelBtn.hide();
17742 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17743 _this.list.setWidth(lw);
17746 this.list.on('mouseover', this.onViewOver, this);
17747 this.list.on('mousemove', this.onViewMove, this);
17749 this.list.on('scroll', this.onViewScroll, this);
17752 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17753 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17756 this.view = new Roo.View(this.list, this.tpl, {
17761 selectedClass: this.selectedClass
17764 //this.view.wrapEl.setDisplayed(false);
17765 this.view.on('click', this.onViewClick, this);
17769 this.store.on('beforeload', this.onBeforeLoad, this);
17770 this.store.on('load', this.onLoad, this);
17771 this.store.on('loadexception', this.onLoadException, this);
17774 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17775 "up" : function(e){
17776 this.inKeyMode = true;
17780 "down" : function(e){
17781 this.inKeyMode = true;
17785 "enter" : function(e){
17786 if(this.fireEvent("specialkey", this, e)){
17787 this.onViewClick(false);
17793 "esc" : function(e){
17794 this.onTickableFooterButtonClick(e, false, false);
17797 "tab" : function(e){
17798 this.fireEvent("specialkey", this, e);
17800 this.onTickableFooterButtonClick(e, false, false);
17807 doRelay : function(e, fn, key){
17808 if(this.scope.isExpanded()){
17809 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17818 this.queryDelay = Math.max(this.queryDelay || 10,
17819 this.mode == 'local' ? 10 : 250);
17822 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17824 if(this.typeAhead){
17825 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17828 if(this.editable !== false){
17829 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17832 this.indicator = this.indicatorEl();
17834 if(this.indicator){
17835 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17836 this.indicator.hide();
17841 onDestroy : function(){
17843 this.view.setStore(null);
17844 this.view.el.removeAllListeners();
17845 this.view.el.remove();
17846 this.view.purgeListeners();
17849 this.list.dom.innerHTML = '';
17853 this.store.un('beforeload', this.onBeforeLoad, this);
17854 this.store.un('load', this.onLoad, this);
17855 this.store.un('loadexception', this.onLoadException, this);
17857 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17861 fireKey : function(e){
17862 if(e.isNavKeyPress() && !this.list.isVisible()){
17863 this.fireEvent("specialkey", this, e);
17868 onResize: function(w, h)
17872 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17874 // if(typeof w != 'number'){
17875 // // we do not handle it!?!?
17878 // var tw = this.trigger.getWidth();
17879 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17880 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17882 // this.inputEl().setWidth( this.adjustWidth('input', x));
17884 // //this.trigger.setStyle('left', x+'px');
17886 // if(this.list && this.listWidth === undefined){
17887 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17888 // this.list.setWidth(lw);
17889 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17897 * Allow or prevent the user from directly editing the field text. If false is passed,
17898 * the user will only be able to select from the items defined in the dropdown list. This method
17899 * is the runtime equivalent of setting the 'editable' config option at config time.
17900 * @param {Boolean} value True to allow the user to directly edit the field text
17902 setEditable : function(value){
17903 if(value == this.editable){
17906 this.editable = value;
17908 this.inputEl().dom.setAttribute('readOnly', true);
17909 this.inputEl().on('mousedown', this.onTriggerClick, this);
17910 this.inputEl().addClass('x-combo-noedit');
17912 this.inputEl().dom.removeAttribute('readOnly');
17913 this.inputEl().un('mousedown', this.onTriggerClick, this);
17914 this.inputEl().removeClass('x-combo-noedit');
17920 onBeforeLoad : function(combo,opts){
17921 if(!this.hasFocus){
17925 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17927 this.restrictHeight();
17928 this.selectedIndex = -1;
17932 onLoad : function(){
17934 this.hasQuery = false;
17936 if(!this.hasFocus){
17940 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17941 this.loading.hide();
17944 if(this.store.getCount() > 0){
17947 this.restrictHeight();
17948 if(this.lastQuery == this.allQuery){
17949 if(this.editable && !this.tickable){
17950 this.inputEl().dom.select();
17954 !this.selectByValue(this.value, true) &&
17957 !this.store.lastOptions ||
17958 typeof(this.store.lastOptions.add) == 'undefined' ||
17959 this.store.lastOptions.add != true
17962 this.select(0, true);
17965 if(this.autoFocus){
17968 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17969 this.taTask.delay(this.typeAheadDelay);
17973 this.onEmptyResults();
17979 onLoadException : function()
17981 this.hasQuery = false;
17983 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17984 this.loading.hide();
17987 if(this.tickable && this.editable){
17992 // only causes errors at present
17993 //Roo.log(this.store.reader.jsonData);
17994 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17996 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18002 onTypeAhead : function(){
18003 if(this.store.getCount() > 0){
18004 var r = this.store.getAt(0);
18005 var newValue = r.data[this.displayField];
18006 var len = newValue.length;
18007 var selStart = this.getRawValue().length;
18009 if(selStart != len){
18010 this.setRawValue(newValue);
18011 this.selectText(selStart, newValue.length);
18017 onSelect : function(record, index){
18019 if(this.fireEvent('beforeselect', this, record, index) !== false){
18021 this.setFromData(index > -1 ? record.data : false);
18024 this.fireEvent('select', this, record, index);
18029 * Returns the currently selected field value or empty string if no value is set.
18030 * @return {String} value The selected value
18032 getValue : function()
18034 if(Roo.isIOS && this.useNativeIOS){
18035 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18039 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18042 if(this.valueField){
18043 return typeof this.value != 'undefined' ? this.value : '';
18045 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18049 getRawValue : function()
18051 if(Roo.isIOS && this.useNativeIOS){
18052 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18055 var v = this.inputEl().getValue();
18061 * Clears any text/value currently set in the field
18063 clearValue : function(){
18065 if(this.hiddenField){
18066 this.hiddenField.dom.value = '';
18069 this.setRawValue('');
18070 this.lastSelectionText = '';
18071 this.lastData = false;
18073 var close = this.closeTriggerEl();
18084 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18085 * will be displayed in the field. If the value does not match the data value of an existing item,
18086 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18087 * Otherwise the field will be blank (although the value will still be set).
18088 * @param {String} value The value to match
18090 setValue : function(v)
18092 if(Roo.isIOS && this.useNativeIOS){
18093 this.setIOSValue(v);
18103 if(this.valueField){
18104 var r = this.findRecord(this.valueField, v);
18106 text = r.data[this.displayField];
18107 }else if(this.valueNotFoundText !== undefined){
18108 text = this.valueNotFoundText;
18111 this.lastSelectionText = text;
18112 if(this.hiddenField){
18113 this.hiddenField.dom.value = v;
18115 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18118 var close = this.closeTriggerEl();
18121 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18127 * @property {Object} the last set data for the element
18132 * Sets the value of the field based on a object which is related to the record format for the store.
18133 * @param {Object} value the value to set as. or false on reset?
18135 setFromData : function(o){
18142 var dv = ''; // display value
18143 var vv = ''; // value value..
18145 if (this.displayField) {
18146 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18148 // this is an error condition!!!
18149 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18152 if(this.valueField){
18153 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18156 var close = this.closeTriggerEl();
18159 if(dv.length || vv * 1 > 0){
18161 this.blockFocus=true;
18167 if(this.hiddenField){
18168 this.hiddenField.dom.value = vv;
18170 this.lastSelectionText = dv;
18171 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18175 // no hidden field.. - we store the value in 'value', but still display
18176 // display field!!!!
18177 this.lastSelectionText = dv;
18178 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18185 reset : function(){
18186 // overridden so that last data is reset..
18193 this.setValue(this.originalValue);
18194 //this.clearInvalid();
18195 this.lastData = false;
18197 this.view.clearSelections();
18203 findRecord : function(prop, value){
18205 if(this.store.getCount() > 0){
18206 this.store.each(function(r){
18207 if(r.data[prop] == value){
18217 getName: function()
18219 // returns hidden if it's set..
18220 if (!this.rendered) {return ''};
18221 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18225 onViewMove : function(e, t){
18226 this.inKeyMode = false;
18230 onViewOver : function(e, t){
18231 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18234 var item = this.view.findItemFromChild(t);
18237 var index = this.view.indexOf(item);
18238 this.select(index, false);
18243 onViewClick : function(view, doFocus, el, e)
18245 var index = this.view.getSelectedIndexes()[0];
18247 var r = this.store.getAt(index);
18251 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18258 Roo.each(this.tickItems, function(v,k){
18260 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18262 _this.tickItems.splice(k, 1);
18264 if(typeof(e) == 'undefined' && view == false){
18265 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18277 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18278 this.tickItems.push(r.data);
18281 if(typeof(e) == 'undefined' && view == false){
18282 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18289 this.onSelect(r, index);
18291 if(doFocus !== false && !this.blockFocus){
18292 this.inputEl().focus();
18297 restrictHeight : function(){
18298 //this.innerList.dom.style.height = '';
18299 //var inner = this.innerList.dom;
18300 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18301 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18302 //this.list.beginUpdate();
18303 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18304 this.list.alignTo(this.inputEl(), this.listAlign);
18305 this.list.alignTo(this.inputEl(), this.listAlign);
18306 //this.list.endUpdate();
18310 onEmptyResults : function(){
18312 if(this.tickable && this.editable){
18313 this.hasFocus = false;
18314 this.restrictHeight();
18322 * Returns true if the dropdown list is expanded, else false.
18324 isExpanded : function(){
18325 return this.list.isVisible();
18329 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18330 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18331 * @param {String} value The data value of the item to select
18332 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18333 * selected item if it is not currently in view (defaults to true)
18334 * @return {Boolean} True if the value matched an item in the list, else false
18336 selectByValue : function(v, scrollIntoView){
18337 if(v !== undefined && v !== null){
18338 var r = this.findRecord(this.valueField || this.displayField, v);
18340 this.select(this.store.indexOf(r), scrollIntoView);
18348 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18349 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18350 * @param {Number} index The zero-based index of the list item to select
18351 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18352 * selected item if it is not currently in view (defaults to true)
18354 select : function(index, scrollIntoView){
18355 this.selectedIndex = index;
18356 this.view.select(index);
18357 if(scrollIntoView !== false){
18358 var el = this.view.getNode(index);
18360 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18363 this.list.scrollChildIntoView(el, false);
18369 selectNext : function(){
18370 var ct = this.store.getCount();
18372 if(this.selectedIndex == -1){
18374 }else if(this.selectedIndex < ct-1){
18375 this.select(this.selectedIndex+1);
18381 selectPrev : function(){
18382 var ct = this.store.getCount();
18384 if(this.selectedIndex == -1){
18386 }else if(this.selectedIndex != 0){
18387 this.select(this.selectedIndex-1);
18393 onKeyUp : function(e){
18394 if(this.editable !== false && !e.isSpecialKey()){
18395 this.lastKey = e.getKey();
18396 this.dqTask.delay(this.queryDelay);
18401 validateBlur : function(){
18402 return !this.list || !this.list.isVisible();
18406 initQuery : function(){
18408 var v = this.getRawValue();
18410 if(this.tickable && this.editable){
18411 v = this.tickableInputEl().getValue();
18418 doForce : function(){
18419 if(this.inputEl().dom.value.length > 0){
18420 this.inputEl().dom.value =
18421 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18427 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18428 * query allowing the query action to be canceled if needed.
18429 * @param {String} query The SQL query to execute
18430 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18431 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18432 * saved in the current store (defaults to false)
18434 doQuery : function(q, forceAll){
18436 if(q === undefined || q === null){
18441 forceAll: forceAll,
18445 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18450 forceAll = qe.forceAll;
18451 if(forceAll === true || (q.length >= this.minChars)){
18453 this.hasQuery = true;
18455 if(this.lastQuery != q || this.alwaysQuery){
18456 this.lastQuery = q;
18457 if(this.mode == 'local'){
18458 this.selectedIndex = -1;
18460 this.store.clearFilter();
18463 if(this.specialFilter){
18464 this.fireEvent('specialfilter', this);
18469 this.store.filter(this.displayField, q);
18472 this.store.fireEvent("datachanged", this.store);
18479 this.store.baseParams[this.queryParam] = q;
18481 var options = {params : this.getParams(q)};
18484 options.add = true;
18485 options.params.start = this.page * this.pageSize;
18488 this.store.load(options);
18491 * this code will make the page width larger, at the beginning, the list not align correctly,
18492 * we should expand the list on onLoad
18493 * so command out it
18498 this.selectedIndex = -1;
18503 this.loadNext = false;
18507 getParams : function(q){
18509 //p[this.queryParam] = q;
18513 p.limit = this.pageSize;
18519 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18521 collapse : function(){
18522 if(!this.isExpanded()){
18528 this.hasFocus = false;
18532 this.cancelBtn.hide();
18533 this.trigger.show();
18536 this.tickableInputEl().dom.value = '';
18537 this.tickableInputEl().blur();
18542 Roo.get(document).un('mousedown', this.collapseIf, this);
18543 Roo.get(document).un('mousewheel', this.collapseIf, this);
18544 if (!this.editable) {
18545 Roo.get(document).un('keydown', this.listKeyPress, this);
18547 this.fireEvent('collapse', this);
18553 collapseIf : function(e){
18554 var in_combo = e.within(this.el);
18555 var in_list = e.within(this.list);
18556 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18558 if (in_combo || in_list || is_list) {
18559 //e.stopPropagation();
18564 this.onTickableFooterButtonClick(e, false, false);
18572 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18574 expand : function(){
18576 if(this.isExpanded() || !this.hasFocus){
18580 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18581 this.list.setWidth(lw);
18587 this.restrictHeight();
18591 this.tickItems = Roo.apply([], this.item);
18594 this.cancelBtn.show();
18595 this.trigger.hide();
18598 this.tickableInputEl().focus();
18603 Roo.get(document).on('mousedown', this.collapseIf, this);
18604 Roo.get(document).on('mousewheel', this.collapseIf, this);
18605 if (!this.editable) {
18606 Roo.get(document).on('keydown', this.listKeyPress, this);
18609 this.fireEvent('expand', this);
18613 // Implements the default empty TriggerField.onTriggerClick function
18614 onTriggerClick : function(e)
18616 Roo.log('trigger click');
18618 if(this.disabled || !this.triggerList){
18623 this.loadNext = false;
18625 if(this.isExpanded()){
18627 if (!this.blockFocus) {
18628 this.inputEl().focus();
18632 this.hasFocus = true;
18633 if(this.triggerAction == 'all') {
18634 this.doQuery(this.allQuery, true);
18636 this.doQuery(this.getRawValue());
18638 if (!this.blockFocus) {
18639 this.inputEl().focus();
18644 onTickableTriggerClick : function(e)
18651 this.loadNext = false;
18652 this.hasFocus = true;
18654 if(this.triggerAction == 'all') {
18655 this.doQuery(this.allQuery, true);
18657 this.doQuery(this.getRawValue());
18661 onSearchFieldClick : function(e)
18663 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18664 this.onTickableFooterButtonClick(e, false, false);
18668 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18673 this.loadNext = false;
18674 this.hasFocus = true;
18676 if(this.triggerAction == 'all') {
18677 this.doQuery(this.allQuery, true);
18679 this.doQuery(this.getRawValue());
18683 listKeyPress : function(e)
18685 //Roo.log('listkeypress');
18686 // scroll to first matching element based on key pres..
18687 if (e.isSpecialKey()) {
18690 var k = String.fromCharCode(e.getKey()).toUpperCase();
18693 var csel = this.view.getSelectedNodes();
18694 var cselitem = false;
18696 var ix = this.view.indexOf(csel[0]);
18697 cselitem = this.store.getAt(ix);
18698 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18704 this.store.each(function(v) {
18706 // start at existing selection.
18707 if (cselitem.id == v.id) {
18713 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18714 match = this.store.indexOf(v);
18720 if (match === false) {
18721 return true; // no more action?
18724 this.view.select(match);
18725 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18726 sn.scrollIntoView(sn.dom.parentNode, false);
18729 onViewScroll : function(e, t){
18731 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){
18735 this.hasQuery = true;
18737 this.loading = this.list.select('.loading', true).first();
18739 if(this.loading === null){
18740 this.list.createChild({
18742 cls: 'loading roo-select2-more-results roo-select2-active',
18743 html: 'Loading more results...'
18746 this.loading = this.list.select('.loading', true).first();
18748 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18750 this.loading.hide();
18753 this.loading.show();
18758 this.loadNext = true;
18760 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18765 addItem : function(o)
18767 var dv = ''; // display value
18769 if (this.displayField) {
18770 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18772 // this is an error condition!!!
18773 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18780 var choice = this.choices.createChild({
18782 cls: 'roo-select2-search-choice',
18791 cls: 'roo-select2-search-choice-close fa fa-times',
18796 }, this.searchField);
18798 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18800 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18808 this.inputEl().dom.value = '';
18813 onRemoveItem : function(e, _self, o)
18815 e.preventDefault();
18817 this.lastItem = Roo.apply([], this.item);
18819 var index = this.item.indexOf(o.data) * 1;
18822 Roo.log('not this item?!');
18826 this.item.splice(index, 1);
18831 this.fireEvent('remove', this, e);
18837 syncValue : function()
18839 if(!this.item.length){
18846 Roo.each(this.item, function(i){
18847 if(_this.valueField){
18848 value.push(i[_this.valueField]);
18855 this.value = value.join(',');
18857 if(this.hiddenField){
18858 this.hiddenField.dom.value = this.value;
18861 this.store.fireEvent("datachanged", this.store);
18866 clearItem : function()
18868 if(!this.multiple){
18874 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18882 if(this.tickable && !Roo.isTouch){
18883 this.view.refresh();
18887 inputEl: function ()
18889 if(Roo.isIOS && this.useNativeIOS){
18890 return this.el.select('select.roo-ios-select', true).first();
18893 if(Roo.isTouch && this.mobileTouchView){
18894 return this.el.select('input.form-control',true).first();
18898 return this.searchField;
18901 return this.el.select('input.form-control',true).first();
18904 onTickableFooterButtonClick : function(e, btn, el)
18906 e.preventDefault();
18908 this.lastItem = Roo.apply([], this.item);
18910 if(btn && btn.name == 'cancel'){
18911 this.tickItems = Roo.apply([], this.item);
18920 Roo.each(this.tickItems, function(o){
18928 validate : function()
18930 if(this.getVisibilityEl().hasClass('hidden')){
18934 var v = this.getRawValue();
18937 v = this.getValue();
18940 if(this.disabled || this.allowBlank || v.length){
18945 this.markInvalid();
18949 tickableInputEl : function()
18951 if(!this.tickable || !this.editable){
18952 return this.inputEl();
18955 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18959 getAutoCreateTouchView : function()
18964 cls: 'form-group' //input-group
18970 type : this.inputType,
18971 cls : 'form-control x-combo-noedit',
18972 autocomplete: 'new-password',
18973 placeholder : this.placeholder || '',
18978 input.name = this.name;
18982 input.cls += ' input-' + this.size;
18985 if (this.disabled) {
18986 input.disabled = true;
18990 cls : 'roo-combobox-wrap',
18997 inputblock.cls += ' input-group';
18999 inputblock.cn.unshift({
19001 cls : 'input-group-addon input-group-prepend input-group-text',
19006 if(this.removable && !this.multiple){
19007 inputblock.cls += ' roo-removable';
19009 inputblock.cn.push({
19012 cls : 'roo-combo-removable-btn close'
19016 if(this.hasFeedback && !this.allowBlank){
19018 inputblock.cls += ' has-feedback';
19020 inputblock.cn.push({
19022 cls: 'glyphicon form-control-feedback'
19029 inputblock.cls += (this.before) ? '' : ' input-group';
19031 inputblock.cn.push({
19033 cls : 'input-group-addon input-group-append input-group-text',
19039 var ibwrap = inputblock;
19044 cls: 'roo-select2-choices',
19048 cls: 'roo-select2-search-field',
19061 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19066 cls: 'form-hidden-field'
19072 if(!this.multiple && this.showToggleBtn){
19078 if (this.caret != false) {
19081 cls: 'fa fa-' + this.caret
19088 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19090 Roo.bootstrap.version == 3 ? caret : '',
19093 cls: 'combobox-clear',
19107 combobox.cls += ' roo-select2-container-multi';
19110 var required = this.allowBlank ? {
19112 style: 'display: none'
19115 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19116 tooltip : 'This field is required'
19119 var align = this.labelAlign || this.parentLabelAlign();
19121 if (align ==='left' && this.fieldLabel.length) {
19127 cls : 'control-label col-form-label',
19128 html : this.fieldLabel
19132 cls : 'roo-combobox-wrap ',
19139 var labelCfg = cfg.cn[1];
19140 var contentCfg = cfg.cn[2];
19143 if(this.indicatorpos == 'right'){
19148 cls : 'control-label col-form-label',
19152 html : this.fieldLabel
19158 cls : "roo-combobox-wrap ",
19166 labelCfg = cfg.cn[0];
19167 contentCfg = cfg.cn[1];
19172 if(this.labelWidth > 12){
19173 labelCfg.style = "width: " + this.labelWidth + 'px';
19176 if(this.labelWidth < 13 && this.labelmd == 0){
19177 this.labelmd = this.labelWidth;
19180 if(this.labellg > 0){
19181 labelCfg.cls += ' col-lg-' + this.labellg;
19182 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19185 if(this.labelmd > 0){
19186 labelCfg.cls += ' col-md-' + this.labelmd;
19187 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19190 if(this.labelsm > 0){
19191 labelCfg.cls += ' col-sm-' + this.labelsm;
19192 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19195 if(this.labelxs > 0){
19196 labelCfg.cls += ' col-xs-' + this.labelxs;
19197 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19201 } else if ( this.fieldLabel.length) {
19206 cls : 'control-label',
19207 html : this.fieldLabel
19218 if(this.indicatorpos == 'right'){
19222 cls : 'control-label',
19223 html : this.fieldLabel,
19241 var settings = this;
19243 ['xs','sm','md','lg'].map(function(size){
19244 if (settings[size]) {
19245 cfg.cls += ' col-' + size + '-' + settings[size];
19252 initTouchView : function()
19254 this.renderTouchView();
19256 this.touchViewEl.on('scroll', function(){
19257 this.el.dom.scrollTop = 0;
19260 this.originalValue = this.getValue();
19262 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19264 this.inputEl().on("click", this.showTouchView, this);
19265 if (this.triggerEl) {
19266 this.triggerEl.on("click", this.showTouchView, this);
19270 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19271 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19273 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19275 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19276 this.store.on('load', this.onTouchViewLoad, this);
19277 this.store.on('loadexception', this.onTouchViewLoadException, this);
19279 if(this.hiddenName){
19281 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19283 this.hiddenField.dom.value =
19284 this.hiddenValue !== undefined ? this.hiddenValue :
19285 this.value !== undefined ? this.value : '';
19287 this.el.dom.removeAttribute('name');
19288 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19292 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19293 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19296 if(this.removable && !this.multiple){
19297 var close = this.closeTriggerEl();
19299 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19300 close.on('click', this.removeBtnClick, this, close);
19304 * fix the bug in Safari iOS8
19306 this.inputEl().on("focus", function(e){
19307 document.activeElement.blur();
19310 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19317 renderTouchView : function()
19319 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19320 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19322 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19323 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19325 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19326 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327 this.touchViewBodyEl.setStyle('overflow', 'auto');
19329 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19330 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19332 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19333 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19337 showTouchView : function()
19343 this.touchViewHeaderEl.hide();
19345 if(this.modalTitle.length){
19346 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19347 this.touchViewHeaderEl.show();
19350 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19351 this.touchViewEl.show();
19353 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19355 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19356 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19358 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19360 if(this.modalTitle.length){
19361 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19364 this.touchViewBodyEl.setHeight(bodyHeight);
19368 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19370 this.touchViewEl.addClass(['in','show']);
19373 if(this._touchViewMask){
19374 Roo.get(document.body).addClass("x-body-masked");
19375 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19376 this._touchViewMask.setStyle('z-index', 10000);
19377 this._touchViewMask.addClass('show');
19380 this.doTouchViewQuery();
19384 hideTouchView : function()
19386 this.touchViewEl.removeClass(['in','show']);
19390 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19392 this.touchViewEl.setStyle('display', 'none');
19395 if(this._touchViewMask){
19396 this._touchViewMask.removeClass('show');
19397 Roo.get(document.body).removeClass("x-body-masked");
19401 setTouchViewValue : function()
19408 Roo.each(this.tickItems, function(o){
19413 this.hideTouchView();
19416 doTouchViewQuery : function()
19425 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19429 if(!this.alwaysQuery || this.mode == 'local'){
19430 this.onTouchViewLoad();
19437 onTouchViewBeforeLoad : function(combo,opts)
19443 onTouchViewLoad : function()
19445 if(this.store.getCount() < 1){
19446 this.onTouchViewEmptyResults();
19450 this.clearTouchView();
19452 var rawValue = this.getRawValue();
19454 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19456 this.tickItems = [];
19458 this.store.data.each(function(d, rowIndex){
19459 var row = this.touchViewListGroup.createChild(template);
19461 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19462 row.addClass(d.data.cls);
19465 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19468 html : d.data[this.displayField]
19471 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19472 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19475 row.removeClass('selected');
19476 if(!this.multiple && this.valueField &&
19477 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19480 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19481 row.addClass('selected');
19484 if(this.multiple && this.valueField &&
19485 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19489 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19490 this.tickItems.push(d.data);
19493 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19497 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19499 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19501 if(this.modalTitle.length){
19502 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19505 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19507 if(this.mobile_restrict_height && listHeight < bodyHeight){
19508 this.touchViewBodyEl.setHeight(listHeight);
19513 if(firstChecked && listHeight > bodyHeight){
19514 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19519 onTouchViewLoadException : function()
19521 this.hideTouchView();
19524 onTouchViewEmptyResults : function()
19526 this.clearTouchView();
19528 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19530 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19534 clearTouchView : function()
19536 this.touchViewListGroup.dom.innerHTML = '';
19539 onTouchViewClick : function(e, el, o)
19541 e.preventDefault();
19544 var rowIndex = o.rowIndex;
19546 var r = this.store.getAt(rowIndex);
19548 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19550 if(!this.multiple){
19551 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19552 c.dom.removeAttribute('checked');
19555 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19557 this.setFromData(r.data);
19559 var close = this.closeTriggerEl();
19565 this.hideTouchView();
19567 this.fireEvent('select', this, r, rowIndex);
19572 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19573 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19574 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19578 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19579 this.addItem(r.data);
19580 this.tickItems.push(r.data);
19584 getAutoCreateNativeIOS : function()
19587 cls: 'form-group' //input-group,
19592 cls : 'roo-ios-select'
19596 combobox.name = this.name;
19599 if (this.disabled) {
19600 combobox.disabled = true;
19603 var settings = this;
19605 ['xs','sm','md','lg'].map(function(size){
19606 if (settings[size]) {
19607 cfg.cls += ' col-' + size + '-' + settings[size];
19617 initIOSView : function()
19619 this.store.on('load', this.onIOSViewLoad, this);
19624 onIOSViewLoad : function()
19626 if(this.store.getCount() < 1){
19630 this.clearIOSView();
19632 if(this.allowBlank) {
19634 var default_text = '-- SELECT --';
19636 if(this.placeholder.length){
19637 default_text = this.placeholder;
19640 if(this.emptyTitle.length){
19641 default_text += ' - ' + this.emptyTitle + ' -';
19644 var opt = this.inputEl().createChild({
19647 html : default_text
19651 o[this.valueField] = 0;
19652 o[this.displayField] = default_text;
19654 this.ios_options.push({
19661 this.store.data.each(function(d, rowIndex){
19665 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19666 html = d.data[this.displayField];
19671 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19672 value = d.data[this.valueField];
19681 if(this.value == d.data[this.valueField]){
19682 option['selected'] = true;
19685 var opt = this.inputEl().createChild(option);
19687 this.ios_options.push({
19694 this.inputEl().on('change', function(){
19695 this.fireEvent('select', this);
19700 clearIOSView: function()
19702 this.inputEl().dom.innerHTML = '';
19704 this.ios_options = [];
19707 setIOSValue: function(v)
19711 if(!this.ios_options){
19715 Roo.each(this.ios_options, function(opts){
19717 opts.el.dom.removeAttribute('selected');
19719 if(opts.data[this.valueField] != v){
19723 opts.el.dom.setAttribute('selected', true);
19729 * @cfg {Boolean} grow
19733 * @cfg {Number} growMin
19737 * @cfg {Number} growMax
19746 Roo.apply(Roo.bootstrap.form.ComboBox, {
19750 cls: 'modal-header',
19772 cls: 'list-group-item',
19776 cls: 'roo-combobox-list-group-item-value'
19780 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19794 listItemCheckbox : {
19796 cls: 'list-group-item',
19800 cls: 'roo-combobox-list-group-item-value'
19804 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19820 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19825 cls: 'modal-footer',
19833 cls: 'col-xs-6 text-left',
19836 cls: 'btn btn-danger roo-touch-view-cancel',
19842 cls: 'col-xs-6 text-right',
19845 cls: 'btn btn-success roo-touch-view-ok',
19856 Roo.apply(Roo.bootstrap.form.ComboBox, {
19858 touchViewTemplate : {
19860 cls: 'modal fade roo-combobox-touch-view',
19864 cls: 'modal-dialog',
19865 style : 'position:fixed', // we have to fix position....
19869 cls: 'modal-content',
19871 Roo.bootstrap.form.ComboBox.header,
19872 Roo.bootstrap.form.ComboBox.body,
19873 Roo.bootstrap.form.ComboBox.footer
19882 * Ext JS Library 1.1.1
19883 * Copyright(c) 2006-2007, Ext JS, LLC.
19885 * Originally Released Under LGPL - original licence link has changed is not relivant.
19888 * <script type="text/javascript">
19893 * @extends Roo.util.Observable
19894 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19895 * This class also supports single and multi selection modes. <br>
19896 * Create a data model bound view:
19898 var store = new Roo.data.Store(...);
19900 var view = new Roo.View({
19902 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19904 singleSelect: true,
19905 selectedClass: "ydataview-selected",
19909 // listen for node click?
19910 view.on("click", function(vw, index, node, e){
19911 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19915 dataModel.load("foobar.xml");
19917 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19919 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19920 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19922 * Note: old style constructor is still suported (container, template, config)
19925 * Create a new View
19926 * @param {Object} config The config object
19929 Roo.View = function(config, depreciated_tpl, depreciated_config){
19931 this.parent = false;
19933 if (typeof(depreciated_tpl) == 'undefined') {
19934 // new way.. - universal constructor.
19935 Roo.apply(this, config);
19936 this.el = Roo.get(this.el);
19939 this.el = Roo.get(config);
19940 this.tpl = depreciated_tpl;
19941 Roo.apply(this, depreciated_config);
19943 this.wrapEl = this.el.wrap().wrap();
19944 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19947 if(typeof(this.tpl) == "string"){
19948 this.tpl = new Roo.Template(this.tpl);
19950 // support xtype ctors..
19951 this.tpl = new Roo.factory(this.tpl, Roo);
19955 this.tpl.compile();
19960 * @event beforeclick
19961 * Fires before a click is processed. Returns false to cancel the default action.
19962 * @param {Roo.View} this
19963 * @param {Number} index The index of the target node
19964 * @param {HTMLElement} node The target node
19965 * @param {Roo.EventObject} e The raw event object
19967 "beforeclick" : true,
19970 * Fires when a template node is clicked.
19971 * @param {Roo.View} this
19972 * @param {Number} index The index of the target node
19973 * @param {HTMLElement} node The target node
19974 * @param {Roo.EventObject} e The raw event object
19979 * Fires when a template node is double clicked.
19980 * @param {Roo.View} this
19981 * @param {Number} index The index of the target node
19982 * @param {HTMLElement} node The target node
19983 * @param {Roo.EventObject} e The raw event object
19987 * @event contextmenu
19988 * Fires when a template node is right clicked.
19989 * @param {Roo.View} this
19990 * @param {Number} index The index of the target node
19991 * @param {HTMLElement} node The target node
19992 * @param {Roo.EventObject} e The raw event object
19994 "contextmenu" : true,
19996 * @event selectionchange
19997 * Fires when the selected nodes change.
19998 * @param {Roo.View} this
19999 * @param {Array} selections Array of the selected nodes
20001 "selectionchange" : true,
20004 * @event beforeselect
20005 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20006 * @param {Roo.View} this
20007 * @param {HTMLElement} node The node to be selected
20008 * @param {Array} selections Array of currently selected nodes
20010 "beforeselect" : true,
20012 * @event preparedata
20013 * Fires on every row to render, to allow you to change the data.
20014 * @param {Roo.View} this
20015 * @param {Object} data to be rendered (change this)
20017 "preparedata" : true
20025 "click": this.onClick,
20026 "dblclick": this.onDblClick,
20027 "contextmenu": this.onContextMenu,
20031 this.selections = [];
20033 this.cmp = new Roo.CompositeElementLite([]);
20035 this.store = Roo.factory(this.store, Roo.data);
20036 this.setStore(this.store, true);
20039 if ( this.footer && this.footer.xtype) {
20041 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20043 this.footer.dataSource = this.store;
20044 this.footer.container = fctr;
20045 this.footer = Roo.factory(this.footer, Roo);
20046 fctr.insertFirst(this.el);
20048 // this is a bit insane - as the paging toolbar seems to detach the el..
20049 // dom.parentNode.parentNode.parentNode
20050 // they get detached?
20054 Roo.View.superclass.constructor.call(this);
20059 Roo.extend(Roo.View, Roo.util.Observable, {
20062 * @cfg {Roo.data.Store} store Data store to load data from.
20067 * @cfg {String|Roo.Element} el The container element.
20072 * @cfg {String|Roo.Template} tpl The template used by this View
20076 * @cfg {String} dataName the named area of the template to use as the data area
20077 * Works with domtemplates roo-name="name"
20081 * @cfg {String} selectedClass The css class to add to selected nodes
20083 selectedClass : "x-view-selected",
20085 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20090 * @cfg {String} text to display on mask (default Loading)
20094 * @cfg {Boolean} multiSelect Allow multiple selection
20096 multiSelect : false,
20098 * @cfg {Boolean} singleSelect Allow single selection
20100 singleSelect: false,
20103 * @cfg {Boolean} toggleSelect - selecting
20105 toggleSelect : false,
20108 * @cfg {Boolean} tickable - selecting
20113 * Returns the element this view is bound to.
20114 * @return {Roo.Element}
20116 getEl : function(){
20117 return this.wrapEl;
20123 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20125 refresh : function(){
20126 //Roo.log('refresh');
20129 // if we are using something like 'domtemplate', then
20130 // the what gets used is:
20131 // t.applySubtemplate(NAME, data, wrapping data..)
20132 // the outer template then get' applied with
20133 // the store 'extra data'
20134 // and the body get's added to the
20135 // roo-name="data" node?
20136 // <span class='roo-tpl-{name}'></span> ?????
20140 this.clearSelections();
20141 this.el.update("");
20143 var records = this.store.getRange();
20144 if(records.length < 1) {
20146 // is this valid?? = should it render a template??
20148 this.el.update(this.emptyText);
20152 if (this.dataName) {
20153 this.el.update(t.apply(this.store.meta)); //????
20154 el = this.el.child('.roo-tpl-' + this.dataName);
20157 for(var i = 0, len = records.length; i < len; i++){
20158 var data = this.prepareData(records[i].data, i, records[i]);
20159 this.fireEvent("preparedata", this, data, i, records[i]);
20161 var d = Roo.apply({}, data);
20164 Roo.apply(d, {'roo-id' : Roo.id()});
20168 Roo.each(this.parent.item, function(item){
20169 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20172 Roo.apply(d, {'roo-data-checked' : 'checked'});
20176 html[html.length] = Roo.util.Format.trim(
20178 t.applySubtemplate(this.dataName, d, this.store.meta) :
20185 el.update(html.join(""));
20186 this.nodes = el.dom.childNodes;
20187 this.updateIndexes(0);
20192 * Function to override to reformat the data that is sent to
20193 * the template for each node.
20194 * DEPRICATED - use the preparedata event handler.
20195 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20196 * a JSON object for an UpdateManager bound view).
20198 prepareData : function(data, index, record)
20200 this.fireEvent("preparedata", this, data, index, record);
20204 onUpdate : function(ds, record){
20205 // Roo.log('on update');
20206 this.clearSelections();
20207 var index = this.store.indexOf(record);
20208 var n = this.nodes[index];
20209 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20210 n.parentNode.removeChild(n);
20211 this.updateIndexes(index, index);
20217 onAdd : function(ds, records, index)
20219 //Roo.log(['on Add', ds, records, index] );
20220 this.clearSelections();
20221 if(this.nodes.length == 0){
20225 var n = this.nodes[index];
20226 for(var i = 0, len = records.length; i < len; i++){
20227 var d = this.prepareData(records[i].data, i, records[i]);
20229 this.tpl.insertBefore(n, d);
20232 this.tpl.append(this.el, d);
20235 this.updateIndexes(index);
20238 onRemove : function(ds, record, index){
20239 // Roo.log('onRemove');
20240 this.clearSelections();
20241 var el = this.dataName ?
20242 this.el.child('.roo-tpl-' + this.dataName) :
20245 el.dom.removeChild(this.nodes[index]);
20246 this.updateIndexes(index);
20250 * Refresh an individual node.
20251 * @param {Number} index
20253 refreshNode : function(index){
20254 this.onUpdate(this.store, this.store.getAt(index));
20257 updateIndexes : function(startIndex, endIndex){
20258 var ns = this.nodes;
20259 startIndex = startIndex || 0;
20260 endIndex = endIndex || ns.length - 1;
20261 for(var i = startIndex; i <= endIndex; i++){
20262 ns[i].nodeIndex = i;
20267 * Changes the data store this view uses and refresh the view.
20268 * @param {Store} store
20270 setStore : function(store, initial){
20271 if(!initial && this.store){
20272 this.store.un("datachanged", this.refresh);
20273 this.store.un("add", this.onAdd);
20274 this.store.un("remove", this.onRemove);
20275 this.store.un("update", this.onUpdate);
20276 this.store.un("clear", this.refresh);
20277 this.store.un("beforeload", this.onBeforeLoad);
20278 this.store.un("load", this.onLoad);
20279 this.store.un("loadexception", this.onLoad);
20283 store.on("datachanged", this.refresh, this);
20284 store.on("add", this.onAdd, this);
20285 store.on("remove", this.onRemove, this);
20286 store.on("update", this.onUpdate, this);
20287 store.on("clear", this.refresh, this);
20288 store.on("beforeload", this.onBeforeLoad, this);
20289 store.on("load", this.onLoad, this);
20290 store.on("loadexception", this.onLoad, this);
20298 * onbeforeLoad - masks the loading area.
20301 onBeforeLoad : function(store,opts)
20303 //Roo.log('onBeforeLoad');
20305 this.el.update("");
20307 this.el.mask(this.mask ? this.mask : "Loading" );
20309 onLoad : function ()
20316 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20317 * @param {HTMLElement} node
20318 * @return {HTMLElement} The template node
20320 findItemFromChild : function(node){
20321 var el = this.dataName ?
20322 this.el.child('.roo-tpl-' + this.dataName,true) :
20325 if(!node || node.parentNode == el){
20328 var p = node.parentNode;
20329 while(p && p != el){
20330 if(p.parentNode == el){
20339 onClick : function(e){
20340 var item = this.findItemFromChild(e.getTarget());
20342 var index = this.indexOf(item);
20343 if(this.onItemClick(item, index, e) !== false){
20344 this.fireEvent("click", this, index, item, e);
20347 this.clearSelections();
20352 onContextMenu : function(e){
20353 var item = this.findItemFromChild(e.getTarget());
20355 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20360 onDblClick : function(e){
20361 var item = this.findItemFromChild(e.getTarget());
20363 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20367 onItemClick : function(item, index, e)
20369 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20372 if (this.toggleSelect) {
20373 var m = this.isSelected(item) ? 'unselect' : 'select';
20376 _t[m](item, true, false);
20379 if(this.multiSelect || this.singleSelect){
20380 if(this.multiSelect && e.shiftKey && this.lastSelection){
20381 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20383 this.select(item, this.multiSelect && e.ctrlKey);
20384 this.lastSelection = item;
20387 if(!this.tickable){
20388 e.preventDefault();
20396 * Get the number of selected nodes.
20399 getSelectionCount : function(){
20400 return this.selections.length;
20404 * Get the currently selected nodes.
20405 * @return {Array} An array of HTMLElements
20407 getSelectedNodes : function(){
20408 return this.selections;
20412 * Get the indexes of the selected nodes.
20415 getSelectedIndexes : function(){
20416 var indexes = [], s = this.selections;
20417 for(var i = 0, len = s.length; i < len; i++){
20418 indexes.push(s[i].nodeIndex);
20424 * Clear all selections
20425 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20427 clearSelections : function(suppressEvent){
20428 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20429 this.cmp.elements = this.selections;
20430 this.cmp.removeClass(this.selectedClass);
20431 this.selections = [];
20432 if(!suppressEvent){
20433 this.fireEvent("selectionchange", this, this.selections);
20439 * Returns true if the passed node is selected
20440 * @param {HTMLElement/Number} node The node or node index
20441 * @return {Boolean}
20443 isSelected : function(node){
20444 var s = this.selections;
20448 node = this.getNode(node);
20449 return s.indexOf(node) !== -1;
20454 * @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
20455 * @param {Boolean} keepExisting (optional) true to keep existing selections
20456 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20458 select : function(nodeInfo, keepExisting, suppressEvent){
20459 if(nodeInfo instanceof Array){
20461 this.clearSelections(true);
20463 for(var i = 0, len = nodeInfo.length; i < len; i++){
20464 this.select(nodeInfo[i], true, true);
20468 var node = this.getNode(nodeInfo);
20469 if(!node || this.isSelected(node)){
20470 return; // already selected.
20473 this.clearSelections(true);
20476 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20477 Roo.fly(node).addClass(this.selectedClass);
20478 this.selections.push(node);
20479 if(!suppressEvent){
20480 this.fireEvent("selectionchange", this, this.selections);
20488 * @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
20489 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20490 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20492 unselect : function(nodeInfo, keepExisting, suppressEvent)
20494 if(nodeInfo instanceof Array){
20495 Roo.each(this.selections, function(s) {
20496 this.unselect(s, nodeInfo);
20500 var node = this.getNode(nodeInfo);
20501 if(!node || !this.isSelected(node)){
20502 //Roo.log("not selected");
20503 return; // not selected.
20507 Roo.each(this.selections, function(s) {
20509 Roo.fly(node).removeClass(this.selectedClass);
20516 this.selections= ns;
20517 this.fireEvent("selectionchange", this, this.selections);
20521 * Gets a template node.
20522 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20523 * @return {HTMLElement} The node or null if it wasn't found
20525 getNode : function(nodeInfo){
20526 if(typeof nodeInfo == "string"){
20527 return document.getElementById(nodeInfo);
20528 }else if(typeof nodeInfo == "number"){
20529 return this.nodes[nodeInfo];
20535 * Gets a range template nodes.
20536 * @param {Number} startIndex
20537 * @param {Number} endIndex
20538 * @return {Array} An array of nodes
20540 getNodes : function(start, end){
20541 var ns = this.nodes;
20542 start = start || 0;
20543 end = typeof end == "undefined" ? ns.length - 1 : end;
20546 for(var i = start; i <= end; i++){
20550 for(var i = start; i >= end; i--){
20558 * Finds the index of the passed node
20559 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20560 * @return {Number} The index of the node or -1
20562 indexOf : function(node){
20563 node = this.getNode(node);
20564 if(typeof node.nodeIndex == "number"){
20565 return node.nodeIndex;
20567 var ns = this.nodes;
20568 for(var i = 0, len = ns.length; i < len; i++){
20579 * based on jquery fullcalendar
20583 Roo.bootstrap = Roo.bootstrap || {};
20585 * @class Roo.bootstrap.Calendar
20586 * @extends Roo.bootstrap.Component
20587 * Bootstrap Calendar class
20588 * @cfg {Boolean} loadMask (true|false) default false
20589 * @cfg {Object} header generate the user specific header of the calendar, default false
20592 * Create a new Container
20593 * @param {Object} config The config object
20598 Roo.bootstrap.Calendar = function(config){
20599 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20603 * Fires when a date is selected
20604 * @param {DatePicker} this
20605 * @param {Date} date The selected date
20609 * @event monthchange
20610 * Fires when the displayed month changes
20611 * @param {DatePicker} this
20612 * @param {Date} date The selected month
20614 'monthchange': true,
20616 * @event evententer
20617 * Fires when mouse over an event
20618 * @param {Calendar} this
20619 * @param {event} Event
20621 'evententer': true,
20623 * @event eventleave
20624 * Fires when the mouse leaves an
20625 * @param {Calendar} this
20628 'eventleave': true,
20630 * @event eventclick
20631 * Fires when the mouse click an
20632 * @param {Calendar} this
20641 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20644 * @cfg {Roo.data.Store} store
20645 * The data source for the calendar
20649 * @cfg {Number} startDay
20650 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20658 getAutoCreate : function(){
20661 var fc_button = function(name, corner, style, content ) {
20662 return Roo.apply({},{
20664 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20666 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20669 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20680 style : 'width:100%',
20687 cls : 'fc-header-left',
20689 fc_button('prev', 'left', 'arrow', '‹' ),
20690 fc_button('next', 'right', 'arrow', '›' ),
20691 { tag: 'span', cls: 'fc-header-space' },
20692 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20700 cls : 'fc-header-center',
20704 cls: 'fc-header-title',
20707 html : 'month / year'
20715 cls : 'fc-header-right',
20717 /* fc_button('month', 'left', '', 'month' ),
20718 fc_button('week', '', '', 'week' ),
20719 fc_button('day', 'right', '', 'day' )
20731 header = this.header;
20734 var cal_heads = function() {
20736 // fixme - handle this.
20738 for (var i =0; i < Date.dayNames.length; i++) {
20739 var d = Date.dayNames[i];
20742 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20743 html : d.substring(0,3)
20747 ret[0].cls += ' fc-first';
20748 ret[6].cls += ' fc-last';
20751 var cal_cell = function(n) {
20754 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20759 cls: 'fc-day-number',
20763 cls: 'fc-day-content',
20767 style: 'position: relative;' // height: 17px;
20779 var cal_rows = function() {
20782 for (var r = 0; r < 6; r++) {
20789 for (var i =0; i < Date.dayNames.length; i++) {
20790 var d = Date.dayNames[i];
20791 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20794 row.cn[0].cls+=' fc-first';
20795 row.cn[0].cn[0].style = 'min-height:90px';
20796 row.cn[6].cls+=' fc-last';
20800 ret[0].cls += ' fc-first';
20801 ret[4].cls += ' fc-prev-last';
20802 ret[5].cls += ' fc-last';
20809 cls: 'fc-border-separate',
20810 style : 'width:100%',
20818 cls : 'fc-first fc-last',
20836 cls : 'fc-content',
20837 style : "position: relative;",
20840 cls : 'fc-view fc-view-month fc-grid',
20841 style : 'position: relative',
20842 unselectable : 'on',
20845 cls : 'fc-event-container',
20846 style : 'position:absolute;z-index:8;top:0;left:0;'
20864 initEvents : function()
20867 throw "can not find store for calendar";
20873 style: "text-align:center",
20877 style: "background-color:white;width:50%;margin:250 auto",
20881 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20892 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20894 var size = this.el.select('.fc-content', true).first().getSize();
20895 this.maskEl.setSize(size.width, size.height);
20896 this.maskEl.enableDisplayMode("block");
20897 if(!this.loadMask){
20898 this.maskEl.hide();
20901 this.store = Roo.factory(this.store, Roo.data);
20902 this.store.on('load', this.onLoad, this);
20903 this.store.on('beforeload', this.onBeforeLoad, this);
20907 this.cells = this.el.select('.fc-day',true);
20908 //Roo.log(this.cells);
20909 this.textNodes = this.el.query('.fc-day-number');
20910 this.cells.addClassOnOver('fc-state-hover');
20912 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20913 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20914 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20915 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20917 this.on('monthchange', this.onMonthChange, this);
20919 this.update(new Date().clearTime());
20922 resize : function() {
20923 var sz = this.el.getSize();
20925 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20926 this.el.select('.fc-day-content div',true).setHeight(34);
20931 showPrevMonth : function(e){
20932 this.update(this.activeDate.add("mo", -1));
20934 showToday : function(e){
20935 this.update(new Date().clearTime());
20938 showNextMonth : function(e){
20939 this.update(this.activeDate.add("mo", 1));
20943 showPrevYear : function(){
20944 this.update(this.activeDate.add("y", -1));
20948 showNextYear : function(){
20949 this.update(this.activeDate.add("y", 1));
20954 update : function(date)
20956 var vd = this.activeDate;
20957 this.activeDate = date;
20958 // if(vd && this.el){
20959 // var t = date.getTime();
20960 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20961 // Roo.log('using add remove');
20963 // this.fireEvent('monthchange', this, date);
20965 // this.cells.removeClass("fc-state-highlight");
20966 // this.cells.each(function(c){
20967 // if(c.dateValue == t){
20968 // c.addClass("fc-state-highlight");
20969 // setTimeout(function(){
20970 // try{c.dom.firstChild.focus();}catch(e){}
20980 var days = date.getDaysInMonth();
20982 var firstOfMonth = date.getFirstDateOfMonth();
20983 var startingPos = firstOfMonth.getDay()-this.startDay;
20985 if(startingPos < this.startDay){
20989 var pm = date.add(Date.MONTH, -1);
20990 var prevStart = pm.getDaysInMonth()-startingPos;
20992 this.cells = this.el.select('.fc-day',true);
20993 this.textNodes = this.el.query('.fc-day-number');
20994 this.cells.addClassOnOver('fc-state-hover');
20996 var cells = this.cells.elements;
20997 var textEls = this.textNodes;
20999 Roo.each(cells, function(cell){
21000 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21003 days += startingPos;
21005 // convert everything to numbers so it's fast
21006 var day = 86400000;
21007 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21010 //Roo.log(prevStart);
21012 var today = new Date().clearTime().getTime();
21013 var sel = date.clearTime().getTime();
21014 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21015 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21016 var ddMatch = this.disabledDatesRE;
21017 var ddText = this.disabledDatesText;
21018 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21019 var ddaysText = this.disabledDaysText;
21020 var format = this.format;
21022 var setCellClass = function(cal, cell){
21026 //Roo.log('set Cell Class');
21028 var t = d.getTime();
21032 cell.dateValue = t;
21034 cell.className += " fc-today";
21035 cell.className += " fc-state-highlight";
21036 cell.title = cal.todayText;
21039 // disable highlight in other month..
21040 //cell.className += " fc-state-highlight";
21045 cell.className = " fc-state-disabled";
21046 cell.title = cal.minText;
21050 cell.className = " fc-state-disabled";
21051 cell.title = cal.maxText;
21055 if(ddays.indexOf(d.getDay()) != -1){
21056 cell.title = ddaysText;
21057 cell.className = " fc-state-disabled";
21060 if(ddMatch && format){
21061 var fvalue = d.dateFormat(format);
21062 if(ddMatch.test(fvalue)){
21063 cell.title = ddText.replace("%0", fvalue);
21064 cell.className = " fc-state-disabled";
21068 if (!cell.initialClassName) {
21069 cell.initialClassName = cell.dom.className;
21072 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21077 for(; i < startingPos; i++) {
21078 textEls[i].innerHTML = (++prevStart);
21079 d.setDate(d.getDate()+1);
21081 cells[i].className = "fc-past fc-other-month";
21082 setCellClass(this, cells[i]);
21087 for(; i < days; i++){
21088 intDay = i - startingPos + 1;
21089 textEls[i].innerHTML = (intDay);
21090 d.setDate(d.getDate()+1);
21092 cells[i].className = ''; // "x-date-active";
21093 setCellClass(this, cells[i]);
21097 for(; i < 42; i++) {
21098 textEls[i].innerHTML = (++extraDays);
21099 d.setDate(d.getDate()+1);
21101 cells[i].className = "fc-future fc-other-month";
21102 setCellClass(this, cells[i]);
21105 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21107 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21109 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21110 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21112 if(totalRows != 6){
21113 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21114 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21117 this.fireEvent('monthchange', this, date);
21121 if(!this.internalRender){
21122 var main = this.el.dom.firstChild;
21123 var w = main.offsetWidth;
21124 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21125 Roo.fly(main).setWidth(w);
21126 this.internalRender = true;
21127 // opera does not respect the auto grow header center column
21128 // then, after it gets a width opera refuses to recalculate
21129 // without a second pass
21130 if(Roo.isOpera && !this.secondPass){
21131 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21132 this.secondPass = true;
21133 this.update.defer(10, this, [date]);
21140 findCell : function(dt) {
21141 dt = dt.clearTime().getTime();
21143 this.cells.each(function(c){
21144 //Roo.log("check " +c.dateValue + '?=' + dt);
21145 if(c.dateValue == dt){
21155 findCells : function(ev) {
21156 var s = ev.start.clone().clearTime().getTime();
21158 var e= ev.end.clone().clearTime().getTime();
21161 this.cells.each(function(c){
21162 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21164 if(c.dateValue > e){
21167 if(c.dateValue < s){
21176 // findBestRow: function(cells)
21180 // for (var i =0 ; i < cells.length;i++) {
21181 // ret = Math.max(cells[i].rows || 0,ret);
21188 addItem : function(ev)
21190 // look for vertical location slot in
21191 var cells = this.findCells(ev);
21193 // ev.row = this.findBestRow(cells);
21195 // work out the location.
21199 for(var i =0; i < cells.length; i++) {
21201 cells[i].row = cells[0].row;
21204 cells[i].row = cells[i].row + 1;
21214 if (crow.start.getY() == cells[i].getY()) {
21216 crow.end = cells[i];
21233 cells[0].events.push(ev);
21235 this.calevents.push(ev);
21238 clearEvents: function() {
21240 if(!this.calevents){
21244 Roo.each(this.cells.elements, function(c){
21250 Roo.each(this.calevents, function(e) {
21251 Roo.each(e.els, function(el) {
21252 el.un('mouseenter' ,this.onEventEnter, this);
21253 el.un('mouseleave' ,this.onEventLeave, this);
21258 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21264 renderEvents: function()
21268 this.cells.each(function(c) {
21277 if(c.row != c.events.length){
21278 r = 4 - (4 - (c.row - c.events.length));
21281 c.events = ev.slice(0, r);
21282 c.more = ev.slice(r);
21284 if(c.more.length && c.more.length == 1){
21285 c.events.push(c.more.pop());
21288 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21292 this.cells.each(function(c) {
21294 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21297 for (var e = 0; e < c.events.length; e++){
21298 var ev = c.events[e];
21299 var rows = ev.rows;
21301 for(var i = 0; i < rows.length; i++) {
21303 // how many rows should it span..
21306 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21307 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21309 unselectable : "on",
21312 cls: 'fc-event-inner',
21316 // cls: 'fc-event-time',
21317 // html : cells.length > 1 ? '' : ev.time
21321 cls: 'fc-event-title',
21322 html : String.format('{0}', ev.title)
21329 cls: 'ui-resizable-handle ui-resizable-e',
21330 html : '  '
21337 cfg.cls += ' fc-event-start';
21339 if ((i+1) == rows.length) {
21340 cfg.cls += ' fc-event-end';
21343 var ctr = _this.el.select('.fc-event-container',true).first();
21344 var cg = ctr.createChild(cfg);
21346 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21347 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21349 var r = (c.more.length) ? 1 : 0;
21350 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21351 cg.setWidth(ebox.right - sbox.x -2);
21353 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21354 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21355 cg.on('click', _this.onEventClick, _this, ev);
21366 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21367 style : 'position: absolute',
21368 unselectable : "on",
21371 cls: 'fc-event-inner',
21375 cls: 'fc-event-title',
21383 cls: 'ui-resizable-handle ui-resizable-e',
21384 html : '  '
21390 var ctr = _this.el.select('.fc-event-container',true).first();
21391 var cg = ctr.createChild(cfg);
21393 var sbox = c.select('.fc-day-content',true).first().getBox();
21394 var ebox = c.select('.fc-day-content',true).first().getBox();
21396 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21397 cg.setWidth(ebox.right - sbox.x -2);
21399 cg.on('click', _this.onMoreEventClick, _this, c.more);
21409 onEventEnter: function (e, el,event,d) {
21410 this.fireEvent('evententer', this, el, event);
21413 onEventLeave: function (e, el,event,d) {
21414 this.fireEvent('eventleave', this, el, event);
21417 onEventClick: function (e, el,event,d) {
21418 this.fireEvent('eventclick', this, el, event);
21421 onMonthChange: function () {
21425 onMoreEventClick: function(e, el, more)
21429 this.calpopover.placement = 'right';
21430 this.calpopover.setTitle('More');
21432 this.calpopover.setContent('');
21434 var ctr = this.calpopover.el.select('.popover-content', true).first();
21436 Roo.each(more, function(m){
21438 cls : 'fc-event-hori fc-event-draggable',
21441 var cg = ctr.createChild(cfg);
21443 cg.on('click', _this.onEventClick, _this, m);
21446 this.calpopover.show(el);
21451 onLoad: function ()
21453 this.calevents = [];
21456 if(this.store.getCount() > 0){
21457 this.store.data.each(function(d){
21460 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21461 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21462 time : d.data.start_time,
21463 title : d.data.title,
21464 description : d.data.description,
21465 venue : d.data.venue
21470 this.renderEvents();
21472 if(this.calevents.length && this.loadMask){
21473 this.maskEl.hide();
21477 onBeforeLoad: function()
21479 this.clearEvents();
21481 this.maskEl.show();
21495 * @class Roo.bootstrap.Popover
21496 * @extends Roo.bootstrap.Component
21497 * @parent none builder
21498 * @children Roo.bootstrap.Component
21499 * Bootstrap Popover class
21500 * @cfg {String} html contents of the popover (or false to use children..)
21501 * @cfg {String} title of popover (or false to hide)
21502 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21503 * @cfg {String} trigger click || hover (or false to trigger manually)
21504 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21505 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21506 * - if false and it has a 'parent' then it will be automatically added to that element
21507 * - if string - Roo.get will be called
21508 * @cfg {Number} delay - delay before showing
21511 * Create a new Popover
21512 * @param {Object} config The config object
21515 Roo.bootstrap.Popover = function(config){
21516 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21522 * After the popover show
21524 * @param {Roo.bootstrap.Popover} this
21529 * After the popover hide
21531 * @param {Roo.bootstrap.Popover} this
21537 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21542 placement : 'right',
21543 trigger : 'hover', // hover
21549 can_build_overlaid : false,
21551 maskEl : false, // the mask element
21554 alignEl : false, // when show is called with an element - this get's stored.
21556 getChildContainer : function()
21558 return this.contentEl;
21561 getPopoverHeader : function()
21563 this.title = true; // flag not to hide it..
21564 this.headerEl.addClass('p-0');
21565 return this.headerEl
21569 getAutoCreate : function(){
21572 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21573 style: 'display:block',
21579 cls : 'popover-inner ',
21583 cls: 'popover-title popover-header',
21584 html : this.title === false ? '' : this.title
21587 cls : 'popover-content popover-body ' + (this.cls || ''),
21588 html : this.html || ''
21599 * @param {string} the title
21601 setTitle: function(str)
21605 this.headerEl.dom.innerHTML = str;
21610 * @param {string} the body content
21612 setContent: function(str)
21615 if (this.contentEl) {
21616 this.contentEl.dom.innerHTML = str;
21620 // as it get's added to the bottom of the page.
21621 onRender : function(ct, position)
21623 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21628 var cfg = Roo.apply({}, this.getAutoCreate());
21632 cfg.cls += ' ' + this.cls;
21635 cfg.style = this.style;
21637 //Roo.log("adding to ");
21638 this.el = Roo.get(document.body).createChild(cfg, position);
21639 // Roo.log(this.el);
21642 this.contentEl = this.el.select('.popover-content',true).first();
21643 this.headerEl = this.el.select('.popover-title',true).first();
21646 if(typeof(this.items) != 'undefined'){
21647 var items = this.items;
21650 for(var i =0;i < items.length;i++) {
21651 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21655 this.items = nitems;
21657 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21658 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21665 resizeMask : function()
21667 this.maskEl.setSize(
21668 Roo.lib.Dom.getViewWidth(true),
21669 Roo.lib.Dom.getViewHeight(true)
21673 initEvents : function()
21677 Roo.bootstrap.Popover.register(this);
21680 this.arrowEl = this.el.select('.arrow',true).first();
21681 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21682 this.el.enableDisplayMode('block');
21686 if (this.over === false && !this.parent()) {
21689 if (this.triggers === false) {
21694 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21695 var triggers = this.trigger ? this.trigger.split(' ') : [];
21696 Roo.each(triggers, function(trigger) {
21698 if (trigger == 'click') {
21699 on_el.on('click', this.toggle, this);
21700 } else if (trigger != 'manual') {
21701 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21702 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21704 on_el.on(eventIn ,this.enter, this);
21705 on_el.on(eventOut, this.leave, this);
21715 toggle : function () {
21716 this.hoverState == 'in' ? this.leave() : this.enter();
21719 enter : function () {
21721 clearTimeout(this.timeout);
21723 this.hoverState = 'in';
21725 if (!this.delay || !this.delay.show) {
21730 this.timeout = setTimeout(function () {
21731 if (_t.hoverState == 'in') {
21734 }, this.delay.show)
21737 leave : function() {
21738 clearTimeout(this.timeout);
21740 this.hoverState = 'out';
21742 if (!this.delay || !this.delay.hide) {
21747 this.timeout = setTimeout(function () {
21748 if (_t.hoverState == 'out') {
21751 }, this.delay.hide)
21755 * update the position of the dialog
21756 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21761 doAlign : function()
21764 if (this.alignEl) {
21765 this.updatePosition(this.placement, true);
21768 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21769 var es = this.el.getSize();
21770 var x = Roo.lib.Dom.getViewWidth()/2;
21771 var y = Roo.lib.Dom.getViewHeight()/2;
21772 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21784 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21785 * @param {string} (left|right|top|bottom) position
21787 show : function (on_el, placement)
21789 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21790 on_el = on_el || false; // default to false
21793 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21794 on_el = this.parent().el;
21795 } else if (this.over) {
21796 on_el = Roo.get(this.over);
21801 this.alignEl = Roo.get( on_el );
21804 this.render(document.body);
21810 if (this.title === false) {
21811 this.headerEl.hide();
21816 this.el.dom.style.display = 'block';
21820 //var arrow = this.el.select('.arrow',true).first();
21821 //arrow.set(align[2],
21823 this.el.addClass('in');
21827 this.hoverState = 'in';
21830 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21831 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21832 this.maskEl.dom.style.display = 'block';
21833 this.maskEl.addClass('show');
21835 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21837 this.fireEvent('show', this);
21841 * fire this manually after loading a grid in the table for example
21842 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21843 * @param {Boolean} try and move it if we cant get right position.
21845 updatePosition : function(placement, try_move)
21847 // allow for calling with no parameters
21848 placement = placement ? placement : this.placement;
21849 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21851 this.el.removeClass([
21852 'fade','top','bottom', 'left', 'right','in',
21853 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21855 this.el.addClass(placement + ' bs-popover-' + placement);
21857 if (!this.alignEl ) {
21861 switch (placement) {
21863 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21864 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21865 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21866 //normal display... or moved up/down.
21867 this.el.setXY(offset);
21868 var xy = this.alignEl.getAnchorXY('tr', false);
21870 this.arrowEl.setXY(xy);
21873 // continue through...
21874 return this.updatePosition('left', false);
21878 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21879 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21880 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21881 //normal display... or moved up/down.
21882 this.el.setXY(offset);
21883 var xy = this.alignEl.getAnchorXY('tl', false);
21884 xy[0]-=10;xy[1]+=5; // << fix me
21885 this.arrowEl.setXY(xy);
21889 return this.updatePosition('right', false);
21892 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21893 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21894 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21895 //normal display... or moved up/down.
21896 this.el.setXY(offset);
21897 var xy = this.alignEl.getAnchorXY('t', false);
21898 xy[1]-=10; // << fix me
21899 this.arrowEl.setXY(xy);
21903 return this.updatePosition('bottom', false);
21906 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21907 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21908 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21909 //normal display... or moved up/down.
21910 this.el.setXY(offset);
21911 var xy = this.alignEl.getAnchorXY('b', false);
21912 xy[1]+=2; // << fix me
21913 this.arrowEl.setXY(xy);
21917 return this.updatePosition('top', false);
21928 this.el.setXY([0,0]);
21929 this.el.removeClass('in');
21931 this.hoverState = null;
21932 this.maskEl.hide(); // always..
21933 this.fireEvent('hide', this);
21939 Roo.apply(Roo.bootstrap.Popover, {
21942 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21943 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21944 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21945 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21950 clickHander : false,
21954 onMouseDown : function(e)
21956 if (this.popups.length && !e.getTarget(".roo-popover")) {
21957 /// what is nothing is showing..
21966 register : function(popup)
21968 if (!Roo.bootstrap.Popover.clickHandler) {
21969 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21971 // hide other popups.
21972 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21973 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21974 this.hideAll(); //<< why?
21975 //this.popups.push(popup);
21977 hideAll : function()
21979 this.popups.forEach(function(p) {
21983 onShow : function() {
21984 Roo.bootstrap.Popover.popups.push(this);
21986 onHide : function() {
21987 Roo.bootstrap.Popover.popups.remove(this);
21992 * @class Roo.bootstrap.PopoverNav
21993 * @extends Roo.bootstrap.nav.Simplebar
21994 * @parent Roo.bootstrap.Popover
21995 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21997 * Bootstrap Popover header navigation class
21998 * FIXME? should this go under nav?
22002 * Create a new Popover Header Navigation
22003 * @param {Object} config The config object
22006 Roo.bootstrap.PopoverNav = function(config){
22007 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22010 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22013 container_method : 'getPopoverHeader'
22031 * @class Roo.bootstrap.Progress
22032 * @extends Roo.bootstrap.Component
22033 * @children Roo.bootstrap.ProgressBar
22034 * Bootstrap Progress class
22035 * @cfg {Boolean} striped striped of the progress bar
22036 * @cfg {Boolean} active animated of the progress bar
22040 * Create a new Progress
22041 * @param {Object} config The config object
22044 Roo.bootstrap.Progress = function(config){
22045 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22048 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22053 getAutoCreate : function(){
22061 cfg.cls += ' progress-striped';
22065 cfg.cls += ' active';
22084 * @class Roo.bootstrap.ProgressBar
22085 * @extends Roo.bootstrap.Component
22086 * Bootstrap ProgressBar class
22087 * @cfg {Number} aria_valuenow aria-value now
22088 * @cfg {Number} aria_valuemin aria-value min
22089 * @cfg {Number} aria_valuemax aria-value max
22090 * @cfg {String} label label for the progress bar
22091 * @cfg {String} panel (success | info | warning | danger )
22092 * @cfg {String} role role of the progress bar
22093 * @cfg {String} sr_only text
22097 * Create a new ProgressBar
22098 * @param {Object} config The config object
22101 Roo.bootstrap.ProgressBar = function(config){
22102 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22105 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22109 aria_valuemax : 100,
22115 getAutoCreate : function()
22120 cls: 'progress-bar',
22121 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22133 cfg.role = this.role;
22136 if(this.aria_valuenow){
22137 cfg['aria-valuenow'] = this.aria_valuenow;
22140 if(this.aria_valuemin){
22141 cfg['aria-valuemin'] = this.aria_valuemin;
22144 if(this.aria_valuemax){
22145 cfg['aria-valuemax'] = this.aria_valuemax;
22148 if(this.label && !this.sr_only){
22149 cfg.html = this.label;
22153 cfg.cls += ' progress-bar-' + this.panel;
22159 update : function(aria_valuenow)
22161 this.aria_valuenow = aria_valuenow;
22163 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22171 * @class Roo.bootstrap.TabGroup
22172 * @extends Roo.bootstrap.Column
22173 * @children Roo.bootstrap.TabPanel
22174 * Bootstrap Column class
22175 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22176 * @cfg {Boolean} carousel true to make the group behave like a carousel
22177 * @cfg {Boolean} bullets show bullets for the panels
22178 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22179 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22180 * @cfg {Boolean} showarrow (true|false) show arrow default true
22183 * Create a new TabGroup
22184 * @param {Object} config The config object
22187 Roo.bootstrap.TabGroup = function(config){
22188 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22190 this.navId = Roo.id();
22193 Roo.bootstrap.TabGroup.register(this);
22197 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22200 transition : false,
22205 slideOnTouch : false,
22208 getAutoCreate : function()
22210 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22212 cfg.cls += ' tab-content';
22214 if (this.carousel) {
22215 cfg.cls += ' carousel slide';
22218 cls : 'carousel-inner',
22222 if(this.bullets && !Roo.isTouch){
22225 cls : 'carousel-bullets',
22229 if(this.bullets_cls){
22230 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22237 cfg.cn[0].cn.push(bullets);
22240 if(this.showarrow){
22241 cfg.cn[0].cn.push({
22243 class : 'carousel-arrow',
22247 class : 'carousel-prev',
22251 class : 'fa fa-chevron-left'
22257 class : 'carousel-next',
22261 class : 'fa fa-chevron-right'
22274 initEvents: function()
22276 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22277 // this.el.on("touchstart", this.onTouchStart, this);
22280 if(this.autoslide){
22283 this.slideFn = window.setInterval(function() {
22284 _this.showPanelNext();
22288 if(this.showarrow){
22289 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22290 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22296 // onTouchStart : function(e, el, o)
22298 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22302 // this.showPanelNext();
22306 getChildContainer : function()
22308 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22312 * register a Navigation item
22313 * @param {Roo.bootstrap.nav.Item} the navitem to add
22315 register : function(item)
22317 this.tabs.push( item);
22318 item.navId = this.navId; // not really needed..
22323 getActivePanel : function()
22326 Roo.each(this.tabs, function(t) {
22336 getPanelByName : function(n)
22339 Roo.each(this.tabs, function(t) {
22340 if (t.tabId == n) {
22348 indexOfPanel : function(p)
22351 Roo.each(this.tabs, function(t,i) {
22352 if (t.tabId == p.tabId) {
22361 * show a specific panel
22362 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22363 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22365 showPanel : function (pan)
22367 if(this.transition || typeof(pan) == 'undefined'){
22368 Roo.log("waiting for the transitionend");
22372 if (typeof(pan) == 'number') {
22373 pan = this.tabs[pan];
22376 if (typeof(pan) == 'string') {
22377 pan = this.getPanelByName(pan);
22380 var cur = this.getActivePanel();
22383 Roo.log('pan or acitve pan is undefined');
22387 if (pan.tabId == this.getActivePanel().tabId) {
22391 if (false === cur.fireEvent('beforedeactivate')) {
22395 if(this.bullets > 0 && !Roo.isTouch){
22396 this.setActiveBullet(this.indexOfPanel(pan));
22399 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22401 //class="carousel-item carousel-item-next carousel-item-left"
22403 this.transition = true;
22404 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22405 var lr = dir == 'next' ? 'left' : 'right';
22406 pan.el.addClass(dir); // or prev
22407 pan.el.addClass('carousel-item-' + dir); // or prev
22408 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22409 cur.el.addClass(lr); // or right
22410 pan.el.addClass(lr);
22411 cur.el.addClass('carousel-item-' +lr); // or right
22412 pan.el.addClass('carousel-item-' +lr);
22416 cur.el.on('transitionend', function() {
22417 Roo.log("trans end?");
22419 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22420 pan.setActive(true);
22422 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22423 cur.setActive(false);
22425 _this.transition = false;
22427 }, this, { single: true } );
22432 cur.setActive(false);
22433 pan.setActive(true);
22438 showPanelNext : function()
22440 var i = this.indexOfPanel(this.getActivePanel());
22442 if (i >= this.tabs.length - 1 && !this.autoslide) {
22446 if (i >= this.tabs.length - 1 && this.autoslide) {
22450 this.showPanel(this.tabs[i+1]);
22453 showPanelPrev : function()
22455 var i = this.indexOfPanel(this.getActivePanel());
22457 if (i < 1 && !this.autoslide) {
22461 if (i < 1 && this.autoslide) {
22462 i = this.tabs.length;
22465 this.showPanel(this.tabs[i-1]);
22469 addBullet: function()
22471 if(!this.bullets || Roo.isTouch){
22474 var ctr = this.el.select('.carousel-bullets',true).first();
22475 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22476 var bullet = ctr.createChild({
22477 cls : 'bullet bullet-' + i
22478 },ctr.dom.lastChild);
22483 bullet.on('click', (function(e, el, o, ii, t){
22485 e.preventDefault();
22487 this.showPanel(ii);
22489 if(this.autoslide && this.slideFn){
22490 clearInterval(this.slideFn);
22491 this.slideFn = window.setInterval(function() {
22492 _this.showPanelNext();
22496 }).createDelegate(this, [i, bullet], true));
22501 setActiveBullet : function(i)
22507 Roo.each(this.el.select('.bullet', true).elements, function(el){
22508 el.removeClass('selected');
22511 var bullet = this.el.select('.bullet-' + i, true).first();
22517 bullet.addClass('selected');
22528 Roo.apply(Roo.bootstrap.TabGroup, {
22532 * register a Navigation Group
22533 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22535 register : function(navgrp)
22537 this.groups[navgrp.navId] = navgrp;
22541 * fetch a Navigation Group based on the navigation ID
22542 * if one does not exist , it will get created.
22543 * @param {string} the navgroup to add
22544 * @returns {Roo.bootstrap.nav.Group} the navgroup
22546 get: function(navId) {
22547 if (typeof(this.groups[navId]) == 'undefined') {
22548 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22550 return this.groups[navId] ;
22565 * @class Roo.bootstrap.TabPanel
22566 * @extends Roo.bootstrap.Component
22567 * @children Roo.bootstrap.Component
22568 * Bootstrap TabPanel class
22569 * @cfg {Boolean} active panel active
22570 * @cfg {String} html panel content
22571 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22572 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22573 * @cfg {String} href click to link..
22574 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22578 * Create a new TabPanel
22579 * @param {Object} config The config object
22582 Roo.bootstrap.TabPanel = function(config){
22583 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22587 * Fires when the active status changes
22588 * @param {Roo.bootstrap.TabPanel} this
22589 * @param {Boolean} state the new state
22594 * @event beforedeactivate
22595 * Fires before a tab is de-activated - can be used to do validation on a form.
22596 * @param {Roo.bootstrap.TabPanel} this
22597 * @return {Boolean} false if there is an error
22600 'beforedeactivate': true
22603 this.tabId = this.tabId || Roo.id();
22607 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22614 touchSlide : false,
22615 getAutoCreate : function(){
22620 // item is needed for carousel - not sure if it has any effect otherwise
22621 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22622 html: this.html || ''
22626 cfg.cls += ' active';
22630 cfg.tabId = this.tabId;
22638 initEvents: function()
22640 var p = this.parent();
22642 this.navId = this.navId || p.navId;
22644 if (typeof(this.navId) != 'undefined') {
22645 // not really needed.. but just in case.. parent should be a NavGroup.
22646 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22650 var i = tg.tabs.length - 1;
22652 if(this.active && tg.bullets > 0 && i < tg.bullets){
22653 tg.setActiveBullet(i);
22657 this.el.on('click', this.onClick, this);
22659 if(Roo.isTouch && this.touchSlide){
22660 this.el.on("touchstart", this.onTouchStart, this);
22661 this.el.on("touchmove", this.onTouchMove, this);
22662 this.el.on("touchend", this.onTouchEnd, this);
22667 onRender : function(ct, position)
22669 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22672 setActive : function(state)
22674 Roo.log("panel - set active " + this.tabId + "=" + state);
22676 this.active = state;
22678 this.el.removeClass('active');
22680 } else if (!this.el.hasClass('active')) {
22681 this.el.addClass('active');
22684 this.fireEvent('changed', this, state);
22687 onClick : function(e)
22689 e.preventDefault();
22691 if(!this.href.length){
22695 window.location.href = this.href;
22704 onTouchStart : function(e)
22706 this.swiping = false;
22708 this.startX = e.browserEvent.touches[0].clientX;
22709 this.startY = e.browserEvent.touches[0].clientY;
22712 onTouchMove : function(e)
22714 this.swiping = true;
22716 this.endX = e.browserEvent.touches[0].clientX;
22717 this.endY = e.browserEvent.touches[0].clientY;
22720 onTouchEnd : function(e)
22727 var tabGroup = this.parent();
22729 if(this.endX > this.startX){ // swiping right
22730 tabGroup.showPanelPrev();
22734 if(this.startX > this.endX){ // swiping left
22735 tabGroup.showPanelNext();
22754 * @class Roo.bootstrap.form.DateField
22755 * @extends Roo.bootstrap.form.Input
22756 * Bootstrap DateField class
22757 * @cfg {Number} weekStart default 0
22758 * @cfg {String} viewMode default empty, (months|years)
22759 * @cfg {String} minViewMode default empty, (months|years)
22760 * @cfg {Number} startDate default -Infinity
22761 * @cfg {Number} endDate default Infinity
22762 * @cfg {Boolean} todayHighlight default false
22763 * @cfg {Boolean} todayBtn default false
22764 * @cfg {Boolean} calendarWeeks default false
22765 * @cfg {Object} daysOfWeekDisabled default empty
22766 * @cfg {Boolean} singleMode default false (true | false)
22768 * @cfg {Boolean} keyboardNavigation default true
22769 * @cfg {String} language default en
22772 * Create a new DateField
22773 * @param {Object} config The config object
22776 Roo.bootstrap.form.DateField = function(config){
22777 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22781 * Fires when this field show.
22782 * @param {Roo.bootstrap.form.DateField} this
22783 * @param {Mixed} date The date value
22788 * Fires when this field hide.
22789 * @param {Roo.bootstrap.form.DateField} this
22790 * @param {Mixed} date The date value
22795 * Fires when select a date.
22796 * @param {Roo.bootstrap.form.DateField} this
22797 * @param {Mixed} date The date value
22801 * @event beforeselect
22802 * Fires when before select a date.
22803 * @param {Roo.bootstrap.form.DateField} this
22804 * @param {Mixed} date The date value
22806 beforeselect : true
22810 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22813 * @cfg {String} format
22814 * The default date format string which can be overriden for localization support. The format must be
22815 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22819 * @cfg {String} altFormats
22820 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22821 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22823 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22831 todayHighlight : false,
22837 keyboardNavigation: true,
22839 calendarWeeks: false,
22841 startDate: -Infinity,
22845 daysOfWeekDisabled: [],
22849 singleMode : false,
22851 UTCDate: function()
22853 return new Date(Date.UTC.apply(Date, arguments));
22856 UTCToday: function()
22858 var today = new Date();
22859 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22862 getDate: function() {
22863 var d = this.getUTCDate();
22864 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22867 getUTCDate: function() {
22871 setDate: function(d) {
22872 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22875 setUTCDate: function(d) {
22877 this.setValue(this.formatDate(this.date));
22880 onRender: function(ct, position)
22883 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22885 this.language = this.language || 'en';
22886 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22887 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22889 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22890 this.format = this.format || 'm/d/y';
22891 this.isInline = false;
22892 this.isInput = true;
22893 this.component = this.el.select('.add-on', true).first() || false;
22894 this.component = (this.component && this.component.length === 0) ? false : this.component;
22895 this.hasInput = this.component && this.inputEl().length;
22897 if (typeof(this.minViewMode === 'string')) {
22898 switch (this.minViewMode) {
22900 this.minViewMode = 1;
22903 this.minViewMode = 2;
22906 this.minViewMode = 0;
22911 if (typeof(this.viewMode === 'string')) {
22912 switch (this.viewMode) {
22925 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22927 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22929 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22931 this.picker().on('mousedown', this.onMousedown, this);
22932 this.picker().on('click', this.onClick, this);
22934 this.picker().addClass('datepicker-dropdown');
22936 this.startViewMode = this.viewMode;
22938 if(this.singleMode){
22939 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22940 v.setVisibilityMode(Roo.Element.DISPLAY);
22944 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22945 v.setStyle('width', '189px');
22949 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22950 if(!this.calendarWeeks){
22955 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22956 v.attr('colspan', function(i, val){
22957 return parseInt(val) + 1;
22962 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22964 this.setStartDate(this.startDate);
22965 this.setEndDate(this.endDate);
22967 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22974 if(this.isInline) {
22979 picker : function()
22981 return this.pickerEl;
22982 // return this.el.select('.datepicker', true).first();
22985 fillDow: function()
22987 var dowCnt = this.weekStart;
22996 if(this.calendarWeeks){
23004 while (dowCnt < this.weekStart + 7) {
23008 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23012 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23015 fillMonths: function()
23018 var months = this.picker().select('>.datepicker-months td', true).first();
23020 months.dom.innerHTML = '';
23026 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23029 months.createChild(month);
23036 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;
23038 if (this.date < this.startDate) {
23039 this.viewDate = new Date(this.startDate);
23040 } else if (this.date > this.endDate) {
23041 this.viewDate = new Date(this.endDate);
23043 this.viewDate = new Date(this.date);
23051 var d = new Date(this.viewDate),
23052 year = d.getUTCFullYear(),
23053 month = d.getUTCMonth(),
23054 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23055 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23056 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23057 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23058 currentDate = this.date && this.date.valueOf(),
23059 today = this.UTCToday();
23061 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23063 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23065 // this.picker.select('>tfoot th.today').
23066 // .text(dates[this.language].today)
23067 // .toggle(this.todayBtn !== false);
23069 this.updateNavArrows();
23072 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23074 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23076 prevMonth.setUTCDate(day);
23078 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23080 var nextMonth = new Date(prevMonth);
23082 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23084 nextMonth = nextMonth.valueOf();
23086 var fillMonths = false;
23088 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23090 while(prevMonth.valueOf() <= nextMonth) {
23093 if (prevMonth.getUTCDay() === this.weekStart) {
23095 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23103 if(this.calendarWeeks){
23104 // ISO 8601: First week contains first thursday.
23105 // ISO also states week starts on Monday, but we can be more abstract here.
23107 // Start of current week: based on weekstart/current date
23108 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23109 // Thursday of this week
23110 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23111 // First Thursday of year, year from thursday
23112 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23113 // Calendar week: ms between thursdays, div ms per day, div 7 days
23114 calWeek = (th - yth) / 864e5 / 7 + 1;
23116 fillMonths.cn.push({
23124 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23126 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23129 if (this.todayHighlight &&
23130 prevMonth.getUTCFullYear() == today.getFullYear() &&
23131 prevMonth.getUTCMonth() == today.getMonth() &&
23132 prevMonth.getUTCDate() == today.getDate()) {
23133 clsName += ' today';
23136 if (currentDate && prevMonth.valueOf() === currentDate) {
23137 clsName += ' active';
23140 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23141 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23142 clsName += ' disabled';
23145 fillMonths.cn.push({
23147 cls: 'day ' + clsName,
23148 html: prevMonth.getDate()
23151 prevMonth.setDate(prevMonth.getDate()+1);
23154 var currentYear = this.date && this.date.getUTCFullYear();
23155 var currentMonth = this.date && this.date.getUTCMonth();
23157 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23159 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23160 v.removeClass('active');
23162 if(currentYear === year && k === currentMonth){
23163 v.addClass('active');
23166 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23167 v.addClass('disabled');
23173 year = parseInt(year/10, 10) * 10;
23175 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23177 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23180 for (var i = -1; i < 11; i++) {
23181 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23183 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23191 showMode: function(dir)
23194 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23197 Roo.each(this.picker().select('>div',true).elements, function(v){
23198 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23201 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23206 if(this.isInline) {
23210 this.picker().removeClass(['bottom', 'top']);
23212 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23214 * place to the top of element!
23218 this.picker().addClass('top');
23219 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23224 this.picker().addClass('bottom');
23226 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23229 parseDate : function(value)
23231 if(!value || value instanceof Date){
23234 var v = Date.parseDate(value, this.format);
23235 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23236 v = Date.parseDate(value, 'Y-m-d');
23238 if(!v && this.altFormats){
23239 if(!this.altFormatsArray){
23240 this.altFormatsArray = this.altFormats.split("|");
23242 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23243 v = Date.parseDate(value, this.altFormatsArray[i]);
23249 formatDate : function(date, fmt)
23251 return (!date || !(date instanceof Date)) ?
23252 date : date.dateFormat(fmt || this.format);
23255 onFocus : function()
23257 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23261 onBlur : function()
23263 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23265 var d = this.inputEl().getValue();
23272 showPopup : function()
23274 this.picker().show();
23278 this.fireEvent('showpopup', this, this.date);
23281 hidePopup : function()
23283 if(this.isInline) {
23286 this.picker().hide();
23287 this.viewMode = this.startViewMode;
23290 this.fireEvent('hidepopup', this, this.date);
23294 onMousedown: function(e)
23296 e.stopPropagation();
23297 e.preventDefault();
23302 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23306 setValue: function(v)
23308 if(this.fireEvent('beforeselect', this, v) !== false){
23309 var d = new Date(this.parseDate(v) ).clearTime();
23311 if(isNaN(d.getTime())){
23312 this.date = this.viewDate = '';
23313 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23317 v = this.formatDate(d);
23319 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23321 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23325 this.fireEvent('select', this, this.date);
23329 getValue: function()
23331 return this.formatDate(this.date);
23334 fireKey: function(e)
23336 if (!this.picker().isVisible()){
23337 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23343 var dateChanged = false,
23345 newDate, newViewDate;
23350 e.preventDefault();
23354 if (!this.keyboardNavigation) {
23357 dir = e.keyCode == 37 ? -1 : 1;
23360 newDate = this.moveYear(this.date, dir);
23361 newViewDate = this.moveYear(this.viewDate, dir);
23362 } else if (e.shiftKey){
23363 newDate = this.moveMonth(this.date, dir);
23364 newViewDate = this.moveMonth(this.viewDate, dir);
23366 newDate = new Date(this.date);
23367 newDate.setUTCDate(this.date.getUTCDate() + dir);
23368 newViewDate = new Date(this.viewDate);
23369 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23371 if (this.dateWithinRange(newDate)){
23372 this.date = newDate;
23373 this.viewDate = newViewDate;
23374 this.setValue(this.formatDate(this.date));
23376 e.preventDefault();
23377 dateChanged = true;
23382 if (!this.keyboardNavigation) {
23385 dir = e.keyCode == 38 ? -1 : 1;
23387 newDate = this.moveYear(this.date, dir);
23388 newViewDate = this.moveYear(this.viewDate, dir);
23389 } else if (e.shiftKey){
23390 newDate = this.moveMonth(this.date, dir);
23391 newViewDate = this.moveMonth(this.viewDate, dir);
23393 newDate = new Date(this.date);
23394 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23395 newViewDate = new Date(this.viewDate);
23396 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23398 if (this.dateWithinRange(newDate)){
23399 this.date = newDate;
23400 this.viewDate = newViewDate;
23401 this.setValue(this.formatDate(this.date));
23403 e.preventDefault();
23404 dateChanged = true;
23408 this.setValue(this.formatDate(this.date));
23410 e.preventDefault();
23413 this.setValue(this.formatDate(this.date));
23427 onClick: function(e)
23429 e.stopPropagation();
23430 e.preventDefault();
23432 var target = e.getTarget();
23434 if(target.nodeName.toLowerCase() === 'i'){
23435 target = Roo.get(target).dom.parentNode;
23438 var nodeName = target.nodeName;
23439 var className = target.className;
23440 var html = target.innerHTML;
23441 //Roo.log(nodeName);
23443 switch(nodeName.toLowerCase()) {
23445 switch(className) {
23451 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23452 switch(this.viewMode){
23454 this.viewDate = this.moveMonth(this.viewDate, dir);
23458 this.viewDate = this.moveYear(this.viewDate, dir);
23464 var date = new Date();
23465 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23467 this.setValue(this.formatDate(this.date));
23474 if (className.indexOf('disabled') < 0) {
23475 if (!this.viewDate) {
23476 this.viewDate = new Date();
23478 this.viewDate.setUTCDate(1);
23479 if (className.indexOf('month') > -1) {
23480 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23482 var year = parseInt(html, 10) || 0;
23483 this.viewDate.setUTCFullYear(year);
23487 if(this.singleMode){
23488 this.setValue(this.formatDate(this.viewDate));
23499 //Roo.log(className);
23500 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23501 var day = parseInt(html, 10) || 1;
23502 var year = (this.viewDate || new Date()).getUTCFullYear(),
23503 month = (this.viewDate || new Date()).getUTCMonth();
23505 if (className.indexOf('old') > -1) {
23512 } else if (className.indexOf('new') > -1) {
23520 //Roo.log([year,month,day]);
23521 this.date = this.UTCDate(year, month, day,0,0,0,0);
23522 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23524 //Roo.log(this.formatDate(this.date));
23525 this.setValue(this.formatDate(this.date));
23532 setStartDate: function(startDate)
23534 this.startDate = startDate || -Infinity;
23535 if (this.startDate !== -Infinity) {
23536 this.startDate = this.parseDate(this.startDate);
23539 this.updateNavArrows();
23542 setEndDate: function(endDate)
23544 this.endDate = endDate || Infinity;
23545 if (this.endDate !== Infinity) {
23546 this.endDate = this.parseDate(this.endDate);
23549 this.updateNavArrows();
23552 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23554 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23555 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23556 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23558 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23559 return parseInt(d, 10);
23562 this.updateNavArrows();
23565 updateNavArrows: function()
23567 if(this.singleMode){
23571 var d = new Date(this.viewDate),
23572 year = d.getUTCFullYear(),
23573 month = d.getUTCMonth();
23575 Roo.each(this.picker().select('.prev', true).elements, function(v){
23577 switch (this.viewMode) {
23580 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23586 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23593 Roo.each(this.picker().select('.next', true).elements, function(v){
23595 switch (this.viewMode) {
23598 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23604 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23612 moveMonth: function(date, dir)
23617 var new_date = new Date(date.valueOf()),
23618 day = new_date.getUTCDate(),
23619 month = new_date.getUTCMonth(),
23620 mag = Math.abs(dir),
23622 dir = dir > 0 ? 1 : -1;
23625 // If going back one month, make sure month is not current month
23626 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23628 return new_date.getUTCMonth() == month;
23630 // If going forward one month, make sure month is as expected
23631 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23633 return new_date.getUTCMonth() != new_month;
23635 new_month = month + dir;
23636 new_date.setUTCMonth(new_month);
23637 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23638 if (new_month < 0 || new_month > 11) {
23639 new_month = (new_month + 12) % 12;
23642 // For magnitudes >1, move one month at a time...
23643 for (var i=0; i<mag; i++) {
23644 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23645 new_date = this.moveMonth(new_date, dir);
23647 // ...then reset the day, keeping it in the new month
23648 new_month = new_date.getUTCMonth();
23649 new_date.setUTCDate(day);
23651 return new_month != new_date.getUTCMonth();
23654 // Common date-resetting loop -- if date is beyond end of month, make it
23657 new_date.setUTCDate(--day);
23658 new_date.setUTCMonth(new_month);
23663 moveYear: function(date, dir)
23665 return this.moveMonth(date, dir*12);
23668 dateWithinRange: function(date)
23670 return date >= this.startDate && date <= this.endDate;
23676 this.picker().remove();
23679 validateValue : function(value)
23681 if(this.getVisibilityEl().hasClass('hidden')){
23685 if(value.length < 1) {
23686 if(this.allowBlank){
23692 if(value.length < this.minLength){
23695 if(value.length > this.maxLength){
23699 var vt = Roo.form.VTypes;
23700 if(!vt[this.vtype](value, this)){
23704 if(typeof this.validator == "function"){
23705 var msg = this.validator(value);
23711 if(this.regex && !this.regex.test(value)){
23715 if(typeof(this.parseDate(value)) == 'undefined'){
23719 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23723 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23733 this.date = this.viewDate = '';
23735 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23740 Roo.apply(Roo.bootstrap.form.DateField, {
23751 html: '<i class="fa fa-arrow-left"/>'
23761 html: '<i class="fa fa-arrow-right"/>'
23803 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23804 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23805 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23806 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23807 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23820 navFnc: 'FullYear',
23825 navFnc: 'FullYear',
23830 Roo.apply(Roo.bootstrap.form.DateField, {
23834 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23838 cls: 'datepicker-days',
23842 cls: 'table-condensed',
23844 Roo.bootstrap.form.DateField.head,
23848 Roo.bootstrap.form.DateField.footer
23855 cls: 'datepicker-months',
23859 cls: 'table-condensed',
23861 Roo.bootstrap.form.DateField.head,
23862 Roo.bootstrap.form.DateField.content,
23863 Roo.bootstrap.form.DateField.footer
23870 cls: 'datepicker-years',
23874 cls: 'table-condensed',
23876 Roo.bootstrap.form.DateField.head,
23877 Roo.bootstrap.form.DateField.content,
23878 Roo.bootstrap.form.DateField.footer
23897 * @class Roo.bootstrap.form.TimeField
23898 * @extends Roo.bootstrap.form.Input
23899 * Bootstrap DateField class
23903 * Create a new TimeField
23904 * @param {Object} config The config object
23907 Roo.bootstrap.form.TimeField = function(config){
23908 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23912 * Fires when this field show.
23913 * @param {Roo.bootstrap.form.DateField} thisthis
23914 * @param {Mixed} date The date value
23919 * Fires when this field hide.
23920 * @param {Roo.bootstrap.form.DateField} this
23921 * @param {Mixed} date The date value
23926 * Fires when select a date.
23927 * @param {Roo.bootstrap.form.DateField} this
23928 * @param {Mixed} date The date value
23934 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23937 * @cfg {String} format
23938 * The default time format string which can be overriden for localization support. The format must be
23939 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23943 getAutoCreate : function()
23945 this.after = '<i class="fa far fa-clock"></i>';
23946 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23950 onRender: function(ct, position)
23953 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23955 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23957 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23959 this.pop = this.picker().select('>.datepicker-time',true).first();
23960 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23962 this.picker().on('mousedown', this.onMousedown, this);
23963 this.picker().on('click', this.onClick, this);
23965 this.picker().addClass('datepicker-dropdown');
23970 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23971 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23972 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23973 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23974 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23975 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23979 fireKey: function(e){
23980 if (!this.picker().isVisible()){
23981 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23987 e.preventDefault();
23995 this.onTogglePeriod();
23998 this.onIncrementMinutes();
24001 this.onDecrementMinutes();
24010 onClick: function(e) {
24011 e.stopPropagation();
24012 e.preventDefault();
24015 picker : function()
24017 return this.pickerEl;
24020 fillTime: function()
24022 var time = this.pop.select('tbody', true).first();
24024 time.dom.innerHTML = '';
24039 cls: 'hours-up fa fas fa-chevron-up'
24059 cls: 'minutes-up fa fas fa-chevron-up'
24080 cls: 'timepicker-hour',
24095 cls: 'timepicker-minute',
24110 cls: 'btn btn-primary period',
24132 cls: 'hours-down fa fas fa-chevron-down'
24152 cls: 'minutes-down fa fas fa-chevron-down'
24170 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24177 var hours = this.time.getHours();
24178 var minutes = this.time.getMinutes();
24191 hours = hours - 12;
24195 hours = '0' + hours;
24199 minutes = '0' + minutes;
24202 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24203 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24204 this.pop.select('button', true).first().dom.innerHTML = period;
24210 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24212 var cls = ['bottom'];
24214 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24221 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24225 //this.picker().setXY(20000,20000);
24226 this.picker().addClass(cls.join('-'));
24230 Roo.each(cls, function(c){
24235 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24236 //_this.picker().setTop(_this.inputEl().getHeight());
24240 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24242 //_this.picker().setTop(0 - _this.picker().getHeight());
24247 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24251 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24259 onFocus : function()
24261 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24265 onBlur : function()
24267 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24273 this.picker().show();
24278 this.fireEvent('show', this, this.date);
24283 this.picker().hide();
24286 this.fireEvent('hide', this, this.date);
24289 setTime : function()
24292 this.setValue(this.time.format(this.format));
24294 this.fireEvent('select', this, this.date);
24299 onMousedown: function(e){
24300 e.stopPropagation();
24301 e.preventDefault();
24304 onIncrementHours: function()
24306 Roo.log('onIncrementHours');
24307 this.time = this.time.add(Date.HOUR, 1);
24312 onDecrementHours: function()
24314 Roo.log('onDecrementHours');
24315 this.time = this.time.add(Date.HOUR, -1);
24319 onIncrementMinutes: function()
24321 Roo.log('onIncrementMinutes');
24322 this.time = this.time.add(Date.MINUTE, 1);
24326 onDecrementMinutes: function()
24328 Roo.log('onDecrementMinutes');
24329 this.time = this.time.add(Date.MINUTE, -1);
24333 onTogglePeriod: function()
24335 Roo.log('onTogglePeriod');
24336 this.time = this.time.add(Date.HOUR, 12);
24344 Roo.apply(Roo.bootstrap.form.TimeField, {
24348 cls: 'datepicker dropdown-menu',
24352 cls: 'datepicker-time',
24356 cls: 'table-condensed',
24385 cls: 'btn btn-info ok',
24413 * @class Roo.bootstrap.form.MonthField
24414 * @extends Roo.bootstrap.form.Input
24415 * Bootstrap MonthField class
24417 * @cfg {String} language default en
24420 * Create a new MonthField
24421 * @param {Object} config The config object
24424 Roo.bootstrap.form.MonthField = function(config){
24425 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24430 * Fires when this field show.
24431 * @param {Roo.bootstrap.form.MonthField} this
24432 * @param {Mixed} date The date value
24437 * Fires when this field hide.
24438 * @param {Roo.bootstrap.form.MonthField} this
24439 * @param {Mixed} date The date value
24444 * Fires when select a date.
24445 * @param {Roo.bootstrap.form.MonthField} this
24446 * @param {String} oldvalue The old value
24447 * @param {String} newvalue The new value
24453 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24455 onRender: function(ct, position)
24458 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24460 this.language = this.language || 'en';
24461 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24462 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24464 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24465 this.isInline = false;
24466 this.isInput = true;
24467 this.component = this.el.select('.add-on', true).first() || false;
24468 this.component = (this.component && this.component.length === 0) ? false : this.component;
24469 this.hasInput = this.component && this.inputEL().length;
24471 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24473 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24475 this.picker().on('mousedown', this.onMousedown, this);
24476 this.picker().on('click', this.onClick, this);
24478 this.picker().addClass('datepicker-dropdown');
24480 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24481 v.setStyle('width', '189px');
24488 if(this.isInline) {
24494 setValue: function(v, suppressEvent)
24496 var o = this.getValue();
24498 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24502 if(suppressEvent !== true){
24503 this.fireEvent('select', this, o, v);
24508 getValue: function()
24513 onClick: function(e)
24515 e.stopPropagation();
24516 e.preventDefault();
24518 var target = e.getTarget();
24520 if(target.nodeName.toLowerCase() === 'i'){
24521 target = Roo.get(target).dom.parentNode;
24524 var nodeName = target.nodeName;
24525 var className = target.className;
24526 var html = target.innerHTML;
24528 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24532 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24534 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24540 picker : function()
24542 return this.pickerEl;
24545 fillMonths: function()
24548 var months = this.picker().select('>.datepicker-months td', true).first();
24550 months.dom.innerHTML = '';
24556 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24559 months.createChild(month);
24568 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24569 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24572 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24573 e.removeClass('active');
24575 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24576 e.addClass('active');
24583 if(this.isInline) {
24587 this.picker().removeClass(['bottom', 'top']);
24589 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24591 * place to the top of element!
24595 this.picker().addClass('top');
24596 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24601 this.picker().addClass('bottom');
24603 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24606 onFocus : function()
24608 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24612 onBlur : function()
24614 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24616 var d = this.inputEl().getValue();
24625 this.picker().show();
24626 this.picker().select('>.datepicker-months', true).first().show();
24630 this.fireEvent('show', this, this.date);
24635 if(this.isInline) {
24638 this.picker().hide();
24639 this.fireEvent('hide', this, this.date);
24643 onMousedown: function(e)
24645 e.stopPropagation();
24646 e.preventDefault();
24651 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24655 fireKey: function(e)
24657 if (!this.picker().isVisible()){
24658 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24669 e.preventDefault();
24673 dir = e.keyCode == 37 ? -1 : 1;
24675 this.vIndex = this.vIndex + dir;
24677 if(this.vIndex < 0){
24681 if(this.vIndex > 11){
24685 if(isNaN(this.vIndex)){
24689 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24695 dir = e.keyCode == 38 ? -1 : 1;
24697 this.vIndex = this.vIndex + dir * 4;
24699 if(this.vIndex < 0){
24703 if(this.vIndex > 11){
24707 if(isNaN(this.vIndex)){
24711 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24716 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24717 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24721 e.preventDefault();
24724 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24725 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24741 this.picker().remove();
24746 Roo.apply(Roo.bootstrap.form.MonthField, {
24765 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24766 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24771 Roo.apply(Roo.bootstrap.form.MonthField, {
24775 cls: 'datepicker dropdown-menu roo-dynamic',
24779 cls: 'datepicker-months',
24783 cls: 'table-condensed',
24785 Roo.bootstrap.form.DateField.content
24805 * @class Roo.bootstrap.form.CheckBox
24806 * @extends Roo.bootstrap.form.Input
24807 * Bootstrap CheckBox class
24809 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24810 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24811 * @cfg {String} boxLabel The text that appears beside the checkbox
24812 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24813 * @cfg {Boolean} checked initnal the element
24814 * @cfg {Boolean} inline inline the element (default false)
24815 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24816 * @cfg {String} tooltip label tooltip
24819 * Create a new CheckBox
24820 * @param {Object} config The config object
24823 Roo.bootstrap.form.CheckBox = function(config){
24824 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24829 * Fires when the element is checked or unchecked.
24830 * @param {Roo.bootstrap.form.CheckBox} this This input
24831 * @param {Boolean} checked The new checked value
24836 * Fires when the element is click.
24837 * @param {Roo.bootstrap.form.CheckBox} this This input
24844 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24846 inputType: 'checkbox',
24855 // checkbox success does not make any sense really..
24860 getAutoCreate : function()
24862 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24868 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24871 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24877 type : this.inputType,
24878 value : this.inputValue,
24879 cls : 'roo-' + this.inputType, //'form-box',
24880 placeholder : this.placeholder || ''
24884 if(this.inputType != 'radio'){
24888 cls : 'roo-hidden-value',
24889 value : this.checked ? this.inputValue : this.valueOff
24894 if (this.weight) { // Validity check?
24895 cfg.cls += " " + this.inputType + "-" + this.weight;
24898 if (this.disabled) {
24899 input.disabled=true;
24903 input.checked = this.checked;
24908 input.name = this.name;
24910 if(this.inputType != 'radio'){
24911 hidden.name = this.name;
24912 input.name = '_hidden_' + this.name;
24917 input.cls += ' input-' + this.size;
24922 ['xs','sm','md','lg'].map(function(size){
24923 if (settings[size]) {
24924 cfg.cls += ' col-' + size + '-' + settings[size];
24928 var inputblock = input;
24930 if (this.before || this.after) {
24933 cls : 'input-group',
24938 inputblock.cn.push({
24940 cls : 'input-group-addon',
24945 inputblock.cn.push(input);
24947 if(this.inputType != 'radio'){
24948 inputblock.cn.push(hidden);
24952 inputblock.cn.push({
24954 cls : 'input-group-addon',
24960 var boxLabelCfg = false;
24966 //'for': id, // box label is handled by onclick - so no for...
24968 html: this.boxLabel
24971 boxLabelCfg.tooltip = this.tooltip;
24977 if (align ==='left' && this.fieldLabel.length) {
24978 // Roo.log("left and has label");
24983 cls : 'control-label',
24984 html : this.fieldLabel
24995 cfg.cn[1].cn.push(boxLabelCfg);
24998 if(this.labelWidth > 12){
24999 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25002 if(this.labelWidth < 13 && this.labelmd == 0){
25003 this.labelmd = this.labelWidth;
25006 if(this.labellg > 0){
25007 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25008 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25011 if(this.labelmd > 0){
25012 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25013 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25016 if(this.labelsm > 0){
25017 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25018 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25021 if(this.labelxs > 0){
25022 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25023 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25026 } else if ( this.fieldLabel.length) {
25027 // Roo.log(" label");
25031 tag: this.boxLabel ? 'span' : 'label',
25033 cls: 'control-label box-input-label',
25034 //cls : 'input-group-addon',
25035 html : this.fieldLabel
25042 cfg.cn.push(boxLabelCfg);
25047 // Roo.log(" no label && no align");
25048 cfg.cn = [ inputblock ] ;
25050 cfg.cn.push(boxLabelCfg);
25058 if(this.inputType != 'radio'){
25059 cfg.cn.push(hidden);
25067 * return the real input element.
25069 inputEl: function ()
25071 return this.el.select('input.roo-' + this.inputType,true).first();
25073 hiddenEl: function ()
25075 return this.el.select('input.roo-hidden-value',true).first();
25078 labelEl: function()
25080 return this.el.select('label.control-label',true).first();
25082 /* depricated... */
25086 return this.labelEl();
25089 boxLabelEl: function()
25091 return this.el.select('label.box-label',true).first();
25094 initEvents : function()
25096 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25098 this.inputEl().on('click', this.onClick, this);
25100 if (this.boxLabel) {
25101 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25104 this.startValue = this.getValue();
25107 Roo.bootstrap.form.CheckBox.register(this);
25111 onClick : function(e)
25113 if(this.fireEvent('click', this, e) !== false){
25114 this.setChecked(!this.checked);
25119 setChecked : function(state,suppressEvent)
25121 this.startValue = this.getValue();
25123 if(this.inputType == 'radio'){
25125 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25126 e.dom.checked = false;
25129 this.inputEl().dom.checked = true;
25131 this.inputEl().dom.value = this.inputValue;
25133 if(suppressEvent !== true){
25134 this.fireEvent('check', this, true);
25142 this.checked = state;
25144 this.inputEl().dom.checked = state;
25147 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25149 if(suppressEvent !== true){
25150 this.fireEvent('check', this, state);
25156 getValue : function()
25158 if(this.inputType == 'radio'){
25159 return this.getGroupValue();
25162 return this.hiddenEl().dom.value;
25166 getGroupValue : function()
25168 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25172 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25175 setValue : function(v,suppressEvent)
25177 if(this.inputType == 'radio'){
25178 this.setGroupValue(v, suppressEvent);
25182 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25187 setGroupValue : function(v, suppressEvent)
25189 this.startValue = this.getValue();
25191 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25192 e.dom.checked = false;
25194 if(e.dom.value == v){
25195 e.dom.checked = true;
25199 if(suppressEvent !== true){
25200 this.fireEvent('check', this, true);
25208 validate : function()
25210 if(this.getVisibilityEl().hasClass('hidden')){
25216 (this.inputType == 'radio' && this.validateRadio()) ||
25217 (this.inputType == 'checkbox' && this.validateCheckbox())
25223 this.markInvalid();
25227 validateRadio : function()
25229 if(this.getVisibilityEl().hasClass('hidden')){
25233 if(this.allowBlank){
25239 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25240 if(!e.dom.checked){
25252 validateCheckbox : function()
25255 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25256 //return (this.getValue() == this.inputValue) ? true : false;
25259 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25267 for(var i in group){
25268 if(group[i].el.isVisible(true)){
25276 for(var i in group){
25281 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25288 * Mark this field as valid
25290 markValid : function()
25294 this.fireEvent('valid', this);
25296 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25299 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25306 if(this.inputType == 'radio'){
25307 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25308 var fg = e.findParent('.form-group', false, true);
25309 if (Roo.bootstrap.version == 3) {
25310 fg.removeClass([_this.invalidClass, _this.validClass]);
25311 fg.addClass(_this.validClass);
25313 fg.removeClass(['is-valid', 'is-invalid']);
25314 fg.addClass('is-valid');
25322 var fg = this.el.findParent('.form-group', false, true);
25323 if (Roo.bootstrap.version == 3) {
25324 fg.removeClass([this.invalidClass, this.validClass]);
25325 fg.addClass(this.validClass);
25327 fg.removeClass(['is-valid', 'is-invalid']);
25328 fg.addClass('is-valid');
25333 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25339 for(var i in group){
25340 var fg = group[i].el.findParent('.form-group', false, true);
25341 if (Roo.bootstrap.version == 3) {
25342 fg.removeClass([this.invalidClass, this.validClass]);
25343 fg.addClass(this.validClass);
25345 fg.removeClass(['is-valid', 'is-invalid']);
25346 fg.addClass('is-valid');
25352 * Mark this field as invalid
25353 * @param {String} msg The validation message
25355 markInvalid : function(msg)
25357 if(this.allowBlank){
25363 this.fireEvent('invalid', this, msg);
25365 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25368 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25372 label.markInvalid();
25375 if(this.inputType == 'radio'){
25377 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25378 var fg = e.findParent('.form-group', false, true);
25379 if (Roo.bootstrap.version == 3) {
25380 fg.removeClass([_this.invalidClass, _this.validClass]);
25381 fg.addClass(_this.invalidClass);
25383 fg.removeClass(['is-invalid', 'is-valid']);
25384 fg.addClass('is-invalid');
25392 var fg = this.el.findParent('.form-group', false, true);
25393 if (Roo.bootstrap.version == 3) {
25394 fg.removeClass([_this.invalidClass, _this.validClass]);
25395 fg.addClass(_this.invalidClass);
25397 fg.removeClass(['is-invalid', 'is-valid']);
25398 fg.addClass('is-invalid');
25403 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25409 for(var i in group){
25410 var fg = group[i].el.findParent('.form-group', false, true);
25411 if (Roo.bootstrap.version == 3) {
25412 fg.removeClass([_this.invalidClass, _this.validClass]);
25413 fg.addClass(_this.invalidClass);
25415 fg.removeClass(['is-invalid', 'is-valid']);
25416 fg.addClass('is-invalid');
25422 clearInvalid : function()
25424 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25426 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25428 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25430 if (label && label.iconEl) {
25431 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25432 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25436 disable : function()
25438 if(this.inputType != 'radio'){
25439 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25446 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25447 _this.getActionEl().addClass(this.disabledClass);
25448 e.dom.disabled = true;
25452 this.disabled = true;
25453 this.fireEvent("disable", this);
25457 enable : function()
25459 if(this.inputType != 'radio'){
25460 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25467 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25468 _this.getActionEl().removeClass(this.disabledClass);
25469 e.dom.disabled = false;
25473 this.disabled = false;
25474 this.fireEvent("enable", this);
25478 setBoxLabel : function(v)
25483 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25489 Roo.apply(Roo.bootstrap.form.CheckBox, {
25494 * register a CheckBox Group
25495 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25497 register : function(checkbox)
25499 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25500 this.groups[checkbox.groupId] = {};
25503 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25507 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25511 * fetch a CheckBox Group based on the group ID
25512 * @param {string} the group ID
25513 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25515 get: function(groupId) {
25516 if (typeof(this.groups[groupId]) == 'undefined') {
25520 return this.groups[groupId] ;
25533 * @class Roo.bootstrap.form.Radio
25534 * @extends Roo.bootstrap.Component
25535 * Bootstrap Radio class
25536 * @cfg {String} boxLabel - the label associated
25537 * @cfg {String} value - the value of radio
25540 * Create a new Radio
25541 * @param {Object} config The config object
25543 Roo.bootstrap.form.Radio = function(config){
25544 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25548 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25554 getAutoCreate : function()
25558 cls : 'form-group radio',
25563 html : this.boxLabel
25571 initEvents : function()
25573 this.parent().register(this);
25575 this.el.on('click', this.onClick, this);
25579 onClick : function(e)
25581 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25582 this.setChecked(true);
25586 setChecked : function(state, suppressEvent)
25588 this.parent().setValue(this.value, suppressEvent);
25592 setBoxLabel : function(v)
25597 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25612 * @class Roo.bootstrap.form.SecurePass
25613 * @extends Roo.bootstrap.form.Input
25614 * Bootstrap SecurePass class
25618 * Create a new SecurePass
25619 * @param {Object} config The config object
25622 Roo.bootstrap.form.SecurePass = function (config) {
25623 // these go here, so the translation tool can replace them..
25625 PwdEmpty: "Please type a password, and then retype it to confirm.",
25626 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25627 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25628 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25629 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25630 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25631 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25632 TooWeak: "Your password is Too Weak."
25634 this.meterLabel = "Password strength:";
25635 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25636 this.meterClass = [
25637 "roo-password-meter-tooweak",
25638 "roo-password-meter-weak",
25639 "roo-password-meter-medium",
25640 "roo-password-meter-strong",
25641 "roo-password-meter-grey"
25646 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25649 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25651 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25653 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25654 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25655 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25656 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25657 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25658 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25659 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25669 * @cfg {String/Object} Label for the strength meter (defaults to
25670 * 'Password strength:')
25675 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25676 * ['Weak', 'Medium', 'Strong'])
25679 pwdStrengths: false,
25692 initEvents: function ()
25694 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25696 if (this.el.is('input[type=password]') && Roo.isSafari) {
25697 this.el.on('keydown', this.SafariOnKeyDown, this);
25700 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25703 onRender: function (ct, position)
25705 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25706 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25707 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25709 this.trigger.createChild({
25714 cls: 'roo-password-meter-grey col-xs-12',
25717 //width: this.meterWidth + 'px'
25721 cls: 'roo-password-meter-text'
25727 if (this.hideTrigger) {
25728 this.trigger.setDisplayed(false);
25730 this.setSize(this.width || '', this.height || '');
25733 onDestroy: function ()
25735 if (this.trigger) {
25736 this.trigger.removeAllListeners();
25737 this.trigger.remove();
25740 this.wrap.remove();
25742 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25745 checkStrength: function ()
25747 var pwd = this.inputEl().getValue();
25748 if (pwd == this._lastPwd) {
25753 if (this.ClientSideStrongPassword(pwd)) {
25755 } else if (this.ClientSideMediumPassword(pwd)) {
25757 } else if (this.ClientSideWeakPassword(pwd)) {
25763 Roo.log('strength1: ' + strength);
25765 //var pm = this.trigger.child('div/div/div').dom;
25766 var pm = this.trigger.child('div/div');
25767 pm.removeClass(this.meterClass);
25768 pm.addClass(this.meterClass[strength]);
25771 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25773 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25775 this._lastPwd = pwd;
25779 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25781 this._lastPwd = '';
25783 var pm = this.trigger.child('div/div');
25784 pm.removeClass(this.meterClass);
25785 pm.addClass('roo-password-meter-grey');
25788 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25791 this.inputEl().dom.type='password';
25794 validateValue: function (value)
25796 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25799 if (value.length == 0) {
25800 if (this.allowBlank) {
25801 this.clearInvalid();
25805 this.markInvalid(this.errors.PwdEmpty);
25806 this.errorMsg = this.errors.PwdEmpty;
25814 if (!value.match(/[\x21-\x7e]+/)) {
25815 this.markInvalid(this.errors.PwdBadChar);
25816 this.errorMsg = this.errors.PwdBadChar;
25819 if (value.length < 6) {
25820 this.markInvalid(this.errors.PwdShort);
25821 this.errorMsg = this.errors.PwdShort;
25824 if (value.length > 16) {
25825 this.markInvalid(this.errors.PwdLong);
25826 this.errorMsg = this.errors.PwdLong;
25830 if (this.ClientSideStrongPassword(value)) {
25832 } else if (this.ClientSideMediumPassword(value)) {
25834 } else if (this.ClientSideWeakPassword(value)) {
25841 if (strength < 2) {
25842 //this.markInvalid(this.errors.TooWeak);
25843 this.errorMsg = this.errors.TooWeak;
25848 console.log('strength2: ' + strength);
25850 //var pm = this.trigger.child('div/div/div').dom;
25852 var pm = this.trigger.child('div/div');
25853 pm.removeClass(this.meterClass);
25854 pm.addClass(this.meterClass[strength]);
25856 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25858 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25860 this.errorMsg = '';
25864 CharacterSetChecks: function (type)
25867 this.fResult = false;
25870 isctype: function (character, type)
25873 case this.kCapitalLetter:
25874 if (character >= 'A' && character <= 'Z') {
25879 case this.kSmallLetter:
25880 if (character >= 'a' && character <= 'z') {
25886 if (character >= '0' && character <= '9') {
25891 case this.kPunctuation:
25892 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25903 IsLongEnough: function (pwd, size)
25905 return !(pwd == null || isNaN(size) || pwd.length < size);
25908 SpansEnoughCharacterSets: function (word, nb)
25910 if (!this.IsLongEnough(word, nb))
25915 var characterSetChecks = new Array(
25916 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25917 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25920 for (var index = 0; index < word.length; ++index) {
25921 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25922 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25923 characterSetChecks[nCharSet].fResult = true;
25930 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25931 if (characterSetChecks[nCharSet].fResult) {
25936 if (nCharSets < nb) {
25942 ClientSideStrongPassword: function (pwd)
25944 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25947 ClientSideMediumPassword: function (pwd)
25949 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25952 ClientSideWeakPassword: function (pwd)
25954 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25957 })//<script type="text/javascript">
25960 * Based Ext JS Library 1.1.1
25961 * Copyright(c) 2006-2007, Ext JS, LLC.
25967 * @class Roo.HtmlEditorCore
25968 * @extends Roo.Component
25969 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25971 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25974 Roo.HtmlEditorCore = function(config){
25977 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25982 * @event initialize
25983 * Fires when the editor is fully initialized (including the iframe)
25984 * @param {Roo.HtmlEditorCore} this
25989 * Fires when the editor is first receives the focus. Any insertion must wait
25990 * until after this event.
25991 * @param {Roo.HtmlEditorCore} this
25995 * @event beforesync
25996 * Fires before the textarea is updated with content from the editor iframe. Return false
25997 * to cancel the sync.
25998 * @param {Roo.HtmlEditorCore} this
25999 * @param {String} html
26003 * @event beforepush
26004 * Fires before the iframe editor is updated with content from the textarea. Return false
26005 * to cancel the push.
26006 * @param {Roo.HtmlEditorCore} this
26007 * @param {String} html
26012 * Fires when the textarea is updated with content from the editor iframe.
26013 * @param {Roo.HtmlEditorCore} this
26014 * @param {String} html
26019 * Fires when the iframe editor is updated with content from the textarea.
26020 * @param {Roo.HtmlEditorCore} this
26021 * @param {String} html
26026 * @event editorevent
26027 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26028 * @param {Roo.HtmlEditorCore} this
26034 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26036 // defaults : white / black...
26037 this.applyBlacklists();
26044 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26048 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26054 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26059 * @cfg {Number} height (in pixels)
26063 * @cfg {Number} width (in pixels)
26068 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26071 stylesheets: false,
26074 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26076 allowComments: false,
26080 // private properties
26081 validationEvent : false,
26083 initialized : false,
26085 sourceEditMode : false,
26086 onFocus : Roo.emptyFn,
26088 hideMode:'offsets',
26092 // blacklist + whitelisted elements..
26099 * Protected method that will not generally be called directly. It
26100 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26101 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26103 getDocMarkup : function(){
26107 // inherit styels from page...??
26108 if (this.stylesheets === false) {
26110 Roo.get(document.head).select('style').each(function(node) {
26111 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26114 Roo.get(document.head).select('link').each(function(node) {
26115 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26118 } else if (!this.stylesheets.length) {
26120 st = '<style type="text/css">' +
26121 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26124 for (var i in this.stylesheets) {
26125 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26130 st += '<style type="text/css">' +
26131 'IMG { cursor: pointer } ' +
26134 var cls = 'roo-htmleditor-body';
26136 if(this.bodyCls.length){
26137 cls += ' ' + this.bodyCls;
26140 return '<html><head>' + st +
26141 //<style type="text/css">' +
26142 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26144 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26148 onRender : function(ct, position)
26151 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26152 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26155 this.el.dom.style.border = '0 none';
26156 this.el.dom.setAttribute('tabIndex', -1);
26157 this.el.addClass('x-hidden hide');
26161 if(Roo.isIE){ // fix IE 1px bogus margin
26162 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26166 this.frameId = Roo.id();
26170 var iframe = this.owner.wrap.createChild({
26172 cls: 'form-control', // bootstrap..
26174 name: this.frameId,
26175 frameBorder : 'no',
26176 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26181 this.iframe = iframe.dom;
26183 this.assignDocWin();
26185 this.doc.designMode = 'on';
26188 this.doc.write(this.getDocMarkup());
26192 var task = { // must defer to wait for browser to be ready
26194 //console.log("run task?" + this.doc.readyState);
26195 this.assignDocWin();
26196 if(this.doc.body || this.doc.readyState == 'complete'){
26198 this.doc.designMode="on";
26202 Roo.TaskMgr.stop(task);
26203 this.initEditor.defer(10, this);
26210 Roo.TaskMgr.start(task);
26215 onResize : function(w, h)
26217 Roo.log('resize: ' +w + ',' + h );
26218 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26222 if(typeof w == 'number'){
26224 this.iframe.style.width = w + 'px';
26226 if(typeof h == 'number'){
26228 this.iframe.style.height = h + 'px';
26230 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26237 * Toggles the editor between standard and source edit mode.
26238 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26240 toggleSourceEdit : function(sourceEditMode){
26242 this.sourceEditMode = sourceEditMode === true;
26244 if(this.sourceEditMode){
26246 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26249 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26250 //this.iframe.className = '';
26253 //this.setSize(this.owner.wrap.getSize());
26254 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26261 * Protected method that will not generally be called directly. If you need/want
26262 * custom HTML cleanup, this is the method you should override.
26263 * @param {String} html The HTML to be cleaned
26264 * return {String} The cleaned HTML
26266 cleanHtml : function(html){
26267 html = String(html);
26268 if(html.length > 5){
26269 if(Roo.isSafari){ // strip safari nonsense
26270 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26273 if(html == ' '){
26280 * HTML Editor -> Textarea
26281 * Protected method that will not generally be called directly. Syncs the contents
26282 * of the editor iframe with the textarea.
26284 syncValue : function(){
26285 if(this.initialized){
26286 var bd = (this.doc.body || this.doc.documentElement);
26287 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26288 var html = bd.innerHTML;
26290 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26291 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26293 html = '<div style="'+m[0]+'">' + html + '</div>';
26296 html = this.cleanHtml(html);
26297 // fix up the special chars.. normaly like back quotes in word...
26298 // however we do not want to do this with chinese..
26299 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26301 var cc = match.charCodeAt();
26303 // Get the character value, handling surrogate pairs
26304 if (match.length == 2) {
26305 // It's a surrogate pair, calculate the Unicode code point
26306 var high = match.charCodeAt(0) - 0xD800;
26307 var low = match.charCodeAt(1) - 0xDC00;
26308 cc = (high * 0x400) + low + 0x10000;
26310 (cc >= 0x4E00 && cc < 0xA000 ) ||
26311 (cc >= 0x3400 && cc < 0x4E00 ) ||
26312 (cc >= 0xf900 && cc < 0xfb00 )
26317 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26318 return "&#" + cc + ";";
26325 if(this.owner.fireEvent('beforesync', this, html) !== false){
26326 this.el.dom.value = html;
26327 this.owner.fireEvent('sync', this, html);
26333 * Protected method that will not generally be called directly. Pushes the value of the textarea
26334 * into the iframe editor.
26336 pushValue : function(){
26337 if(this.initialized){
26338 var v = this.el.dom.value.trim();
26340 // if(v.length < 1){
26344 if(this.owner.fireEvent('beforepush', this, v) !== false){
26345 var d = (this.doc.body || this.doc.documentElement);
26347 this.cleanUpPaste();
26348 this.el.dom.value = d.innerHTML;
26349 this.owner.fireEvent('push', this, v);
26355 deferFocus : function(){
26356 this.focus.defer(10, this);
26360 focus : function(){
26361 if(this.win && !this.sourceEditMode){
26368 assignDocWin: function()
26370 var iframe = this.iframe;
26373 this.doc = iframe.contentWindow.document;
26374 this.win = iframe.contentWindow;
26376 // if (!Roo.get(this.frameId)) {
26379 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26380 // this.win = Roo.get(this.frameId).dom.contentWindow;
26382 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26386 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26387 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26392 initEditor : function(){
26393 //console.log("INIT EDITOR");
26394 this.assignDocWin();
26398 this.doc.designMode="on";
26400 this.doc.write(this.getDocMarkup());
26403 var dbody = (this.doc.body || this.doc.documentElement);
26404 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26405 // this copies styles from the containing element into thsi one..
26406 // not sure why we need all of this..
26407 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26409 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26410 //ss['background-attachment'] = 'fixed'; // w3c
26411 dbody.bgProperties = 'fixed'; // ie
26412 //Roo.DomHelper.applyStyles(dbody, ss);
26413 Roo.EventManager.on(this.doc, {
26414 //'mousedown': this.onEditorEvent,
26415 'mouseup': this.onEditorEvent,
26416 'dblclick': this.onEditorEvent,
26417 'click': this.onEditorEvent,
26418 'keyup': this.onEditorEvent,
26423 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26425 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26426 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26428 this.initialized = true;
26430 this.owner.fireEvent('initialize', this);
26435 onDestroy : function(){
26441 //for (var i =0; i < this.toolbars.length;i++) {
26442 // // fixme - ask toolbars for heights?
26443 // this.toolbars[i].onDestroy();
26446 //this.wrap.dom.innerHTML = '';
26447 //this.wrap.remove();
26452 onFirstFocus : function(){
26454 this.assignDocWin();
26457 this.activated = true;
26460 if(Roo.isGecko){ // prevent silly gecko errors
26462 var s = this.win.getSelection();
26463 if(!s.focusNode || s.focusNode.nodeType != 3){
26464 var r = s.getRangeAt(0);
26465 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26470 this.execCmd('useCSS', true);
26471 this.execCmd('styleWithCSS', false);
26474 this.owner.fireEvent('activate', this);
26478 adjustFont: function(btn){
26479 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26480 //if(Roo.isSafari){ // safari
26483 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26484 if(Roo.isSafari){ // safari
26485 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26486 v = (v < 10) ? 10 : v;
26487 v = (v > 48) ? 48 : v;
26488 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26493 v = Math.max(1, v+adjust);
26495 this.execCmd('FontSize', v );
26498 onEditorEvent : function(e)
26500 this.owner.fireEvent('editorevent', this, e);
26501 // this.updateToolbar();
26502 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26505 insertTag : function(tg)
26507 // could be a bit smarter... -> wrap the current selected tRoo..
26508 if (tg.toLowerCase() == 'span' ||
26509 tg.toLowerCase() == 'code' ||
26510 tg.toLowerCase() == 'sup' ||
26511 tg.toLowerCase() == 'sub'
26514 range = this.createRange(this.getSelection());
26515 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26516 wrappingNode.appendChild(range.extractContents());
26517 range.insertNode(wrappingNode);
26524 this.execCmd("formatblock", tg);
26528 insertText : function(txt)
26532 var range = this.createRange();
26533 range.deleteContents();
26534 //alert(Sender.getAttribute('label'));
26536 range.insertNode(this.doc.createTextNode(txt));
26542 * Executes a Midas editor command on the editor document and performs necessary focus and
26543 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26544 * @param {String} cmd The Midas command
26545 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26547 relayCmd : function(cmd, value){
26549 this.execCmd(cmd, value);
26550 this.owner.fireEvent('editorevent', this);
26551 //this.updateToolbar();
26552 this.owner.deferFocus();
26556 * Executes a Midas editor command directly on the editor document.
26557 * For visual commands, you should use {@link #relayCmd} instead.
26558 * <b>This should only be called after the editor is initialized.</b>
26559 * @param {String} cmd The Midas command
26560 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26562 execCmd : function(cmd, value){
26563 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26570 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26572 * @param {String} text | dom node..
26574 insertAtCursor : function(text)
26577 if(!this.activated){
26583 var r = this.doc.selection.createRange();
26594 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26598 // from jquery ui (MIT licenced)
26600 var win = this.win;
26602 if (win.getSelection && win.getSelection().getRangeAt) {
26603 range = win.getSelection().getRangeAt(0);
26604 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26605 range.insertNode(node);
26606 } else if (win.document.selection && win.document.selection.createRange) {
26607 // no firefox support
26608 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26609 win.document.selection.createRange().pasteHTML(txt);
26611 // no firefox support
26612 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26613 this.execCmd('InsertHTML', txt);
26622 mozKeyPress : function(e){
26624 var c = e.getCharCode(), cmd;
26627 c = String.fromCharCode(c).toLowerCase();
26641 this.cleanUpPaste.defer(100, this);
26649 e.preventDefault();
26657 fixKeys : function(){ // load time branching for fastest keydown performance
26659 return function(e){
26660 var k = e.getKey(), r;
26663 r = this.doc.selection.createRange();
26666 r.pasteHTML('    ');
26673 r = this.doc.selection.createRange();
26675 var target = r.parentElement();
26676 if(!target || target.tagName.toLowerCase() != 'li'){
26678 r.pasteHTML('<br />');
26684 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26685 this.cleanUpPaste.defer(100, this);
26691 }else if(Roo.isOpera){
26692 return function(e){
26693 var k = e.getKey();
26697 this.execCmd('InsertHTML','    ');
26700 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26701 this.cleanUpPaste.defer(100, this);
26706 }else if(Roo.isSafari){
26707 return function(e){
26708 var k = e.getKey();
26712 this.execCmd('InsertText','\t');
26716 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26717 this.cleanUpPaste.defer(100, this);
26725 getAllAncestors: function()
26727 var p = this.getSelectedNode();
26730 a.push(p); // push blank onto stack..
26731 p = this.getParentElement();
26735 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26739 a.push(this.doc.body);
26743 lastSelNode : false,
26746 getSelection : function()
26748 this.assignDocWin();
26749 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26752 getSelectedNode: function()
26754 // this may only work on Gecko!!!
26756 // should we cache this!!!!
26761 var range = this.createRange(this.getSelection()).cloneRange();
26764 var parent = range.parentElement();
26766 var testRange = range.duplicate();
26767 testRange.moveToElementText(parent);
26768 if (testRange.inRange(range)) {
26771 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26774 parent = parent.parentElement;
26779 // is ancestor a text element.
26780 var ac = range.commonAncestorContainer;
26781 if (ac.nodeType == 3) {
26782 ac = ac.parentNode;
26785 var ar = ac.childNodes;
26788 var other_nodes = [];
26789 var has_other_nodes = false;
26790 for (var i=0;i<ar.length;i++) {
26791 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26794 // fullly contained node.
26796 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26801 // probably selected..
26802 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26803 other_nodes.push(ar[i]);
26807 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26812 has_other_nodes = true;
26814 if (!nodes.length && other_nodes.length) {
26815 nodes= other_nodes;
26817 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26823 createRange: function(sel)
26825 // this has strange effects when using with
26826 // top toolbar - not sure if it's a great idea.
26827 //this.editor.contentWindow.focus();
26828 if (typeof sel != "undefined") {
26830 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26832 return this.doc.createRange();
26835 return this.doc.createRange();
26838 getParentElement: function()
26841 this.assignDocWin();
26842 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26844 var range = this.createRange(sel);
26847 var p = range.commonAncestorContainer;
26848 while (p.nodeType == 3) { // text node
26859 * Range intersection.. the hard stuff...
26863 * [ -- selected range --- ]
26867 * if end is before start or hits it. fail.
26868 * if start is after end or hits it fail.
26870 * if either hits (but other is outside. - then it's not
26876 // @see http://www.thismuchiknow.co.uk/?p=64.
26877 rangeIntersectsNode : function(range, node)
26879 var nodeRange = node.ownerDocument.createRange();
26881 nodeRange.selectNode(node);
26883 nodeRange.selectNodeContents(node);
26886 var rangeStartRange = range.cloneRange();
26887 rangeStartRange.collapse(true);
26889 var rangeEndRange = range.cloneRange();
26890 rangeEndRange.collapse(false);
26892 var nodeStartRange = nodeRange.cloneRange();
26893 nodeStartRange.collapse(true);
26895 var nodeEndRange = nodeRange.cloneRange();
26896 nodeEndRange.collapse(false);
26898 return rangeStartRange.compareBoundaryPoints(
26899 Range.START_TO_START, nodeEndRange) == -1 &&
26900 rangeEndRange.compareBoundaryPoints(
26901 Range.START_TO_START, nodeStartRange) == 1;
26905 rangeCompareNode : function(range, node)
26907 var nodeRange = node.ownerDocument.createRange();
26909 nodeRange.selectNode(node);
26911 nodeRange.selectNodeContents(node);
26915 range.collapse(true);
26917 nodeRange.collapse(true);
26919 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26920 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26922 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26924 var nodeIsBefore = ss == 1;
26925 var nodeIsAfter = ee == -1;
26927 if (nodeIsBefore && nodeIsAfter) {
26930 if (!nodeIsBefore && nodeIsAfter) {
26931 return 1; //right trailed.
26934 if (nodeIsBefore && !nodeIsAfter) {
26935 return 2; // left trailed.
26941 // private? - in a new class?
26942 cleanUpPaste : function()
26944 // cleans up the whole document..
26945 Roo.log('cleanuppaste');
26947 this.cleanUpChildren(this.doc.body);
26948 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26949 if (clean != this.doc.body.innerHTML) {
26950 this.doc.body.innerHTML = clean;
26955 cleanWordChars : function(input) {// change the chars to hex code
26956 var he = Roo.HtmlEditorCore;
26958 var output = input;
26959 Roo.each(he.swapCodes, function(sw) {
26960 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26962 output = output.replace(swapper, sw[1]);
26969 cleanUpChildren : function (n)
26971 if (!n.childNodes.length) {
26974 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26975 this.cleanUpChild(n.childNodes[i]);
26982 cleanUpChild : function (node)
26985 //console.log(node);
26986 if (node.nodeName == "#text") {
26987 // clean up silly Windows -- stuff?
26990 if (node.nodeName == "#comment") {
26991 if (!this.allowComments) {
26992 node.parentNode.removeChild(node);
26994 // clean up silly Windows -- stuff?
26997 var lcname = node.tagName.toLowerCase();
26998 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26999 // whitelist of tags..
27001 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27003 node.parentNode.removeChild(node);
27008 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27010 // spans with no attributes - just remove them..
27011 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27012 remove_keep_children = true;
27015 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27016 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27018 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27019 // remove_keep_children = true;
27022 if (remove_keep_children) {
27023 this.cleanUpChildren(node);
27024 // inserts everything just before this node...
27025 while (node.childNodes.length) {
27026 var cn = node.childNodes[0];
27027 node.removeChild(cn);
27028 node.parentNode.insertBefore(cn, node);
27030 node.parentNode.removeChild(node);
27034 if (!node.attributes || !node.attributes.length) {
27039 this.cleanUpChildren(node);
27043 function cleanAttr(n,v)
27046 if (v.match(/^\./) || v.match(/^\//)) {
27049 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27052 if (v.match(/^#/)) {
27055 if (v.match(/^\{/)) { // allow template editing.
27058 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27059 node.removeAttribute(n);
27063 var cwhite = this.cwhite;
27064 var cblack = this.cblack;
27066 function cleanStyle(n,v)
27068 if (v.match(/expression/)) { //XSS?? should we even bother..
27069 node.removeAttribute(n);
27073 var parts = v.split(/;/);
27076 Roo.each(parts, function(p) {
27077 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27081 var l = p.split(':').shift().replace(/\s+/g,'');
27082 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27084 if ( cwhite.length && cblack.indexOf(l) > -1) {
27085 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27086 //node.removeAttribute(n);
27090 // only allow 'c whitelisted system attributes'
27091 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27092 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27093 //node.removeAttribute(n);
27103 if (clean.length) {
27104 node.setAttribute(n, clean.join(';'));
27106 node.removeAttribute(n);
27112 for (var i = node.attributes.length-1; i > -1 ; i--) {
27113 var a = node.attributes[i];
27116 if (a.name.toLowerCase().substr(0,2)=='on') {
27117 node.removeAttribute(a.name);
27120 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27121 node.removeAttribute(a.name);
27124 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27125 cleanAttr(a.name,a.value); // fixme..
27128 if (a.name == 'style') {
27129 cleanStyle(a.name,a.value);
27132 /// clean up MS crap..
27133 // tecnically this should be a list of valid class'es..
27136 if (a.name == 'class') {
27137 if (a.value.match(/^Mso/)) {
27138 node.removeAttribute('class');
27141 if (a.value.match(/^body$/)) {
27142 node.removeAttribute('class');
27153 this.cleanUpChildren(node);
27159 * Clean up MS wordisms...
27161 cleanWord : function(node)
27164 this.cleanWord(this.doc.body);
27169 node.nodeName == 'SPAN' &&
27170 !node.hasAttributes() &&
27171 node.childNodes.length == 1 &&
27172 node.firstChild.nodeName == "#text"
27174 var textNode = node.firstChild;
27175 node.removeChild(textNode);
27176 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27177 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27179 node.parentNode.insertBefore(textNode, node);
27180 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27181 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27183 node.parentNode.removeChild(node);
27186 if (node.nodeName == "#text") {
27187 // clean up silly Windows -- stuff?
27190 if (node.nodeName == "#comment") {
27191 node.parentNode.removeChild(node);
27192 // clean up silly Windows -- stuff?
27196 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27197 node.parentNode.removeChild(node);
27200 //Roo.log(node.tagName);
27201 // remove - but keep children..
27202 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27203 //Roo.log('-- removed');
27204 while (node.childNodes.length) {
27205 var cn = node.childNodes[0];
27206 node.removeChild(cn);
27207 node.parentNode.insertBefore(cn, node);
27208 // move node to parent - and clean it..
27209 this.cleanWord(cn);
27211 node.parentNode.removeChild(node);
27212 /// no need to iterate chidlren = it's got none..
27213 //this.iterateChildren(node, this.cleanWord);
27217 if (node.className.length) {
27219 var cn = node.className.split(/\W+/);
27221 Roo.each(cn, function(cls) {
27222 if (cls.match(/Mso[a-zA-Z]+/)) {
27227 node.className = cna.length ? cna.join(' ') : '';
27229 node.removeAttribute("class");
27233 if (node.hasAttribute("lang")) {
27234 node.removeAttribute("lang");
27237 if (node.hasAttribute("style")) {
27239 var styles = node.getAttribute("style").split(";");
27241 Roo.each(styles, function(s) {
27242 if (!s.match(/:/)) {
27245 var kv = s.split(":");
27246 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27249 // what ever is left... we allow.
27252 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27253 if (!nstyle.length) {
27254 node.removeAttribute('style');
27257 this.iterateChildren(node, this.cleanWord);
27263 * iterateChildren of a Node, calling fn each time, using this as the scole..
27264 * @param {DomNode} node node to iterate children of.
27265 * @param {Function} fn method of this class to call on each item.
27267 iterateChildren : function(node, fn)
27269 if (!node.childNodes.length) {
27272 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27273 fn.call(this, node.childNodes[i])
27279 * cleanTableWidths.
27281 * Quite often pasting from word etc.. results in tables with column and widths.
27282 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27285 cleanTableWidths : function(node)
27290 this.cleanTableWidths(this.doc.body);
27295 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27298 Roo.log(node.tagName);
27299 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27300 this.iterateChildren(node, this.cleanTableWidths);
27303 if (node.hasAttribute('width')) {
27304 node.removeAttribute('width');
27308 if (node.hasAttribute("style")) {
27311 var styles = node.getAttribute("style").split(";");
27313 Roo.each(styles, function(s) {
27314 if (!s.match(/:/)) {
27317 var kv = s.split(":");
27318 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27321 // what ever is left... we allow.
27324 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27325 if (!nstyle.length) {
27326 node.removeAttribute('style');
27330 this.iterateChildren(node, this.cleanTableWidths);
27338 domToHTML : function(currentElement, depth, nopadtext) {
27340 depth = depth || 0;
27341 nopadtext = nopadtext || false;
27343 if (!currentElement) {
27344 return this.domToHTML(this.doc.body);
27347 //Roo.log(currentElement);
27349 var allText = false;
27350 var nodeName = currentElement.nodeName;
27351 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27353 if (nodeName == '#text') {
27355 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27360 if (nodeName != 'BODY') {
27363 // Prints the node tagName, such as <A>, <IMG>, etc
27366 for(i = 0; i < currentElement.attributes.length;i++) {
27368 var aname = currentElement.attributes.item(i).name;
27369 if (!currentElement.attributes.item(i).value.length) {
27372 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27375 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27384 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27387 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27392 // Traverse the tree
27394 var currentElementChild = currentElement.childNodes.item(i);
27395 var allText = true;
27396 var innerHTML = '';
27398 while (currentElementChild) {
27399 // Formatting code (indent the tree so it looks nice on the screen)
27400 var nopad = nopadtext;
27401 if (lastnode == 'SPAN') {
27405 if (currentElementChild.nodeName == '#text') {
27406 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27407 toadd = nopadtext ? toadd : toadd.trim();
27408 if (!nopad && toadd.length > 80) {
27409 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27411 innerHTML += toadd;
27414 currentElementChild = currentElement.childNodes.item(i);
27420 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27422 // Recursively traverse the tree structure of the child node
27423 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27424 lastnode = currentElementChild.nodeName;
27426 currentElementChild=currentElement.childNodes.item(i);
27432 // The remaining code is mostly for formatting the tree
27433 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27438 ret+= "</"+tagName+">";
27444 applyBlacklists : function()
27446 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27447 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27451 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27452 if (b.indexOf(tag) > -1) {
27455 this.white.push(tag);
27459 Roo.each(w, function(tag) {
27460 if (b.indexOf(tag) > -1) {
27463 if (this.white.indexOf(tag) > -1) {
27466 this.white.push(tag);
27471 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27472 if (w.indexOf(tag) > -1) {
27475 this.black.push(tag);
27479 Roo.each(b, function(tag) {
27480 if (w.indexOf(tag) > -1) {
27483 if (this.black.indexOf(tag) > -1) {
27486 this.black.push(tag);
27491 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27492 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27496 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27497 if (b.indexOf(tag) > -1) {
27500 this.cwhite.push(tag);
27504 Roo.each(w, function(tag) {
27505 if (b.indexOf(tag) > -1) {
27508 if (this.cwhite.indexOf(tag) > -1) {
27511 this.cwhite.push(tag);
27516 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27517 if (w.indexOf(tag) > -1) {
27520 this.cblack.push(tag);
27524 Roo.each(b, function(tag) {
27525 if (w.indexOf(tag) > -1) {
27528 if (this.cblack.indexOf(tag) > -1) {
27531 this.cblack.push(tag);
27536 setStylesheets : function(stylesheets)
27538 if(typeof(stylesheets) == 'string'){
27539 Roo.get(this.iframe.contentDocument.head).createChild({
27541 rel : 'stylesheet',
27550 Roo.each(stylesheets, function(s) {
27555 Roo.get(_this.iframe.contentDocument.head).createChild({
27557 rel : 'stylesheet',
27566 removeStylesheets : function()
27570 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27575 setStyle : function(style)
27577 Roo.get(this.iframe.contentDocument.head).createChild({
27586 // hide stuff that is not compatible
27600 * @event specialkey
27604 * @cfg {String} fieldClass @hide
27607 * @cfg {String} focusClass @hide
27610 * @cfg {String} autoCreate @hide
27613 * @cfg {String} inputType @hide
27616 * @cfg {String} invalidClass @hide
27619 * @cfg {String} invalidText @hide
27622 * @cfg {String} msgFx @hide
27625 * @cfg {String} validateOnBlur @hide
27629 Roo.HtmlEditorCore.white = [
27630 'area', 'br', 'img', 'input', 'hr', 'wbr',
27632 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27633 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27634 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27635 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27636 'table', 'ul', 'xmp',
27638 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27641 'dir', 'menu', 'ol', 'ul', 'dl',
27647 Roo.HtmlEditorCore.black = [
27648 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27650 'base', 'basefont', 'bgsound', 'blink', 'body',
27651 'frame', 'frameset', 'head', 'html', 'ilayer',
27652 'iframe', 'layer', 'link', 'meta', 'object',
27653 'script', 'style' ,'title', 'xml' // clean later..
27655 Roo.HtmlEditorCore.clean = [
27656 'script', 'style', 'title', 'xml'
27658 Roo.HtmlEditorCore.remove = [
27663 Roo.HtmlEditorCore.ablack = [
27667 Roo.HtmlEditorCore.aclean = [
27668 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27672 Roo.HtmlEditorCore.pwhite= [
27673 'http', 'https', 'mailto'
27676 // white listed style attributes.
27677 Roo.HtmlEditorCore.cwhite= [
27678 // 'text-align', /// default is to allow most things..
27684 // black listed style attributes.
27685 Roo.HtmlEditorCore.cblack= [
27686 // 'font-size' -- this can be set by the project
27690 Roo.HtmlEditorCore.swapCodes =[
27691 [ 8211, "–" ],
27692 [ 8212, "—" ],
27709 * @class Roo.bootstrap.form.HtmlEditor
27710 * @extends Roo.bootstrap.form.TextArea
27711 * Bootstrap HtmlEditor class
27714 * Create a new HtmlEditor
27715 * @param {Object} config The config object
27718 Roo.bootstrap.form.HtmlEditor = function(config){
27719 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27720 if (!this.toolbars) {
27721 this.toolbars = [];
27724 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27727 * @event initialize
27728 * Fires when the editor is fully initialized (including the iframe)
27729 * @param {HtmlEditor} this
27734 * Fires when the editor is first receives the focus. Any insertion must wait
27735 * until after this event.
27736 * @param {HtmlEditor} this
27740 * @event beforesync
27741 * Fires before the textarea is updated with content from the editor iframe. Return false
27742 * to cancel the sync.
27743 * @param {HtmlEditor} this
27744 * @param {String} html
27748 * @event beforepush
27749 * Fires before the iframe editor is updated with content from the textarea. Return false
27750 * to cancel the push.
27751 * @param {HtmlEditor} this
27752 * @param {String} html
27757 * Fires when the textarea is updated with content from the editor iframe.
27758 * @param {HtmlEditor} this
27759 * @param {String} html
27764 * Fires when the iframe editor is updated with content from the textarea.
27765 * @param {HtmlEditor} this
27766 * @param {String} html
27770 * @event editmodechange
27771 * Fires when the editor switches edit modes
27772 * @param {HtmlEditor} this
27773 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27775 editmodechange: true,
27777 * @event editorevent
27778 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27779 * @param {HtmlEditor} this
27783 * @event firstfocus
27784 * Fires when on first focus - needed by toolbars..
27785 * @param {HtmlEditor} this
27790 * Auto save the htmlEditor value as a file into Events
27791 * @param {HtmlEditor} this
27795 * @event savedpreview
27796 * preview the saved version of htmlEditor
27797 * @param {HtmlEditor} this
27804 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27808 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27813 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27818 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27823 * @cfg {Number} height (in pixels)
27827 * @cfg {Number} width (in pixels)
27832 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27835 stylesheets: false,
27840 // private properties
27841 validationEvent : false,
27843 initialized : false,
27846 onFocus : Roo.emptyFn,
27848 hideMode:'offsets',
27850 tbContainer : false,
27854 toolbarContainer :function() {
27855 return this.wrap.select('.x-html-editor-tb',true).first();
27859 * Protected method that will not generally be called directly. It
27860 * is called when the editor creates its toolbar. Override this method if you need to
27861 * add custom toolbar buttons.
27862 * @param {HtmlEditor} editor
27864 createToolbar : function(){
27865 Roo.log('renewing');
27866 Roo.log("create toolbars");
27868 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27869 this.toolbars[0].render(this.toolbarContainer());
27873 // if (!editor.toolbars || !editor.toolbars.length) {
27874 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27877 // for (var i =0 ; i < editor.toolbars.length;i++) {
27878 // editor.toolbars[i] = Roo.factory(
27879 // typeof(editor.toolbars[i]) == 'string' ?
27880 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27881 // Roo.bootstrap.form.HtmlEditor);
27882 // editor.toolbars[i].init(editor);
27888 onRender : function(ct, position)
27890 // Roo.log("Call onRender: " + this.xtype);
27892 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27894 this.wrap = this.inputEl().wrap({
27895 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27898 this.editorcore.onRender(ct, position);
27900 if (this.resizable) {
27901 this.resizeEl = new Roo.Resizable(this.wrap, {
27905 minHeight : this.height,
27906 height: this.height,
27907 handles : this.resizable,
27910 resize : function(r, w, h) {
27911 _t.onResize(w,h); // -something
27917 this.createToolbar(this);
27920 if(!this.width && this.resizable){
27921 this.setSize(this.wrap.getSize());
27923 if (this.resizeEl) {
27924 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27925 // should trigger onReize..
27931 onResize : function(w, h)
27933 Roo.log('resize: ' +w + ',' + h );
27934 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27938 if(this.inputEl() ){
27939 if(typeof w == 'number'){
27940 var aw = w - this.wrap.getFrameWidth('lr');
27941 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27944 if(typeof h == 'number'){
27945 var tbh = -11; // fixme it needs to tool bar size!
27946 for (var i =0; i < this.toolbars.length;i++) {
27947 // fixme - ask toolbars for heights?
27948 tbh += this.toolbars[i].el.getHeight();
27949 //if (this.toolbars[i].footer) {
27950 // tbh += this.toolbars[i].footer.el.getHeight();
27958 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27959 ah -= 5; // knock a few pixes off for look..
27960 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27964 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27965 this.editorcore.onResize(ew,eh);
27970 * Toggles the editor between standard and source edit mode.
27971 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27973 toggleSourceEdit : function(sourceEditMode)
27975 this.editorcore.toggleSourceEdit(sourceEditMode);
27977 if(this.editorcore.sourceEditMode){
27978 Roo.log('editor - showing textarea');
27981 // Roo.log(this.syncValue());
27983 this.inputEl().removeClass(['hide', 'x-hidden']);
27984 this.inputEl().dom.removeAttribute('tabIndex');
27985 this.inputEl().focus();
27987 Roo.log('editor - hiding textarea');
27989 // Roo.log(this.pushValue());
27992 this.inputEl().addClass(['hide', 'x-hidden']);
27993 this.inputEl().dom.setAttribute('tabIndex', -1);
27994 //this.deferFocus();
27997 if(this.resizable){
27998 this.setSize(this.wrap.getSize());
28001 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28004 // private (for BoxComponent)
28005 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28007 // private (for BoxComponent)
28008 getResizeEl : function(){
28012 // private (for BoxComponent)
28013 getPositionEl : function(){
28018 initEvents : function(){
28019 this.originalValue = this.getValue();
28023 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28026 // markInvalid : Roo.emptyFn,
28028 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28031 // clearInvalid : Roo.emptyFn,
28033 setValue : function(v){
28034 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28035 this.editorcore.pushValue();
28040 deferFocus : function(){
28041 this.focus.defer(10, this);
28045 focus : function(){
28046 this.editorcore.focus();
28052 onDestroy : function(){
28058 for (var i =0; i < this.toolbars.length;i++) {
28059 // fixme - ask toolbars for heights?
28060 this.toolbars[i].onDestroy();
28063 this.wrap.dom.innerHTML = '';
28064 this.wrap.remove();
28069 onFirstFocus : function(){
28070 //Roo.log("onFirstFocus");
28071 this.editorcore.onFirstFocus();
28072 for (var i =0; i < this.toolbars.length;i++) {
28073 this.toolbars[i].onFirstFocus();
28079 syncValue : function()
28081 this.editorcore.syncValue();
28084 pushValue : function()
28086 this.editorcore.pushValue();
28090 // hide stuff that is not compatible
28104 * @event specialkey
28108 * @cfg {String} fieldClass @hide
28111 * @cfg {String} focusClass @hide
28114 * @cfg {String} autoCreate @hide
28117 * @cfg {String} inputType @hide
28121 * @cfg {String} invalidText @hide
28124 * @cfg {String} msgFx @hide
28127 * @cfg {String} validateOnBlur @hide
28136 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28138 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28139 * @parent Roo.bootstrap.form.HtmlEditor
28140 * @extends Roo.bootstrap.nav.Simplebar
28146 new Roo.bootstrap.form.HtmlEditor({
28149 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28150 disable : { fonts: 1 , format: 1, ..., ... , ...],
28156 * @cfg {Object} disable List of elements to disable..
28157 * @cfg {Array} btns List of additional buttons.
28161 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28164 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28167 Roo.apply(this, config);
28169 // default disabled, based on 'good practice'..
28170 this.disable = this.disable || {};
28171 Roo.applyIf(this.disable, {
28174 specialElements : true
28176 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28178 this.editor = config.editor;
28179 this.editorcore = config.editor.editorcore;
28181 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28183 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28184 // dont call parent... till later.
28186 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28191 editorcore : false,
28196 "h1","h2","h3","h4","h5","h6",
28198 "abbr", "acronym", "address", "cite", "samp", "var",
28202 onRender : function(ct, position)
28204 // Roo.log("Call onRender: " + this.xtype);
28206 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28208 this.el.dom.style.marginBottom = '0';
28210 var editorcore = this.editorcore;
28211 var editor= this.editor;
28214 var btn = function(id,cmd , toggle, handler, html){
28216 var event = toggle ? 'toggle' : 'click';
28221 xns: Roo.bootstrap,
28225 enableToggle:toggle !== false,
28227 pressed : toggle ? false : null,
28230 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28231 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28237 // var cb_box = function...
28242 xns: Roo.bootstrap,
28247 xns: Roo.bootstrap,
28251 Roo.each(this.formats, function(f) {
28252 style.menu.items.push({
28254 xns: Roo.bootstrap,
28255 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28260 editorcore.insertTag(this.tagname);
28267 children.push(style);
28269 btn('bold',false,true);
28270 btn('italic',false,true);
28271 btn('align-left', 'justifyleft',true);
28272 btn('align-center', 'justifycenter',true);
28273 btn('align-right' , 'justifyright',true);
28274 btn('link', false, false, function(btn) {
28275 //Roo.log("create link?");
28276 var url = prompt(this.createLinkText, this.defaultLinkValue);
28277 if(url && url != 'http:/'+'/'){
28278 this.editorcore.relayCmd('createlink', url);
28281 btn('list','insertunorderedlist',true);
28282 btn('pencil', false,true, function(btn){
28284 this.toggleSourceEdit(btn.pressed);
28287 if (this.editor.btns.length > 0) {
28288 for (var i = 0; i<this.editor.btns.length; i++) {
28289 children.push(this.editor.btns[i]);
28297 xns: Roo.bootstrap,
28302 xns: Roo.bootstrap,
28307 cog.menu.items.push({
28309 xns: Roo.bootstrap,
28310 html : Clean styles,
28315 editorcore.insertTag(this.tagname);
28324 this.xtype = 'NavSimplebar';
28326 for(var i=0;i< children.length;i++) {
28328 this.buttons.add(this.addxtypeChild(children[i]));
28332 editor.on('editorevent', this.updateToolbar, this);
28334 onBtnClick : function(id)
28336 this.editorcore.relayCmd(id);
28337 this.editorcore.focus();
28341 * Protected method that will not generally be called directly. It triggers
28342 * a toolbar update by reading the markup state of the current selection in the editor.
28344 updateToolbar: function(){
28346 if(!this.editorcore.activated){
28347 this.editor.onFirstFocus(); // is this neeed?
28351 var btns = this.buttons;
28352 var doc = this.editorcore.doc;
28353 btns.get('bold').setActive(doc.queryCommandState('bold'));
28354 btns.get('italic').setActive(doc.queryCommandState('italic'));
28355 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28357 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28358 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28359 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28361 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28362 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28365 var ans = this.editorcore.getAllAncestors();
28366 if (this.formatCombo) {
28369 var store = this.formatCombo.store;
28370 this.formatCombo.setValue("");
28371 for (var i =0; i < ans.length;i++) {
28372 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28374 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28382 // hides menus... - so this cant be on a menu...
28383 Roo.bootstrap.MenuMgr.hideAll();
28385 Roo.bootstrap.menu.Manager.hideAll();
28386 //this.editorsyncValue();
28388 onFirstFocus: function() {
28389 this.buttons.each(function(item){
28393 toggleSourceEdit : function(sourceEditMode){
28396 if(sourceEditMode){
28397 Roo.log("disabling buttons");
28398 this.buttons.each( function(item){
28399 if(item.cmd != 'pencil'){
28405 Roo.log("enabling buttons");
28406 if(this.editorcore.initialized){
28407 this.buttons.each( function(item){
28413 Roo.log("calling toggole on editor");
28414 // tell the editor that it's been pressed..
28415 this.editor.toggleSourceEdit(sourceEditMode);
28429 * @class Roo.bootstrap.form.Markdown
28430 * @extends Roo.bootstrap.form.TextArea
28431 * Bootstrap Showdown editable area
28432 * @cfg {string} content
28435 * Create a new Showdown
28438 Roo.bootstrap.form.Markdown = function(config){
28439 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28443 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28447 initEvents : function()
28450 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28451 this.markdownEl = this.el.createChild({
28452 cls : 'roo-markdown-area'
28454 this.inputEl().addClass('d-none');
28455 if (this.getValue() == '') {
28456 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28459 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28461 this.markdownEl.on('click', this.toggleTextEdit, this);
28462 this.on('blur', this.toggleTextEdit, this);
28463 this.on('specialkey', this.resizeTextArea, this);
28466 toggleTextEdit : function()
28468 var sh = this.markdownEl.getHeight();
28469 this.inputEl().addClass('d-none');
28470 this.markdownEl.addClass('d-none');
28471 if (!this.editing) {
28473 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28474 this.inputEl().removeClass('d-none');
28475 this.inputEl().focus();
28476 this.editing = true;
28479 // show showdown...
28480 this.updateMarkdown();
28481 this.markdownEl.removeClass('d-none');
28482 this.editing = false;
28485 updateMarkdown : function()
28487 if (this.getValue() == '') {
28488 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28492 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28495 resizeTextArea: function () {
28498 Roo.log([sh, this.getValue().split("\n").length * 30]);
28499 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28501 setValue : function(val)
28503 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28504 if (!this.editing) {
28505 this.updateMarkdown();
28511 if (!this.editing) {
28512 this.toggleTextEdit();
28520 * Ext JS Library 1.1.1
28521 * Copyright(c) 2006-2007, Ext JS, LLC.
28523 * Originally Released Under LGPL - original licence link has changed is not relivant.
28526 * <script type="text/javascript">
28530 * @class Roo.bootstrap.PagingToolbar
28531 * @extends Roo.bootstrap.nav.Simplebar
28532 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28534 * Create a new PagingToolbar
28535 * @param {Object} config The config object
28536 * @param {Roo.data.Store} store
28538 Roo.bootstrap.PagingToolbar = function(config)
28540 // old args format still supported... - xtype is prefered..
28541 // created from xtype...
28543 this.ds = config.dataSource;
28545 if (config.store && !this.ds) {
28546 this.store= Roo.factory(config.store, Roo.data);
28547 this.ds = this.store;
28548 this.ds.xmodule = this.xmodule || false;
28551 this.toolbarItems = [];
28552 if (config.items) {
28553 this.toolbarItems = config.items;
28556 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28561 this.bind(this.ds);
28564 if (Roo.bootstrap.version == 4) {
28565 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28567 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28572 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28574 * @cfg {Roo.bootstrap.Button} buttons[]
28575 * Buttons for the toolbar
28578 * @cfg {Roo.data.Store} store
28579 * The underlying data store providing the paged data
28582 * @cfg {String/HTMLElement/Element} container
28583 * container The id or element that will contain the toolbar
28586 * @cfg {Boolean} displayInfo
28587 * True to display the displayMsg (defaults to false)
28590 * @cfg {Number} pageSize
28591 * The number of records to display per page (defaults to 20)
28595 * @cfg {String} displayMsg
28596 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28598 displayMsg : 'Displaying {0} - {1} of {2}',
28600 * @cfg {String} emptyMsg
28601 * The message to display when no records are found (defaults to "No data to display")
28603 emptyMsg : 'No data to display',
28605 * Customizable piece of the default paging text (defaults to "Page")
28608 beforePageText : "Page",
28610 * Customizable piece of the default paging text (defaults to "of %0")
28613 afterPageText : "of {0}",
28615 * Customizable piece of the default paging text (defaults to "First Page")
28618 firstText : "First Page",
28620 * Customizable piece of the default paging text (defaults to "Previous Page")
28623 prevText : "Previous Page",
28625 * Customizable piece of the default paging text (defaults to "Next Page")
28628 nextText : "Next Page",
28630 * Customizable piece of the default paging text (defaults to "Last Page")
28633 lastText : "Last Page",
28635 * Customizable piece of the default paging text (defaults to "Refresh")
28638 refreshText : "Refresh",
28642 onRender : function(ct, position)
28644 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28645 this.navgroup.parentId = this.id;
28646 this.navgroup.onRender(this.el, null);
28647 // add the buttons to the navgroup
28649 if(this.displayInfo){
28650 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28651 this.displayEl = this.el.select('.x-paging-info', true).first();
28652 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28653 // this.displayEl = navel.el.select('span',true).first();
28659 Roo.each(_this.buttons, function(e){ // this might need to use render????
28660 Roo.factory(e).render(_this.el);
28664 Roo.each(_this.toolbarItems, function(e) {
28665 _this.navgroup.addItem(e);
28669 this.first = this.navgroup.addItem({
28670 tooltip: this.firstText,
28671 cls: "prev btn-outline-secondary",
28672 html : ' <i class="fa fa-step-backward"></i>',
28674 preventDefault: true,
28675 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28678 this.prev = this.navgroup.addItem({
28679 tooltip: this.prevText,
28680 cls: "prev btn-outline-secondary",
28681 html : ' <i class="fa fa-backward"></i>',
28683 preventDefault: true,
28684 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28686 //this.addSeparator();
28689 var field = this.navgroup.addItem( {
28691 cls : 'x-paging-position btn-outline-secondary',
28693 html : this.beforePageText +
28694 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28695 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28698 this.field = field.el.select('input', true).first();
28699 this.field.on("keydown", this.onPagingKeydown, this);
28700 this.field.on("focus", function(){this.dom.select();});
28703 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28704 //this.field.setHeight(18);
28705 //this.addSeparator();
28706 this.next = this.navgroup.addItem({
28707 tooltip: this.nextText,
28708 cls: "next btn-outline-secondary",
28709 html : ' <i class="fa fa-forward"></i>',
28711 preventDefault: true,
28712 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28714 this.last = this.navgroup.addItem({
28715 tooltip: this.lastText,
28716 html : ' <i class="fa fa-step-forward"></i>',
28717 cls: "next btn-outline-secondary",
28719 preventDefault: true,
28720 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28722 //this.addSeparator();
28723 this.loading = this.navgroup.addItem({
28724 tooltip: this.refreshText,
28725 cls: "btn-outline-secondary",
28726 html : ' <i class="fa fa-refresh"></i>',
28727 preventDefault: true,
28728 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28734 updateInfo : function(){
28735 if(this.displayEl){
28736 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28737 var msg = count == 0 ?
28741 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28743 this.displayEl.update(msg);
28748 onLoad : function(ds, r, o)
28750 this.cursor = o.params && o.params.start ? o.params.start : 0;
28752 var d = this.getPageData(),
28757 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28758 this.field.dom.value = ap;
28759 this.first.setDisabled(ap == 1);
28760 this.prev.setDisabled(ap == 1);
28761 this.next.setDisabled(ap == ps);
28762 this.last.setDisabled(ap == ps);
28763 this.loading.enable();
28768 getPageData : function(){
28769 var total = this.ds.getTotalCount();
28772 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28773 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28778 onLoadError : function(){
28779 this.loading.enable();
28783 onPagingKeydown : function(e){
28784 var k = e.getKey();
28785 var d = this.getPageData();
28787 var v = this.field.dom.value, pageNum;
28788 if(!v || isNaN(pageNum = parseInt(v, 10))){
28789 this.field.dom.value = d.activePage;
28792 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28793 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28796 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))
28798 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28799 this.field.dom.value = pageNum;
28800 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28803 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28805 var v = this.field.dom.value, pageNum;
28806 var increment = (e.shiftKey) ? 10 : 1;
28807 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28810 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28811 this.field.dom.value = d.activePage;
28814 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28816 this.field.dom.value = parseInt(v, 10) + increment;
28817 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28818 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28825 beforeLoad : function(){
28827 this.loading.disable();
28832 onClick : function(which){
28841 ds.load({params:{start: 0, limit: this.pageSize}});
28844 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28847 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28850 var total = ds.getTotalCount();
28851 var extra = total % this.pageSize;
28852 var lastStart = extra ? (total - extra) : total-this.pageSize;
28853 ds.load({params:{start: lastStart, limit: this.pageSize}});
28856 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28862 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28863 * @param {Roo.data.Store} store The data store to unbind
28865 unbind : function(ds){
28866 ds.un("beforeload", this.beforeLoad, this);
28867 ds.un("load", this.onLoad, this);
28868 ds.un("loadexception", this.onLoadError, this);
28869 ds.un("remove", this.updateInfo, this);
28870 ds.un("add", this.updateInfo, this);
28871 this.ds = undefined;
28875 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28876 * @param {Roo.data.Store} store The data store to bind
28878 bind : function(ds){
28879 ds.on("beforeload", this.beforeLoad, this);
28880 ds.on("load", this.onLoad, this);
28881 ds.on("loadexception", this.onLoadError, this);
28882 ds.on("remove", this.updateInfo, this);
28883 ds.on("add", this.updateInfo, this);
28894 * @class Roo.bootstrap.MessageBar
28895 * @extends Roo.bootstrap.Component
28896 * Bootstrap MessageBar class
28897 * @cfg {String} html contents of the MessageBar
28898 * @cfg {String} weight (info | success | warning | danger) default info
28899 * @cfg {String} beforeClass insert the bar before the given class
28900 * @cfg {Boolean} closable (true | false) default false
28901 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28904 * Create a new Element
28905 * @param {Object} config The config object
28908 Roo.bootstrap.MessageBar = function(config){
28909 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28912 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28918 beforeClass: 'bootstrap-sticky-wrap',
28920 getAutoCreate : function(){
28924 cls: 'alert alert-dismissable alert-' + this.weight,
28929 html: this.html || ''
28935 cfg.cls += ' alert-messages-fixed';
28949 onRender : function(ct, position)
28951 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28954 var cfg = Roo.apply({}, this.getAutoCreate());
28958 cfg.cls += ' ' + this.cls;
28961 cfg.style = this.style;
28963 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28965 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28968 this.el.select('>button.close').on('click', this.hide, this);
28974 if (!this.rendered) {
28980 this.fireEvent('show', this);
28986 if (!this.rendered) {
28992 this.fireEvent('hide', this);
28995 update : function()
28997 // var e = this.el.dom.firstChild;
28999 // if(this.closable){
29000 // e = e.nextSibling;
29003 // e.data = this.html || '';
29005 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29021 * @class Roo.bootstrap.Graph
29022 * @extends Roo.bootstrap.Component
29023 * Bootstrap Graph class
29027 @cfg {String} graphtype bar | vbar | pie
29028 @cfg {number} g_x coodinator | centre x (pie)
29029 @cfg {number} g_y coodinator | centre y (pie)
29030 @cfg {number} g_r radius (pie)
29031 @cfg {number} g_height height of the chart (respected by all elements in the set)
29032 @cfg {number} g_width width of the chart (respected by all elements in the set)
29033 @cfg {Object} title The title of the chart
29036 -opts (object) options for the chart
29038 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29039 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29041 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.
29042 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29044 o stretch (boolean)
29046 -opts (object) options for the pie
29049 o startAngle (number)
29050 o endAngle (number)
29054 * Create a new Input
29055 * @param {Object} config The config object
29058 Roo.bootstrap.Graph = function(config){
29059 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29065 * The img click event for the img.
29066 * @param {Roo.EventObject} e
29072 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29083 //g_colors: this.colors,
29090 getAutoCreate : function(){
29101 onRender : function(ct,position){
29104 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29106 if (typeof(Raphael) == 'undefined') {
29107 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29111 this.raphael = Raphael(this.el.dom);
29113 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29114 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29115 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29116 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29118 r.text(160, 10, "Single Series Chart").attr(txtattr);
29119 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29120 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29121 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29123 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29124 r.barchart(330, 10, 300, 220, data1);
29125 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29126 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29129 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29130 // r.barchart(30, 30, 560, 250, xdata, {
29131 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29132 // axis : "0 0 1 1",
29133 // axisxlabels : xdata
29134 // //yvalues : cols,
29137 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29139 // this.load(null,xdata,{
29140 // axis : "0 0 1 1",
29141 // axisxlabels : xdata
29146 load : function(graphtype,xdata,opts)
29148 this.raphael.clear();
29150 graphtype = this.graphtype;
29155 var r = this.raphael,
29156 fin = function () {
29157 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29159 fout = function () {
29160 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29162 pfin = function() {
29163 this.sector.stop();
29164 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29167 this.label[0].stop();
29168 this.label[0].attr({ r: 7.5 });
29169 this.label[1].attr({ "font-weight": 800 });
29172 pfout = function() {
29173 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29176 this.label[0].animate({ r: 5 }, 500, "bounce");
29177 this.label[1].attr({ "font-weight": 400 });
29183 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29186 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29189 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29190 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29192 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29199 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29204 setTitle: function(o)
29209 initEvents: function() {
29212 this.el.on('click', this.onClick, this);
29216 onClick : function(e)
29218 Roo.log('img onclick');
29219 this.fireEvent('click', this, e);
29231 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29234 * @class Roo.bootstrap.dash.NumberBox
29235 * @extends Roo.bootstrap.Component
29236 * Bootstrap NumberBox class
29237 * @cfg {String} headline Box headline
29238 * @cfg {String} content Box content
29239 * @cfg {String} icon Box icon
29240 * @cfg {String} footer Footer text
29241 * @cfg {String} fhref Footer href
29244 * Create a new NumberBox
29245 * @param {Object} config The config object
29249 Roo.bootstrap.dash.NumberBox = function(config){
29250 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29254 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29263 getAutoCreate : function(){
29267 cls : 'small-box ',
29275 cls : 'roo-headline',
29276 html : this.headline
29280 cls : 'roo-content',
29281 html : this.content
29295 cls : 'ion ' + this.icon
29304 cls : 'small-box-footer',
29305 href : this.fhref || '#',
29309 cfg.cn.push(footer);
29316 onRender : function(ct,position){
29317 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29324 setHeadline: function (value)
29326 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29329 setFooter: function (value, href)
29331 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29334 this.el.select('a.small-box-footer',true).first().attr('href', href);
29339 setContent: function (value)
29341 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29344 initEvents: function()
29358 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29361 * @class Roo.bootstrap.dash.TabBox
29362 * @extends Roo.bootstrap.Component
29363 * @children Roo.bootstrap.dash.TabPane
29364 * Bootstrap TabBox class
29365 * @cfg {String} title Title of the TabBox
29366 * @cfg {String} icon Icon of the TabBox
29367 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29368 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29371 * Create a new TabBox
29372 * @param {Object} config The config object
29376 Roo.bootstrap.dash.TabBox = function(config){
29377 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29382 * When a pane is added
29383 * @param {Roo.bootstrap.dash.TabPane} pane
29387 * @event activatepane
29388 * When a pane is activated
29389 * @param {Roo.bootstrap.dash.TabPane} pane
29391 "activatepane" : true
29399 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29404 tabScrollable : false,
29406 getChildContainer : function()
29408 return this.el.select('.tab-content', true).first();
29411 getAutoCreate : function(){
29415 cls: 'pull-left header',
29423 cls: 'fa ' + this.icon
29429 cls: 'nav nav-tabs pull-right',
29435 if(this.tabScrollable){
29442 cls: 'nav nav-tabs pull-right',
29453 cls: 'nav-tabs-custom',
29458 cls: 'tab-content no-padding',
29466 initEvents : function()
29468 //Roo.log('add add pane handler');
29469 this.on('addpane', this.onAddPane, this);
29472 * Updates the box title
29473 * @param {String} html to set the title to.
29475 setTitle : function(value)
29477 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29479 onAddPane : function(pane)
29481 this.panes.push(pane);
29482 //Roo.log('addpane');
29484 // tabs are rendere left to right..
29485 if(!this.showtabs){
29489 var ctr = this.el.select('.nav-tabs', true).first();
29492 var existing = ctr.select('.nav-tab',true);
29493 var qty = existing.getCount();;
29496 var tab = ctr.createChild({
29498 cls : 'nav-tab' + (qty ? '' : ' active'),
29506 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29509 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29511 pane.el.addClass('active');
29516 onTabClick : function(ev,un,ob,pane)
29518 //Roo.log('tab - prev default');
29519 ev.preventDefault();
29522 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29523 pane.tab.addClass('active');
29524 //Roo.log(pane.title);
29525 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29526 // technically we should have a deactivate event.. but maybe add later.
29527 // and it should not de-activate the selected tab...
29528 this.fireEvent('activatepane', pane);
29529 pane.el.addClass('active');
29530 pane.fireEvent('activate');
29535 getActivePane : function()
29538 Roo.each(this.panes, function(p) {
29539 if(p.el.hasClass('active')){
29560 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29562 * @class Roo.bootstrap.TabPane
29563 * @extends Roo.bootstrap.Component
29564 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29565 * Bootstrap TabPane class
29566 * @cfg {Boolean} active (false | true) Default false
29567 * @cfg {String} title title of panel
29571 * Create a new TabPane
29572 * @param {Object} config The config object
29575 Roo.bootstrap.dash.TabPane = function(config){
29576 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29582 * When a pane is activated
29583 * @param {Roo.bootstrap.dash.TabPane} pane
29590 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29595 // the tabBox that this is attached to.
29598 getAutoCreate : function()
29606 cfg.cls += ' active';
29611 initEvents : function()
29613 //Roo.log('trigger add pane handler');
29614 this.parent().fireEvent('addpane', this)
29618 * Updates the tab title
29619 * @param {String} html to set the title to.
29621 setTitle: function(str)
29627 this.tab.select('a', true).first().dom.innerHTML = str;
29646 * @class Roo.bootstrap.Tooltip
29647 * Bootstrap Tooltip class
29648 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29649 * to determine which dom element triggers the tooltip.
29651 * It needs to add support for additional attributes like tooltip-position
29654 * Create a new Toolti
29655 * @param {Object} config The config object
29658 Roo.bootstrap.Tooltip = function(config){
29659 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29661 this.alignment = Roo.bootstrap.Tooltip.alignment;
29663 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29664 this.alignment = config.alignment;
29669 Roo.apply(Roo.bootstrap.Tooltip, {
29671 * @function init initialize tooltip monitoring.
29675 currentTip : false,
29676 currentRegion : false,
29682 Roo.get(document).on('mouseover', this.enter ,this);
29683 Roo.get(document).on('mouseout', this.leave, this);
29686 this.currentTip = new Roo.bootstrap.Tooltip();
29689 enter : function(ev)
29691 var dom = ev.getTarget();
29693 //Roo.log(['enter',dom]);
29694 var el = Roo.fly(dom);
29695 if (this.currentEl) {
29697 //Roo.log(this.currentEl);
29698 //Roo.log(this.currentEl.contains(dom));
29699 if (this.currentEl == el) {
29702 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29708 if (this.currentTip.el) {
29709 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29713 if(!el || el.dom == document){
29719 if (!el.attr('tooltip')) {
29720 pel = el.findParent("[tooltip]");
29722 bindEl = Roo.get(pel);
29728 // you can not look for children, as if el is the body.. then everythign is the child..
29729 if (!pel && !el.attr('tooltip')) { //
29730 if (!el.select("[tooltip]").elements.length) {
29733 // is the mouse over this child...?
29734 bindEl = el.select("[tooltip]").first();
29735 var xy = ev.getXY();
29736 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29737 //Roo.log("not in region.");
29740 //Roo.log("child element over..");
29743 this.currentEl = el;
29744 this.currentTip.bind(bindEl);
29745 this.currentRegion = Roo.lib.Region.getRegion(dom);
29746 this.currentTip.enter();
29749 leave : function(ev)
29751 var dom = ev.getTarget();
29752 //Roo.log(['leave',dom]);
29753 if (!this.currentEl) {
29758 if (dom != this.currentEl.dom) {
29761 var xy = ev.getXY();
29762 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29765 // only activate leave if mouse cursor is outside... bounding box..
29770 if (this.currentTip) {
29771 this.currentTip.leave();
29773 //Roo.log('clear currentEl');
29774 this.currentEl = false;
29779 'left' : ['r-l', [-2,0], 'right'],
29780 'right' : ['l-r', [2,0], 'left'],
29781 'bottom' : ['t-b', [0,2], 'top'],
29782 'top' : [ 'b-t', [0,-2], 'bottom']
29788 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29793 delay : null, // can be { show : 300 , hide: 500}
29797 hoverState : null, //???
29799 placement : 'bottom',
29803 getAutoCreate : function(){
29810 cls : 'tooltip-arrow arrow'
29813 cls : 'tooltip-inner'
29820 bind : function(el)
29825 initEvents : function()
29827 this.arrowEl = this.el.select('.arrow', true).first();
29828 this.innerEl = this.el.select('.tooltip-inner', true).first();
29831 enter : function () {
29833 if (this.timeout != null) {
29834 clearTimeout(this.timeout);
29837 this.hoverState = 'in';
29838 //Roo.log("enter - show");
29839 if (!this.delay || !this.delay.show) {
29844 this.timeout = setTimeout(function () {
29845 if (_t.hoverState == 'in') {
29848 }, this.delay.show);
29852 clearTimeout(this.timeout);
29854 this.hoverState = 'out';
29855 if (!this.delay || !this.delay.hide) {
29861 this.timeout = setTimeout(function () {
29862 //Roo.log("leave - timeout");
29864 if (_t.hoverState == 'out') {
29866 Roo.bootstrap.Tooltip.currentEl = false;
29871 show : function (msg)
29874 this.render(document.body);
29877 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29879 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29881 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29883 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29884 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29886 var placement = typeof this.placement == 'function' ?
29887 this.placement.call(this, this.el, on_el) :
29890 var autoToken = /\s?auto?\s?/i;
29891 var autoPlace = autoToken.test(placement);
29893 placement = placement.replace(autoToken, '') || 'top';
29897 //this.el.setXY([0,0]);
29899 //this.el.dom.style.display='block';
29901 //this.el.appendTo(on_el);
29903 var p = this.getPosition();
29904 var box = this.el.getBox();
29910 var align = this.alignment[placement];
29912 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29914 if(placement == 'top' || placement == 'bottom'){
29916 placement = 'right';
29919 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29920 placement = 'left';
29923 var scroll = Roo.select('body', true).first().getScroll();
29925 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29929 align = this.alignment[placement];
29931 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29935 var elems = document.getElementsByTagName('div');
29936 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29937 for (var i = 0; i < elems.length; i++) {
29938 var zindex = Number.parseInt(
29939 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29942 if (zindex > highest) {
29949 this.el.dom.style.zIndex = highest;
29951 this.el.alignTo(this.bindEl, align[0],align[1]);
29952 //var arrow = this.el.select('.arrow',true).first();
29953 //arrow.set(align[2],
29955 this.el.addClass(placement);
29956 this.el.addClass("bs-tooltip-"+ placement);
29958 this.el.addClass('in fade show');
29960 this.hoverState = null;
29962 if (this.el.hasClass('fade')) {
29977 //this.el.setXY([0,0]);
29978 this.el.removeClass(['show', 'in']);
29994 * @class Roo.bootstrap.LocationPicker
29995 * @extends Roo.bootstrap.Component
29996 * Bootstrap LocationPicker class
29997 * @cfg {Number} latitude Position when init default 0
29998 * @cfg {Number} longitude Position when init default 0
29999 * @cfg {Number} zoom default 15
30000 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30001 * @cfg {Boolean} mapTypeControl default false
30002 * @cfg {Boolean} disableDoubleClickZoom default false
30003 * @cfg {Boolean} scrollwheel default true
30004 * @cfg {Boolean} streetViewControl default false
30005 * @cfg {Number} radius default 0
30006 * @cfg {String} locationName
30007 * @cfg {Boolean} draggable default true
30008 * @cfg {Boolean} enableAutocomplete default false
30009 * @cfg {Boolean} enableReverseGeocode default true
30010 * @cfg {String} markerTitle
30013 * Create a new LocationPicker
30014 * @param {Object} config The config object
30018 Roo.bootstrap.LocationPicker = function(config){
30020 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30025 * Fires when the picker initialized.
30026 * @param {Roo.bootstrap.LocationPicker} this
30027 * @param {Google Location} location
30031 * @event positionchanged
30032 * Fires when the picker position changed.
30033 * @param {Roo.bootstrap.LocationPicker} this
30034 * @param {Google Location} location
30036 positionchanged : true,
30039 * Fires when the map resize.
30040 * @param {Roo.bootstrap.LocationPicker} this
30045 * Fires when the map show.
30046 * @param {Roo.bootstrap.LocationPicker} this
30051 * Fires when the map hide.
30052 * @param {Roo.bootstrap.LocationPicker} this
30057 * Fires when click the map.
30058 * @param {Roo.bootstrap.LocationPicker} this
30059 * @param {Map event} e
30063 * @event mapRightClick
30064 * Fires when right click the map.
30065 * @param {Roo.bootstrap.LocationPicker} this
30066 * @param {Map event} e
30068 mapRightClick : true,
30070 * @event markerClick
30071 * Fires when click the marker.
30072 * @param {Roo.bootstrap.LocationPicker} this
30073 * @param {Map event} e
30075 markerClick : true,
30077 * @event markerRightClick
30078 * Fires when right click the marker.
30079 * @param {Roo.bootstrap.LocationPicker} this
30080 * @param {Map event} e
30082 markerRightClick : true,
30084 * @event OverlayViewDraw
30085 * Fires when OverlayView Draw
30086 * @param {Roo.bootstrap.LocationPicker} this
30088 OverlayViewDraw : true,
30090 * @event OverlayViewOnAdd
30091 * Fires when OverlayView Draw
30092 * @param {Roo.bootstrap.LocationPicker} this
30094 OverlayViewOnAdd : true,
30096 * @event OverlayViewOnRemove
30097 * Fires when OverlayView Draw
30098 * @param {Roo.bootstrap.LocationPicker} this
30100 OverlayViewOnRemove : true,
30102 * @event OverlayViewShow
30103 * Fires when OverlayView Draw
30104 * @param {Roo.bootstrap.LocationPicker} this
30105 * @param {Pixel} cpx
30107 OverlayViewShow : true,
30109 * @event OverlayViewHide
30110 * Fires when OverlayView Draw
30111 * @param {Roo.bootstrap.LocationPicker} this
30113 OverlayViewHide : true,
30115 * @event loadexception
30116 * Fires when load google lib failed.
30117 * @param {Roo.bootstrap.LocationPicker} this
30119 loadexception : true
30124 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30126 gMapContext: false,
30132 mapTypeControl: false,
30133 disableDoubleClickZoom: false,
30135 streetViewControl: false,
30139 enableAutocomplete: false,
30140 enableReverseGeocode: true,
30143 getAutoCreate: function()
30148 cls: 'roo-location-picker'
30154 initEvents: function(ct, position)
30156 if(!this.el.getWidth() || this.isApplied()){
30160 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30165 initial: function()
30167 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30168 this.fireEvent('loadexception', this);
30172 if(!this.mapTypeId){
30173 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30176 this.gMapContext = this.GMapContext();
30178 this.initOverlayView();
30180 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30184 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30185 _this.setPosition(_this.gMapContext.marker.position);
30188 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30189 _this.fireEvent('mapClick', this, event);
30193 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30194 _this.fireEvent('mapRightClick', this, event);
30198 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30199 _this.fireEvent('markerClick', this, event);
30203 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30204 _this.fireEvent('markerRightClick', this, event);
30208 this.setPosition(this.gMapContext.location);
30210 this.fireEvent('initial', this, this.gMapContext.location);
30213 initOverlayView: function()
30217 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30221 _this.fireEvent('OverlayViewDraw', _this);
30226 _this.fireEvent('OverlayViewOnAdd', _this);
30229 onRemove: function()
30231 _this.fireEvent('OverlayViewOnRemove', _this);
30234 show: function(cpx)
30236 _this.fireEvent('OverlayViewShow', _this, cpx);
30241 _this.fireEvent('OverlayViewHide', _this);
30247 fromLatLngToContainerPixel: function(event)
30249 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30252 isApplied: function()
30254 return this.getGmapContext() == false ? false : true;
30257 getGmapContext: function()
30259 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30262 GMapContext: function()
30264 var position = new google.maps.LatLng(this.latitude, this.longitude);
30266 var _map = new google.maps.Map(this.el.dom, {
30269 mapTypeId: this.mapTypeId,
30270 mapTypeControl: this.mapTypeControl,
30271 disableDoubleClickZoom: this.disableDoubleClickZoom,
30272 scrollwheel: this.scrollwheel,
30273 streetViewControl: this.streetViewControl,
30274 locationName: this.locationName,
30275 draggable: this.draggable,
30276 enableAutocomplete: this.enableAutocomplete,
30277 enableReverseGeocode: this.enableReverseGeocode
30280 var _marker = new google.maps.Marker({
30281 position: position,
30283 title: this.markerTitle,
30284 draggable: this.draggable
30291 location: position,
30292 radius: this.radius,
30293 locationName: this.locationName,
30294 addressComponents: {
30295 formatted_address: null,
30296 addressLine1: null,
30297 addressLine2: null,
30299 streetNumber: null,
30303 stateOrProvince: null
30306 domContainer: this.el.dom,
30307 geodecoder: new google.maps.Geocoder()
30311 drawCircle: function(center, radius, options)
30313 if (this.gMapContext.circle != null) {
30314 this.gMapContext.circle.setMap(null);
30318 options = Roo.apply({}, options, {
30319 strokeColor: "#0000FF",
30320 strokeOpacity: .35,
30322 fillColor: "#0000FF",
30326 options.map = this.gMapContext.map;
30327 options.radius = radius;
30328 options.center = center;
30329 this.gMapContext.circle = new google.maps.Circle(options);
30330 return this.gMapContext.circle;
30336 setPosition: function(location)
30338 this.gMapContext.location = location;
30339 this.gMapContext.marker.setPosition(location);
30340 this.gMapContext.map.panTo(location);
30341 this.drawCircle(location, this.gMapContext.radius, {});
30345 if (this.gMapContext.settings.enableReverseGeocode) {
30346 this.gMapContext.geodecoder.geocode({
30347 latLng: this.gMapContext.location
30348 }, function(results, status) {
30350 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30351 _this.gMapContext.locationName = results[0].formatted_address;
30352 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30354 _this.fireEvent('positionchanged', this, location);
30361 this.fireEvent('positionchanged', this, location);
30366 google.maps.event.trigger(this.gMapContext.map, "resize");
30368 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30370 this.fireEvent('resize', this);
30373 setPositionByLatLng: function(latitude, longitude)
30375 this.setPosition(new google.maps.LatLng(latitude, longitude));
30378 getCurrentPosition: function()
30381 latitude: this.gMapContext.location.lat(),
30382 longitude: this.gMapContext.location.lng()
30386 getAddressName: function()
30388 return this.gMapContext.locationName;
30391 getAddressComponents: function()
30393 return this.gMapContext.addressComponents;
30396 address_component_from_google_geocode: function(address_components)
30400 for (var i = 0; i < address_components.length; i++) {
30401 var component = address_components[i];
30402 if (component.types.indexOf("postal_code") >= 0) {
30403 result.postalCode = component.short_name;
30404 } else if (component.types.indexOf("street_number") >= 0) {
30405 result.streetNumber = component.short_name;
30406 } else if (component.types.indexOf("route") >= 0) {
30407 result.streetName = component.short_name;
30408 } else if (component.types.indexOf("neighborhood") >= 0) {
30409 result.city = component.short_name;
30410 } else if (component.types.indexOf("locality") >= 0) {
30411 result.city = component.short_name;
30412 } else if (component.types.indexOf("sublocality") >= 0) {
30413 result.district = component.short_name;
30414 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30415 result.stateOrProvince = component.short_name;
30416 } else if (component.types.indexOf("country") >= 0) {
30417 result.country = component.short_name;
30421 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30422 result.addressLine2 = "";
30426 setZoomLevel: function(zoom)
30428 this.gMapContext.map.setZoom(zoom);
30441 this.fireEvent('show', this);
30452 this.fireEvent('hide', this);
30457 Roo.apply(Roo.bootstrap.LocationPicker, {
30459 OverlayView : function(map, options)
30461 options = options || {};
30468 * @class Roo.bootstrap.Alert
30469 * @extends Roo.bootstrap.Component
30470 * Bootstrap Alert class - shows an alert area box
30472 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30473 Enter a valid email address
30476 * @cfg {String} title The title of alert
30477 * @cfg {String} html The content of alert
30478 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30479 * @cfg {String} fa font-awesomeicon
30480 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30481 * @cfg {Boolean} close true to show a x closer
30485 * Create a new alert
30486 * @param {Object} config The config object
30490 Roo.bootstrap.Alert = function(config){
30491 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30495 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30501 faicon: false, // BC
30505 getAutoCreate : function()
30517 style : this.close ? '' : 'display:none'
30521 cls : 'roo-alert-icon'
30526 cls : 'roo-alert-title',
30531 cls : 'roo-alert-text',
30538 cfg.cn[0].cls += ' fa ' + this.faicon;
30541 cfg.cn[0].cls += ' fa ' + this.fa;
30545 cfg.cls += ' alert-' + this.weight;
30551 initEvents: function()
30553 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30554 this.titleEl = this.el.select('.roo-alert-title',true).first();
30555 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30556 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30557 if (this.seconds > 0) {
30558 this.hide.defer(this.seconds, this);
30562 * Set the Title Message HTML
30563 * @param {String} html
30565 setTitle : function(str)
30567 this.titleEl.dom.innerHTML = str;
30571 * Set the Body Message HTML
30572 * @param {String} html
30574 setHtml : function(str)
30576 this.htmlEl.dom.innerHTML = str;
30579 * Set the Weight of the alert
30580 * @param {String} (success|info|warning|danger) weight
30583 setWeight : function(weight)
30586 this.el.removeClass('alert-' + this.weight);
30589 this.weight = weight;
30591 this.el.addClass('alert-' + this.weight);
30594 * Set the Icon of the alert
30595 * @param {String} see fontawsome names (name without the 'fa-' bit)
30597 setIcon : function(icon)
30600 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30603 this.faicon = icon;
30605 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30630 * @class Roo.bootstrap.UploadCropbox
30631 * @extends Roo.bootstrap.Component
30632 * Bootstrap UploadCropbox class
30633 * @cfg {String} emptyText show when image has been loaded
30634 * @cfg {String} rotateNotify show when image too small to rotate
30635 * @cfg {Number} errorTimeout default 3000
30636 * @cfg {Number} minWidth default 300
30637 * @cfg {Number} minHeight default 300
30638 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30639 * @cfg {Boolean} isDocument (true|false) default false
30640 * @cfg {String} url action url
30641 * @cfg {String} paramName default 'imageUpload'
30642 * @cfg {String} method default POST
30643 * @cfg {Boolean} loadMask (true|false) default true
30644 * @cfg {Boolean} loadingText default 'Loading...'
30647 * Create a new UploadCropbox
30648 * @param {Object} config The config object
30651 Roo.bootstrap.UploadCropbox = function(config){
30652 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30656 * @event beforeselectfile
30657 * Fire before select file
30658 * @param {Roo.bootstrap.UploadCropbox} this
30660 "beforeselectfile" : true,
30663 * Fire after initEvent
30664 * @param {Roo.bootstrap.UploadCropbox} this
30669 * Fire after initEvent
30670 * @param {Roo.bootstrap.UploadCropbox} this
30671 * @param {String} data
30676 * Fire when preparing the file data
30677 * @param {Roo.bootstrap.UploadCropbox} this
30678 * @param {Object} file
30683 * Fire when get exception
30684 * @param {Roo.bootstrap.UploadCropbox} this
30685 * @param {XMLHttpRequest} xhr
30687 "exception" : true,
30689 * @event beforeloadcanvas
30690 * Fire before load the canvas
30691 * @param {Roo.bootstrap.UploadCropbox} this
30692 * @param {String} src
30694 "beforeloadcanvas" : true,
30697 * Fire when trash image
30698 * @param {Roo.bootstrap.UploadCropbox} this
30703 * Fire when download the image
30704 * @param {Roo.bootstrap.UploadCropbox} this
30708 * @event footerbuttonclick
30709 * Fire when footerbuttonclick
30710 * @param {Roo.bootstrap.UploadCropbox} this
30711 * @param {String} type
30713 "footerbuttonclick" : true,
30717 * @param {Roo.bootstrap.UploadCropbox} this
30722 * Fire when rotate the image
30723 * @param {Roo.bootstrap.UploadCropbox} this
30724 * @param {String} pos
30729 * Fire when inspect the file
30730 * @param {Roo.bootstrap.UploadCropbox} this
30731 * @param {Object} file
30736 * Fire when xhr upload the file
30737 * @param {Roo.bootstrap.UploadCropbox} this
30738 * @param {Object} data
30743 * Fire when arrange the file data
30744 * @param {Roo.bootstrap.UploadCropbox} this
30745 * @param {Object} formData
30750 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30753 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30755 emptyText : 'Click to upload image',
30756 rotateNotify : 'Image is too small to rotate',
30757 errorTimeout : 3000,
30771 cropType : 'image/jpeg',
30773 canvasLoaded : false,
30774 isDocument : false,
30776 paramName : 'imageUpload',
30778 loadingText : 'Loading...',
30781 getAutoCreate : function()
30785 cls : 'roo-upload-cropbox',
30789 cls : 'roo-upload-cropbox-selector',
30794 cls : 'roo-upload-cropbox-body',
30795 style : 'cursor:pointer',
30799 cls : 'roo-upload-cropbox-preview'
30803 cls : 'roo-upload-cropbox-thumb'
30807 cls : 'roo-upload-cropbox-empty-notify',
30808 html : this.emptyText
30812 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30813 html : this.rotateNotify
30819 cls : 'roo-upload-cropbox-footer',
30822 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30832 onRender : function(ct, position)
30834 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30836 if (this.buttons.length) {
30838 Roo.each(this.buttons, function(bb) {
30840 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30842 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30848 this.maskEl = this.el;
30852 initEvents : function()
30854 this.urlAPI = (window.createObjectURL && window) ||
30855 (window.URL && URL.revokeObjectURL && URL) ||
30856 (window.webkitURL && webkitURL);
30858 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30859 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30861 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30862 this.selectorEl.hide();
30864 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30865 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30867 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30868 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30869 this.thumbEl.hide();
30871 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30872 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30874 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30875 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30876 this.errorEl.hide();
30878 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30879 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30880 this.footerEl.hide();
30882 this.setThumbBoxSize();
30888 this.fireEvent('initial', this);
30895 window.addEventListener("resize", function() { _this.resize(); } );
30897 this.bodyEl.on('click', this.beforeSelectFile, this);
30900 this.bodyEl.on('touchstart', this.onTouchStart, this);
30901 this.bodyEl.on('touchmove', this.onTouchMove, this);
30902 this.bodyEl.on('touchend', this.onTouchEnd, this);
30906 this.bodyEl.on('mousedown', this.onMouseDown, this);
30907 this.bodyEl.on('mousemove', this.onMouseMove, this);
30908 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30909 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30910 Roo.get(document).on('mouseup', this.onMouseUp, this);
30913 this.selectorEl.on('change', this.onFileSelected, this);
30919 this.baseScale = 1;
30921 this.baseRotate = 1;
30922 this.dragable = false;
30923 this.pinching = false;
30926 this.cropData = false;
30927 this.notifyEl.dom.innerHTML = this.emptyText;
30929 this.selectorEl.dom.value = '';
30933 resize : function()
30935 if(this.fireEvent('resize', this) != false){
30936 this.setThumbBoxPosition();
30937 this.setCanvasPosition();
30941 onFooterButtonClick : function(e, el, o, type)
30944 case 'rotate-left' :
30945 this.onRotateLeft(e);
30947 case 'rotate-right' :
30948 this.onRotateRight(e);
30951 this.beforeSelectFile(e);
30966 this.fireEvent('footerbuttonclick', this, type);
30969 beforeSelectFile : function(e)
30971 e.preventDefault();
30973 if(this.fireEvent('beforeselectfile', this) != false){
30974 this.selectorEl.dom.click();
30978 onFileSelected : function(e)
30980 e.preventDefault();
30982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30986 var file = this.selectorEl.dom.files[0];
30988 if(this.fireEvent('inspect', this, file) != false){
30989 this.prepare(file);
30994 trash : function(e)
30996 this.fireEvent('trash', this);
30999 download : function(e)
31001 this.fireEvent('download', this);
31004 loadCanvas : function(src)
31006 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31010 this.imageEl = document.createElement('img');
31014 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31016 this.imageEl.src = src;
31020 onLoadCanvas : function()
31022 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31023 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31025 this.bodyEl.un('click', this.beforeSelectFile, this);
31027 this.notifyEl.hide();
31028 this.thumbEl.show();
31029 this.footerEl.show();
31031 this.baseRotateLevel();
31033 if(this.isDocument){
31034 this.setThumbBoxSize();
31037 this.setThumbBoxPosition();
31039 this.baseScaleLevel();
31045 this.canvasLoaded = true;
31048 this.maskEl.unmask();
31053 setCanvasPosition : function()
31055 if(!this.canvasEl){
31059 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31060 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31062 this.previewEl.setLeft(pw);
31063 this.previewEl.setTop(ph);
31067 onMouseDown : function(e)
31071 this.dragable = true;
31072 this.pinching = false;
31074 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31075 this.dragable = false;
31079 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31080 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31084 onMouseMove : function(e)
31088 if(!this.canvasLoaded){
31092 if (!this.dragable){
31096 var minX = Math.ceil(this.thumbEl.getLeft(true));
31097 var minY = Math.ceil(this.thumbEl.getTop(true));
31099 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31100 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31102 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31103 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31105 x = x - this.mouseX;
31106 y = y - this.mouseY;
31108 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31109 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31111 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31112 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31114 this.previewEl.setLeft(bgX);
31115 this.previewEl.setTop(bgY);
31117 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31118 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31121 onMouseUp : function(e)
31125 this.dragable = false;
31128 onMouseWheel : function(e)
31132 this.startScale = this.scale;
31134 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31136 if(!this.zoomable()){
31137 this.scale = this.startScale;
31146 zoomable : function()
31148 var minScale = this.thumbEl.getWidth() / this.minWidth;
31150 if(this.minWidth < this.minHeight){
31151 minScale = this.thumbEl.getHeight() / this.minHeight;
31154 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31155 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31159 (this.rotate == 0 || this.rotate == 180) &&
31161 width > this.imageEl.OriginWidth ||
31162 height > this.imageEl.OriginHeight ||
31163 (width < this.minWidth && height < this.minHeight)
31171 (this.rotate == 90 || this.rotate == 270) &&
31173 width > this.imageEl.OriginWidth ||
31174 height > this.imageEl.OriginHeight ||
31175 (width < this.minHeight && height < this.minWidth)
31182 !this.isDocument &&
31183 (this.rotate == 0 || this.rotate == 180) &&
31185 width < this.minWidth ||
31186 width > this.imageEl.OriginWidth ||
31187 height < this.minHeight ||
31188 height > this.imageEl.OriginHeight
31195 !this.isDocument &&
31196 (this.rotate == 90 || this.rotate == 270) &&
31198 width < this.minHeight ||
31199 width > this.imageEl.OriginWidth ||
31200 height < this.minWidth ||
31201 height > this.imageEl.OriginHeight
31211 onRotateLeft : function(e)
31213 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31215 var minScale = this.thumbEl.getWidth() / this.minWidth;
31217 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31218 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31220 this.startScale = this.scale;
31222 while (this.getScaleLevel() < minScale){
31224 this.scale = this.scale + 1;
31226 if(!this.zoomable()){
31231 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31232 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31237 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31244 this.scale = this.startScale;
31246 this.onRotateFail();
31251 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31253 if(this.isDocument){
31254 this.setThumbBoxSize();
31255 this.setThumbBoxPosition();
31256 this.setCanvasPosition();
31261 this.fireEvent('rotate', this, 'left');
31265 onRotateRight : function(e)
31267 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31269 var minScale = this.thumbEl.getWidth() / this.minWidth;
31271 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31272 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31274 this.startScale = this.scale;
31276 while (this.getScaleLevel() < minScale){
31278 this.scale = this.scale + 1;
31280 if(!this.zoomable()){
31285 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31286 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31291 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31298 this.scale = this.startScale;
31300 this.onRotateFail();
31305 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31307 if(this.isDocument){
31308 this.setThumbBoxSize();
31309 this.setThumbBoxPosition();
31310 this.setCanvasPosition();
31315 this.fireEvent('rotate', this, 'right');
31318 onRotateFail : function()
31320 this.errorEl.show(true);
31324 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31329 this.previewEl.dom.innerHTML = '';
31331 var canvasEl = document.createElement("canvas");
31333 var contextEl = canvasEl.getContext("2d");
31335 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31336 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31337 var center = this.imageEl.OriginWidth / 2;
31339 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31340 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31341 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31342 center = this.imageEl.OriginHeight / 2;
31345 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31347 contextEl.translate(center, center);
31348 contextEl.rotate(this.rotate * Math.PI / 180);
31350 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31352 this.canvasEl = document.createElement("canvas");
31354 this.contextEl = this.canvasEl.getContext("2d");
31356 switch (this.rotate) {
31359 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31360 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31362 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31367 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31368 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31370 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31371 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);
31375 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31380 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31381 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31383 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31384 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);
31388 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);
31393 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31394 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31396 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31397 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31401 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);
31408 this.previewEl.appendChild(this.canvasEl);
31410 this.setCanvasPosition();
31415 if(!this.canvasLoaded){
31419 var imageCanvas = document.createElement("canvas");
31421 var imageContext = imageCanvas.getContext("2d");
31423 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31424 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31426 var center = imageCanvas.width / 2;
31428 imageContext.translate(center, center);
31430 imageContext.rotate(this.rotate * Math.PI / 180);
31432 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31434 var canvas = document.createElement("canvas");
31436 var context = canvas.getContext("2d");
31438 canvas.width = this.minWidth;
31439 canvas.height = this.minHeight;
31441 switch (this.rotate) {
31444 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31445 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31447 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31448 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31450 var targetWidth = this.minWidth - 2 * x;
31451 var targetHeight = this.minHeight - 2 * y;
31455 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31456 scale = targetWidth / width;
31459 if(x > 0 && y == 0){
31460 scale = targetHeight / height;
31463 if(x > 0 && y > 0){
31464 scale = targetWidth / width;
31466 if(width < height){
31467 scale = targetHeight / height;
31471 context.scale(scale, scale);
31473 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31474 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31476 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31477 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31479 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31484 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31485 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31487 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31488 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31490 var targetWidth = this.minWidth - 2 * x;
31491 var targetHeight = this.minHeight - 2 * y;
31495 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31496 scale = targetWidth / width;
31499 if(x > 0 && y == 0){
31500 scale = targetHeight / height;
31503 if(x > 0 && y > 0){
31504 scale = targetWidth / width;
31506 if(width < height){
31507 scale = targetHeight / height;
31511 context.scale(scale, scale);
31513 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31514 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31516 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31517 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31519 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31521 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31526 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31527 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31529 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31530 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31532 var targetWidth = this.minWidth - 2 * x;
31533 var targetHeight = this.minHeight - 2 * y;
31537 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31538 scale = targetWidth / width;
31541 if(x > 0 && y == 0){
31542 scale = targetHeight / height;
31545 if(x > 0 && y > 0){
31546 scale = targetWidth / width;
31548 if(width < height){
31549 scale = targetHeight / height;
31553 context.scale(scale, scale);
31555 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31556 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31558 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31559 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31561 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31562 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31564 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31569 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31570 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31572 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31573 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31575 var targetWidth = this.minWidth - 2 * x;
31576 var targetHeight = this.minHeight - 2 * y;
31580 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31581 scale = targetWidth / width;
31584 if(x > 0 && y == 0){
31585 scale = targetHeight / height;
31588 if(x > 0 && y > 0){
31589 scale = targetWidth / width;
31591 if(width < height){
31592 scale = targetHeight / height;
31596 context.scale(scale, scale);
31598 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31599 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31601 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31602 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31604 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31606 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31613 this.cropData = canvas.toDataURL(this.cropType);
31615 if(this.fireEvent('crop', this, this.cropData) !== false){
31616 this.process(this.file, this.cropData);
31623 setThumbBoxSize : function()
31627 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31628 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31629 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31631 this.minWidth = width;
31632 this.minHeight = height;
31634 if(this.rotate == 90 || this.rotate == 270){
31635 this.minWidth = height;
31636 this.minHeight = width;
31641 width = Math.ceil(this.minWidth * height / this.minHeight);
31643 if(this.minWidth > this.minHeight){
31645 height = Math.ceil(this.minHeight * width / this.minWidth);
31648 this.thumbEl.setStyle({
31649 width : width + 'px',
31650 height : height + 'px'
31657 setThumbBoxPosition : function()
31659 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31660 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31662 this.thumbEl.setLeft(x);
31663 this.thumbEl.setTop(y);
31667 baseRotateLevel : function()
31669 this.baseRotate = 1;
31672 typeof(this.exif) != 'undefined' &&
31673 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31674 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31676 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31679 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31683 baseScaleLevel : function()
31687 if(this.isDocument){
31689 if(this.baseRotate == 6 || this.baseRotate == 8){
31691 height = this.thumbEl.getHeight();
31692 this.baseScale = height / this.imageEl.OriginWidth;
31694 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31695 width = this.thumbEl.getWidth();
31696 this.baseScale = width / this.imageEl.OriginHeight;
31702 height = this.thumbEl.getHeight();
31703 this.baseScale = height / this.imageEl.OriginHeight;
31705 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31706 width = this.thumbEl.getWidth();
31707 this.baseScale = width / this.imageEl.OriginWidth;
31713 if(this.baseRotate == 6 || this.baseRotate == 8){
31715 width = this.thumbEl.getHeight();
31716 this.baseScale = width / this.imageEl.OriginHeight;
31718 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31719 height = this.thumbEl.getWidth();
31720 this.baseScale = height / this.imageEl.OriginHeight;
31723 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31724 height = this.thumbEl.getWidth();
31725 this.baseScale = height / this.imageEl.OriginHeight;
31727 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31728 width = this.thumbEl.getHeight();
31729 this.baseScale = width / this.imageEl.OriginWidth;
31736 width = this.thumbEl.getWidth();
31737 this.baseScale = width / this.imageEl.OriginWidth;
31739 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31740 height = this.thumbEl.getHeight();
31741 this.baseScale = height / this.imageEl.OriginHeight;
31744 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31746 height = this.thumbEl.getHeight();
31747 this.baseScale = height / this.imageEl.OriginHeight;
31749 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31750 width = this.thumbEl.getWidth();
31751 this.baseScale = width / this.imageEl.OriginWidth;
31759 getScaleLevel : function()
31761 return this.baseScale * Math.pow(1.1, this.scale);
31764 onTouchStart : function(e)
31766 if(!this.canvasLoaded){
31767 this.beforeSelectFile(e);
31771 var touches = e.browserEvent.touches;
31777 if(touches.length == 1){
31778 this.onMouseDown(e);
31782 if(touches.length != 2){
31788 for(var i = 0, finger; finger = touches[i]; i++){
31789 coords.push(finger.pageX, finger.pageY);
31792 var x = Math.pow(coords[0] - coords[2], 2);
31793 var y = Math.pow(coords[1] - coords[3], 2);
31795 this.startDistance = Math.sqrt(x + y);
31797 this.startScale = this.scale;
31799 this.pinching = true;
31800 this.dragable = false;
31804 onTouchMove : function(e)
31806 if(!this.pinching && !this.dragable){
31810 var touches = e.browserEvent.touches;
31817 this.onMouseMove(e);
31823 for(var i = 0, finger; finger = touches[i]; i++){
31824 coords.push(finger.pageX, finger.pageY);
31827 var x = Math.pow(coords[0] - coords[2], 2);
31828 var y = Math.pow(coords[1] - coords[3], 2);
31830 this.endDistance = Math.sqrt(x + y);
31832 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31834 if(!this.zoomable()){
31835 this.scale = this.startScale;
31843 onTouchEnd : function(e)
31845 this.pinching = false;
31846 this.dragable = false;
31850 process : function(file, crop)
31853 this.maskEl.mask(this.loadingText);
31856 this.xhr = new XMLHttpRequest();
31858 file.xhr = this.xhr;
31860 this.xhr.open(this.method, this.url, true);
31863 "Accept": "application/json",
31864 "Cache-Control": "no-cache",
31865 "X-Requested-With": "XMLHttpRequest"
31868 for (var headerName in headers) {
31869 var headerValue = headers[headerName];
31871 this.xhr.setRequestHeader(headerName, headerValue);
31877 this.xhr.onload = function()
31879 _this.xhrOnLoad(_this.xhr);
31882 this.xhr.onerror = function()
31884 _this.xhrOnError(_this.xhr);
31887 var formData = new FormData();
31889 formData.append('returnHTML', 'NO');
31892 formData.append('crop', crop);
31895 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31896 formData.append(this.paramName, file, file.name);
31899 if(typeof(file.filename) != 'undefined'){
31900 formData.append('filename', file.filename);
31903 if(typeof(file.mimetype) != 'undefined'){
31904 formData.append('mimetype', file.mimetype);
31907 if(this.fireEvent('arrange', this, formData) != false){
31908 this.xhr.send(formData);
31912 xhrOnLoad : function(xhr)
31915 this.maskEl.unmask();
31918 if (xhr.readyState !== 4) {
31919 this.fireEvent('exception', this, xhr);
31923 var response = Roo.decode(xhr.responseText);
31925 if(!response.success){
31926 this.fireEvent('exception', this, xhr);
31930 var response = Roo.decode(xhr.responseText);
31932 this.fireEvent('upload', this, response);
31936 xhrOnError : function()
31939 this.maskEl.unmask();
31942 Roo.log('xhr on error');
31944 var response = Roo.decode(xhr.responseText);
31950 prepare : function(file)
31953 this.maskEl.mask(this.loadingText);
31959 if(typeof(file) === 'string'){
31960 this.loadCanvas(file);
31964 if(!file || !this.urlAPI){
31969 this.cropType = file.type;
31973 if(this.fireEvent('prepare', this, this.file) != false){
31975 var reader = new FileReader();
31977 reader.onload = function (e) {
31978 if (e.target.error) {
31979 Roo.log(e.target.error);
31983 var buffer = e.target.result,
31984 dataView = new DataView(buffer),
31986 maxOffset = dataView.byteLength - 4,
31990 if (dataView.getUint16(0) === 0xffd8) {
31991 while (offset < maxOffset) {
31992 markerBytes = dataView.getUint16(offset);
31994 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31995 markerLength = dataView.getUint16(offset + 2) + 2;
31996 if (offset + markerLength > dataView.byteLength) {
31997 Roo.log('Invalid meta data: Invalid segment size.');
32001 if(markerBytes == 0xffe1){
32002 _this.parseExifData(
32009 offset += markerLength;
32019 var url = _this.urlAPI.createObjectURL(_this.file);
32021 _this.loadCanvas(url);
32026 reader.readAsArrayBuffer(this.file);
32032 parseExifData : function(dataView, offset, length)
32034 var tiffOffset = offset + 10,
32038 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32039 // No Exif data, might be XMP data instead
32043 // Check for the ASCII code for "Exif" (0x45786966):
32044 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32045 // No Exif data, might be XMP data instead
32048 if (tiffOffset + 8 > dataView.byteLength) {
32049 Roo.log('Invalid Exif data: Invalid segment size.');
32052 // Check for the two null bytes:
32053 if (dataView.getUint16(offset + 8) !== 0x0000) {
32054 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32057 // Check the byte alignment:
32058 switch (dataView.getUint16(tiffOffset)) {
32060 littleEndian = true;
32063 littleEndian = false;
32066 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32069 // Check for the TIFF tag marker (0x002A):
32070 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32071 Roo.log('Invalid Exif data: Missing TIFF marker.');
32074 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32075 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32077 this.parseExifTags(
32080 tiffOffset + dirOffset,
32085 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32090 if (dirOffset + 6 > dataView.byteLength) {
32091 Roo.log('Invalid Exif data: Invalid directory offset.');
32094 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32095 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32096 if (dirEndOffset + 4 > dataView.byteLength) {
32097 Roo.log('Invalid Exif data: Invalid directory size.');
32100 for (i = 0; i < tagsNumber; i += 1) {
32104 dirOffset + 2 + 12 * i, // tag offset
32108 // Return the offset to the next directory:
32109 return dataView.getUint32(dirEndOffset, littleEndian);
32112 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32114 var tag = dataView.getUint16(offset, littleEndian);
32116 this.exif[tag] = this.getExifValue(
32120 dataView.getUint16(offset + 2, littleEndian), // tag type
32121 dataView.getUint32(offset + 4, littleEndian), // tag length
32126 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32128 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32137 Roo.log('Invalid Exif data: Invalid tag type.');
32141 tagSize = tagType.size * length;
32142 // Determine if the value is contained in the dataOffset bytes,
32143 // or if the value at the dataOffset is a pointer to the actual data:
32144 dataOffset = tagSize > 4 ?
32145 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32146 if (dataOffset + tagSize > dataView.byteLength) {
32147 Roo.log('Invalid Exif data: Invalid data offset.');
32150 if (length === 1) {
32151 return tagType.getValue(dataView, dataOffset, littleEndian);
32154 for (i = 0; i < length; i += 1) {
32155 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32158 if (tagType.ascii) {
32160 // Concatenate the chars:
32161 for (i = 0; i < values.length; i += 1) {
32163 // Ignore the terminating NULL byte(s):
32164 if (c === '\u0000') {
32176 Roo.apply(Roo.bootstrap.UploadCropbox, {
32178 'Orientation': 0x0112
32182 1: 0, //'top-left',
32184 3: 180, //'bottom-right',
32185 // 4: 'bottom-left',
32187 6: 90, //'right-top',
32188 // 7: 'right-bottom',
32189 8: 270 //'left-bottom'
32193 // byte, 8-bit unsigned int:
32195 getValue: function (dataView, dataOffset) {
32196 return dataView.getUint8(dataOffset);
32200 // ascii, 8-bit byte:
32202 getValue: function (dataView, dataOffset) {
32203 return String.fromCharCode(dataView.getUint8(dataOffset));
32208 // short, 16 bit int:
32210 getValue: function (dataView, dataOffset, littleEndian) {
32211 return dataView.getUint16(dataOffset, littleEndian);
32215 // long, 32 bit int:
32217 getValue: function (dataView, dataOffset, littleEndian) {
32218 return dataView.getUint32(dataOffset, littleEndian);
32222 // rational = two long values, first is numerator, second is denominator:
32224 getValue: function (dataView, dataOffset, littleEndian) {
32225 return dataView.getUint32(dataOffset, littleEndian) /
32226 dataView.getUint32(dataOffset + 4, littleEndian);
32230 // slong, 32 bit signed int:
32232 getValue: function (dataView, dataOffset, littleEndian) {
32233 return dataView.getInt32(dataOffset, littleEndian);
32237 // srational, two slongs, first is numerator, second is denominator:
32239 getValue: function (dataView, dataOffset, littleEndian) {
32240 return dataView.getInt32(dataOffset, littleEndian) /
32241 dataView.getInt32(dataOffset + 4, littleEndian);
32251 cls : 'btn-group roo-upload-cropbox-rotate-left',
32252 action : 'rotate-left',
32256 cls : 'btn btn-default',
32257 html : '<i class="fa fa-undo"></i>'
32263 cls : 'btn-group roo-upload-cropbox-picture',
32264 action : 'picture',
32268 cls : 'btn btn-default',
32269 html : '<i class="fa fa-picture-o"></i>'
32275 cls : 'btn-group roo-upload-cropbox-rotate-right',
32276 action : 'rotate-right',
32280 cls : 'btn btn-default',
32281 html : '<i class="fa fa-repeat"></i>'
32289 cls : 'btn-group roo-upload-cropbox-rotate-left',
32290 action : 'rotate-left',
32294 cls : 'btn btn-default',
32295 html : '<i class="fa fa-undo"></i>'
32301 cls : 'btn-group roo-upload-cropbox-download',
32302 action : 'download',
32306 cls : 'btn btn-default',
32307 html : '<i class="fa fa-download"></i>'
32313 cls : 'btn-group roo-upload-cropbox-crop',
32318 cls : 'btn btn-default',
32319 html : '<i class="fa fa-crop"></i>'
32325 cls : 'btn-group roo-upload-cropbox-trash',
32330 cls : 'btn btn-default',
32331 html : '<i class="fa fa-trash"></i>'
32337 cls : 'btn-group roo-upload-cropbox-rotate-right',
32338 action : 'rotate-right',
32342 cls : 'btn btn-default',
32343 html : '<i class="fa fa-repeat"></i>'
32351 cls : 'btn-group roo-upload-cropbox-rotate-left',
32352 action : 'rotate-left',
32356 cls : 'btn btn-default',
32357 html : '<i class="fa fa-undo"></i>'
32363 cls : 'btn-group roo-upload-cropbox-rotate-right',
32364 action : 'rotate-right',
32368 cls : 'btn btn-default',
32369 html : '<i class="fa fa-repeat"></i>'
32382 * @class Roo.bootstrap.DocumentManager
32383 * @extends Roo.bootstrap.Component
32384 * Bootstrap DocumentManager class
32385 * @cfg {String} paramName default 'imageUpload'
32386 * @cfg {String} toolTipName default 'filename'
32387 * @cfg {String} method default POST
32388 * @cfg {String} url action url
32389 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32390 * @cfg {Boolean} multiple multiple upload default true
32391 * @cfg {Number} thumbSize default 300
32392 * @cfg {String} fieldLabel
32393 * @cfg {Number} labelWidth default 4
32394 * @cfg {String} labelAlign (left|top) default left
32395 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32396 * @cfg {Number} labellg set the width of label (1-12)
32397 * @cfg {Number} labelmd set the width of label (1-12)
32398 * @cfg {Number} labelsm set the width of label (1-12)
32399 * @cfg {Number} labelxs set the width of label (1-12)
32402 * Create a new DocumentManager
32403 * @param {Object} config The config object
32406 Roo.bootstrap.DocumentManager = function(config){
32407 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32410 this.delegates = [];
32415 * Fire when initial the DocumentManager
32416 * @param {Roo.bootstrap.DocumentManager} this
32421 * inspect selected file
32422 * @param {Roo.bootstrap.DocumentManager} this
32423 * @param {File} file
32428 * Fire when xhr load exception
32429 * @param {Roo.bootstrap.DocumentManager} this
32430 * @param {XMLHttpRequest} xhr
32432 "exception" : true,
32434 * @event afterupload
32435 * Fire when xhr load exception
32436 * @param {Roo.bootstrap.DocumentManager} this
32437 * @param {XMLHttpRequest} xhr
32439 "afterupload" : true,
32442 * prepare the form data
32443 * @param {Roo.bootstrap.DocumentManager} this
32444 * @param {Object} formData
32449 * Fire when remove the file
32450 * @param {Roo.bootstrap.DocumentManager} this
32451 * @param {Object} file
32456 * Fire after refresh the file
32457 * @param {Roo.bootstrap.DocumentManager} this
32462 * Fire after click the image
32463 * @param {Roo.bootstrap.DocumentManager} this
32464 * @param {Object} file
32469 * Fire when upload a image and editable set to true
32470 * @param {Roo.bootstrap.DocumentManager} this
32471 * @param {Object} file
32475 * @event beforeselectfile
32476 * Fire before select file
32477 * @param {Roo.bootstrap.DocumentManager} this
32479 "beforeselectfile" : true,
32482 * Fire before process file
32483 * @param {Roo.bootstrap.DocumentManager} this
32484 * @param {Object} file
32488 * @event previewrendered
32489 * Fire when preview rendered
32490 * @param {Roo.bootstrap.DocumentManager} this
32491 * @param {Object} file
32493 "previewrendered" : true,
32496 "previewResize" : true
32501 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32510 paramName : 'imageUpload',
32511 toolTipName : 'filename',
32514 labelAlign : 'left',
32524 getAutoCreate : function()
32526 var managerWidget = {
32528 cls : 'roo-document-manager',
32532 cls : 'roo-document-manager-selector',
32537 cls : 'roo-document-manager-uploader',
32541 cls : 'roo-document-manager-upload-btn',
32542 html : '<i class="fa fa-plus"></i>'
32553 cls : 'column col-md-12',
32558 if(this.fieldLabel.length){
32563 cls : 'column col-md-12',
32564 html : this.fieldLabel
32568 cls : 'column col-md-12',
32573 if(this.labelAlign == 'left'){
32578 html : this.fieldLabel
32587 if(this.labelWidth > 12){
32588 content[0].style = "width: " + this.labelWidth + 'px';
32591 if(this.labelWidth < 13 && this.labelmd == 0){
32592 this.labelmd = this.labelWidth;
32595 if(this.labellg > 0){
32596 content[0].cls += ' col-lg-' + this.labellg;
32597 content[1].cls += ' col-lg-' + (12 - this.labellg);
32600 if(this.labelmd > 0){
32601 content[0].cls += ' col-md-' + this.labelmd;
32602 content[1].cls += ' col-md-' + (12 - this.labelmd);
32605 if(this.labelsm > 0){
32606 content[0].cls += ' col-sm-' + this.labelsm;
32607 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32610 if(this.labelxs > 0){
32611 content[0].cls += ' col-xs-' + this.labelxs;
32612 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32620 cls : 'row clearfix',
32628 initEvents : function()
32630 this.managerEl = this.el.select('.roo-document-manager', true).first();
32631 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32633 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32634 this.selectorEl.hide();
32637 this.selectorEl.attr('multiple', 'multiple');
32640 this.selectorEl.on('change', this.onFileSelected, this);
32642 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32643 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32645 this.uploader.on('click', this.onUploaderClick, this);
32647 this.renderProgressDialog();
32651 window.addEventListener("resize", function() { _this.refresh(); } );
32653 this.fireEvent('initial', this);
32656 renderProgressDialog : function()
32660 this.progressDialog = new Roo.bootstrap.Modal({
32661 cls : 'roo-document-manager-progress-dialog',
32662 allow_close : false,
32673 btnclick : function() {
32674 _this.uploadCancel();
32680 this.progressDialog.render(Roo.get(document.body));
32682 this.progress = new Roo.bootstrap.Progress({
32683 cls : 'roo-document-manager-progress',
32688 this.progress.render(this.progressDialog.getChildContainer());
32690 this.progressBar = new Roo.bootstrap.ProgressBar({
32691 cls : 'roo-document-manager-progress-bar',
32694 aria_valuemax : 12,
32698 this.progressBar.render(this.progress.getChildContainer());
32701 onUploaderClick : function(e)
32703 e.preventDefault();
32705 if(this.fireEvent('beforeselectfile', this) != false){
32706 this.selectorEl.dom.click();
32711 onFileSelected : function(e)
32713 e.preventDefault();
32715 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32719 Roo.each(this.selectorEl.dom.files, function(file){
32720 if(this.fireEvent('inspect', this, file) != false){
32721 this.files.push(file);
32731 this.selectorEl.dom.value = '';
32733 if(!this.files || !this.files.length){
32737 if(this.boxes > 0 && this.files.length > this.boxes){
32738 this.files = this.files.slice(0, this.boxes);
32741 this.uploader.show();
32743 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32744 this.uploader.hide();
32753 Roo.each(this.files, function(file){
32755 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32756 var f = this.renderPreview(file);
32761 if(file.type.indexOf('image') != -1){
32762 this.delegates.push(
32764 _this.process(file);
32765 }).createDelegate(this)
32773 _this.process(file);
32774 }).createDelegate(this)
32779 this.files = files;
32781 this.delegates = this.delegates.concat(docs);
32783 if(!this.delegates.length){
32788 this.progressBar.aria_valuemax = this.delegates.length;
32795 arrange : function()
32797 if(!this.delegates.length){
32798 this.progressDialog.hide();
32803 var delegate = this.delegates.shift();
32805 this.progressDialog.show();
32807 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32809 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32814 refresh : function()
32816 this.uploader.show();
32818 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32819 this.uploader.hide();
32822 Roo.isTouch ? this.closable(false) : this.closable(true);
32824 this.fireEvent('refresh', this);
32827 onRemove : function(e, el, o)
32829 e.preventDefault();
32831 this.fireEvent('remove', this, o);
32835 remove : function(o)
32839 Roo.each(this.files, function(file){
32840 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32849 this.files = files;
32856 Roo.each(this.files, function(file){
32861 file.target.remove();
32870 onClick : function(e, el, o)
32872 e.preventDefault();
32874 this.fireEvent('click', this, o);
32878 closable : function(closable)
32880 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32882 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32894 xhrOnLoad : function(xhr)
32896 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32900 if (xhr.readyState !== 4) {
32902 this.fireEvent('exception', this, xhr);
32906 var response = Roo.decode(xhr.responseText);
32908 if(!response.success){
32910 this.fireEvent('exception', this, xhr);
32914 var file = this.renderPreview(response.data);
32916 this.files.push(file);
32920 this.fireEvent('afterupload', this, xhr);
32924 xhrOnError : function(xhr)
32926 Roo.log('xhr on error');
32928 var response = Roo.decode(xhr.responseText);
32935 process : function(file)
32937 if(this.fireEvent('process', this, file) !== false){
32938 if(this.editable && file.type.indexOf('image') != -1){
32939 this.fireEvent('edit', this, file);
32943 this.uploadStart(file, false);
32950 uploadStart : function(file, crop)
32952 this.xhr = new XMLHttpRequest();
32954 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32959 file.xhr = this.xhr;
32961 this.managerEl.createChild({
32963 cls : 'roo-document-manager-loading',
32967 tooltip : file.name,
32968 cls : 'roo-document-manager-thumb',
32969 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32975 this.xhr.open(this.method, this.url, true);
32978 "Accept": "application/json",
32979 "Cache-Control": "no-cache",
32980 "X-Requested-With": "XMLHttpRequest"
32983 for (var headerName in headers) {
32984 var headerValue = headers[headerName];
32986 this.xhr.setRequestHeader(headerName, headerValue);
32992 this.xhr.onload = function()
32994 _this.xhrOnLoad(_this.xhr);
32997 this.xhr.onerror = function()
32999 _this.xhrOnError(_this.xhr);
33002 var formData = new FormData();
33004 formData.append('returnHTML', 'NO');
33007 formData.append('crop', crop);
33010 formData.append(this.paramName, file, file.name);
33017 if(this.fireEvent('prepare', this, formData, options) != false){
33019 if(options.manually){
33023 this.xhr.send(formData);
33027 this.uploadCancel();
33030 uploadCancel : function()
33036 this.delegates = [];
33038 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33045 renderPreview : function(file)
33047 if(typeof(file.target) != 'undefined' && file.target){
33051 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33053 var previewEl = this.managerEl.createChild({
33055 cls : 'roo-document-manager-preview',
33059 tooltip : file[this.toolTipName],
33060 cls : 'roo-document-manager-thumb',
33061 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33066 html : '<i class="fa fa-times-circle"></i>'
33071 var close = previewEl.select('button.close', true).first();
33073 close.on('click', this.onRemove, this, file);
33075 file.target = previewEl;
33077 var image = previewEl.select('img', true).first();
33081 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33083 image.on('click', this.onClick, this, file);
33085 this.fireEvent('previewrendered', this, file);
33091 onPreviewLoad : function(file, image)
33093 if(typeof(file.target) == 'undefined' || !file.target){
33097 var width = image.dom.naturalWidth || image.dom.width;
33098 var height = image.dom.naturalHeight || image.dom.height;
33100 if(!this.previewResize) {
33104 if(width > height){
33105 file.target.addClass('wide');
33109 file.target.addClass('tall');
33114 uploadFromSource : function(file, crop)
33116 this.xhr = new XMLHttpRequest();
33118 this.managerEl.createChild({
33120 cls : 'roo-document-manager-loading',
33124 tooltip : file.name,
33125 cls : 'roo-document-manager-thumb',
33126 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33132 this.xhr.open(this.method, this.url, true);
33135 "Accept": "application/json",
33136 "Cache-Control": "no-cache",
33137 "X-Requested-With": "XMLHttpRequest"
33140 for (var headerName in headers) {
33141 var headerValue = headers[headerName];
33143 this.xhr.setRequestHeader(headerName, headerValue);
33149 this.xhr.onload = function()
33151 _this.xhrOnLoad(_this.xhr);
33154 this.xhr.onerror = function()
33156 _this.xhrOnError(_this.xhr);
33159 var formData = new FormData();
33161 formData.append('returnHTML', 'NO');
33163 formData.append('crop', crop);
33165 if(typeof(file.filename) != 'undefined'){
33166 formData.append('filename', file.filename);
33169 if(typeof(file.mimetype) != 'undefined'){
33170 formData.append('mimetype', file.mimetype);
33175 if(this.fireEvent('prepare', this, formData) != false){
33176 this.xhr.send(formData);
33186 * @class Roo.bootstrap.DocumentViewer
33187 * @extends Roo.bootstrap.Component
33188 * Bootstrap DocumentViewer class
33189 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33190 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33193 * Create a new DocumentViewer
33194 * @param {Object} config The config object
33197 Roo.bootstrap.DocumentViewer = function(config){
33198 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33203 * Fire after initEvent
33204 * @param {Roo.bootstrap.DocumentViewer} this
33210 * @param {Roo.bootstrap.DocumentViewer} this
33215 * Fire after download button
33216 * @param {Roo.bootstrap.DocumentViewer} this
33221 * Fire after trash button
33222 * @param {Roo.bootstrap.DocumentViewer} this
33229 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33231 showDownload : true,
33235 getAutoCreate : function()
33239 cls : 'roo-document-viewer',
33243 cls : 'roo-document-viewer-body',
33247 cls : 'roo-document-viewer-thumb',
33251 cls : 'roo-document-viewer-image'
33259 cls : 'roo-document-viewer-footer',
33262 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33266 cls : 'btn-group roo-document-viewer-download',
33270 cls : 'btn btn-default',
33271 html : '<i class="fa fa-download"></i>'
33277 cls : 'btn-group roo-document-viewer-trash',
33281 cls : 'btn btn-default',
33282 html : '<i class="fa fa-trash"></i>'
33295 initEvents : function()
33297 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33298 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33300 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33301 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33303 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33304 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33306 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33307 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33309 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33310 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33312 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33313 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33315 this.bodyEl.on('click', this.onClick, this);
33316 this.downloadBtn.on('click', this.onDownload, this);
33317 this.trashBtn.on('click', this.onTrash, this);
33319 this.downloadBtn.hide();
33320 this.trashBtn.hide();
33322 if(this.showDownload){
33323 this.downloadBtn.show();
33326 if(this.showTrash){
33327 this.trashBtn.show();
33330 if(!this.showDownload && !this.showTrash) {
33331 this.footerEl.hide();
33336 initial : function()
33338 this.fireEvent('initial', this);
33342 onClick : function(e)
33344 e.preventDefault();
33346 this.fireEvent('click', this);
33349 onDownload : function(e)
33351 e.preventDefault();
33353 this.fireEvent('download', this);
33356 onTrash : function(e)
33358 e.preventDefault();
33360 this.fireEvent('trash', this);
33372 * @class Roo.bootstrap.form.FieldLabel
33373 * @extends Roo.bootstrap.Component
33374 * Bootstrap FieldLabel class
33375 * @cfg {String} html contents of the element
33376 * @cfg {String} tag tag of the element default label
33377 * @cfg {String} cls class of the element
33378 * @cfg {String} target label target
33379 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33380 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33381 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33382 * @cfg {String} iconTooltip default "This field is required"
33383 * @cfg {String} indicatorpos (left|right) default left
33386 * Create a new FieldLabel
33387 * @param {Object} config The config object
33390 Roo.bootstrap.form.FieldLabel = function(config){
33391 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33396 * Fires after the field has been marked as invalid.
33397 * @param {Roo.form.FieldLabel} this
33398 * @param {String} msg The validation message
33403 * Fires after the field has been validated with no errors.
33404 * @param {Roo.form.FieldLabel} this
33410 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33417 invalidClass : 'has-warning',
33418 validClass : 'has-success',
33419 iconTooltip : 'This field is required',
33420 indicatorpos : 'left',
33422 getAutoCreate : function(){
33425 if (!this.allowBlank) {
33431 cls : 'roo-bootstrap-field-label ' + this.cls,
33436 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33437 tooltip : this.iconTooltip
33446 if(this.indicatorpos == 'right'){
33449 cls : 'roo-bootstrap-field-label ' + this.cls,
33458 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33459 tooltip : this.iconTooltip
33468 initEvents: function()
33470 Roo.bootstrap.Element.superclass.initEvents.call(this);
33472 this.indicator = this.indicatorEl();
33474 if(this.indicator){
33475 this.indicator.removeClass('visible');
33476 this.indicator.addClass('invisible');
33479 Roo.bootstrap.form.FieldLabel.register(this);
33482 indicatorEl : function()
33484 var indicator = this.el.select('i.roo-required-indicator',true).first();
33495 * Mark this field as valid
33497 markValid : function()
33499 if(this.indicator){
33500 this.indicator.removeClass('visible');
33501 this.indicator.addClass('invisible');
33503 if (Roo.bootstrap.version == 3) {
33504 this.el.removeClass(this.invalidClass);
33505 this.el.addClass(this.validClass);
33507 this.el.removeClass('is-invalid');
33508 this.el.addClass('is-valid');
33512 this.fireEvent('valid', this);
33516 * Mark this field as invalid
33517 * @param {String} msg The validation message
33519 markInvalid : function(msg)
33521 if(this.indicator){
33522 this.indicator.removeClass('invisible');
33523 this.indicator.addClass('visible');
33525 if (Roo.bootstrap.version == 3) {
33526 this.el.removeClass(this.validClass);
33527 this.el.addClass(this.invalidClass);
33529 this.el.removeClass('is-valid');
33530 this.el.addClass('is-invalid');
33534 this.fireEvent('invalid', this, msg);
33540 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33545 * register a FieldLabel Group
33546 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33548 register : function(label)
33550 if(this.groups.hasOwnProperty(label.target)){
33554 this.groups[label.target] = label;
33558 * fetch a FieldLabel Group based on the target
33559 * @param {string} target
33560 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33562 get: function(target) {
33563 if (typeof(this.groups[target]) == 'undefined') {
33567 return this.groups[target] ;
33576 * page DateSplitField.
33582 * @class Roo.bootstrap.form.DateSplitField
33583 * @extends Roo.bootstrap.Component
33584 * Bootstrap DateSplitField class
33585 * @cfg {string} fieldLabel - the label associated
33586 * @cfg {Number} labelWidth set the width of label (0-12)
33587 * @cfg {String} labelAlign (top|left)
33588 * @cfg {Boolean} dayAllowBlank (true|false) default false
33589 * @cfg {Boolean} monthAllowBlank (true|false) default false
33590 * @cfg {Boolean} yearAllowBlank (true|false) default false
33591 * @cfg {string} dayPlaceholder
33592 * @cfg {string} monthPlaceholder
33593 * @cfg {string} yearPlaceholder
33594 * @cfg {string} dayFormat default 'd'
33595 * @cfg {string} monthFormat default 'm'
33596 * @cfg {string} yearFormat default 'Y'
33597 * @cfg {Number} labellg set the width of label (1-12)
33598 * @cfg {Number} labelmd set the width of label (1-12)
33599 * @cfg {Number} labelsm set the width of label (1-12)
33600 * @cfg {Number} labelxs set the width of label (1-12)
33604 * Create a new DateSplitField
33605 * @param {Object} config The config object
33608 Roo.bootstrap.form.DateSplitField = function(config){
33609 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33615 * getting the data of years
33616 * @param {Roo.bootstrap.form.DateSplitField} this
33617 * @param {Object} years
33622 * getting the data of days
33623 * @param {Roo.bootstrap.form.DateSplitField} this
33624 * @param {Object} days
33629 * Fires after the field has been marked as invalid.
33630 * @param {Roo.form.Field} this
33631 * @param {String} msg The validation message
33636 * Fires after the field has been validated with no errors.
33637 * @param {Roo.form.Field} this
33643 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33646 labelAlign : 'top',
33648 dayAllowBlank : false,
33649 monthAllowBlank : false,
33650 yearAllowBlank : false,
33651 dayPlaceholder : '',
33652 monthPlaceholder : '',
33653 yearPlaceholder : '',
33657 isFormField : true,
33663 getAutoCreate : function()
33667 cls : 'row roo-date-split-field-group',
33672 cls : 'form-hidden-field roo-date-split-field-group-value',
33678 var labelCls = 'col-md-12';
33679 var contentCls = 'col-md-4';
33681 if(this.fieldLabel){
33685 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33689 html : this.fieldLabel
33694 if(this.labelAlign == 'left'){
33696 if(this.labelWidth > 12){
33697 label.style = "width: " + this.labelWidth + 'px';
33700 if(this.labelWidth < 13 && this.labelmd == 0){
33701 this.labelmd = this.labelWidth;
33704 if(this.labellg > 0){
33705 labelCls = ' col-lg-' + this.labellg;
33706 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33709 if(this.labelmd > 0){
33710 labelCls = ' col-md-' + this.labelmd;
33711 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33714 if(this.labelsm > 0){
33715 labelCls = ' col-sm-' + this.labelsm;
33716 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33719 if(this.labelxs > 0){
33720 labelCls = ' col-xs-' + this.labelxs;
33721 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33725 label.cls += ' ' + labelCls;
33727 cfg.cn.push(label);
33730 Roo.each(['day', 'month', 'year'], function(t){
33733 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33740 inputEl: function ()
33742 return this.el.select('.roo-date-split-field-group-value', true).first();
33745 onRender : function(ct, position)
33749 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33751 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33753 this.dayField = new Roo.bootstrap.form.ComboBox({
33754 allowBlank : this.dayAllowBlank,
33755 alwaysQuery : true,
33756 displayField : 'value',
33759 forceSelection : true,
33761 placeholder : this.dayPlaceholder,
33762 selectOnFocus : true,
33763 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33764 triggerAction : 'all',
33766 valueField : 'value',
33767 store : new Roo.data.SimpleStore({
33768 data : (function() {
33770 _this.fireEvent('days', _this, days);
33773 fields : [ 'value' ]
33776 select : function (_self, record, index)
33778 _this.setValue(_this.getValue());
33783 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33785 this.monthField = new Roo.bootstrap.form.MonthField({
33786 after : '<i class=\"fa fa-calendar\"></i>',
33787 allowBlank : this.monthAllowBlank,
33788 placeholder : this.monthPlaceholder,
33791 render : function (_self)
33793 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33794 e.preventDefault();
33798 select : function (_self, oldvalue, newvalue)
33800 _this.setValue(_this.getValue());
33805 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33807 this.yearField = new Roo.bootstrap.form.ComboBox({
33808 allowBlank : this.yearAllowBlank,
33809 alwaysQuery : true,
33810 displayField : 'value',
33813 forceSelection : true,
33815 placeholder : this.yearPlaceholder,
33816 selectOnFocus : true,
33817 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33818 triggerAction : 'all',
33820 valueField : 'value',
33821 store : new Roo.data.SimpleStore({
33822 data : (function() {
33824 _this.fireEvent('years', _this, years);
33827 fields : [ 'value' ]
33830 select : function (_self, record, index)
33832 _this.setValue(_this.getValue());
33837 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33840 setValue : function(v, format)
33842 this.inputEl.dom.value = v;
33844 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33846 var d = Date.parseDate(v, f);
33853 this.setDay(d.format(this.dayFormat));
33854 this.setMonth(d.format(this.monthFormat));
33855 this.setYear(d.format(this.yearFormat));
33862 setDay : function(v)
33864 this.dayField.setValue(v);
33865 this.inputEl.dom.value = this.getValue();
33870 setMonth : function(v)
33872 this.monthField.setValue(v, true);
33873 this.inputEl.dom.value = this.getValue();
33878 setYear : function(v)
33880 this.yearField.setValue(v);
33881 this.inputEl.dom.value = this.getValue();
33886 getDay : function()
33888 return this.dayField.getValue();
33891 getMonth : function()
33893 return this.monthField.getValue();
33896 getYear : function()
33898 return this.yearField.getValue();
33901 getValue : function()
33903 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33905 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33915 this.inputEl.dom.value = '';
33920 validate : function()
33922 var d = this.dayField.validate();
33923 var m = this.monthField.validate();
33924 var y = this.yearField.validate();
33929 (!this.dayAllowBlank && !d) ||
33930 (!this.monthAllowBlank && !m) ||
33931 (!this.yearAllowBlank && !y)
33936 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33945 this.markInvalid();
33950 markValid : function()
33953 var label = this.el.select('label', true).first();
33954 var icon = this.el.select('i.fa-star', true).first();
33960 this.fireEvent('valid', this);
33964 * Mark this field as invalid
33965 * @param {String} msg The validation message
33967 markInvalid : function(msg)
33970 var label = this.el.select('label', true).first();
33971 var icon = this.el.select('i.fa-star', true).first();
33973 if(label && !icon){
33974 this.el.select('.roo-date-split-field-label', true).createChild({
33976 cls : 'text-danger fa fa-lg fa-star',
33977 tooltip : 'This field is required',
33978 style : 'margin-right:5px;'
33982 this.fireEvent('invalid', this, msg);
33985 clearInvalid : function()
33987 var label = this.el.select('label', true).first();
33988 var icon = this.el.select('i.fa-star', true).first();
33994 this.fireEvent('valid', this);
33997 getName: function()
34007 * @class Roo.bootstrap.LayoutMasonry
34008 * @extends Roo.bootstrap.Component
34009 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34010 * Bootstrap Layout Masonry class
34013 * http://masonry.desandro.com
34015 * The idea is to render all the bricks based on vertical width...
34017 * The original code extends 'outlayer' - we might need to use that....
34020 * Create a new Element
34021 * @param {Object} config The config object
34024 Roo.bootstrap.LayoutMasonry = function(config){
34026 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34030 Roo.bootstrap.LayoutMasonry.register(this);
34036 * Fire after layout the items
34037 * @param {Roo.bootstrap.LayoutMasonry} this
34038 * @param {Roo.EventObject} e
34045 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34048 * @cfg {Boolean} isLayoutInstant = no animation?
34050 isLayoutInstant : false, // needed?
34053 * @cfg {Number} boxWidth width of the columns
34058 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34063 * @cfg {Number} padWidth padding below box..
34068 * @cfg {Number} gutter gutter width..
34073 * @cfg {Number} maxCols maximum number of columns
34079 * @cfg {Boolean} isAutoInitial defalut true
34081 isAutoInitial : true,
34086 * @cfg {Boolean} isHorizontal defalut false
34088 isHorizontal : false,
34090 currentSize : null,
34096 bricks: null, //CompositeElement
34100 _isLayoutInited : false,
34102 // isAlternative : false, // only use for vertical layout...
34105 * @cfg {Number} alternativePadWidth padding below box..
34107 alternativePadWidth : 50,
34109 selectedBrick : [],
34111 getAutoCreate : function(){
34113 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34117 cls: 'blog-masonary-wrapper ' + this.cls,
34119 cls : 'mas-boxes masonary'
34126 getChildContainer: function( )
34128 if (this.boxesEl) {
34129 return this.boxesEl;
34132 this.boxesEl = this.el.select('.mas-boxes').first();
34134 return this.boxesEl;
34138 initEvents : function()
34142 if(this.isAutoInitial){
34143 Roo.log('hook children rendered');
34144 this.on('childrenrendered', function() {
34145 Roo.log('children rendered');
34151 initial : function()
34153 this.selectedBrick = [];
34155 this.currentSize = this.el.getBox(true);
34157 Roo.EventManager.onWindowResize(this.resize, this);
34159 if(!this.isAutoInitial){
34167 //this.layout.defer(500,this);
34171 resize : function()
34173 var cs = this.el.getBox(true);
34176 this.currentSize.width == cs.width &&
34177 this.currentSize.x == cs.x &&
34178 this.currentSize.height == cs.height &&
34179 this.currentSize.y == cs.y
34181 Roo.log("no change in with or X or Y");
34185 this.currentSize = cs;
34191 layout : function()
34193 this._resetLayout();
34195 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34197 this.layoutItems( isInstant );
34199 this._isLayoutInited = true;
34201 this.fireEvent('layout', this);
34205 _resetLayout : function()
34207 if(this.isHorizontal){
34208 this.horizontalMeasureColumns();
34212 this.verticalMeasureColumns();
34216 verticalMeasureColumns : function()
34218 this.getContainerWidth();
34220 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34221 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34225 var boxWidth = this.boxWidth + this.padWidth;
34227 if(this.containerWidth < this.boxWidth){
34228 boxWidth = this.containerWidth
34231 var containerWidth = this.containerWidth;
34233 var cols = Math.floor(containerWidth / boxWidth);
34235 this.cols = Math.max( cols, 1 );
34237 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34239 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34241 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34243 this.colWidth = boxWidth + avail - this.padWidth;
34245 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34246 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34249 horizontalMeasureColumns : function()
34251 this.getContainerWidth();
34253 var boxWidth = this.boxWidth;
34255 if(this.containerWidth < boxWidth){
34256 boxWidth = this.containerWidth;
34259 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34261 this.el.setHeight(boxWidth);
34265 getContainerWidth : function()
34267 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34270 layoutItems : function( isInstant )
34272 Roo.log(this.bricks);
34274 var items = Roo.apply([], this.bricks);
34276 if(this.isHorizontal){
34277 this._horizontalLayoutItems( items , isInstant );
34281 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34282 // this._verticalAlternativeLayoutItems( items , isInstant );
34286 this._verticalLayoutItems( items , isInstant );
34290 _verticalLayoutItems : function ( items , isInstant)
34292 if ( !items || !items.length ) {
34297 ['xs', 'xs', 'xs', 'tall'],
34298 ['xs', 'xs', 'tall'],
34299 ['xs', 'xs', 'sm'],
34300 ['xs', 'xs', 'xs'],
34306 ['sm', 'xs', 'xs'],
34310 ['tall', 'xs', 'xs', 'xs'],
34311 ['tall', 'xs', 'xs'],
34323 Roo.each(items, function(item, k){
34325 switch (item.size) {
34326 // these layouts take up a full box,
34337 boxes.push([item]);
34360 var filterPattern = function(box, length)
34368 var pattern = box.slice(0, length);
34372 Roo.each(pattern, function(i){
34373 format.push(i.size);
34376 Roo.each(standard, function(s){
34378 if(String(s) != String(format)){
34387 if(!match && length == 1){
34392 filterPattern(box, length - 1);
34396 queue.push(pattern);
34398 box = box.slice(length, box.length);
34400 filterPattern(box, 4);
34406 Roo.each(boxes, function(box, k){
34412 if(box.length == 1){
34417 filterPattern(box, 4);
34421 this._processVerticalLayoutQueue( queue, isInstant );
34425 // _verticalAlternativeLayoutItems : function( items , isInstant )
34427 // if ( !items || !items.length ) {
34431 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34435 _horizontalLayoutItems : function ( items , isInstant)
34437 if ( !items || !items.length || items.length < 3) {
34443 var eItems = items.slice(0, 3);
34445 items = items.slice(3, items.length);
34448 ['xs', 'xs', 'xs', 'wide'],
34449 ['xs', 'xs', 'wide'],
34450 ['xs', 'xs', 'sm'],
34451 ['xs', 'xs', 'xs'],
34457 ['sm', 'xs', 'xs'],
34461 ['wide', 'xs', 'xs', 'xs'],
34462 ['wide', 'xs', 'xs'],
34475 Roo.each(items, function(item, k){
34477 switch (item.size) {
34488 boxes.push([item]);
34512 var filterPattern = function(box, length)
34520 var pattern = box.slice(0, length);
34524 Roo.each(pattern, function(i){
34525 format.push(i.size);
34528 Roo.each(standard, function(s){
34530 if(String(s) != String(format)){
34539 if(!match && length == 1){
34544 filterPattern(box, length - 1);
34548 queue.push(pattern);
34550 box = box.slice(length, box.length);
34552 filterPattern(box, 4);
34558 Roo.each(boxes, function(box, k){
34564 if(box.length == 1){
34569 filterPattern(box, 4);
34576 var pos = this.el.getBox(true);
34580 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34582 var hit_end = false;
34584 Roo.each(queue, function(box){
34588 Roo.each(box, function(b){
34590 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34600 Roo.each(box, function(b){
34602 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34605 mx = Math.max(mx, b.x);
34609 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34613 Roo.each(box, function(b){
34615 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34629 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34632 /** Sets position of item in DOM
34633 * @param {Element} item
34634 * @param {Number} x - horizontal position
34635 * @param {Number} y - vertical position
34636 * @param {Boolean} isInstant - disables transitions
34638 _processVerticalLayoutQueue : function( queue, isInstant )
34640 var pos = this.el.getBox(true);
34645 for (var i = 0; i < this.cols; i++){
34649 Roo.each(queue, function(box, k){
34651 var col = k % this.cols;
34653 Roo.each(box, function(b,kk){
34655 b.el.position('absolute');
34657 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34658 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34660 if(b.size == 'md-left' || b.size == 'md-right'){
34661 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34662 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34665 b.el.setWidth(width);
34666 b.el.setHeight(height);
34668 b.el.select('iframe',true).setSize(width,height);
34672 for (var i = 0; i < this.cols; i++){
34674 if(maxY[i] < maxY[col]){
34679 col = Math.min(col, i);
34683 x = pos.x + col * (this.colWidth + this.padWidth);
34687 var positions = [];
34689 switch (box.length){
34691 positions = this.getVerticalOneBoxColPositions(x, y, box);
34694 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34697 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34700 positions = this.getVerticalFourBoxColPositions(x, y, box);
34706 Roo.each(box, function(b,kk){
34708 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34710 var sz = b.el.getSize();
34712 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34720 for (var i = 0; i < this.cols; i++){
34721 mY = Math.max(mY, maxY[i]);
34724 this.el.setHeight(mY - pos.y);
34728 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34730 // var pos = this.el.getBox(true);
34733 // var maxX = pos.right;
34735 // var maxHeight = 0;
34737 // Roo.each(items, function(item, k){
34741 // item.el.position('absolute');
34743 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34745 // item.el.setWidth(width);
34747 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34749 // item.el.setHeight(height);
34752 // item.el.setXY([x, y], isInstant ? false : true);
34754 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34757 // y = y + height + this.alternativePadWidth;
34759 // maxHeight = maxHeight + height + this.alternativePadWidth;
34763 // this.el.setHeight(maxHeight);
34767 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34769 var pos = this.el.getBox(true);
34774 var maxX = pos.right;
34776 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34778 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34780 Roo.each(queue, function(box, k){
34782 Roo.each(box, function(b, kk){
34784 b.el.position('absolute');
34786 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34787 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34789 if(b.size == 'md-left' || b.size == 'md-right'){
34790 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34791 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34794 b.el.setWidth(width);
34795 b.el.setHeight(height);
34803 var positions = [];
34805 switch (box.length){
34807 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34810 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34813 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34816 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34822 Roo.each(box, function(b,kk){
34824 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34826 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34834 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34836 Roo.each(eItems, function(b,k){
34838 b.size = (k == 0) ? 'sm' : 'xs';
34839 b.x = (k == 0) ? 2 : 1;
34840 b.y = (k == 0) ? 2 : 1;
34842 b.el.position('absolute');
34844 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34846 b.el.setWidth(width);
34848 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34850 b.el.setHeight(height);
34854 var positions = [];
34857 x : maxX - this.unitWidth * 2 - this.gutter,
34862 x : maxX - this.unitWidth,
34863 y : minY + (this.unitWidth + this.gutter) * 2
34867 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34871 Roo.each(eItems, function(b,k){
34873 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34879 getVerticalOneBoxColPositions : function(x, y, box)
34883 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34885 if(box[0].size == 'md-left'){
34889 if(box[0].size == 'md-right'){
34894 x : x + (this.unitWidth + this.gutter) * rand,
34901 getVerticalTwoBoxColPositions : function(x, y, box)
34905 if(box[0].size == 'xs'){
34909 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34913 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34927 x : x + (this.unitWidth + this.gutter) * 2,
34928 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34935 getVerticalThreeBoxColPositions : function(x, y, box)
34939 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34947 x : x + (this.unitWidth + this.gutter) * 1,
34952 x : x + (this.unitWidth + this.gutter) * 2,
34960 if(box[0].size == 'xs' && box[1].size == 'xs'){
34969 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34973 x : x + (this.unitWidth + this.gutter) * 1,
34987 x : x + (this.unitWidth + this.gutter) * 2,
34992 x : x + (this.unitWidth + this.gutter) * 2,
34993 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35000 getVerticalFourBoxColPositions : function(x, y, box)
35004 if(box[0].size == 'xs'){
35013 y : y + (this.unitHeight + this.gutter) * 1
35018 y : y + (this.unitHeight + this.gutter) * 2
35022 x : x + (this.unitWidth + this.gutter) * 1,
35036 x : x + (this.unitWidth + this.gutter) * 2,
35041 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35042 y : y + (this.unitHeight + this.gutter) * 1
35046 x : x + (this.unitWidth + this.gutter) * 2,
35047 y : y + (this.unitWidth + this.gutter) * 2
35054 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35058 if(box[0].size == 'md-left'){
35060 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35067 if(box[0].size == 'md-right'){
35069 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35070 y : minY + (this.unitWidth + this.gutter) * 1
35076 var rand = Math.floor(Math.random() * (4 - box[0].y));
35079 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35080 y : minY + (this.unitWidth + this.gutter) * rand
35087 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35091 if(box[0].size == 'xs'){
35094 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35099 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35100 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35108 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35113 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35114 y : minY + (this.unitWidth + this.gutter) * 2
35121 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35125 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35128 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35133 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35134 y : minY + (this.unitWidth + this.gutter) * 1
35138 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35139 y : minY + (this.unitWidth + this.gutter) * 2
35146 if(box[0].size == 'xs' && box[1].size == 'xs'){
35149 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35154 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35159 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35160 y : minY + (this.unitWidth + this.gutter) * 1
35168 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35173 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35174 y : minY + (this.unitWidth + this.gutter) * 2
35178 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35179 y : minY + (this.unitWidth + this.gutter) * 2
35186 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35190 if(box[0].size == 'xs'){
35193 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35198 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35203 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),
35208 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35209 y : minY + (this.unitWidth + this.gutter) * 1
35217 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35222 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35223 y : minY + (this.unitWidth + this.gutter) * 2
35227 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35228 y : minY + (this.unitWidth + this.gutter) * 2
35232 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),
35233 y : minY + (this.unitWidth + this.gutter) * 2
35241 * remove a Masonry Brick
35242 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35244 removeBrick : function(brick_id)
35250 for (var i = 0; i<this.bricks.length; i++) {
35251 if (this.bricks[i].id == brick_id) {
35252 this.bricks.splice(i,1);
35253 this.el.dom.removeChild(Roo.get(brick_id).dom);
35260 * adds a Masonry Brick
35261 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35263 addBrick : function(cfg)
35265 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35266 //this.register(cn);
35267 cn.parentId = this.id;
35268 cn.render(this.el);
35273 * register a Masonry Brick
35274 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35277 register : function(brick)
35279 this.bricks.push(brick);
35280 brick.masonryId = this.id;
35284 * clear all the Masonry Brick
35286 clearAll : function()
35289 //this.getChildContainer().dom.innerHTML = "";
35290 this.el.dom.innerHTML = '';
35293 getSelected : function()
35295 if (!this.selectedBrick) {
35299 return this.selectedBrick;
35303 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35307 * register a Masonry Layout
35308 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35311 register : function(layout)
35313 this.groups[layout.id] = layout;
35316 * fetch a Masonry Layout based on the masonry layout ID
35317 * @param {string} the masonry layout to add
35318 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35321 get: function(layout_id) {
35322 if (typeof(this.groups[layout_id]) == 'undefined') {
35325 return this.groups[layout_id] ;
35337 * http://masonry.desandro.com
35339 * The idea is to render all the bricks based on vertical width...
35341 * The original code extends 'outlayer' - we might need to use that....
35347 * @class Roo.bootstrap.LayoutMasonryAuto
35348 * @extends Roo.bootstrap.Component
35349 * Bootstrap Layout Masonry class
35352 * Create a new Element
35353 * @param {Object} config The config object
35356 Roo.bootstrap.LayoutMasonryAuto = function(config){
35357 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35360 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35363 * @cfg {Boolean} isFitWidth - resize the width..
35365 isFitWidth : false, // options..
35367 * @cfg {Boolean} isOriginLeft = left align?
35369 isOriginLeft : true,
35371 * @cfg {Boolean} isOriginTop = top align?
35373 isOriginTop : false,
35375 * @cfg {Boolean} isLayoutInstant = no animation?
35377 isLayoutInstant : false, // needed?
35379 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35381 isResizingContainer : true,
35383 * @cfg {Number} columnWidth width of the columns
35389 * @cfg {Number} maxCols maximum number of columns
35394 * @cfg {Number} padHeight padding below box..
35400 * @cfg {Boolean} isAutoInitial defalut true
35403 isAutoInitial : true,
35409 initialColumnWidth : 0,
35410 currentSize : null,
35412 colYs : null, // array.
35419 bricks: null, //CompositeElement
35420 cols : 0, // array?
35421 // element : null, // wrapped now this.el
35422 _isLayoutInited : null,
35425 getAutoCreate : function(){
35429 cls: 'blog-masonary-wrapper ' + this.cls,
35431 cls : 'mas-boxes masonary'
35438 getChildContainer: function( )
35440 if (this.boxesEl) {
35441 return this.boxesEl;
35444 this.boxesEl = this.el.select('.mas-boxes').first();
35446 return this.boxesEl;
35450 initEvents : function()
35454 if(this.isAutoInitial){
35455 Roo.log('hook children rendered');
35456 this.on('childrenrendered', function() {
35457 Roo.log('children rendered');
35464 initial : function()
35466 this.reloadItems();
35468 this.currentSize = this.el.getBox(true);
35470 /// was window resize... - let's see if this works..
35471 Roo.EventManager.onWindowResize(this.resize, this);
35473 if(!this.isAutoInitial){
35478 this.layout.defer(500,this);
35481 reloadItems: function()
35483 this.bricks = this.el.select('.masonry-brick', true);
35485 this.bricks.each(function(b) {
35486 //Roo.log(b.getSize());
35487 if (!b.attr('originalwidth')) {
35488 b.attr('originalwidth', b.getSize().width);
35493 Roo.log(this.bricks.elements.length);
35496 resize : function()
35499 var cs = this.el.getBox(true);
35501 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35502 Roo.log("no change in with or X");
35505 this.currentSize = cs;
35509 layout : function()
35512 this._resetLayout();
35513 //this._manageStamps();
35515 // don't animate first layout
35516 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35517 this.layoutItems( isInstant );
35519 // flag for initalized
35520 this._isLayoutInited = true;
35523 layoutItems : function( isInstant )
35525 //var items = this._getItemsForLayout( this.items );
35526 // original code supports filtering layout items.. we just ignore it..
35528 this._layoutItems( this.bricks , isInstant );
35530 this._postLayout();
35532 _layoutItems : function ( items , isInstant)
35534 //this.fireEvent( 'layout', this, items );
35537 if ( !items || !items.elements.length ) {
35538 // no items, emit event with empty array
35543 items.each(function(item) {
35544 Roo.log("layout item");
35546 // get x/y object from method
35547 var position = this._getItemLayoutPosition( item );
35549 position.item = item;
35550 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35551 queue.push( position );
35554 this._processLayoutQueue( queue );
35556 /** Sets position of item in DOM
35557 * @param {Element} item
35558 * @param {Number} x - horizontal position
35559 * @param {Number} y - vertical position
35560 * @param {Boolean} isInstant - disables transitions
35562 _processLayoutQueue : function( queue )
35564 for ( var i=0, len = queue.length; i < len; i++ ) {
35565 var obj = queue[i];
35566 obj.item.position('absolute');
35567 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35573 * Any logic you want to do after each layout,
35574 * i.e. size the container
35576 _postLayout : function()
35578 this.resizeContainer();
35581 resizeContainer : function()
35583 if ( !this.isResizingContainer ) {
35586 var size = this._getContainerSize();
35588 this.el.setSize(size.width,size.height);
35589 this.boxesEl.setSize(size.width,size.height);
35595 _resetLayout : function()
35597 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35598 this.colWidth = this.el.getWidth();
35599 //this.gutter = this.el.getWidth();
35601 this.measureColumns();
35607 this.colYs.push( 0 );
35613 measureColumns : function()
35615 this.getContainerWidth();
35616 // if columnWidth is 0, default to outerWidth of first item
35617 if ( !this.columnWidth ) {
35618 var firstItem = this.bricks.first();
35619 Roo.log(firstItem);
35620 this.columnWidth = this.containerWidth;
35621 if (firstItem && firstItem.attr('originalwidth') ) {
35622 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35624 // columnWidth fall back to item of first element
35625 Roo.log("set column width?");
35626 this.initialColumnWidth = this.columnWidth ;
35628 // if first elem has no width, default to size of container
35633 if (this.initialColumnWidth) {
35634 this.columnWidth = this.initialColumnWidth;
35639 // column width is fixed at the top - however if container width get's smaller we should
35642 // this bit calcs how man columns..
35644 var columnWidth = this.columnWidth += this.gutter;
35646 // calculate columns
35647 var containerWidth = this.containerWidth + this.gutter;
35649 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35650 // fix rounding errors, typically with gutters
35651 var excess = columnWidth - containerWidth % columnWidth;
35654 // if overshoot is less than a pixel, round up, otherwise floor it
35655 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35656 cols = Math[ mathMethod ]( cols );
35657 this.cols = Math.max( cols, 1 );
35658 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35660 // padding positioning..
35661 var totalColWidth = this.cols * this.columnWidth;
35662 var padavail = this.containerWidth - totalColWidth;
35663 // so for 2 columns - we need 3 'pads'
35665 var padNeeded = (1+this.cols) * this.padWidth;
35667 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35669 this.columnWidth += padExtra
35670 //this.padWidth = Math.floor(padavail / ( this.cols));
35672 // adjust colum width so that padding is fixed??
35674 // we have 3 columns ... total = width * 3
35675 // we have X left over... that should be used by
35677 //if (this.expandC) {
35685 getContainerWidth : function()
35687 /* // container is parent if fit width
35688 var container = this.isFitWidth ? this.element.parentNode : this.element;
35689 // check that this.size and size are there
35690 // IE8 triggers resize on body size change, so they might not be
35692 var size = getSize( container ); //FIXME
35693 this.containerWidth = size && size.innerWidth; //FIXME
35696 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35700 _getItemLayoutPosition : function( item ) // what is item?
35702 // we resize the item to our columnWidth..
35704 item.setWidth(this.columnWidth);
35705 item.autoBoxAdjust = false;
35707 var sz = item.getSize();
35709 // how many columns does this brick span
35710 var remainder = this.containerWidth % this.columnWidth;
35712 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35713 // round if off by 1 pixel, otherwise use ceil
35714 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35715 colSpan = Math.min( colSpan, this.cols );
35717 // normally this should be '1' as we dont' currently allow multi width columns..
35719 var colGroup = this._getColGroup( colSpan );
35720 // get the minimum Y value from the columns
35721 var minimumY = Math.min.apply( Math, colGroup );
35722 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35724 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35726 // position the brick
35728 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35729 y: this.currentSize.y + minimumY + this.padHeight
35733 // apply setHeight to necessary columns
35734 var setHeight = minimumY + sz.height + this.padHeight;
35735 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35737 var setSpan = this.cols + 1 - colGroup.length;
35738 for ( var i = 0; i < setSpan; i++ ) {
35739 this.colYs[ shortColIndex + i ] = setHeight ;
35746 * @param {Number} colSpan - number of columns the element spans
35747 * @returns {Array} colGroup
35749 _getColGroup : function( colSpan )
35751 if ( colSpan < 2 ) {
35752 // if brick spans only one column, use all the column Ys
35757 // how many different places could this brick fit horizontally
35758 var groupCount = this.cols + 1 - colSpan;
35759 // for each group potential horizontal position
35760 for ( var i = 0; i < groupCount; i++ ) {
35761 // make an array of colY values for that one group
35762 var groupColYs = this.colYs.slice( i, i + colSpan );
35763 // and get the max value of the array
35764 colGroup[i] = Math.max.apply( Math, groupColYs );
35769 _manageStamp : function( stamp )
35771 var stampSize = stamp.getSize();
35772 var offset = stamp.getBox();
35773 // get the columns that this stamp affects
35774 var firstX = this.isOriginLeft ? offset.x : offset.right;
35775 var lastX = firstX + stampSize.width;
35776 var firstCol = Math.floor( firstX / this.columnWidth );
35777 firstCol = Math.max( 0, firstCol );
35779 var lastCol = Math.floor( lastX / this.columnWidth );
35780 // lastCol should not go over if multiple of columnWidth #425
35781 lastCol -= lastX % this.columnWidth ? 0 : 1;
35782 lastCol = Math.min( this.cols - 1, lastCol );
35784 // set colYs to bottom of the stamp
35785 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35788 for ( var i = firstCol; i <= lastCol; i++ ) {
35789 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35794 _getContainerSize : function()
35796 this.maxY = Math.max.apply( Math, this.colYs );
35801 if ( this.isFitWidth ) {
35802 size.width = this._getContainerFitWidth();
35808 _getContainerFitWidth : function()
35810 var unusedCols = 0;
35811 // count unused columns
35814 if ( this.colYs[i] !== 0 ) {
35819 // fit container to columns that have been used
35820 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35823 needsResizeLayout : function()
35825 var previousWidth = this.containerWidth;
35826 this.getContainerWidth();
35827 return previousWidth !== this.containerWidth;
35842 * @class Roo.bootstrap.MasonryBrick
35843 * @extends Roo.bootstrap.Component
35844 * Bootstrap MasonryBrick class
35847 * Create a new MasonryBrick
35848 * @param {Object} config The config object
35851 Roo.bootstrap.MasonryBrick = function(config){
35853 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35855 Roo.bootstrap.MasonryBrick.register(this);
35861 * When a MasonryBrick is clcik
35862 * @param {Roo.bootstrap.MasonryBrick} this
35863 * @param {Roo.EventObject} e
35869 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35872 * @cfg {String} title
35876 * @cfg {String} html
35880 * @cfg {String} bgimage
35884 * @cfg {String} videourl
35888 * @cfg {String} cls
35892 * @cfg {String} href
35896 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35901 * @cfg {String} placetitle (center|bottom)
35906 * @cfg {Boolean} isFitContainer defalut true
35908 isFitContainer : true,
35911 * @cfg {Boolean} preventDefault defalut false
35913 preventDefault : false,
35916 * @cfg {Boolean} inverse defalut false
35918 maskInverse : false,
35920 getAutoCreate : function()
35922 if(!this.isFitContainer){
35923 return this.getSplitAutoCreate();
35926 var cls = 'masonry-brick masonry-brick-full';
35928 if(this.href.length){
35929 cls += ' masonry-brick-link';
35932 if(this.bgimage.length){
35933 cls += ' masonry-brick-image';
35936 if(this.maskInverse){
35937 cls += ' mask-inverse';
35940 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35941 cls += ' enable-mask';
35945 cls += ' masonry-' + this.size + '-brick';
35948 if(this.placetitle.length){
35950 switch (this.placetitle) {
35952 cls += ' masonry-center-title';
35955 cls += ' masonry-bottom-title';
35962 if(!this.html.length && !this.bgimage.length){
35963 cls += ' masonry-center-title';
35966 if(!this.html.length && this.bgimage.length){
35967 cls += ' masonry-bottom-title';
35972 cls += ' ' + this.cls;
35976 tag: (this.href.length) ? 'a' : 'div',
35981 cls: 'masonry-brick-mask'
35985 cls: 'masonry-brick-paragraph',
35991 if(this.href.length){
35992 cfg.href = this.href;
35995 var cn = cfg.cn[1].cn;
35997 if(this.title.length){
36000 cls: 'masonry-brick-title',
36005 if(this.html.length){
36008 cls: 'masonry-brick-text',
36013 if (!this.title.length && !this.html.length) {
36014 cfg.cn[1].cls += ' hide';
36017 if(this.bgimage.length){
36020 cls: 'masonry-brick-image-view',
36025 if(this.videourl.length){
36026 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36027 // youtube support only?
36030 cls: 'masonry-brick-image-view',
36033 allowfullscreen : true
36041 getSplitAutoCreate : function()
36043 var cls = 'masonry-brick masonry-brick-split';
36045 if(this.href.length){
36046 cls += ' masonry-brick-link';
36049 if(this.bgimage.length){
36050 cls += ' masonry-brick-image';
36054 cls += ' masonry-' + this.size + '-brick';
36057 switch (this.placetitle) {
36059 cls += ' masonry-center-title';
36062 cls += ' masonry-bottom-title';
36065 if(!this.bgimage.length){
36066 cls += ' masonry-center-title';
36069 if(this.bgimage.length){
36070 cls += ' masonry-bottom-title';
36076 cls += ' ' + this.cls;
36080 tag: (this.href.length) ? 'a' : 'div',
36085 cls: 'masonry-brick-split-head',
36089 cls: 'masonry-brick-paragraph',
36096 cls: 'masonry-brick-split-body',
36102 if(this.href.length){
36103 cfg.href = this.href;
36106 if(this.title.length){
36107 cfg.cn[0].cn[0].cn.push({
36109 cls: 'masonry-brick-title',
36114 if(this.html.length){
36115 cfg.cn[1].cn.push({
36117 cls: 'masonry-brick-text',
36122 if(this.bgimage.length){
36123 cfg.cn[0].cn.push({
36125 cls: 'masonry-brick-image-view',
36130 if(this.videourl.length){
36131 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36132 // youtube support only?
36133 cfg.cn[0].cn.cn.push({
36135 cls: 'masonry-brick-image-view',
36138 allowfullscreen : true
36145 initEvents: function()
36147 switch (this.size) {
36180 this.el.on('touchstart', this.onTouchStart, this);
36181 this.el.on('touchmove', this.onTouchMove, this);
36182 this.el.on('touchend', this.onTouchEnd, this);
36183 this.el.on('contextmenu', this.onContextMenu, this);
36185 this.el.on('mouseenter' ,this.enter, this);
36186 this.el.on('mouseleave', this.leave, this);
36187 this.el.on('click', this.onClick, this);
36190 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36191 this.parent().bricks.push(this);
36196 onClick: function(e, el)
36198 var time = this.endTimer - this.startTimer;
36199 // Roo.log(e.preventDefault());
36202 e.preventDefault();
36207 if(!this.preventDefault){
36211 e.preventDefault();
36213 if (this.activeClass != '') {
36214 this.selectBrick();
36217 this.fireEvent('click', this, e);
36220 enter: function(e, el)
36222 e.preventDefault();
36224 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36228 if(this.bgimage.length && this.html.length){
36229 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36233 leave: function(e, el)
36235 e.preventDefault();
36237 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36241 if(this.bgimage.length && this.html.length){
36242 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36246 onTouchStart: function(e, el)
36248 // e.preventDefault();
36250 this.touchmoved = false;
36252 if(!this.isFitContainer){
36256 if(!this.bgimage.length || !this.html.length){
36260 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36262 this.timer = new Date().getTime();
36266 onTouchMove: function(e, el)
36268 this.touchmoved = true;
36271 onContextMenu : function(e,el)
36273 e.preventDefault();
36274 e.stopPropagation();
36278 onTouchEnd: function(e, el)
36280 // e.preventDefault();
36282 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36289 if(!this.bgimage.length || !this.html.length){
36291 if(this.href.length){
36292 window.location.href = this.href;
36298 if(!this.isFitContainer){
36302 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36304 window.location.href = this.href;
36307 //selection on single brick only
36308 selectBrick : function() {
36310 if (!this.parentId) {
36314 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36315 var index = m.selectedBrick.indexOf(this.id);
36318 m.selectedBrick.splice(index,1);
36319 this.el.removeClass(this.activeClass);
36323 for(var i = 0; i < m.selectedBrick.length; i++) {
36324 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36325 b.el.removeClass(b.activeClass);
36328 m.selectedBrick = [];
36330 m.selectedBrick.push(this.id);
36331 this.el.addClass(this.activeClass);
36335 isSelected : function(){
36336 return this.el.hasClass(this.activeClass);
36341 Roo.apply(Roo.bootstrap.MasonryBrick, {
36344 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36346 * register a Masonry Brick
36347 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36350 register : function(brick)
36352 //this.groups[brick.id] = brick;
36353 this.groups.add(brick.id, brick);
36356 * fetch a masonry brick based on the masonry brick ID
36357 * @param {string} the masonry brick to add
36358 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36361 get: function(brick_id)
36363 // if (typeof(this.groups[brick_id]) == 'undefined') {
36366 // return this.groups[brick_id] ;
36368 if(this.groups.key(brick_id)) {
36369 return this.groups.key(brick_id);
36387 * @class Roo.bootstrap.Brick
36388 * @extends Roo.bootstrap.Component
36389 * Bootstrap Brick class
36392 * Create a new Brick
36393 * @param {Object} config The config object
36396 Roo.bootstrap.Brick = function(config){
36397 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36403 * When a Brick is click
36404 * @param {Roo.bootstrap.Brick} this
36405 * @param {Roo.EventObject} e
36411 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36414 * @cfg {String} title
36418 * @cfg {String} html
36422 * @cfg {String} bgimage
36426 * @cfg {String} cls
36430 * @cfg {String} href
36434 * @cfg {String} video
36438 * @cfg {Boolean} square
36442 getAutoCreate : function()
36444 var cls = 'roo-brick';
36446 if(this.href.length){
36447 cls += ' roo-brick-link';
36450 if(this.bgimage.length){
36451 cls += ' roo-brick-image';
36454 if(!this.html.length && !this.bgimage.length){
36455 cls += ' roo-brick-center-title';
36458 if(!this.html.length && this.bgimage.length){
36459 cls += ' roo-brick-bottom-title';
36463 cls += ' ' + this.cls;
36467 tag: (this.href.length) ? 'a' : 'div',
36472 cls: 'roo-brick-paragraph',
36478 if(this.href.length){
36479 cfg.href = this.href;
36482 var cn = cfg.cn[0].cn;
36484 if(this.title.length){
36487 cls: 'roo-brick-title',
36492 if(this.html.length){
36495 cls: 'roo-brick-text',
36502 if(this.bgimage.length){
36505 cls: 'roo-brick-image-view',
36513 initEvents: function()
36515 if(this.title.length || this.html.length){
36516 this.el.on('mouseenter' ,this.enter, this);
36517 this.el.on('mouseleave', this.leave, this);
36520 Roo.EventManager.onWindowResize(this.resize, this);
36522 if(this.bgimage.length){
36523 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36524 this.imageEl.on('load', this.onImageLoad, this);
36531 onImageLoad : function()
36536 resize : function()
36538 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36540 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36542 if(this.bgimage.length){
36543 var image = this.el.select('.roo-brick-image-view', true).first();
36545 image.setWidth(paragraph.getWidth());
36548 image.setHeight(paragraph.getWidth());
36551 this.el.setHeight(image.getHeight());
36552 paragraph.setHeight(image.getHeight());
36558 enter: function(e, el)
36560 e.preventDefault();
36562 if(this.bgimage.length){
36563 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36564 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36568 leave: function(e, el)
36570 e.preventDefault();
36572 if(this.bgimage.length){
36573 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36574 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36589 * @class Roo.bootstrap.form.NumberField
36590 * @extends Roo.bootstrap.form.Input
36591 * Bootstrap NumberField class
36597 * Create a new NumberField
36598 * @param {Object} config The config object
36601 Roo.bootstrap.form.NumberField = function(config){
36602 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36605 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36608 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36610 allowDecimals : true,
36612 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36614 decimalSeparator : ".",
36616 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36618 decimalPrecision : 2,
36620 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36622 allowNegative : true,
36625 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36629 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36631 minValue : Number.NEGATIVE_INFINITY,
36633 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36635 maxValue : Number.MAX_VALUE,
36637 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36639 minText : "The minimum value for this field is {0}",
36641 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36643 maxText : "The maximum value for this field is {0}",
36645 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36646 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36648 nanText : "{0} is not a valid number",
36650 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36652 thousandsDelimiter : false,
36654 * @cfg {String} valueAlign alignment of value
36656 valueAlign : "left",
36658 getAutoCreate : function()
36660 var hiddenInput = {
36664 cls: 'hidden-number-input'
36668 hiddenInput.name = this.name;
36673 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36675 this.name = hiddenInput.name;
36677 if(cfg.cn.length > 0) {
36678 cfg.cn.push(hiddenInput);
36685 initEvents : function()
36687 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36689 var allowed = "0123456789";
36691 if(this.allowDecimals){
36692 allowed += this.decimalSeparator;
36695 if(this.allowNegative){
36699 if(this.thousandsDelimiter) {
36703 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36705 var keyPress = function(e){
36707 var k = e.getKey();
36709 var c = e.getCharCode();
36712 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36713 allowed.indexOf(String.fromCharCode(c)) === -1
36719 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36723 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36728 this.el.on("keypress", keyPress, this);
36731 validateValue : function(value)
36734 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36738 var num = this.parseValue(value);
36741 this.markInvalid(String.format(this.nanText, value));
36745 if(num < this.minValue){
36746 this.markInvalid(String.format(this.minText, this.minValue));
36750 if(num > this.maxValue){
36751 this.markInvalid(String.format(this.maxText, this.maxValue));
36758 getValue : function()
36760 var v = this.hiddenEl().getValue();
36762 return this.fixPrecision(this.parseValue(v));
36765 parseValue : function(value)
36767 if(this.thousandsDelimiter) {
36769 r = new RegExp(",", "g");
36770 value = value.replace(r, "");
36773 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36774 return isNaN(value) ? '' : value;
36777 fixPrecision : function(value)
36779 if(this.thousandsDelimiter) {
36781 r = new RegExp(",", "g");
36782 value = value.replace(r, "");
36785 var nan = isNaN(value);
36787 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36788 return nan ? '' : value;
36790 return parseFloat(value).toFixed(this.decimalPrecision);
36793 setValue : function(v)
36795 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36801 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36803 this.inputEl().dom.value = (v == '') ? '' :
36804 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36806 if(!this.allowZero && v === '0') {
36807 this.hiddenEl().dom.value = '';
36808 this.inputEl().dom.value = '';
36815 decimalPrecisionFcn : function(v)
36817 return Math.floor(v);
36820 beforeBlur : function()
36822 var v = this.parseValue(this.getRawValue());
36824 if(v || v === 0 || v === ''){
36829 hiddenEl : function()
36831 return this.el.select('input.hidden-number-input',true).first();
36843 * @class Roo.bootstrap.DocumentSlider
36844 * @extends Roo.bootstrap.Component
36845 * Bootstrap DocumentSlider class
36848 * Create a new DocumentViewer
36849 * @param {Object} config The config object
36852 Roo.bootstrap.DocumentSlider = function(config){
36853 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36860 * Fire after initEvent
36861 * @param {Roo.bootstrap.DocumentSlider} this
36866 * Fire after update
36867 * @param {Roo.bootstrap.DocumentSlider} this
36873 * @param {Roo.bootstrap.DocumentSlider} this
36879 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36885 getAutoCreate : function()
36889 cls : 'roo-document-slider',
36893 cls : 'roo-document-slider-header',
36897 cls : 'roo-document-slider-header-title'
36903 cls : 'roo-document-slider-body',
36907 cls : 'roo-document-slider-prev',
36911 cls : 'fa fa-chevron-left'
36917 cls : 'roo-document-slider-thumb',
36921 cls : 'roo-document-slider-image'
36927 cls : 'roo-document-slider-next',
36931 cls : 'fa fa-chevron-right'
36943 initEvents : function()
36945 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36946 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36948 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36949 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36951 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36952 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36954 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36955 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36957 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36958 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36960 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36961 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36963 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36964 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36966 this.thumbEl.on('click', this.onClick, this);
36968 this.prevIndicator.on('click', this.prev, this);
36970 this.nextIndicator.on('click', this.next, this);
36974 initial : function()
36976 if(this.files.length){
36977 this.indicator = 1;
36981 this.fireEvent('initial', this);
36984 update : function()
36986 this.imageEl.attr('src', this.files[this.indicator - 1]);
36988 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36990 this.prevIndicator.show();
36992 if(this.indicator == 1){
36993 this.prevIndicator.hide();
36996 this.nextIndicator.show();
36998 if(this.indicator == this.files.length){
36999 this.nextIndicator.hide();
37002 this.thumbEl.scrollTo('top');
37004 this.fireEvent('update', this);
37007 onClick : function(e)
37009 e.preventDefault();
37011 this.fireEvent('click', this);
37016 e.preventDefault();
37018 this.indicator = Math.max(1, this.indicator - 1);
37025 e.preventDefault();
37027 this.indicator = Math.min(this.files.length, this.indicator + 1);
37041 * @class Roo.bootstrap.form.RadioSet
37042 * @extends Roo.bootstrap.form.Input
37043 * @children Roo.bootstrap.form.Radio
37044 * Bootstrap RadioSet class
37045 * @cfg {String} indicatorpos (left|right) default left
37046 * @cfg {Boolean} inline (true|false) inline the element (default true)
37047 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37049 * Create a new RadioSet
37050 * @param {Object} config The config object
37053 Roo.bootstrap.form.RadioSet = function(config){
37055 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37059 Roo.bootstrap.form.RadioSet.register(this);
37064 * Fires when the element is checked or unchecked.
37065 * @param {Roo.bootstrap.form.RadioSet} this This radio
37066 * @param {Roo.bootstrap.form.Radio} item The checked item
37071 * Fires when the element is click.
37072 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37073 * @param {Roo.bootstrap.form.Radio} item The checked item
37074 * @param {Roo.EventObject} e The event object
37081 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37089 indicatorpos : 'left',
37091 getAutoCreate : function()
37095 cls : 'roo-radio-set-label',
37099 html : this.fieldLabel
37103 if (Roo.bootstrap.version == 3) {
37106 if(this.indicatorpos == 'left'){
37109 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37110 tooltip : 'This field is required'
37115 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37116 tooltip : 'This field is required'
37122 cls : 'roo-radio-set-items'
37125 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37127 if (align === 'left' && this.fieldLabel.length) {
37130 cls : "roo-radio-set-right",
37136 if(this.labelWidth > 12){
37137 label.style = "width: " + this.labelWidth + 'px';
37140 if(this.labelWidth < 13 && this.labelmd == 0){
37141 this.labelmd = this.labelWidth;
37144 if(this.labellg > 0){
37145 label.cls += ' col-lg-' + this.labellg;
37146 items.cls += ' col-lg-' + (12 - this.labellg);
37149 if(this.labelmd > 0){
37150 label.cls += ' col-md-' + this.labelmd;
37151 items.cls += ' col-md-' + (12 - this.labelmd);
37154 if(this.labelsm > 0){
37155 label.cls += ' col-sm-' + this.labelsm;
37156 items.cls += ' col-sm-' + (12 - this.labelsm);
37159 if(this.labelxs > 0){
37160 label.cls += ' col-xs-' + this.labelxs;
37161 items.cls += ' col-xs-' + (12 - this.labelxs);
37167 cls : 'roo-radio-set',
37171 cls : 'roo-radio-set-input',
37174 value : this.value ? this.value : ''
37181 if(this.weight.length){
37182 cfg.cls += ' roo-radio-' + this.weight;
37186 cfg.cls += ' roo-radio-set-inline';
37190 ['xs','sm','md','lg'].map(function(size){
37191 if (settings[size]) {
37192 cfg.cls += ' col-' + size + '-' + settings[size];
37200 initEvents : function()
37202 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37203 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37205 if(!this.fieldLabel.length){
37206 this.labelEl.hide();
37209 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37210 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37212 this.indicator = this.indicatorEl();
37214 if(this.indicator){
37215 this.indicator.addClass('invisible');
37218 this.originalValue = this.getValue();
37222 inputEl: function ()
37224 return this.el.select('.roo-radio-set-input', true).first();
37227 getChildContainer : function()
37229 return this.itemsEl;
37232 register : function(item)
37234 this.radioes.push(item);
37238 validate : function()
37240 if(this.getVisibilityEl().hasClass('hidden')){
37246 Roo.each(this.radioes, function(i){
37255 if(this.allowBlank) {
37259 if(this.disabled || valid){
37264 this.markInvalid();
37269 markValid : function()
37271 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37272 this.indicatorEl().removeClass('visible');
37273 this.indicatorEl().addClass('invisible');
37277 if (Roo.bootstrap.version == 3) {
37278 this.el.removeClass([this.invalidClass, this.validClass]);
37279 this.el.addClass(this.validClass);
37281 this.el.removeClass(['is-invalid','is-valid']);
37282 this.el.addClass(['is-valid']);
37284 this.fireEvent('valid', this);
37287 markInvalid : function(msg)
37289 if(this.allowBlank || this.disabled){
37293 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37294 this.indicatorEl().removeClass('invisible');
37295 this.indicatorEl().addClass('visible');
37297 if (Roo.bootstrap.version == 3) {
37298 this.el.removeClass([this.invalidClass, this.validClass]);
37299 this.el.addClass(this.invalidClass);
37301 this.el.removeClass(['is-invalid','is-valid']);
37302 this.el.addClass(['is-invalid']);
37305 this.fireEvent('invalid', this, msg);
37309 setValue : function(v, suppressEvent)
37311 if(this.value === v){
37318 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37321 Roo.each(this.radioes, function(i){
37323 i.el.removeClass('checked');
37326 Roo.each(this.radioes, function(i){
37328 if(i.value === v || i.value.toString() === v.toString()){
37330 i.el.addClass('checked');
37332 if(suppressEvent !== true){
37333 this.fireEvent('check', this, i);
37344 clearInvalid : function(){
37346 if(!this.el || this.preventMark){
37350 this.el.removeClass([this.invalidClass]);
37352 this.fireEvent('valid', this);
37357 Roo.apply(Roo.bootstrap.form.RadioSet, {
37361 register : function(set)
37363 this.groups[set.name] = set;
37366 get: function(name)
37368 if (typeof(this.groups[name]) == 'undefined') {
37372 return this.groups[name] ;
37378 * Ext JS Library 1.1.1
37379 * Copyright(c) 2006-2007, Ext JS, LLC.
37381 * Originally Released Under LGPL - original licence link has changed is not relivant.
37384 * <script type="text/javascript">
37389 * @class Roo.bootstrap.SplitBar
37390 * @extends Roo.util.Observable
37391 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37395 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37396 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37397 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37398 split.minSize = 100;
37399 split.maxSize = 600;
37400 split.animate = true;
37401 split.on('moved', splitterMoved);
37404 * Create a new SplitBar
37405 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37406 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37407 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37408 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37409 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37410 position of the SplitBar).
37412 Roo.bootstrap.SplitBar = function(cfg){
37417 // dragElement : elm
37418 // resizingElement: el,
37420 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37421 // placement : Roo.bootstrap.SplitBar.LEFT ,
37422 // existingProxy ???
37425 this.el = Roo.get(cfg.dragElement, true);
37426 this.el.dom.unselectable = "on";
37428 this.resizingEl = Roo.get(cfg.resizingElement, true);
37432 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37433 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37436 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37439 * The minimum size of the resizing element. (Defaults to 0)
37445 * The maximum size of the resizing element. (Defaults to 2000)
37448 this.maxSize = 2000;
37451 * Whether to animate the transition to the new size
37454 this.animate = false;
37457 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37460 this.useShim = false;
37465 if(!cfg.existingProxy){
37467 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37469 this.proxy = Roo.get(cfg.existingProxy).dom;
37472 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37475 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37478 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37481 this.dragSpecs = {};
37484 * @private The adapter to use to positon and resize elements
37486 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37487 this.adapter.init(this);
37489 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37491 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37492 this.el.addClass("roo-splitbar-h");
37495 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37496 this.el.addClass("roo-splitbar-v");
37502 * Fires when the splitter is moved (alias for {@link #event-moved})
37503 * @param {Roo.bootstrap.SplitBar} this
37504 * @param {Number} newSize the new width or height
37509 * Fires when the splitter is moved
37510 * @param {Roo.bootstrap.SplitBar} this
37511 * @param {Number} newSize the new width or height
37515 * @event beforeresize
37516 * Fires before the splitter is dragged
37517 * @param {Roo.bootstrap.SplitBar} this
37519 "beforeresize" : true,
37521 "beforeapply" : true
37524 Roo.util.Observable.call(this);
37527 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37528 onStartProxyDrag : function(x, y){
37529 this.fireEvent("beforeresize", this);
37531 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37533 o.enableDisplayMode("block");
37534 // all splitbars share the same overlay
37535 Roo.bootstrap.SplitBar.prototype.overlay = o;
37537 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37538 this.overlay.show();
37539 Roo.get(this.proxy).setDisplayed("block");
37540 var size = this.adapter.getElementSize(this);
37541 this.activeMinSize = this.getMinimumSize();;
37542 this.activeMaxSize = this.getMaximumSize();;
37543 var c1 = size - this.activeMinSize;
37544 var c2 = Math.max(this.activeMaxSize - size, 0);
37545 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37546 this.dd.resetConstraints();
37547 this.dd.setXConstraint(
37548 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37549 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37551 this.dd.setYConstraint(0, 0);
37553 this.dd.resetConstraints();
37554 this.dd.setXConstraint(0, 0);
37555 this.dd.setYConstraint(
37556 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37557 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37560 this.dragSpecs.startSize = size;
37561 this.dragSpecs.startPoint = [x, y];
37562 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37566 * @private Called after the drag operation by the DDProxy
37568 onEndProxyDrag : function(e){
37569 Roo.get(this.proxy).setDisplayed(false);
37570 var endPoint = Roo.lib.Event.getXY(e);
37572 this.overlay.hide();
37575 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37576 newSize = this.dragSpecs.startSize +
37577 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37578 endPoint[0] - this.dragSpecs.startPoint[0] :
37579 this.dragSpecs.startPoint[0] - endPoint[0]
37582 newSize = this.dragSpecs.startSize +
37583 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37584 endPoint[1] - this.dragSpecs.startPoint[1] :
37585 this.dragSpecs.startPoint[1] - endPoint[1]
37588 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37589 if(newSize != this.dragSpecs.startSize){
37590 if(this.fireEvent('beforeapply', this, newSize) !== false){
37591 this.adapter.setElementSize(this, newSize);
37592 this.fireEvent("moved", this, newSize);
37593 this.fireEvent("resize", this, newSize);
37599 * Get the adapter this SplitBar uses
37600 * @return The adapter object
37602 getAdapter : function(){
37603 return this.adapter;
37607 * Set the adapter this SplitBar uses
37608 * @param {Object} adapter A SplitBar adapter object
37610 setAdapter : function(adapter){
37611 this.adapter = adapter;
37612 this.adapter.init(this);
37616 * Gets the minimum size for the resizing element
37617 * @return {Number} The minimum size
37619 getMinimumSize : function(){
37620 return this.minSize;
37624 * Sets the minimum size for the resizing element
37625 * @param {Number} minSize The minimum size
37627 setMinimumSize : function(minSize){
37628 this.minSize = minSize;
37632 * Gets the maximum size for the resizing element
37633 * @return {Number} The maximum size
37635 getMaximumSize : function(){
37636 return this.maxSize;
37640 * Sets the maximum size for the resizing element
37641 * @param {Number} maxSize The maximum size
37643 setMaximumSize : function(maxSize){
37644 this.maxSize = maxSize;
37648 * Sets the initialize size for the resizing element
37649 * @param {Number} size The initial size
37651 setCurrentSize : function(size){
37652 var oldAnimate = this.animate;
37653 this.animate = false;
37654 this.adapter.setElementSize(this, size);
37655 this.animate = oldAnimate;
37659 * Destroy this splitbar.
37660 * @param {Boolean} removeEl True to remove the element
37662 destroy : function(removeEl){
37664 this.shim.remove();
37667 this.proxy.parentNode.removeChild(this.proxy);
37675 * @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.
37677 Roo.bootstrap.SplitBar.createProxy = function(dir){
37678 var proxy = new Roo.Element(document.createElement("div"));
37679 proxy.unselectable();
37680 var cls = 'roo-splitbar-proxy';
37681 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37682 document.body.appendChild(proxy.dom);
37687 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37688 * Default Adapter. It assumes the splitter and resizing element are not positioned
37689 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37691 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37694 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37695 // do nothing for now
37696 init : function(s){
37700 * Called before drag operations to get the current size of the resizing element.
37701 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37703 getElementSize : function(s){
37704 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37705 return s.resizingEl.getWidth();
37707 return s.resizingEl.getHeight();
37712 * Called after drag operations to set the size of the resizing element.
37713 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37714 * @param {Number} newSize The new size to set
37715 * @param {Function} onComplete A function to be invoked when resizing is complete
37717 setElementSize : function(s, newSize, onComplete){
37718 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37720 s.resizingEl.setWidth(newSize);
37722 onComplete(s, newSize);
37725 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37730 s.resizingEl.setHeight(newSize);
37732 onComplete(s, newSize);
37735 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37742 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37743 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37744 * Adapter that moves the splitter element to align with the resized sizing element.
37745 * Used with an absolute positioned SplitBar.
37746 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37747 * document.body, make sure you assign an id to the body element.
37749 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37750 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37751 this.container = Roo.get(container);
37754 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37755 init : function(s){
37756 this.basic.init(s);
37759 getElementSize : function(s){
37760 return this.basic.getElementSize(s);
37763 setElementSize : function(s, newSize, onComplete){
37764 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37767 moveSplitter : function(s){
37768 var yes = Roo.bootstrap.SplitBar;
37769 switch(s.placement){
37771 s.el.setX(s.resizingEl.getRight());
37774 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37777 s.el.setY(s.resizingEl.getBottom());
37780 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37787 * Orientation constant - Create a vertical SplitBar
37791 Roo.bootstrap.SplitBar.VERTICAL = 1;
37794 * Orientation constant - Create a horizontal SplitBar
37798 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37801 * Placement constant - The resizing element is to the left of the splitter element
37805 Roo.bootstrap.SplitBar.LEFT = 1;
37808 * Placement constant - The resizing element is to the right of the splitter element
37812 Roo.bootstrap.SplitBar.RIGHT = 2;
37815 * Placement constant - The resizing element is positioned above the splitter element
37819 Roo.bootstrap.SplitBar.TOP = 3;
37822 * Placement constant - The resizing element is positioned under splitter element
37826 Roo.bootstrap.SplitBar.BOTTOM = 4;
37829 * Ext JS Library 1.1.1
37830 * Copyright(c) 2006-2007, Ext JS, LLC.
37832 * Originally Released Under LGPL - original licence link has changed is not relivant.
37835 * <script type="text/javascript">
37839 * @class Roo.bootstrap.layout.Manager
37840 * @extends Roo.bootstrap.Component
37842 * Base class for layout managers.
37844 Roo.bootstrap.layout.Manager = function(config)
37846 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37852 /** false to disable window resize monitoring @type Boolean */
37853 this.monitorWindowResize = true;
37858 * Fires when a layout is performed.
37859 * @param {Roo.LayoutManager} this
37863 * @event regionresized
37864 * Fires when the user resizes a region.
37865 * @param {Roo.LayoutRegion} region The resized region
37866 * @param {Number} newSize The new size (width for east/west, height for north/south)
37868 "regionresized" : true,
37870 * @event regioncollapsed
37871 * Fires when a region is collapsed.
37872 * @param {Roo.LayoutRegion} region The collapsed region
37874 "regioncollapsed" : true,
37876 * @event regionexpanded
37877 * Fires when a region is expanded.
37878 * @param {Roo.LayoutRegion} region The expanded region
37880 "regionexpanded" : true
37882 this.updating = false;
37885 this.el = Roo.get(config.el);
37891 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37896 monitorWindowResize : true,
37902 onRender : function(ct, position)
37905 this.el = Roo.get(ct);
37908 //this.fireEvent('render',this);
37912 initEvents: function()
37916 // ie scrollbar fix
37917 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37918 document.body.scroll = "no";
37919 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37920 this.el.position('relative');
37922 this.id = this.el.id;
37923 this.el.addClass("roo-layout-container");
37924 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37925 if(this.el.dom != document.body ) {
37926 this.el.on('resize', this.layout,this);
37927 this.el.on('show', this.layout,this);
37933 * Returns true if this layout is currently being updated
37934 * @return {Boolean}
37936 isUpdating : function(){
37937 return this.updating;
37941 * Suspend the LayoutManager from doing auto-layouts while
37942 * making multiple add or remove calls
37944 beginUpdate : function(){
37945 this.updating = true;
37949 * Restore auto-layouts and optionally disable the manager from performing a layout
37950 * @param {Boolean} noLayout true to disable a layout update
37952 endUpdate : function(noLayout){
37953 this.updating = false;
37959 layout: function(){
37963 onRegionResized : function(region, newSize){
37964 this.fireEvent("regionresized", region, newSize);
37968 onRegionCollapsed : function(region){
37969 this.fireEvent("regioncollapsed", region);
37972 onRegionExpanded : function(region){
37973 this.fireEvent("regionexpanded", region);
37977 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37978 * performs box-model adjustments.
37979 * @return {Object} The size as an object {width: (the width), height: (the height)}
37981 getViewSize : function()
37984 if(this.el.dom != document.body){
37985 size = this.el.getSize();
37987 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37989 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37990 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37995 * Returns the Element this layout is bound to.
37996 * @return {Roo.Element}
37998 getEl : function(){
38003 * Returns the specified region.
38004 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38005 * @return {Roo.LayoutRegion}
38007 getRegion : function(target){
38008 return this.regions[target.toLowerCase()];
38011 onWindowResize : function(){
38012 if(this.monitorWindowResize){
38019 * Ext JS Library 1.1.1
38020 * Copyright(c) 2006-2007, Ext JS, LLC.
38022 * Originally Released Under LGPL - original licence link has changed is not relivant.
38025 * <script type="text/javascript">
38028 * @class Roo.bootstrap.layout.Border
38029 * @extends Roo.bootstrap.layout.Manager
38030 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38031 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38032 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38033 * please see: examples/bootstrap/nested.html<br><br>
38035 <b>The container the layout is rendered into can be either the body element or any other element.
38036 If it is not the body element, the container needs to either be an absolute positioned element,
38037 or you will need to add "position:relative" to the css of the container. You will also need to specify
38038 the container size if it is not the body element.</b>
38041 * Create a new Border
38042 * @param {Object} config Configuration options
38044 Roo.bootstrap.layout.Border = function(config){
38045 config = config || {};
38046 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38050 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38051 if(config[region]){
38052 config[region].region = region;
38053 this.addRegion(config[region]);
38059 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38061 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38064 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38067 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38070 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38073 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38076 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38082 parent : false, // this might point to a 'nest' or a ???
38085 * Creates and adds a new region if it doesn't already exist.
38086 * @param {String} target The target region key (north, south, east, west or center).
38087 * @param {Object} config The regions config object
38088 * @return {BorderLayoutRegion} The new region
38090 addRegion : function(config)
38092 if(!this.regions[config.region]){
38093 var r = this.factory(config);
38094 this.bindRegion(r);
38096 return this.regions[config.region];
38100 bindRegion : function(r){
38101 this.regions[r.config.region] = r;
38103 r.on("visibilitychange", this.layout, this);
38104 r.on("paneladded", this.layout, this);
38105 r.on("panelremoved", this.layout, this);
38106 r.on("invalidated", this.layout, this);
38107 r.on("resized", this.onRegionResized, this);
38108 r.on("collapsed", this.onRegionCollapsed, this);
38109 r.on("expanded", this.onRegionExpanded, this);
38113 * Performs a layout update.
38115 layout : function()
38117 if(this.updating) {
38121 // render all the rebions if they have not been done alreayd?
38122 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38123 if(this.regions[region] && !this.regions[region].bodyEl){
38124 this.regions[region].onRender(this.el)
38128 var size = this.getViewSize();
38129 var w = size.width;
38130 var h = size.height;
38135 //var x = 0, y = 0;
38137 var rs = this.regions;
38138 var north = rs["north"];
38139 var south = rs["south"];
38140 var west = rs["west"];
38141 var east = rs["east"];
38142 var center = rs["center"];
38143 //if(this.hideOnLayout){ // not supported anymore
38144 //c.el.setStyle("display", "none");
38146 if(north && north.isVisible()){
38147 var b = north.getBox();
38148 var m = north.getMargins();
38149 b.width = w - (m.left+m.right);
38152 centerY = b.height + b.y + m.bottom;
38153 centerH -= centerY;
38154 north.updateBox(this.safeBox(b));
38156 if(south && south.isVisible()){
38157 var b = south.getBox();
38158 var m = south.getMargins();
38159 b.width = w - (m.left+m.right);
38161 var totalHeight = (b.height + m.top + m.bottom);
38162 b.y = h - totalHeight + m.top;
38163 centerH -= totalHeight;
38164 south.updateBox(this.safeBox(b));
38166 if(west && west.isVisible()){
38167 var b = west.getBox();
38168 var m = west.getMargins();
38169 b.height = centerH - (m.top+m.bottom);
38171 b.y = centerY + m.top;
38172 var totalWidth = (b.width + m.left + m.right);
38173 centerX += totalWidth;
38174 centerW -= totalWidth;
38175 west.updateBox(this.safeBox(b));
38177 if(east && east.isVisible()){
38178 var b = east.getBox();
38179 var m = east.getMargins();
38180 b.height = centerH - (m.top+m.bottom);
38181 var totalWidth = (b.width + m.left + m.right);
38182 b.x = w - totalWidth + m.left;
38183 b.y = centerY + m.top;
38184 centerW -= totalWidth;
38185 east.updateBox(this.safeBox(b));
38188 var m = center.getMargins();
38190 x: centerX + m.left,
38191 y: centerY + m.top,
38192 width: centerW - (m.left+m.right),
38193 height: centerH - (m.top+m.bottom)
38195 //if(this.hideOnLayout){
38196 //center.el.setStyle("display", "block");
38198 center.updateBox(this.safeBox(centerBox));
38201 this.fireEvent("layout", this);
38205 safeBox : function(box){
38206 box.width = Math.max(0, box.width);
38207 box.height = Math.max(0, box.height);
38212 * Adds a ContentPanel (or subclass) to this layout.
38213 * @param {String} target The target region key (north, south, east, west or center).
38214 * @param {Roo.ContentPanel} panel The panel to add
38215 * @return {Roo.ContentPanel} The added panel
38217 add : function(target, panel){
38219 target = target.toLowerCase();
38220 return this.regions[target].add(panel);
38224 * Remove a ContentPanel (or subclass) to this layout.
38225 * @param {String} target The target region key (north, south, east, west or center).
38226 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38227 * @return {Roo.ContentPanel} The removed panel
38229 remove : function(target, panel){
38230 target = target.toLowerCase();
38231 return this.regions[target].remove(panel);
38235 * Searches all regions for a panel with the specified id
38236 * @param {String} panelId
38237 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38239 findPanel : function(panelId){
38240 var rs = this.regions;
38241 for(var target in rs){
38242 if(typeof rs[target] != "function"){
38243 var p = rs[target].getPanel(panelId);
38253 * Searches all regions for a panel with the specified id and activates (shows) it.
38254 * @param {String/ContentPanel} panelId The panels id or the panel itself
38255 * @return {Roo.ContentPanel} The shown panel or null
38257 showPanel : function(panelId) {
38258 var rs = this.regions;
38259 for(var target in rs){
38260 var r = rs[target];
38261 if(typeof r != "function"){
38262 if(r.hasPanel(panelId)){
38263 return r.showPanel(panelId);
38271 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38272 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38275 restoreState : function(provider){
38277 provider = Roo.state.Manager;
38279 var sm = new Roo.LayoutStateManager();
38280 sm.init(this, provider);
38286 * Adds a xtype elements to the layout.
38290 xtype : 'ContentPanel',
38297 xtype : 'NestedLayoutPanel',
38303 items : [ ... list of content panels or nested layout panels.. ]
38307 * @param {Object} cfg Xtype definition of item to add.
38309 addxtype : function(cfg)
38311 // basically accepts a pannel...
38312 // can accept a layout region..!?!?
38313 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38316 // theory? children can only be panels??
38318 //if (!cfg.xtype.match(/Panel$/)) {
38323 if (typeof(cfg.region) == 'undefined') {
38324 Roo.log("Failed to add Panel, region was not set");
38328 var region = cfg.region;
38334 xitems = cfg.items;
38339 if ( region == 'center') {
38340 Roo.log("Center: " + cfg.title);
38346 case 'Content': // ContentPanel (el, cfg)
38347 case 'Scroll': // ContentPanel (el, cfg)
38349 cfg.autoCreate = cfg.autoCreate || true;
38350 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38352 // var el = this.el.createChild();
38353 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38356 this.add(region, ret);
38360 case 'TreePanel': // our new panel!
38361 cfg.el = this.el.createChild();
38362 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38363 this.add(region, ret);
38368 // create a new Layout (which is a Border Layout...
38370 var clayout = cfg.layout;
38371 clayout.el = this.el.createChild();
38372 clayout.items = clayout.items || [];
38376 // replace this exitems with the clayout ones..
38377 xitems = clayout.items;
38379 // force background off if it's in center...
38380 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38381 cfg.background = false;
38383 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38386 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38387 //console.log('adding nested layout panel ' + cfg.toSource());
38388 this.add(region, ret);
38389 nb = {}; /// find first...
38394 // needs grid and region
38396 //var el = this.getRegion(region).el.createChild();
38398 *var el = this.el.createChild();
38399 // create the grid first...
38400 cfg.grid.container = el;
38401 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38404 if (region == 'center' && this.active ) {
38405 cfg.background = false;
38408 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38410 this.add(region, ret);
38412 if (cfg.background) {
38413 // render grid on panel activation (if panel background)
38414 ret.on('activate', function(gp) {
38415 if (!gp.grid.rendered) {
38416 // gp.grid.render(el);
38420 // cfg.grid.render(el);
38426 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38427 // it was the old xcomponent building that caused this before.
38428 // espeically if border is the top element in the tree.
38438 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38440 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38441 this.add(region, ret);
38445 throw "Can not add '" + cfg.xtype + "' to Border";
38451 this.beginUpdate();
38455 Roo.each(xitems, function(i) {
38456 region = nb && i.region ? i.region : false;
38458 var add = ret.addxtype(i);
38461 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38462 if (!i.background) {
38463 abn[region] = nb[region] ;
38470 // make the last non-background panel active..
38471 //if (nb) { Roo.log(abn); }
38474 for(var r in abn) {
38475 region = this.getRegion(r);
38477 // tried using nb[r], but it does not work..
38479 region.showPanel(abn[r]);
38490 factory : function(cfg)
38493 var validRegions = Roo.bootstrap.layout.Border.regions;
38495 var target = cfg.region;
38498 var r = Roo.bootstrap.layout;
38502 return new r.North(cfg);
38504 return new r.South(cfg);
38506 return new r.East(cfg);
38508 return new r.West(cfg);
38510 return new r.Center(cfg);
38512 throw 'Layout region "'+target+'" not supported.';
38519 * Ext JS Library 1.1.1
38520 * Copyright(c) 2006-2007, Ext JS, LLC.
38522 * Originally Released Under LGPL - original licence link has changed is not relivant.
38525 * <script type="text/javascript">
38529 * @class Roo.bootstrap.layout.Basic
38530 * @extends Roo.util.Observable
38531 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38532 * and does not have a titlebar, tabs or any other features. All it does is size and position
38533 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38534 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38535 * @cfg {string} region the region that it inhabits..
38536 * @cfg {bool} skipConfig skip config?
38540 Roo.bootstrap.layout.Basic = function(config){
38542 this.mgr = config.mgr;
38544 this.position = config.region;
38546 var skipConfig = config.skipConfig;
38550 * @scope Roo.BasicLayoutRegion
38554 * @event beforeremove
38555 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38556 * @param {Roo.LayoutRegion} this
38557 * @param {Roo.ContentPanel} panel The panel
38558 * @param {Object} e The cancel event object
38560 "beforeremove" : true,
38562 * @event invalidated
38563 * Fires when the layout for this region is changed.
38564 * @param {Roo.LayoutRegion} this
38566 "invalidated" : true,
38568 * @event visibilitychange
38569 * Fires when this region is shown or hidden
38570 * @param {Roo.LayoutRegion} this
38571 * @param {Boolean} visibility true or false
38573 "visibilitychange" : true,
38575 * @event paneladded
38576 * Fires when a panel is added.
38577 * @param {Roo.LayoutRegion} this
38578 * @param {Roo.ContentPanel} panel The panel
38580 "paneladded" : true,
38582 * @event panelremoved
38583 * Fires when a panel is removed.
38584 * @param {Roo.LayoutRegion} this
38585 * @param {Roo.ContentPanel} panel The panel
38587 "panelremoved" : true,
38589 * @event beforecollapse
38590 * Fires when this region before collapse.
38591 * @param {Roo.LayoutRegion} this
38593 "beforecollapse" : true,
38596 * Fires when this region is collapsed.
38597 * @param {Roo.LayoutRegion} this
38599 "collapsed" : true,
38602 * Fires when this region is expanded.
38603 * @param {Roo.LayoutRegion} this
38608 * Fires when this region is slid into view.
38609 * @param {Roo.LayoutRegion} this
38611 "slideshow" : true,
38614 * Fires when this region slides out of view.
38615 * @param {Roo.LayoutRegion} this
38617 "slidehide" : true,
38619 * @event panelactivated
38620 * Fires when a panel is activated.
38621 * @param {Roo.LayoutRegion} this
38622 * @param {Roo.ContentPanel} panel The activated panel
38624 "panelactivated" : true,
38627 * Fires when the user resizes this region.
38628 * @param {Roo.LayoutRegion} this
38629 * @param {Number} newSize The new size (width for east/west, height for north/south)
38633 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38634 this.panels = new Roo.util.MixedCollection();
38635 this.panels.getKey = this.getPanelId.createDelegate(this);
38637 this.activePanel = null;
38638 // ensure listeners are added...
38640 if (config.listeners || config.events) {
38641 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38642 listeners : config.listeners || {},
38643 events : config.events || {}
38647 if(skipConfig !== true){
38648 this.applyConfig(config);
38652 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38654 getPanelId : function(p){
38658 applyConfig : function(config){
38659 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38660 this.config = config;
38665 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38666 * the width, for horizontal (north, south) the height.
38667 * @param {Number} newSize The new width or height
38669 resizeTo : function(newSize){
38670 var el = this.el ? this.el :
38671 (this.activePanel ? this.activePanel.getEl() : null);
38673 switch(this.position){
38676 el.setWidth(newSize);
38677 this.fireEvent("resized", this, newSize);
38681 el.setHeight(newSize);
38682 this.fireEvent("resized", this, newSize);
38688 getBox : function(){
38689 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38692 getMargins : function(){
38693 return this.margins;
38696 updateBox : function(box){
38698 var el = this.activePanel.getEl();
38699 el.dom.style.left = box.x + "px";
38700 el.dom.style.top = box.y + "px";
38701 this.activePanel.setSize(box.width, box.height);
38705 * Returns the container element for this region.
38706 * @return {Roo.Element}
38708 getEl : function(){
38709 return this.activePanel;
38713 * Returns true if this region is currently visible.
38714 * @return {Boolean}
38716 isVisible : function(){
38717 return this.activePanel ? true : false;
38720 setActivePanel : function(panel){
38721 panel = this.getPanel(panel);
38722 if(this.activePanel && this.activePanel != panel){
38723 this.activePanel.setActiveState(false);
38724 this.activePanel.getEl().setLeftTop(-10000,-10000);
38726 this.activePanel = panel;
38727 panel.setActiveState(true);
38729 panel.setSize(this.box.width, this.box.height);
38731 this.fireEvent("panelactivated", this, panel);
38732 this.fireEvent("invalidated");
38736 * Show the specified panel.
38737 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38738 * @return {Roo.ContentPanel} The shown panel or null
38740 showPanel : function(panel){
38741 panel = this.getPanel(panel);
38743 this.setActivePanel(panel);
38749 * Get the active panel for this region.
38750 * @return {Roo.ContentPanel} The active panel or null
38752 getActivePanel : function(){
38753 return this.activePanel;
38757 * Add the passed ContentPanel(s)
38758 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38759 * @return {Roo.ContentPanel} The panel added (if only one was added)
38761 add : function(panel){
38762 if(arguments.length > 1){
38763 for(var i = 0, len = arguments.length; i < len; i++) {
38764 this.add(arguments[i]);
38768 if(this.hasPanel(panel)){
38769 this.showPanel(panel);
38772 var el = panel.getEl();
38773 if(el.dom.parentNode != this.mgr.el.dom){
38774 this.mgr.el.dom.appendChild(el.dom);
38776 if(panel.setRegion){
38777 panel.setRegion(this);
38779 this.panels.add(panel);
38780 el.setStyle("position", "absolute");
38781 if(!panel.background){
38782 this.setActivePanel(panel);
38783 if(this.config.initialSize && this.panels.getCount()==1){
38784 this.resizeTo(this.config.initialSize);
38787 this.fireEvent("paneladded", this, panel);
38792 * Returns true if the panel is in this region.
38793 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38794 * @return {Boolean}
38796 hasPanel : function(panel){
38797 if(typeof panel == "object"){ // must be panel obj
38798 panel = panel.getId();
38800 return this.getPanel(panel) ? true : false;
38804 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38805 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38806 * @param {Boolean} preservePanel Overrides the config preservePanel option
38807 * @return {Roo.ContentPanel} The panel that was removed
38809 remove : function(panel, preservePanel){
38810 panel = this.getPanel(panel);
38815 this.fireEvent("beforeremove", this, panel, e);
38816 if(e.cancel === true){
38819 var panelId = panel.getId();
38820 this.panels.removeKey(panelId);
38825 * Returns the panel specified or null if it's not in this region.
38826 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38827 * @return {Roo.ContentPanel}
38829 getPanel : function(id){
38830 if(typeof id == "object"){ // must be panel obj
38833 return this.panels.get(id);
38837 * Returns this regions position (north/south/east/west/center).
38840 getPosition: function(){
38841 return this.position;
38845 * Ext JS Library 1.1.1
38846 * Copyright(c) 2006-2007, Ext JS, LLC.
38848 * Originally Released Under LGPL - original licence link has changed is not relivant.
38851 * <script type="text/javascript">
38855 * @class Roo.bootstrap.layout.Region
38856 * @extends Roo.bootstrap.layout.Basic
38857 * This class represents a region in a layout manager.
38859 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38860 * @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})
38861 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38862 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38863 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38864 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38865 * @cfg {String} title The title for the region (overrides panel titles)
38866 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38867 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38868 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38869 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38870 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38871 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38872 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38873 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38874 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38875 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38877 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38878 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38879 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38880 * @cfg {Number} width For East/West panels
38881 * @cfg {Number} height For North/South panels
38882 * @cfg {Boolean} split To show the splitter
38883 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38885 * @cfg {string} cls Extra CSS classes to add to region
38887 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38888 * @cfg {string} region the region that it inhabits..
38891 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38892 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38894 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38895 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38896 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38898 Roo.bootstrap.layout.Region = function(config)
38900 this.applyConfig(config);
38902 var mgr = config.mgr;
38903 var pos = config.region;
38904 config.skipConfig = true;
38905 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38908 this.onRender(mgr.el);
38911 this.visible = true;
38912 this.collapsed = false;
38913 this.unrendered_panels = [];
38916 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38918 position: '', // set by wrapper (eg. north/south etc..)
38919 unrendered_panels : null, // unrendered panels.
38921 tabPosition : false,
38923 mgr: false, // points to 'Border'
38926 createBody : function(){
38927 /** This region's body element
38928 * @type Roo.Element */
38929 this.bodyEl = this.el.createChild({
38931 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38935 onRender: function(ctr, pos)
38937 var dh = Roo.DomHelper;
38938 /** This region's container element
38939 * @type Roo.Element */
38940 this.el = dh.append(ctr.dom, {
38942 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38944 /** This region's title element
38945 * @type Roo.Element */
38947 this.titleEl = dh.append(this.el.dom, {
38949 unselectable: "on",
38950 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38952 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38953 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38957 this.titleEl.enableDisplayMode();
38958 /** This region's title text element
38959 * @type HTMLElement */
38960 this.titleTextEl = this.titleEl.dom.firstChild;
38961 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38963 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38964 this.closeBtn.enableDisplayMode();
38965 this.closeBtn.on("click", this.closeClicked, this);
38966 this.closeBtn.hide();
38968 this.createBody(this.config);
38969 if(this.config.hideWhenEmpty){
38971 this.on("paneladded", this.validateVisibility, this);
38972 this.on("panelremoved", this.validateVisibility, this);
38974 if(this.autoScroll){
38975 this.bodyEl.setStyle("overflow", "auto");
38977 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38979 //if(c.titlebar !== false){
38980 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38981 this.titleEl.hide();
38983 this.titleEl.show();
38984 if(this.config.title){
38985 this.titleTextEl.innerHTML = this.config.title;
38989 if(this.config.collapsed){
38990 this.collapse(true);
38992 if(this.config.hidden){
38996 if (this.unrendered_panels && this.unrendered_panels.length) {
38997 for (var i =0;i< this.unrendered_panels.length; i++) {
38998 this.add(this.unrendered_panels[i]);
39000 this.unrendered_panels = null;
39006 applyConfig : function(c)
39009 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39010 var dh = Roo.DomHelper;
39011 if(c.titlebar !== false){
39012 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39013 this.collapseBtn.on("click", this.collapse, this);
39014 this.collapseBtn.enableDisplayMode();
39016 if(c.showPin === true || this.showPin){
39017 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39018 this.stickBtn.enableDisplayMode();
39019 this.stickBtn.on("click", this.expand, this);
39020 this.stickBtn.hide();
39025 /** This region's collapsed element
39026 * @type Roo.Element */
39029 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39030 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39033 if(c.floatable !== false){
39034 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39035 this.collapsedEl.on("click", this.collapseClick, this);
39038 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39039 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39040 id: "message", unselectable: "on", style:{"float":"left"}});
39041 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39043 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39044 this.expandBtn.on("click", this.expand, this);
39048 if(this.collapseBtn){
39049 this.collapseBtn.setVisible(c.collapsible == true);
39052 this.cmargins = c.cmargins || this.cmargins ||
39053 (this.position == "west" || this.position == "east" ?
39054 {top: 0, left: 2, right:2, bottom: 0} :
39055 {top: 2, left: 0, right:0, bottom: 2});
39057 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39060 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39062 this.autoScroll = c.autoScroll || false;
39067 this.duration = c.duration || .30;
39068 this.slideDuration = c.slideDuration || .45;
39073 * Returns true if this region is currently visible.
39074 * @return {Boolean}
39076 isVisible : function(){
39077 return this.visible;
39081 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39082 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39084 //setCollapsedTitle : function(title){
39085 // title = title || " ";
39086 // if(this.collapsedTitleTextEl){
39087 // this.collapsedTitleTextEl.innerHTML = title;
39091 getBox : function(){
39093 // if(!this.collapsed){
39094 b = this.el.getBox(false, true);
39096 // b = this.collapsedEl.getBox(false, true);
39101 getMargins : function(){
39102 return this.margins;
39103 //return this.collapsed ? this.cmargins : this.margins;
39106 highlight : function(){
39107 this.el.addClass("x-layout-panel-dragover");
39110 unhighlight : function(){
39111 this.el.removeClass("x-layout-panel-dragover");
39114 updateBox : function(box)
39116 if (!this.bodyEl) {
39117 return; // not rendered yet..
39121 if(!this.collapsed){
39122 this.el.dom.style.left = box.x + "px";
39123 this.el.dom.style.top = box.y + "px";
39124 this.updateBody(box.width, box.height);
39126 this.collapsedEl.dom.style.left = box.x + "px";
39127 this.collapsedEl.dom.style.top = box.y + "px";
39128 this.collapsedEl.setSize(box.width, box.height);
39131 this.tabs.autoSizeTabs();
39135 updateBody : function(w, h)
39138 this.el.setWidth(w);
39139 w -= this.el.getBorderWidth("rl");
39140 if(this.config.adjustments){
39141 w += this.config.adjustments[0];
39144 if(h !== null && h > 0){
39145 this.el.setHeight(h);
39146 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39147 h -= this.el.getBorderWidth("tb");
39148 if(this.config.adjustments){
39149 h += this.config.adjustments[1];
39151 this.bodyEl.setHeight(h);
39153 h = this.tabs.syncHeight(h);
39156 if(this.panelSize){
39157 w = w !== null ? w : this.panelSize.width;
39158 h = h !== null ? h : this.panelSize.height;
39160 if(this.activePanel){
39161 var el = this.activePanel.getEl();
39162 w = w !== null ? w : el.getWidth();
39163 h = h !== null ? h : el.getHeight();
39164 this.panelSize = {width: w, height: h};
39165 this.activePanel.setSize(w, h);
39167 if(Roo.isIE && this.tabs){
39168 this.tabs.el.repaint();
39173 * Returns the container element for this region.
39174 * @return {Roo.Element}
39176 getEl : function(){
39181 * Hides this region.
39184 //if(!this.collapsed){
39185 this.el.dom.style.left = "-2000px";
39188 // this.collapsedEl.dom.style.left = "-2000px";
39189 // this.collapsedEl.hide();
39191 this.visible = false;
39192 this.fireEvent("visibilitychange", this, false);
39196 * Shows this region if it was previously hidden.
39199 //if(!this.collapsed){
39202 // this.collapsedEl.show();
39204 this.visible = true;
39205 this.fireEvent("visibilitychange", this, true);
39208 closeClicked : function(){
39209 if(this.activePanel){
39210 this.remove(this.activePanel);
39214 collapseClick : function(e){
39216 e.stopPropagation();
39219 e.stopPropagation();
39225 * Collapses this region.
39226 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39229 collapse : function(skipAnim, skipCheck = false){
39230 if(this.collapsed) {
39234 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39236 this.collapsed = true;
39238 this.split.el.hide();
39240 if(this.config.animate && skipAnim !== true){
39241 this.fireEvent("invalidated", this);
39242 this.animateCollapse();
39244 this.el.setLocation(-20000,-20000);
39246 this.collapsedEl.show();
39247 this.fireEvent("collapsed", this);
39248 this.fireEvent("invalidated", this);
39254 animateCollapse : function(){
39259 * Expands this region if it was previously collapsed.
39260 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39261 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39264 expand : function(e, skipAnim){
39266 e.stopPropagation();
39268 if(!this.collapsed || this.el.hasActiveFx()) {
39272 this.afterSlideIn();
39275 this.collapsed = false;
39276 if(this.config.animate && skipAnim !== true){
39277 this.animateExpand();
39281 this.split.el.show();
39283 this.collapsedEl.setLocation(-2000,-2000);
39284 this.collapsedEl.hide();
39285 this.fireEvent("invalidated", this);
39286 this.fireEvent("expanded", this);
39290 animateExpand : function(){
39294 initTabs : function()
39296 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39298 var ts = new Roo.bootstrap.panel.Tabs({
39299 el: this.bodyEl.dom,
39301 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39302 disableTooltips: this.config.disableTabTips,
39303 toolbar : this.config.toolbar
39306 if(this.config.hideTabs){
39307 ts.stripWrap.setDisplayed(false);
39310 ts.resizeTabs = this.config.resizeTabs === true;
39311 ts.minTabWidth = this.config.minTabWidth || 40;
39312 ts.maxTabWidth = this.config.maxTabWidth || 250;
39313 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39314 ts.monitorResize = false;
39315 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39316 ts.bodyEl.addClass('roo-layout-tabs-body');
39317 this.panels.each(this.initPanelAsTab, this);
39320 initPanelAsTab : function(panel){
39321 var ti = this.tabs.addTab(
39325 this.config.closeOnTab && panel.isClosable(),
39328 if(panel.tabTip !== undefined){
39329 ti.setTooltip(panel.tabTip);
39331 ti.on("activate", function(){
39332 this.setActivePanel(panel);
39335 if(this.config.closeOnTab){
39336 ti.on("beforeclose", function(t, e){
39338 this.remove(panel);
39342 panel.tabItem = ti;
39347 updatePanelTitle : function(panel, title)
39349 if(this.activePanel == panel){
39350 this.updateTitle(title);
39353 var ti = this.tabs.getTab(panel.getEl().id);
39355 if(panel.tabTip !== undefined){
39356 ti.setTooltip(panel.tabTip);
39361 updateTitle : function(title){
39362 if(this.titleTextEl && !this.config.title){
39363 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39367 setActivePanel : function(panel)
39369 panel = this.getPanel(panel);
39370 if(this.activePanel && this.activePanel != panel){
39371 if(this.activePanel.setActiveState(false) === false){
39375 this.activePanel = panel;
39376 panel.setActiveState(true);
39377 if(this.panelSize){
39378 panel.setSize(this.panelSize.width, this.panelSize.height);
39381 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39383 this.updateTitle(panel.getTitle());
39385 this.fireEvent("invalidated", this);
39387 this.fireEvent("panelactivated", this, panel);
39391 * Shows the specified panel.
39392 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39393 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39395 showPanel : function(panel)
39397 panel = this.getPanel(panel);
39400 var tab = this.tabs.getTab(panel.getEl().id);
39401 if(tab.isHidden()){
39402 this.tabs.unhideTab(tab.id);
39406 this.setActivePanel(panel);
39413 * Get the active panel for this region.
39414 * @return {Roo.ContentPanel} The active panel or null
39416 getActivePanel : function(){
39417 return this.activePanel;
39420 validateVisibility : function(){
39421 if(this.panels.getCount() < 1){
39422 this.updateTitle(" ");
39423 this.closeBtn.hide();
39426 if(!this.isVisible()){
39433 * Adds the passed ContentPanel(s) to this region.
39434 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39435 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39437 add : function(panel)
39439 if(arguments.length > 1){
39440 for(var i = 0, len = arguments.length; i < len; i++) {
39441 this.add(arguments[i]);
39446 // if we have not been rendered yet, then we can not really do much of this..
39447 if (!this.bodyEl) {
39448 this.unrendered_panels.push(panel);
39455 if(this.hasPanel(panel)){
39456 this.showPanel(panel);
39459 panel.setRegion(this);
39460 this.panels.add(panel);
39461 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39462 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39463 // and hide them... ???
39464 this.bodyEl.dom.appendChild(panel.getEl().dom);
39465 if(panel.background !== true){
39466 this.setActivePanel(panel);
39468 this.fireEvent("paneladded", this, panel);
39475 this.initPanelAsTab(panel);
39479 if(panel.background !== true){
39480 this.tabs.activate(panel.getEl().id);
39482 this.fireEvent("paneladded", this, panel);
39487 * Hides the tab for the specified panel.
39488 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39490 hidePanel : function(panel){
39491 if(this.tabs && (panel = this.getPanel(panel))){
39492 this.tabs.hideTab(panel.getEl().id);
39497 * Unhides the tab for a previously hidden panel.
39498 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39500 unhidePanel : function(panel){
39501 if(this.tabs && (panel = this.getPanel(panel))){
39502 this.tabs.unhideTab(panel.getEl().id);
39506 clearPanels : function(){
39507 while(this.panels.getCount() > 0){
39508 this.remove(this.panels.first());
39513 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39514 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39515 * @param {Boolean} preservePanel Overrides the config preservePanel option
39516 * @return {Roo.ContentPanel} The panel that was removed
39518 remove : function(panel, preservePanel)
39520 panel = this.getPanel(panel);
39525 this.fireEvent("beforeremove", this, panel, e);
39526 if(e.cancel === true){
39529 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39530 var panelId = panel.getId();
39531 this.panels.removeKey(panelId);
39533 document.body.appendChild(panel.getEl().dom);
39536 this.tabs.removeTab(panel.getEl().id);
39537 }else if (!preservePanel){
39538 this.bodyEl.dom.removeChild(panel.getEl().dom);
39540 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39541 var p = this.panels.first();
39542 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39543 tempEl.appendChild(p.getEl().dom);
39544 this.bodyEl.update("");
39545 this.bodyEl.dom.appendChild(p.getEl().dom);
39547 this.updateTitle(p.getTitle());
39549 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39550 this.setActivePanel(p);
39552 panel.setRegion(null);
39553 if(this.activePanel == panel){
39554 this.activePanel = null;
39556 if(this.config.autoDestroy !== false && preservePanel !== true){
39557 try{panel.destroy();}catch(e){}
39559 this.fireEvent("panelremoved", this, panel);
39564 * Returns the TabPanel component used by this region
39565 * @return {Roo.TabPanel}
39567 getTabs : function(){
39571 createTool : function(parentEl, className){
39572 var btn = Roo.DomHelper.append(parentEl, {
39574 cls: "x-layout-tools-button",
39577 cls: "roo-layout-tools-button-inner " + className,
39581 btn.addClassOnOver("roo-layout-tools-button-over");
39586 * Ext JS Library 1.1.1
39587 * Copyright(c) 2006-2007, Ext JS, LLC.
39589 * Originally Released Under LGPL - original licence link has changed is not relivant.
39592 * <script type="text/javascript">
39598 * @class Roo.SplitLayoutRegion
39599 * @extends Roo.LayoutRegion
39600 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39602 Roo.bootstrap.layout.Split = function(config){
39603 this.cursor = config.cursor;
39604 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39607 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39609 splitTip : "Drag to resize.",
39610 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39611 useSplitTips : false,
39613 applyConfig : function(config){
39614 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39617 onRender : function(ctr,pos) {
39619 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39620 if(!this.config.split){
39625 var splitEl = Roo.DomHelper.append(ctr.dom, {
39627 id: this.el.id + "-split",
39628 cls: "roo-layout-split roo-layout-split-"+this.position,
39631 /** The SplitBar for this region
39632 * @type Roo.SplitBar */
39633 // does not exist yet...
39634 Roo.log([this.position, this.orientation]);
39636 this.split = new Roo.bootstrap.SplitBar({
39637 dragElement : splitEl,
39638 resizingElement: this.el,
39639 orientation : this.orientation
39642 this.split.on("moved", this.onSplitMove, this);
39643 this.split.useShim = this.config.useShim === true;
39644 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39645 if(this.useSplitTips){
39646 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39648 //if(config.collapsible){
39649 // this.split.el.on("dblclick", this.collapse, this);
39652 if(typeof this.config.minSize != "undefined"){
39653 this.split.minSize = this.config.minSize;
39655 if(typeof this.config.maxSize != "undefined"){
39656 this.split.maxSize = this.config.maxSize;
39658 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39659 this.hideSplitter();
39664 getHMaxSize : function(){
39665 var cmax = this.config.maxSize || 10000;
39666 var center = this.mgr.getRegion("center");
39667 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39670 getVMaxSize : function(){
39671 var cmax = this.config.maxSize || 10000;
39672 var center = this.mgr.getRegion("center");
39673 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39676 onSplitMove : function(split, newSize){
39677 this.fireEvent("resized", this, newSize);
39681 * Returns the {@link Roo.SplitBar} for this region.
39682 * @return {Roo.SplitBar}
39684 getSplitBar : function(){
39689 this.hideSplitter();
39690 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39693 hideSplitter : function(){
39695 this.split.el.setLocation(-2000,-2000);
39696 this.split.el.hide();
39702 this.split.el.show();
39704 Roo.bootstrap.layout.Split.superclass.show.call(this);
39707 beforeSlide: function(){
39708 if(Roo.isGecko){// firefox overflow auto bug workaround
39709 this.bodyEl.clip();
39711 this.tabs.bodyEl.clip();
39713 if(this.activePanel){
39714 this.activePanel.getEl().clip();
39716 if(this.activePanel.beforeSlide){
39717 this.activePanel.beforeSlide();
39723 afterSlide : function(){
39724 if(Roo.isGecko){// firefox overflow auto bug workaround
39725 this.bodyEl.unclip();
39727 this.tabs.bodyEl.unclip();
39729 if(this.activePanel){
39730 this.activePanel.getEl().unclip();
39731 if(this.activePanel.afterSlide){
39732 this.activePanel.afterSlide();
39738 initAutoHide : function(){
39739 if(this.autoHide !== false){
39740 if(!this.autoHideHd){
39741 var st = new Roo.util.DelayedTask(this.slideIn, this);
39742 this.autoHideHd = {
39743 "mouseout": function(e){
39744 if(!e.within(this.el, true)){
39748 "mouseover" : function(e){
39754 this.el.on(this.autoHideHd);
39758 clearAutoHide : function(){
39759 if(this.autoHide !== false){
39760 this.el.un("mouseout", this.autoHideHd.mouseout);
39761 this.el.un("mouseover", this.autoHideHd.mouseover);
39765 clearMonitor : function(){
39766 Roo.get(document).un("click", this.slideInIf, this);
39769 // these names are backwards but not changed for compat
39770 slideOut : function(){
39771 if(this.isSlid || this.el.hasActiveFx()){
39774 this.isSlid = true;
39775 if(this.collapseBtn){
39776 this.collapseBtn.hide();
39778 this.closeBtnState = this.closeBtn.getStyle('display');
39779 this.closeBtn.hide();
39781 this.stickBtn.show();
39784 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39785 this.beforeSlide();
39786 this.el.setStyle("z-index", 10001);
39787 this.el.slideIn(this.getSlideAnchor(), {
39788 callback: function(){
39790 this.initAutoHide();
39791 Roo.get(document).on("click", this.slideInIf, this);
39792 this.fireEvent("slideshow", this);
39799 afterSlideIn : function(){
39800 this.clearAutoHide();
39801 this.isSlid = false;
39802 this.clearMonitor();
39803 this.el.setStyle("z-index", "");
39804 if(this.collapseBtn){
39805 this.collapseBtn.show();
39807 this.closeBtn.setStyle('display', this.closeBtnState);
39809 this.stickBtn.hide();
39811 this.fireEvent("slidehide", this);
39814 slideIn : function(cb){
39815 if(!this.isSlid || this.el.hasActiveFx()){
39819 this.isSlid = false;
39820 this.beforeSlide();
39821 this.el.slideOut(this.getSlideAnchor(), {
39822 callback: function(){
39823 this.el.setLeftTop(-10000, -10000);
39825 this.afterSlideIn();
39833 slideInIf : function(e){
39834 if(!e.within(this.el)){
39839 animateCollapse : function(){
39840 this.beforeSlide();
39841 this.el.setStyle("z-index", 20000);
39842 var anchor = this.getSlideAnchor();
39843 this.el.slideOut(anchor, {
39844 callback : function(){
39845 this.el.setStyle("z-index", "");
39846 this.collapsedEl.slideIn(anchor, {duration:.3});
39848 this.el.setLocation(-10000,-10000);
39850 this.fireEvent("collapsed", this);
39857 animateExpand : function(){
39858 this.beforeSlide();
39859 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39860 this.el.setStyle("z-index", 20000);
39861 this.collapsedEl.hide({
39864 this.el.slideIn(this.getSlideAnchor(), {
39865 callback : function(){
39866 this.el.setStyle("z-index", "");
39869 this.split.el.show();
39871 this.fireEvent("invalidated", this);
39872 this.fireEvent("expanded", this);
39900 getAnchor : function(){
39901 return this.anchors[this.position];
39904 getCollapseAnchor : function(){
39905 return this.canchors[this.position];
39908 getSlideAnchor : function(){
39909 return this.sanchors[this.position];
39912 getAlignAdj : function(){
39913 var cm = this.cmargins;
39914 switch(this.position){
39930 getExpandAdj : function(){
39931 var c = this.collapsedEl, cm = this.cmargins;
39932 switch(this.position){
39934 return [-(cm.right+c.getWidth()+cm.left), 0];
39937 return [cm.right+c.getWidth()+cm.left, 0];
39940 return [0, -(cm.top+cm.bottom+c.getHeight())];
39943 return [0, cm.top+cm.bottom+c.getHeight()];
39949 * Ext JS Library 1.1.1
39950 * Copyright(c) 2006-2007, Ext JS, LLC.
39952 * Originally Released Under LGPL - original licence link has changed is not relivant.
39955 * <script type="text/javascript">
39958 * These classes are private internal classes
39960 Roo.bootstrap.layout.Center = function(config){
39961 config.region = "center";
39962 Roo.bootstrap.layout.Region.call(this, config);
39963 this.visible = true;
39964 this.minWidth = config.minWidth || 20;
39965 this.minHeight = config.minHeight || 20;
39968 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39970 // center panel can't be hidden
39974 // center panel can't be hidden
39977 getMinWidth: function(){
39978 return this.minWidth;
39981 getMinHeight: function(){
39982 return this.minHeight;
39996 Roo.bootstrap.layout.North = function(config)
39998 config.region = 'north';
39999 config.cursor = 'n-resize';
40001 Roo.bootstrap.layout.Split.call(this, config);
40005 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40006 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40007 this.split.el.addClass("roo-layout-split-v");
40009 //var size = config.initialSize || config.height;
40010 //if(this.el && typeof size != "undefined"){
40011 // this.el.setHeight(size);
40014 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40016 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40019 onRender : function(ctr, pos)
40021 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40022 var size = this.config.initialSize || this.config.height;
40023 if(this.el && typeof size != "undefined"){
40024 this.el.setHeight(size);
40029 getBox : function(){
40030 if(this.collapsed){
40031 return this.collapsedEl.getBox();
40033 var box = this.el.getBox();
40035 box.height += this.split.el.getHeight();
40040 updateBox : function(box){
40041 if(this.split && !this.collapsed){
40042 box.height -= this.split.el.getHeight();
40043 this.split.el.setLeft(box.x);
40044 this.split.el.setTop(box.y+box.height);
40045 this.split.el.setWidth(box.width);
40047 if(this.collapsed){
40048 this.updateBody(box.width, null);
40050 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40058 Roo.bootstrap.layout.South = function(config){
40059 config.region = 'south';
40060 config.cursor = 's-resize';
40061 Roo.bootstrap.layout.Split.call(this, config);
40063 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40064 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40065 this.split.el.addClass("roo-layout-split-v");
40070 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40071 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40073 onRender : function(ctr, pos)
40075 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40076 var size = this.config.initialSize || this.config.height;
40077 if(this.el && typeof size != "undefined"){
40078 this.el.setHeight(size);
40083 getBox : function(){
40084 if(this.collapsed){
40085 return this.collapsedEl.getBox();
40087 var box = this.el.getBox();
40089 var sh = this.split.el.getHeight();
40096 updateBox : function(box){
40097 if(this.split && !this.collapsed){
40098 var sh = this.split.el.getHeight();
40101 this.split.el.setLeft(box.x);
40102 this.split.el.setTop(box.y-sh);
40103 this.split.el.setWidth(box.width);
40105 if(this.collapsed){
40106 this.updateBody(box.width, null);
40108 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40112 Roo.bootstrap.layout.East = function(config){
40113 config.region = "east";
40114 config.cursor = "e-resize";
40115 Roo.bootstrap.layout.Split.call(this, config);
40117 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40118 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40119 this.split.el.addClass("roo-layout-split-h");
40123 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40124 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40126 onRender : function(ctr, pos)
40128 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40129 var size = this.config.initialSize || this.config.width;
40130 if(this.el && typeof size != "undefined"){
40131 this.el.setWidth(size);
40136 getBox : function(){
40137 if(this.collapsed){
40138 return this.collapsedEl.getBox();
40140 var box = this.el.getBox();
40142 var sw = this.split.el.getWidth();
40149 updateBox : function(box){
40150 if(this.split && !this.collapsed){
40151 var sw = this.split.el.getWidth();
40153 this.split.el.setLeft(box.x);
40154 this.split.el.setTop(box.y);
40155 this.split.el.setHeight(box.height);
40158 if(this.collapsed){
40159 this.updateBody(null, box.height);
40161 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40165 Roo.bootstrap.layout.West = function(config){
40166 config.region = "west";
40167 config.cursor = "w-resize";
40169 Roo.bootstrap.layout.Split.call(this, config);
40171 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40172 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40173 this.split.el.addClass("roo-layout-split-h");
40177 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40178 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40180 onRender: function(ctr, pos)
40182 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40183 var size = this.config.initialSize || this.config.width;
40184 if(typeof size != "undefined"){
40185 this.el.setWidth(size);
40189 getBox : function(){
40190 if(this.collapsed){
40191 return this.collapsedEl.getBox();
40193 var box = this.el.getBox();
40194 if (box.width == 0) {
40195 box.width = this.config.width; // kludge?
40198 box.width += this.split.el.getWidth();
40203 updateBox : function(box){
40204 if(this.split && !this.collapsed){
40205 var sw = this.split.el.getWidth();
40207 this.split.el.setLeft(box.x+box.width);
40208 this.split.el.setTop(box.y);
40209 this.split.el.setHeight(box.height);
40211 if(this.collapsed){
40212 this.updateBody(null, box.height);
40214 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40218 * Ext JS Library 1.1.1
40219 * Copyright(c) 2006-2007, Ext JS, LLC.
40221 * Originally Released Under LGPL - original licence link has changed is not relivant.
40224 * <script type="text/javascript">
40227 * @class Roo.bootstrap.paenl.Content
40228 * @extends Roo.util.Observable
40229 * @children Roo.bootstrap.Component
40230 * @parent builder Roo.bootstrap.layout.Border
40231 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40232 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40233 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40234 * @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
40235 * @cfg {Boolean} closable True if the panel can be closed/removed
40236 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40237 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40238 * @cfg {Toolbar} toolbar A toolbar for this panel
40239 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40240 * @cfg {String} title The title for this panel
40241 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40242 * @cfg {String} url Calls {@link #setUrl} with this value
40243 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40244 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40245 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40246 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40247 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40248 * @cfg {Boolean} badges render the badges
40249 * @cfg {String} cls extra classes to use
40250 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40253 * Create a new ContentPanel.
40254 * @param {String/Object} config A string to set only the title or a config object
40257 Roo.bootstrap.panel.Content = function( config){
40259 this.tpl = config.tpl || false;
40261 var el = config.el;
40262 var content = config.content;
40264 if(config.autoCreate){ // xtype is available if this is called from factory
40267 this.el = Roo.get(el);
40268 if(!this.el && config && config.autoCreate){
40269 if(typeof config.autoCreate == "object"){
40270 if(!config.autoCreate.id){
40271 config.autoCreate.id = config.id||el;
40273 this.el = Roo.DomHelper.append(document.body,
40274 config.autoCreate, true);
40278 cls: (config.cls || '') +
40279 (config.background ? ' bg-' + config.background : '') +
40280 " roo-layout-inactive-content",
40283 if (config.iframe) {
40287 style : 'border: 0px',
40288 src : 'about:blank'
40294 elcfg.html = config.html;
40298 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40299 if (config.iframe) {
40300 this.iframeEl = this.el.select('iframe',true).first();
40305 this.closable = false;
40306 this.loaded = false;
40307 this.active = false;
40310 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40312 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40314 this.wrapEl = this.el; //this.el.wrap();
40316 if (config.toolbar.items) {
40317 ti = config.toolbar.items ;
40318 delete config.toolbar.items ;
40322 this.toolbar.render(this.wrapEl, 'before');
40323 for(var i =0;i < ti.length;i++) {
40324 // Roo.log(['add child', items[i]]);
40325 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40327 this.toolbar.items = nitems;
40328 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40329 delete config.toolbar;
40333 // xtype created footer. - not sure if will work as we normally have to render first..
40334 if (this.footer && !this.footer.el && this.footer.xtype) {
40335 if (!this.wrapEl) {
40336 this.wrapEl = this.el.wrap();
40339 this.footer.container = this.wrapEl.createChild();
40341 this.footer = Roo.factory(this.footer, Roo);
40346 if(typeof config == "string"){
40347 this.title = config;
40349 Roo.apply(this, config);
40353 this.resizeEl = Roo.get(this.resizeEl, true);
40355 this.resizeEl = this.el;
40357 // handle view.xtype
40365 * Fires when this panel is activated.
40366 * @param {Roo.ContentPanel} this
40370 * @event deactivate
40371 * Fires when this panel is activated.
40372 * @param {Roo.ContentPanel} this
40374 "deactivate" : true,
40378 * Fires when this panel is resized if fitToFrame is true.
40379 * @param {Roo.ContentPanel} this
40380 * @param {Number} width The width after any component adjustments
40381 * @param {Number} height The height after any component adjustments
40387 * Fires when this tab is created
40388 * @param {Roo.ContentPanel} this
40394 * Fires when this content is scrolled
40395 * @param {Roo.ContentPanel} this
40396 * @param {Event} scrollEvent
40407 if(this.autoScroll && !this.iframe){
40408 this.resizeEl.setStyle("overflow", "auto");
40409 this.resizeEl.on('scroll', this.onScroll, this);
40411 // fix randome scrolling
40412 //this.el.on('scroll', function() {
40413 // Roo.log('fix random scolling');
40414 // this.scrollTo('top',0);
40417 content = content || this.content;
40419 this.setContent(content);
40421 if(config && config.url){
40422 this.setUrl(this.url, this.params, this.loadOnce);
40427 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40429 if (this.view && typeof(this.view.xtype) != 'undefined') {
40430 this.view.el = this.el.appendChild(document.createElement("div"));
40431 this.view = Roo.factory(this.view);
40432 this.view.render && this.view.render(false, '');
40436 this.fireEvent('render', this);
40439 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40449 /* Resize Element - use this to work out scroll etc. */
40452 setRegion : function(region){
40453 this.region = region;
40454 this.setActiveClass(region && !this.background);
40458 setActiveClass: function(state)
40461 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40462 this.el.setStyle('position','relative');
40464 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40465 this.el.setStyle('position', 'absolute');
40470 * Returns the toolbar for this Panel if one was configured.
40471 * @return {Roo.Toolbar}
40473 getToolbar : function(){
40474 return this.toolbar;
40477 setActiveState : function(active)
40479 this.active = active;
40480 this.setActiveClass(active);
40482 if(this.fireEvent("deactivate", this) === false){
40487 this.fireEvent("activate", this);
40491 * Updates this panel's element (not for iframe)
40492 * @param {String} content The new content
40493 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40495 setContent : function(content, loadScripts){
40500 this.el.update(content, loadScripts);
40503 ignoreResize : function(w, h)
40505 return false; // always resize?
40506 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40509 this.lastSize = {width: w, height: h};
40514 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40515 * @return {Roo.UpdateManager} The UpdateManager
40517 getUpdateManager : function(){
40521 return this.el.getUpdateManager();
40524 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40525 * Does not work with IFRAME contents
40526 * @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:
40529 url: "your-url.php",
40530 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40531 callback: yourFunction,
40532 scope: yourObject, //(optional scope)
40535 text: "Loading...",
40541 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40542 * 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.
40543 * @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}
40544 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40545 * @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.
40546 * @return {Roo.ContentPanel} this
40554 var um = this.el.getUpdateManager();
40555 um.update.apply(um, arguments);
40561 * 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.
40562 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40563 * @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)
40564 * @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)
40565 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40567 setUrl : function(url, params, loadOnce){
40569 this.iframeEl.dom.src = url;
40573 if(this.refreshDelegate){
40574 this.removeListener("activate", this.refreshDelegate);
40576 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40577 this.on("activate", this.refreshDelegate);
40578 return this.el.getUpdateManager();
40581 _handleRefresh : function(url, params, loadOnce){
40582 if(!loadOnce || !this.loaded){
40583 var updater = this.el.getUpdateManager();
40584 updater.update(url, params, this._setLoaded.createDelegate(this));
40588 _setLoaded : function(){
40589 this.loaded = true;
40593 * Returns this panel's id
40596 getId : function(){
40601 * Returns this panel's element - used by regiosn to add.
40602 * @return {Roo.Element}
40604 getEl : function(){
40605 return this.wrapEl || this.el;
40610 adjustForComponents : function(width, height)
40612 //Roo.log('adjustForComponents ');
40613 if(this.resizeEl != this.el){
40614 width -= this.el.getFrameWidth('lr');
40615 height -= this.el.getFrameWidth('tb');
40618 var te = this.toolbar.getEl();
40619 te.setWidth(width);
40620 height -= te.getHeight();
40623 var te = this.footer.getEl();
40624 te.setWidth(width);
40625 height -= te.getHeight();
40629 if(this.adjustments){
40630 width += this.adjustments[0];
40631 height += this.adjustments[1];
40633 return {"width": width, "height": height};
40636 setSize : function(width, height){
40637 if(this.fitToFrame && !this.ignoreResize(width, height)){
40638 if(this.fitContainer && this.resizeEl != this.el){
40639 this.el.setSize(width, height);
40641 var size = this.adjustForComponents(width, height);
40643 this.iframeEl.setSize(width,height);
40646 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40647 this.fireEvent('resize', this, size.width, size.height);
40654 * Returns this panel's title
40657 getTitle : function(){
40659 if (typeof(this.title) != 'object') {
40664 for (var k in this.title) {
40665 if (!this.title.hasOwnProperty(k)) {
40669 if (k.indexOf('-') >= 0) {
40670 var s = k.split('-');
40671 for (var i = 0; i<s.length; i++) {
40672 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40675 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40682 * Set this panel's title
40683 * @param {String} title
40685 setTitle : function(title){
40686 this.title = title;
40688 this.region.updatePanelTitle(this, title);
40693 * Returns true is this panel was configured to be closable
40694 * @return {Boolean}
40696 isClosable : function(){
40697 return this.closable;
40700 beforeSlide : function(){
40702 this.resizeEl.clip();
40705 afterSlide : function(){
40707 this.resizeEl.unclip();
40711 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40712 * Will fail silently if the {@link #setUrl} method has not been called.
40713 * This does not activate the panel, just updates its content.
40715 refresh : function(){
40716 if(this.refreshDelegate){
40717 this.loaded = false;
40718 this.refreshDelegate();
40723 * Destroys this panel
40725 destroy : function(){
40726 this.el.removeAllListeners();
40727 var tempEl = document.createElement("span");
40728 tempEl.appendChild(this.el.dom);
40729 tempEl.innerHTML = "";
40735 * form - if the content panel contains a form - this is a reference to it.
40736 * @type {Roo.form.Form}
40740 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40741 * This contains a reference to it.
40747 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40757 * @param {Object} cfg Xtype definition of item to add.
40761 getChildContainer: function () {
40762 return this.getEl();
40766 onScroll : function(e)
40768 this.fireEvent('scroll', this, e);
40773 var ret = new Roo.factory(cfg);
40778 if (cfg.xtype.match(/^Form$/)) {
40781 //if (this.footer) {
40782 // el = this.footer.container.insertSibling(false, 'before');
40784 el = this.el.createChild();
40787 this.form = new Roo.form.Form(cfg);
40790 if ( this.form.allItems.length) {
40791 this.form.render(el.dom);
40795 // should only have one of theses..
40796 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40797 // views.. should not be just added - used named prop 'view''
40799 cfg.el = this.el.appendChild(document.createElement("div"));
40802 var ret = new Roo.factory(cfg);
40804 ret.render && ret.render(false, ''); // render blank..
40814 * @class Roo.bootstrap.panel.Grid
40815 * @extends Roo.bootstrap.panel.Content
40817 * Create a new GridPanel.
40818 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40819 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40820 * @param {Object} config A the config object
40826 Roo.bootstrap.panel.Grid = function(config)
40830 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40831 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40833 config.el = this.wrapper;
40834 //this.el = this.wrapper;
40836 if (config.container) {
40837 // ctor'ed from a Border/panel.grid
40840 this.wrapper.setStyle("overflow", "hidden");
40841 this.wrapper.addClass('roo-grid-container');
40846 if(config.toolbar){
40847 var tool_el = this.wrapper.createChild();
40848 this.toolbar = Roo.factory(config.toolbar);
40850 if (config.toolbar.items) {
40851 ti = config.toolbar.items ;
40852 delete config.toolbar.items ;
40856 this.toolbar.render(tool_el);
40857 for(var i =0;i < ti.length;i++) {
40858 // Roo.log(['add child', items[i]]);
40859 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40861 this.toolbar.items = nitems;
40863 delete config.toolbar;
40866 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40867 config.grid.scrollBody = true;;
40868 config.grid.monitorWindowResize = false; // turn off autosizing
40869 config.grid.autoHeight = false;
40870 config.grid.autoWidth = false;
40872 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40874 if (config.background) {
40875 // render grid on panel activation (if panel background)
40876 this.on('activate', function(gp) {
40877 if (!gp.grid.rendered) {
40878 gp.grid.render(this.wrapper);
40879 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40884 this.grid.render(this.wrapper);
40885 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40888 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40889 // ??? needed ??? config.el = this.wrapper;
40894 // xtype created footer. - not sure if will work as we normally have to render first..
40895 if (this.footer && !this.footer.el && this.footer.xtype) {
40897 var ctr = this.grid.getView().getFooterPanel(true);
40898 this.footer.dataSource = this.grid.dataSource;
40899 this.footer = Roo.factory(this.footer, Roo);
40900 this.footer.render(ctr);
40910 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40913 is_resizing : false,
40915 getId : function(){
40916 return this.grid.id;
40920 * Returns the grid for this panel
40921 * @return {Roo.bootstrap.Table}
40923 getGrid : function(){
40927 setSize : function(width, height)
40929 if (this.is_resizing) {
40933 this.is_resizing = true;
40934 if(!this.ignoreResize(width, height)){
40935 var grid = this.grid;
40936 var size = this.adjustForComponents(width, height);
40937 // tfoot is not a footer?
40940 var gridel = grid.getGridEl();
40941 gridel.setSize(size.width, size.height);
40943 var tbd = grid.getGridEl().select('tbody', true).first();
40944 var thd = grid.getGridEl().select('thead',true).first();
40945 var tbf= grid.getGridEl().select('tfoot', true).first();
40948 size.height -= tbf.getHeight();
40951 size.height -= thd.getHeight();
40954 tbd.setSize(size.width, size.height );
40955 // this is for the account management tab -seems to work there.
40956 var thd = grid.getGridEl().select('thead',true).first();
40958 // tbd.setSize(size.width, size.height - thd.getHeight());
40963 this.is_resizing = false;
40968 beforeSlide : function(){
40969 this.grid.getView().scroller.clip();
40972 afterSlide : function(){
40973 this.grid.getView().scroller.unclip();
40976 destroy : function(){
40977 this.grid.destroy();
40979 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40984 * @class Roo.bootstrap.panel.Nest
40985 * @extends Roo.bootstrap.panel.Content
40987 * Create a new Panel, that can contain a layout.Border.
40990 * @param {String/Object} config A string to set only the title or a config object
40992 Roo.bootstrap.panel.Nest = function(config)
40994 // construct with only one argument..
40995 /* FIXME - implement nicer consturctors
40996 if (layout.layout) {
40998 layout = config.layout;
40999 delete config.layout;
41001 if (layout.xtype && !layout.getEl) {
41002 // then layout needs constructing..
41003 layout = Roo.factory(layout, Roo);
41007 config.el = config.layout.getEl();
41009 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41011 config.layout.monitorWindowResize = false; // turn off autosizing
41012 this.layout = config.layout;
41013 this.layout.getEl().addClass("roo-layout-nested-layout");
41014 this.layout.parent = this;
41021 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41023 * @cfg {Roo.BorderLayout} layout The layout for this panel
41027 setSize : function(width, height){
41028 if(!this.ignoreResize(width, height)){
41029 var size = this.adjustForComponents(width, height);
41030 var el = this.layout.getEl();
41031 if (size.height < 1) {
41032 el.setWidth(size.width);
41034 el.setSize(size.width, size.height);
41036 var touch = el.dom.offsetWidth;
41037 this.layout.layout();
41038 // ie requires a double layout on the first pass
41039 if(Roo.isIE && !this.initialized){
41040 this.initialized = true;
41041 this.layout.layout();
41046 // activate all subpanels if not currently active..
41048 setActiveState : function(active){
41049 this.active = active;
41050 this.setActiveClass(active);
41053 this.fireEvent("deactivate", this);
41057 this.fireEvent("activate", this);
41058 // not sure if this should happen before or after..
41059 if (!this.layout) {
41060 return; // should not happen..
41063 for (var r in this.layout.regions) {
41064 reg = this.layout.getRegion(r);
41065 if (reg.getActivePanel()) {
41066 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41067 reg.setActivePanel(reg.getActivePanel());
41070 if (!reg.panels.length) {
41073 reg.showPanel(reg.getPanel(0));
41082 * Returns the nested BorderLayout for this panel
41083 * @return {Roo.BorderLayout}
41085 getLayout : function(){
41086 return this.layout;
41090 * Adds a xtype elements to the layout of the nested panel
41094 xtype : 'ContentPanel',
41101 xtype : 'NestedLayoutPanel',
41107 items : [ ... list of content panels or nested layout panels.. ]
41111 * @param {Object} cfg Xtype definition of item to add.
41113 addxtype : function(cfg) {
41114 return this.layout.addxtype(cfg);
41119 * Ext JS Library 1.1.1
41120 * Copyright(c) 2006-2007, Ext JS, LLC.
41122 * Originally Released Under LGPL - original licence link has changed is not relivant.
41125 * <script type="text/javascript">
41128 * @class Roo.TabPanel
41129 * @extends Roo.util.Observable
41130 * A lightweight tab container.
41134 // basic tabs 1, built from existing content
41135 var tabs = new Roo.TabPanel("tabs1");
41136 tabs.addTab("script", "View Script");
41137 tabs.addTab("markup", "View Markup");
41138 tabs.activate("script");
41140 // more advanced tabs, built from javascript
41141 var jtabs = new Roo.TabPanel("jtabs");
41142 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41144 // set up the UpdateManager
41145 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41146 var updater = tab2.getUpdateManager();
41147 updater.setDefaultUrl("ajax1.htm");
41148 tab2.on('activate', updater.refresh, updater, true);
41150 // Use setUrl for Ajax loading
41151 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41152 tab3.setUrl("ajax2.htm", null, true);
41155 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41158 jtabs.activate("jtabs-1");
41161 * Create a new TabPanel.
41162 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41163 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41165 Roo.bootstrap.panel.Tabs = function(config){
41167 * The container element for this TabPanel.
41168 * @type Roo.Element
41170 this.el = Roo.get(config.el);
41173 if(typeof config == "boolean"){
41174 this.tabPosition = config ? "bottom" : "top";
41176 Roo.apply(this, config);
41180 if(this.tabPosition == "bottom"){
41181 // if tabs are at the bottom = create the body first.
41182 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41183 this.el.addClass("roo-tabs-bottom");
41185 // next create the tabs holders
41187 if (this.tabPosition == "west"){
41189 var reg = this.region; // fake it..
41191 if (!reg.mgr.parent) {
41194 reg = reg.mgr.parent.region;
41196 Roo.log("got nest?");
41198 if (reg.mgr.getRegion('west')) {
41199 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41200 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41201 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41202 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41203 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41211 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41212 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41213 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41214 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41219 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41222 // finally - if tabs are at the top, then create the body last..
41223 if(this.tabPosition != "bottom"){
41224 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41225 * @type Roo.Element
41227 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41228 this.el.addClass("roo-tabs-top");
41232 this.bodyEl.setStyle("position", "relative");
41234 this.active = null;
41235 this.activateDelegate = this.activate.createDelegate(this);
41240 * Fires when the active tab changes
41241 * @param {Roo.TabPanel} this
41242 * @param {Roo.TabPanelItem} activePanel The new active tab
41246 * @event beforetabchange
41247 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41248 * @param {Roo.TabPanel} this
41249 * @param {Object} e Set cancel to true on this object to cancel the tab change
41250 * @param {Roo.TabPanelItem} tab The tab being changed to
41252 "beforetabchange" : true
41255 Roo.EventManager.onWindowResize(this.onResize, this);
41256 this.cpad = this.el.getPadding("lr");
41257 this.hiddenCount = 0;
41260 // toolbar on the tabbar support...
41261 if (this.toolbar) {
41262 alert("no toolbar support yet");
41263 this.toolbar = false;
41265 var tcfg = this.toolbar;
41266 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41267 this.toolbar = new Roo.Toolbar(tcfg);
41268 if (Roo.isSafari) {
41269 var tbl = tcfg.container.child('table', true);
41270 tbl.setAttribute('width', '100%');
41278 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41281 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41283 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41285 tabPosition : "top",
41287 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41289 currentTabWidth : 0,
41291 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41295 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41299 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41301 preferredTabWidth : 175,
41303 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41305 resizeTabs : false,
41307 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41309 monitorResize : true,
41311 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41313 toolbar : false, // set by caller..
41315 region : false, /// set by caller
41317 disableTooltips : true, // not used yet...
41320 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41321 * @param {String} id The id of the div to use <b>or create</b>
41322 * @param {String} text The text for the tab
41323 * @param {String} content (optional) Content to put in the TabPanelItem body
41324 * @param {Boolean} closable (optional) True to create a close icon on the tab
41325 * @return {Roo.TabPanelItem} The created TabPanelItem
41327 addTab : function(id, text, content, closable, tpl)
41329 var item = new Roo.bootstrap.panel.TabItem({
41333 closable : closable,
41336 this.addTabItem(item);
41338 item.setContent(content);
41344 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41345 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41346 * @return {Roo.TabPanelItem}
41348 getTab : function(id){
41349 return this.items[id];
41353 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41354 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41356 hideTab : function(id){
41357 var t = this.items[id];
41360 this.hiddenCount++;
41361 this.autoSizeTabs();
41366 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41367 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41369 unhideTab : function(id){
41370 var t = this.items[id];
41372 t.setHidden(false);
41373 this.hiddenCount--;
41374 this.autoSizeTabs();
41379 * Adds an existing {@link Roo.TabPanelItem}.
41380 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41382 addTabItem : function(item)
41384 this.items[item.id] = item;
41385 this.items.push(item);
41386 this.autoSizeTabs();
41387 // if(this.resizeTabs){
41388 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41389 // this.autoSizeTabs();
41391 // item.autoSize();
41396 * Removes a {@link Roo.TabPanelItem}.
41397 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41399 removeTab : function(id){
41400 var items = this.items;
41401 var tab = items[id];
41402 if(!tab) { return; }
41403 var index = items.indexOf(tab);
41404 if(this.active == tab && items.length > 1){
41405 var newTab = this.getNextAvailable(index);
41410 this.stripEl.dom.removeChild(tab.pnode.dom);
41411 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41412 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41414 items.splice(index, 1);
41415 delete this.items[tab.id];
41416 tab.fireEvent("close", tab);
41417 tab.purgeListeners();
41418 this.autoSizeTabs();
41421 getNextAvailable : function(start){
41422 var items = this.items;
41424 // look for a next tab that will slide over to
41425 // replace the one being removed
41426 while(index < items.length){
41427 var item = items[++index];
41428 if(item && !item.isHidden()){
41432 // if one isn't found select the previous tab (on the left)
41435 var item = items[--index];
41436 if(item && !item.isHidden()){
41444 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41445 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41447 disableTab : function(id){
41448 var tab = this.items[id];
41449 if(tab && this.active != tab){
41455 * Enables a {@link Roo.TabPanelItem} that is disabled.
41456 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41458 enableTab : function(id){
41459 var tab = this.items[id];
41464 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41465 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41466 * @return {Roo.TabPanelItem} The TabPanelItem.
41468 activate : function(id)
41470 //Roo.log('activite:' + id);
41472 var tab = this.items[id];
41476 if(tab == this.active || tab.disabled){
41480 this.fireEvent("beforetabchange", this, e, tab);
41481 if(e.cancel !== true && !tab.disabled){
41483 this.active.hide();
41485 this.active = this.items[id];
41486 this.active.show();
41487 this.fireEvent("tabchange", this, this.active);
41493 * Gets the active {@link Roo.TabPanelItem}.
41494 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41496 getActiveTab : function(){
41497 return this.active;
41501 * Updates the tab body element to fit the height of the container element
41502 * for overflow scrolling
41503 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41505 syncHeight : function(targetHeight){
41506 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41507 var bm = this.bodyEl.getMargins();
41508 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41509 this.bodyEl.setHeight(newHeight);
41513 onResize : function(){
41514 if(this.monitorResize){
41515 this.autoSizeTabs();
41520 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41522 beginUpdate : function(){
41523 this.updating = true;
41527 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41529 endUpdate : function(){
41530 this.updating = false;
41531 this.autoSizeTabs();
41535 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41537 autoSizeTabs : function()
41539 var count = this.items.length;
41540 var vcount = count - this.hiddenCount;
41543 this.stripEl.hide();
41545 this.stripEl.show();
41548 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41553 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41554 var availWidth = Math.floor(w / vcount);
41555 var b = this.stripBody;
41556 if(b.getWidth() > w){
41557 var tabs = this.items;
41558 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41559 if(availWidth < this.minTabWidth){
41560 /*if(!this.sleft){ // incomplete scrolling code
41561 this.createScrollButtons();
41564 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41567 if(this.currentTabWidth < this.preferredTabWidth){
41568 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41574 * Returns the number of tabs in this TabPanel.
41577 getCount : function(){
41578 return this.items.length;
41582 * Resizes all the tabs to the passed width
41583 * @param {Number} The new width
41585 setTabWidth : function(width){
41586 this.currentTabWidth = width;
41587 for(var i = 0, len = this.items.length; i < len; i++) {
41588 if(!this.items[i].isHidden()) {
41589 this.items[i].setWidth(width);
41595 * Destroys this TabPanel
41596 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41598 destroy : function(removeEl){
41599 Roo.EventManager.removeResizeListener(this.onResize, this);
41600 for(var i = 0, len = this.items.length; i < len; i++){
41601 this.items[i].purgeListeners();
41603 if(removeEl === true){
41604 this.el.update("");
41609 createStrip : function(container)
41611 var strip = document.createElement("nav");
41612 strip.className = Roo.bootstrap.version == 4 ?
41613 "navbar-light bg-light" :
41614 "navbar navbar-default"; //"x-tabs-wrap";
41615 container.appendChild(strip);
41619 createStripList : function(strip)
41621 // div wrapper for retard IE
41622 // returns the "tr" element.
41623 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41624 //'<div class="x-tabs-strip-wrap">'+
41625 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41626 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41627 return strip.firstChild; //.firstChild.firstChild.firstChild;
41629 createBody : function(container)
41631 var body = document.createElement("div");
41632 Roo.id(body, "tab-body");
41633 //Roo.fly(body).addClass("x-tabs-body");
41634 Roo.fly(body).addClass("tab-content");
41635 container.appendChild(body);
41638 createItemBody :function(bodyEl, id){
41639 var body = Roo.getDom(id);
41641 body = document.createElement("div");
41644 //Roo.fly(body).addClass("x-tabs-item-body");
41645 Roo.fly(body).addClass("tab-pane");
41646 bodyEl.insertBefore(body, bodyEl.firstChild);
41650 createStripElements : function(stripEl, text, closable, tpl)
41652 var td = document.createElement("li"); // was td..
41653 td.className = 'nav-item';
41655 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41658 stripEl.appendChild(td);
41660 td.className = "x-tabs-closable";
41661 if(!this.closeTpl){
41662 this.closeTpl = new Roo.Template(
41663 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41664 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41665 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41668 var el = this.closeTpl.overwrite(td, {"text": text});
41669 var close = el.getElementsByTagName("div")[0];
41670 var inner = el.getElementsByTagName("em")[0];
41671 return {"el": el, "close": close, "inner": inner};
41674 // not sure what this is..
41675 // if(!this.tabTpl){
41676 //this.tabTpl = new Roo.Template(
41677 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41678 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41680 // this.tabTpl = new Roo.Template(
41681 // '<a href="#">' +
41682 // '<span unselectable="on"' +
41683 // (this.disableTooltips ? '' : ' title="{text}"') +
41684 // ' >{text}</span></a>'
41690 var template = tpl || this.tabTpl || false;
41693 template = new Roo.Template(
41694 Roo.bootstrap.version == 4 ?
41696 '<a class="nav-link" href="#" unselectable="on"' +
41697 (this.disableTooltips ? '' : ' title="{text}"') +
41700 '<a class="nav-link" href="#">' +
41701 '<span unselectable="on"' +
41702 (this.disableTooltips ? '' : ' title="{text}"') +
41703 ' >{text}</span></a>'
41708 switch (typeof(template)) {
41712 template = new Roo.Template(template);
41718 var el = template.overwrite(td, {"text": text});
41720 var inner = el.getElementsByTagName("span")[0];
41722 return {"el": el, "inner": inner};
41730 * @class Roo.TabPanelItem
41731 * @extends Roo.util.Observable
41732 * Represents an individual item (tab plus body) in a TabPanel.
41733 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41734 * @param {String} id The id of this TabPanelItem
41735 * @param {String} text The text for the tab of this TabPanelItem
41736 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41738 Roo.bootstrap.panel.TabItem = function(config){
41740 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41741 * @type Roo.TabPanel
41743 this.tabPanel = config.panel;
41745 * The id for this TabPanelItem
41748 this.id = config.id;
41750 this.disabled = false;
41752 this.text = config.text;
41754 this.loaded = false;
41755 this.closable = config.closable;
41758 * The body element for this TabPanelItem.
41759 * @type Roo.Element
41761 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41762 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41763 this.bodyEl.setStyle("display", "block");
41764 this.bodyEl.setStyle("zoom", "1");
41765 //this.hideAction();
41767 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41769 this.el = Roo.get(els.el);
41770 this.inner = Roo.get(els.inner, true);
41771 this.textEl = Roo.bootstrap.version == 4 ?
41772 this.el : Roo.get(this.el.dom.firstChild, true);
41774 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41775 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41778 // this.el.on("mousedown", this.onTabMouseDown, this);
41779 this.el.on("click", this.onTabClick, this);
41781 if(config.closable){
41782 var c = Roo.get(els.close, true);
41783 c.dom.title = this.closeText;
41784 c.addClassOnOver("close-over");
41785 c.on("click", this.closeClick, this);
41791 * Fires when this tab becomes the active tab.
41792 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41793 * @param {Roo.TabPanelItem} this
41797 * @event beforeclose
41798 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41799 * @param {Roo.TabPanelItem} this
41800 * @param {Object} e Set cancel to true on this object to cancel the close.
41802 "beforeclose": true,
41805 * Fires when this tab is closed.
41806 * @param {Roo.TabPanelItem} this
41810 * @event deactivate
41811 * Fires when this tab is no longer the active tab.
41812 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41813 * @param {Roo.TabPanelItem} this
41815 "deactivate" : true
41817 this.hidden = false;
41819 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41822 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41824 purgeListeners : function(){
41825 Roo.util.Observable.prototype.purgeListeners.call(this);
41826 this.el.removeAllListeners();
41829 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41832 this.status_node.addClass("active");
41835 this.tabPanel.stripWrap.repaint();
41837 this.fireEvent("activate", this.tabPanel, this);
41841 * Returns true if this tab is the active tab.
41842 * @return {Boolean}
41844 isActive : function(){
41845 return this.tabPanel.getActiveTab() == this;
41849 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41852 this.status_node.removeClass("active");
41854 this.fireEvent("deactivate", this.tabPanel, this);
41857 hideAction : function(){
41858 this.bodyEl.hide();
41859 this.bodyEl.setStyle("position", "absolute");
41860 this.bodyEl.setLeft("-20000px");
41861 this.bodyEl.setTop("-20000px");
41864 showAction : function(){
41865 this.bodyEl.setStyle("position", "relative");
41866 this.bodyEl.setTop("");
41867 this.bodyEl.setLeft("");
41868 this.bodyEl.show();
41872 * Set the tooltip for the tab.
41873 * @param {String} tooltip The tab's tooltip
41875 setTooltip : function(text){
41876 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41877 this.textEl.dom.qtip = text;
41878 this.textEl.dom.removeAttribute('title');
41880 this.textEl.dom.title = text;
41884 onTabClick : function(e){
41885 e.preventDefault();
41886 this.tabPanel.activate(this.id);
41889 onTabMouseDown : function(e){
41890 e.preventDefault();
41891 this.tabPanel.activate(this.id);
41894 getWidth : function(){
41895 return this.inner.getWidth();
41898 setWidth : function(width){
41899 var iwidth = width - this.linode.getPadding("lr");
41900 this.inner.setWidth(iwidth);
41901 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41902 this.linode.setWidth(width);
41906 * Show or hide the tab
41907 * @param {Boolean} hidden True to hide or false to show.
41909 setHidden : function(hidden){
41910 this.hidden = hidden;
41911 this.linode.setStyle("display", hidden ? "none" : "");
41915 * Returns true if this tab is "hidden"
41916 * @return {Boolean}
41918 isHidden : function(){
41919 return this.hidden;
41923 * Returns the text for this tab
41926 getText : function(){
41930 autoSize : function(){
41931 //this.el.beginMeasure();
41932 this.textEl.setWidth(1);
41934 * #2804 [new] Tabs in Roojs
41935 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41937 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41938 //this.el.endMeasure();
41942 * Sets the text for the tab (Note: this also sets the tooltip text)
41943 * @param {String} text The tab's text and tooltip
41945 setText : function(text){
41947 this.textEl.update(text);
41948 this.setTooltip(text);
41949 //if(!this.tabPanel.resizeTabs){
41950 // this.autoSize();
41954 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41956 activate : function(){
41957 this.tabPanel.activate(this.id);
41961 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41963 disable : function(){
41964 if(this.tabPanel.active != this){
41965 this.disabled = true;
41966 this.status_node.addClass("disabled");
41971 * Enables this TabPanelItem if it was previously disabled.
41973 enable : function(){
41974 this.disabled = false;
41975 this.status_node.removeClass("disabled");
41979 * Sets the content for this TabPanelItem.
41980 * @param {String} content The content
41981 * @param {Boolean} loadScripts true to look for and load scripts
41983 setContent : function(content, loadScripts){
41984 this.bodyEl.update(content, loadScripts);
41988 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41989 * @return {Roo.UpdateManager} The UpdateManager
41991 getUpdateManager : function(){
41992 return this.bodyEl.getUpdateManager();
41996 * Set a URL to be used to load the content for this TabPanelItem.
41997 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41998 * @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)
41999 * @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)
42000 * @return {Roo.UpdateManager} The UpdateManager
42002 setUrl : function(url, params, loadOnce){
42003 if(this.refreshDelegate){
42004 this.un('activate', this.refreshDelegate);
42006 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42007 this.on("activate", this.refreshDelegate);
42008 return this.bodyEl.getUpdateManager();
42012 _handleRefresh : function(url, params, loadOnce){
42013 if(!loadOnce || !this.loaded){
42014 var updater = this.bodyEl.getUpdateManager();
42015 updater.update(url, params, this._setLoaded.createDelegate(this));
42020 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42021 * Will fail silently if the setUrl method has not been called.
42022 * This does not activate the panel, just updates its content.
42024 refresh : function(){
42025 if(this.refreshDelegate){
42026 this.loaded = false;
42027 this.refreshDelegate();
42032 _setLoaded : function(){
42033 this.loaded = true;
42037 closeClick : function(e){
42040 this.fireEvent("beforeclose", this, o);
42041 if(o.cancel !== true){
42042 this.tabPanel.removeTab(this.id);
42046 * The text displayed in the tooltip for the close icon.
42049 closeText : "Close this tab"
42052 * This script refer to:
42053 * Title: International Telephone Input
42054 * Author: Jack O'Connor
42055 * Code version: v12.1.12
42056 * Availability: https://github.com/jackocnr/intl-tel-input.git
42059 Roo.bootstrap.form.PhoneInputData = function() {
42062 "Afghanistan (افغانستان)",
42067 "Albania (Shqipëri)",
42072 "Algeria (الجزائر)",
42097 "Antigua and Barbuda",
42107 "Armenia (Հայաստան)",
42123 "Austria (Österreich)",
42128 "Azerbaijan (Azərbaycan)",
42138 "Bahrain (البحرين)",
42143 "Bangladesh (বাংলাদেশ)",
42153 "Belarus (Беларусь)",
42158 "Belgium (België)",
42188 "Bosnia and Herzegovina (Босна и Херцеговина)",
42203 "British Indian Ocean Territory",
42208 "British Virgin Islands",
42218 "Bulgaria (България)",
42228 "Burundi (Uburundi)",
42233 "Cambodia (កម្ពុជា)",
42238 "Cameroon (Cameroun)",
42247 ["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"]
42250 "Cape Verde (Kabu Verdi)",
42255 "Caribbean Netherlands",
42266 "Central African Republic (République centrafricaine)",
42286 "Christmas Island",
42292 "Cocos (Keeling) Islands",
42303 "Comoros (جزر القمر)",
42308 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42313 "Congo (Republic) (Congo-Brazzaville)",
42333 "Croatia (Hrvatska)",
42354 "Czech Republic (Česká republika)",
42359 "Denmark (Danmark)",
42374 "Dominican Republic (República Dominicana)",
42378 ["809", "829", "849"]
42396 "Equatorial Guinea (Guinea Ecuatorial)",
42416 "Falkland Islands (Islas Malvinas)",
42421 "Faroe Islands (Føroyar)",
42442 "French Guiana (Guyane française)",
42447 "French Polynesia (Polynésie française)",
42462 "Georgia (საქართველო)",
42467 "Germany (Deutschland)",
42487 "Greenland (Kalaallit Nunaat)",
42524 "Guinea-Bissau (Guiné Bissau)",
42549 "Hungary (Magyarország)",
42554 "Iceland (Ísland)",
42574 "Iraq (العراق)",
42590 "Israel (ישראל)",
42617 "Jordan (الأردن)",
42622 "Kazakhstan (Казахстан)",
42643 "Kuwait (الكويت)",
42648 "Kyrgyzstan (Кыргызстан)",
42658 "Latvia (Latvija)",
42663 "Lebanon (لبنان)",
42678 "Libya (ليبيا)",
42688 "Lithuania (Lietuva)",
42703 "Macedonia (FYROM) (Македонија)",
42708 "Madagascar (Madagasikara)",
42738 "Marshall Islands",
42748 "Mauritania (موريتانيا)",
42753 "Mauritius (Moris)",
42774 "Moldova (Republica Moldova)",
42784 "Mongolia (Монгол)",
42789 "Montenegro (Crna Gora)",
42799 "Morocco (المغرب)",
42805 "Mozambique (Moçambique)",
42810 "Myanmar (Burma) (မြန်မာ)",
42815 "Namibia (Namibië)",
42830 "Netherlands (Nederland)",
42835 "New Caledonia (Nouvelle-Calédonie)",
42870 "North Korea (조선 민주주의 인민 공화국)",
42875 "Northern Mariana Islands",
42891 "Pakistan (پاکستان)",
42901 "Palestine (فلسطين)",
42911 "Papua New Guinea",
42953 "Réunion (La Réunion)",
42959 "Romania (România)",
42975 "Saint Barthélemy",
42986 "Saint Kitts and Nevis",
42996 "Saint Martin (Saint-Martin (partie française))",
43002 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43007 "Saint Vincent and the Grenadines",
43022 "São Tomé and Príncipe (São Tomé e Príncipe)",
43027 "Saudi Arabia (المملكة العربية السعودية)",
43032 "Senegal (Sénégal)",
43062 "Slovakia (Slovensko)",
43067 "Slovenia (Slovenija)",
43077 "Somalia (Soomaaliya)",
43087 "South Korea (대한민국)",
43092 "South Sudan (جنوب السودان)",
43102 "Sri Lanka (ශ්රී ලංකාව)",
43107 "Sudan (السودان)",
43117 "Svalbard and Jan Mayen",
43128 "Sweden (Sverige)",
43133 "Switzerland (Schweiz)",
43138 "Syria (سوريا)",
43183 "Trinidad and Tobago",
43188 "Tunisia (تونس)",
43193 "Turkey (Türkiye)",
43203 "Turks and Caicos Islands",
43213 "U.S. Virgin Islands",
43223 "Ukraine (Україна)",
43228 "United Arab Emirates (الإمارات العربية المتحدة)",
43250 "Uzbekistan (Oʻzbekiston)",
43260 "Vatican City (Città del Vaticano)",
43271 "Vietnam (Việt Nam)",
43276 "Wallis and Futuna (Wallis-et-Futuna)",
43281 "Western Sahara (الصحراء الغربية)",
43287 "Yemen (اليمن)",
43311 * This script refer to:
43312 * Title: International Telephone Input
43313 * Author: Jack O'Connor
43314 * Code version: v12.1.12
43315 * Availability: https://github.com/jackocnr/intl-tel-input.git
43319 * @class Roo.bootstrap.form.PhoneInput
43320 * @extends Roo.bootstrap.form.TriggerField
43321 * An input with International dial-code selection
43323 * @cfg {String} defaultDialCode default '+852'
43324 * @cfg {Array} preferedCountries default []
43327 * Create a new PhoneInput.
43328 * @param {Object} config Configuration options
43331 Roo.bootstrap.form.PhoneInput = function(config) {
43332 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43335 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43337 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43339 listWidth: undefined,
43341 selectedClass: 'active',
43343 invalidClass : "has-warning",
43345 validClass: 'has-success',
43347 allowed: '0123456789',
43352 * @cfg {String} defaultDialCode The default dial code when initializing the input
43354 defaultDialCode: '+852',
43357 * @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
43359 preferedCountries: false,
43361 getAutoCreate : function()
43363 var data = Roo.bootstrap.form.PhoneInputData();
43364 var align = this.labelAlign || this.parentLabelAlign();
43367 this.allCountries = [];
43368 this.dialCodeMapping = [];
43370 for (var i = 0; i < data.length; i++) {
43372 this.allCountries[i] = {
43376 priority: c[3] || 0,
43377 areaCodes: c[4] || null
43379 this.dialCodeMapping[c[2]] = {
43382 priority: c[3] || 0,
43383 areaCodes: c[4] || null
43395 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43396 maxlength: this.max_length,
43397 cls : 'form-control tel-input',
43398 autocomplete: 'new-password'
43401 var hiddenInput = {
43404 cls: 'hidden-tel-input'
43408 hiddenInput.name = this.name;
43411 if (this.disabled) {
43412 input.disabled = true;
43415 var flag_container = {
43432 cls: this.hasFeedback ? 'has-feedback' : '',
43438 cls: 'dial-code-holder',
43445 cls: 'roo-select2-container input-group',
43452 if (this.fieldLabel.length) {
43455 tooltip: 'This field is required'
43461 cls: 'control-label',
43467 html: this.fieldLabel
43470 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43476 if(this.indicatorpos == 'right') {
43477 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43484 if(align == 'left') {
43492 if(this.labelWidth > 12){
43493 label.style = "width: " + this.labelWidth + 'px';
43495 if(this.labelWidth < 13 && this.labelmd == 0){
43496 this.labelmd = this.labelWidth;
43498 if(this.labellg > 0){
43499 label.cls += ' col-lg-' + this.labellg;
43500 input.cls += ' col-lg-' + (12 - this.labellg);
43502 if(this.labelmd > 0){
43503 label.cls += ' col-md-' + this.labelmd;
43504 container.cls += ' col-md-' + (12 - this.labelmd);
43506 if(this.labelsm > 0){
43507 label.cls += ' col-sm-' + this.labelsm;
43508 container.cls += ' col-sm-' + (12 - this.labelsm);
43510 if(this.labelxs > 0){
43511 label.cls += ' col-xs-' + this.labelxs;
43512 container.cls += ' col-xs-' + (12 - this.labelxs);
43522 var settings = this;
43524 ['xs','sm','md','lg'].map(function(size){
43525 if (settings[size]) {
43526 cfg.cls += ' col-' + size + '-' + settings[size];
43530 this.store = new Roo.data.Store({
43531 proxy : new Roo.data.MemoryProxy({}),
43532 reader : new Roo.data.JsonReader({
43543 'name' : 'dialCode',
43547 'name' : 'priority',
43551 'name' : 'areaCodes',
43558 if(!this.preferedCountries) {
43559 this.preferedCountries = [
43566 var p = this.preferedCountries.reverse();
43569 for (var i = 0; i < p.length; i++) {
43570 for (var j = 0; j < this.allCountries.length; j++) {
43571 if(this.allCountries[j].iso2 == p[i]) {
43572 var t = this.allCountries[j];
43573 this.allCountries.splice(j,1);
43574 this.allCountries.unshift(t);
43580 this.store.proxy.data = {
43582 data: this.allCountries
43588 initEvents : function()
43591 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43593 this.indicator = this.indicatorEl();
43594 this.flag = this.flagEl();
43595 this.dialCodeHolder = this.dialCodeHolderEl();
43597 this.trigger = this.el.select('div.flag-box',true).first();
43598 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43603 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43604 _this.list.setWidth(lw);
43607 this.list.on('mouseover', this.onViewOver, this);
43608 this.list.on('mousemove', this.onViewMove, this);
43609 this.inputEl().on("keyup", this.onKeyUp, this);
43610 this.inputEl().on("keypress", this.onKeyPress, this);
43612 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43614 this.view = new Roo.View(this.list, this.tpl, {
43615 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43618 this.view.on('click', this.onViewClick, this);
43619 this.setValue(this.defaultDialCode);
43622 onTriggerClick : function(e)
43624 Roo.log('trigger click');
43629 if(this.isExpanded()){
43631 this.hasFocus = false;
43633 this.store.load({});
43634 this.hasFocus = true;
43639 isExpanded : function()
43641 return this.list.isVisible();
43644 collapse : function()
43646 if(!this.isExpanded()){
43650 Roo.get(document).un('mousedown', this.collapseIf, this);
43651 Roo.get(document).un('mousewheel', this.collapseIf, this);
43652 this.fireEvent('collapse', this);
43656 expand : function()
43660 if(this.isExpanded() || !this.hasFocus){
43664 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43665 this.list.setWidth(lw);
43668 this.restrictHeight();
43670 Roo.get(document).on('mousedown', this.collapseIf, this);
43671 Roo.get(document).on('mousewheel', this.collapseIf, this);
43673 this.fireEvent('expand', this);
43676 restrictHeight : function()
43678 this.list.alignTo(this.inputEl(), this.listAlign);
43679 this.list.alignTo(this.inputEl(), this.listAlign);
43682 onViewOver : function(e, t)
43684 if(this.inKeyMode){
43687 var item = this.view.findItemFromChild(t);
43690 var index = this.view.indexOf(item);
43691 this.select(index, false);
43696 onViewClick : function(view, doFocus, el, e)
43698 var index = this.view.getSelectedIndexes()[0];
43700 var r = this.store.getAt(index);
43703 this.onSelect(r, index);
43705 if(doFocus !== false && !this.blockFocus){
43706 this.inputEl().focus();
43710 onViewMove : function(e, t)
43712 this.inKeyMode = false;
43715 select : function(index, scrollIntoView)
43717 this.selectedIndex = index;
43718 this.view.select(index);
43719 if(scrollIntoView !== false){
43720 var el = this.view.getNode(index);
43722 this.list.scrollChildIntoView(el, false);
43727 createList : function()
43729 this.list = Roo.get(document.body).createChild({
43731 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43732 style: 'display:none'
43735 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43738 collapseIf : function(e)
43740 var in_combo = e.within(this.el);
43741 var in_list = e.within(this.list);
43742 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43744 if (in_combo || in_list || is_list) {
43750 onSelect : function(record, index)
43752 if(this.fireEvent('beforeselect', this, record, index) !== false){
43754 this.setFlagClass(record.data.iso2);
43755 this.setDialCode(record.data.dialCode);
43756 this.hasFocus = false;
43758 this.fireEvent('select', this, record, index);
43762 flagEl : function()
43764 var flag = this.el.select('div.flag',true).first();
43771 dialCodeHolderEl : function()
43773 var d = this.el.select('input.dial-code-holder',true).first();
43780 setDialCode : function(v)
43782 this.dialCodeHolder.dom.value = '+'+v;
43785 setFlagClass : function(n)
43787 this.flag.dom.className = 'flag '+n;
43790 getValue : function()
43792 var v = this.inputEl().getValue();
43793 if(this.dialCodeHolder) {
43794 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43799 setValue : function(v)
43801 var d = this.getDialCode(v);
43803 //invalid dial code
43804 if(v.length == 0 || !d || d.length == 0) {
43806 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43807 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43813 this.setFlagClass(this.dialCodeMapping[d].iso2);
43814 this.setDialCode(d);
43815 this.inputEl().dom.value = v.replace('+'+d,'');
43816 this.hiddenEl().dom.value = this.getValue();
43821 getDialCode : function(v)
43825 if (v.length == 0) {
43826 return this.dialCodeHolder.dom.value;
43830 if (v.charAt(0) != "+") {
43833 var numericChars = "";
43834 for (var i = 1; i < v.length; i++) {
43835 var c = v.charAt(i);
43838 if (this.dialCodeMapping[numericChars]) {
43839 dialCode = v.substr(1, i);
43841 if (numericChars.length == 4) {
43851 this.setValue(this.defaultDialCode);
43855 hiddenEl : function()
43857 return this.el.select('input.hidden-tel-input',true).first();
43860 // after setting val
43861 onKeyUp : function(e){
43862 this.setValue(this.getValue());
43865 onKeyPress : function(e){
43866 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43873 * @class Roo.bootstrap.form.MoneyField
43874 * @extends Roo.bootstrap.form.ComboBox
43875 * Bootstrap MoneyField class
43878 * Create a new MoneyField.
43879 * @param {Object} config Configuration options
43882 Roo.bootstrap.form.MoneyField = function(config) {
43884 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43888 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43891 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43893 allowDecimals : true,
43895 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43897 decimalSeparator : ".",
43899 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43901 decimalPrecision : 0,
43903 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43905 allowNegative : true,
43907 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43911 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43913 minValue : Number.NEGATIVE_INFINITY,
43915 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43917 maxValue : Number.MAX_VALUE,
43919 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43921 minText : "The minimum value for this field is {0}",
43923 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43925 maxText : "The maximum value for this field is {0}",
43927 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43928 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43930 nanText : "{0} is not a valid number",
43932 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43936 * @cfg {String} defaults currency of the MoneyField
43937 * value should be in lkey
43939 defaultCurrency : false,
43941 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43943 thousandsDelimiter : false,
43945 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43954 * @cfg {Roo.data.Store} store Store to lookup currency??
43958 getAutoCreate : function()
43960 var align = this.labelAlign || this.parentLabelAlign();
43972 cls : 'form-control roo-money-amount-input',
43973 autocomplete: 'new-password'
43976 var hiddenInput = {
43980 cls: 'hidden-number-input'
43983 if(this.max_length) {
43984 input.maxlength = this.max_length;
43988 hiddenInput.name = this.name;
43991 if (this.disabled) {
43992 input.disabled = true;
43995 var clg = 12 - this.inputlg;
43996 var cmd = 12 - this.inputmd;
43997 var csm = 12 - this.inputsm;
43998 var cxs = 12 - this.inputxs;
44002 cls : 'row roo-money-field',
44006 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44010 cls: 'roo-select2-container input-group',
44014 cls : 'form-control roo-money-currency-input',
44015 autocomplete: 'new-password',
44017 name : this.currencyName
44021 cls : 'input-group-addon',
44035 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44039 cls: this.hasFeedback ? 'has-feedback' : '',
44050 if (this.fieldLabel.length) {
44053 tooltip: 'This field is required'
44059 cls: 'control-label',
44065 html: this.fieldLabel
44068 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44074 if(this.indicatorpos == 'right') {
44075 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44082 if(align == 'left') {
44090 if(this.labelWidth > 12){
44091 label.style = "width: " + this.labelWidth + 'px';
44093 if(this.labelWidth < 13 && this.labelmd == 0){
44094 this.labelmd = this.labelWidth;
44096 if(this.labellg > 0){
44097 label.cls += ' col-lg-' + this.labellg;
44098 input.cls += ' col-lg-' + (12 - this.labellg);
44100 if(this.labelmd > 0){
44101 label.cls += ' col-md-' + this.labelmd;
44102 container.cls += ' col-md-' + (12 - this.labelmd);
44104 if(this.labelsm > 0){
44105 label.cls += ' col-sm-' + this.labelsm;
44106 container.cls += ' col-sm-' + (12 - this.labelsm);
44108 if(this.labelxs > 0){
44109 label.cls += ' col-xs-' + this.labelxs;
44110 container.cls += ' col-xs-' + (12 - this.labelxs);
44121 var settings = this;
44123 ['xs','sm','md','lg'].map(function(size){
44124 if (settings[size]) {
44125 cfg.cls += ' col-' + size + '-' + settings[size];
44132 initEvents : function()
44134 this.indicator = this.indicatorEl();
44136 this.initCurrencyEvent();
44138 this.initNumberEvent();
44141 initCurrencyEvent : function()
44144 throw "can not find store for combo";
44147 this.store = Roo.factory(this.store, Roo.data);
44148 this.store.parent = this;
44152 this.triggerEl = this.el.select('.input-group-addon', true).first();
44154 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44159 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44160 _this.list.setWidth(lw);
44163 this.list.on('mouseover', this.onViewOver, this);
44164 this.list.on('mousemove', this.onViewMove, this);
44165 this.list.on('scroll', this.onViewScroll, this);
44168 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44171 this.view = new Roo.View(this.list, this.tpl, {
44172 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44175 this.view.on('click', this.onViewClick, this);
44177 this.store.on('beforeload', this.onBeforeLoad, this);
44178 this.store.on('load', this.onLoad, this);
44179 this.store.on('loadexception', this.onLoadException, this);
44181 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44182 "up" : function(e){
44183 this.inKeyMode = true;
44187 "down" : function(e){
44188 if(!this.isExpanded()){
44189 this.onTriggerClick();
44191 this.inKeyMode = true;
44196 "enter" : function(e){
44199 if(this.fireEvent("specialkey", this, e)){
44200 this.onViewClick(false);
44206 "esc" : function(e){
44210 "tab" : function(e){
44213 if(this.fireEvent("specialkey", this, e)){
44214 this.onViewClick(false);
44222 doRelay : function(foo, bar, hname){
44223 if(hname == 'down' || this.scope.isExpanded()){
44224 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44232 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44236 initNumberEvent : function(e)
44238 this.inputEl().on("keydown" , this.fireKey, this);
44239 this.inputEl().on("focus", this.onFocus, this);
44240 this.inputEl().on("blur", this.onBlur, this);
44242 this.inputEl().relayEvent('keyup', this);
44244 if(this.indicator){
44245 this.indicator.addClass('invisible');
44248 this.originalValue = this.getValue();
44250 if(this.validationEvent == 'keyup'){
44251 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44252 this.inputEl().on('keyup', this.filterValidation, this);
44254 else if(this.validationEvent !== false){
44255 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44258 if(this.selectOnFocus){
44259 this.on("focus", this.preFocus, this);
44262 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44263 this.inputEl().on("keypress", this.filterKeys, this);
44265 this.inputEl().relayEvent('keypress', this);
44268 var allowed = "0123456789";
44270 if(this.allowDecimals){
44271 allowed += this.decimalSeparator;
44274 if(this.allowNegative){
44278 if(this.thousandsDelimiter) {
44282 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44284 var keyPress = function(e){
44286 var k = e.getKey();
44288 var c = e.getCharCode();
44291 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44292 allowed.indexOf(String.fromCharCode(c)) === -1
44298 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44302 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44307 this.inputEl().on("keypress", keyPress, this);
44311 onTriggerClick : function(e)
44318 this.loadNext = false;
44320 if(this.isExpanded()){
44325 this.hasFocus = true;
44327 if(this.triggerAction == 'all') {
44328 this.doQuery(this.allQuery, true);
44332 this.doQuery(this.getRawValue());
44335 getCurrency : function()
44337 var v = this.currencyEl().getValue();
44342 restrictHeight : function()
44344 this.list.alignTo(this.currencyEl(), this.listAlign);
44345 this.list.alignTo(this.currencyEl(), this.listAlign);
44348 onViewClick : function(view, doFocus, el, e)
44350 var index = this.view.getSelectedIndexes()[0];
44352 var r = this.store.getAt(index);
44355 this.onSelect(r, index);
44359 onSelect : function(record, index){
44361 if(this.fireEvent('beforeselect', this, record, index) !== false){
44363 this.setFromCurrencyData(index > -1 ? record.data : false);
44367 this.fireEvent('select', this, record, index);
44371 setFromCurrencyData : function(o)
44375 this.lastCurrency = o;
44377 if (this.currencyField) {
44378 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44380 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44383 this.lastSelectionText = currency;
44385 //setting default currency
44386 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44387 this.setCurrency(this.defaultCurrency);
44391 this.setCurrency(currency);
44394 setFromData : function(o)
44398 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44400 this.setFromCurrencyData(c);
44405 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44407 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44410 this.setValue(value);
44414 setCurrency : function(v)
44416 this.currencyValue = v;
44419 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44424 setValue : function(v)
44426 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44432 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44434 this.inputEl().dom.value = (v == '') ? '' :
44435 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44437 if(!this.allowZero && v === '0') {
44438 this.hiddenEl().dom.value = '';
44439 this.inputEl().dom.value = '';
44446 getRawValue : function()
44448 var v = this.inputEl().getValue();
44453 getValue : function()
44455 return this.fixPrecision(this.parseValue(this.getRawValue()));
44458 parseValue : function(value)
44460 if(this.thousandsDelimiter) {
44462 r = new RegExp(",", "g");
44463 value = value.replace(r, "");
44466 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44467 return isNaN(value) ? '' : value;
44471 fixPrecision : function(value)
44473 if(this.thousandsDelimiter) {
44475 r = new RegExp(",", "g");
44476 value = value.replace(r, "");
44479 var nan = isNaN(value);
44481 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44482 return nan ? '' : value;
44484 return parseFloat(value).toFixed(this.decimalPrecision);
44487 decimalPrecisionFcn : function(v)
44489 return Math.floor(v);
44492 validateValue : function(value)
44494 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44498 var num = this.parseValue(value);
44501 this.markInvalid(String.format(this.nanText, value));
44505 if(num < this.minValue){
44506 this.markInvalid(String.format(this.minText, this.minValue));
44510 if(num > this.maxValue){
44511 this.markInvalid(String.format(this.maxText, this.maxValue));
44518 validate : function()
44520 if(this.disabled || this.allowBlank){
44525 var currency = this.getCurrency();
44527 if(this.validateValue(this.getRawValue()) && currency.length){
44532 this.markInvalid();
44536 getName: function()
44541 beforeBlur : function()
44547 var v = this.parseValue(this.getRawValue());
44554 onBlur : function()
44558 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44559 //this.el.removeClass(this.focusClass);
44562 this.hasFocus = false;
44564 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44568 var v = this.getValue();
44570 if(String(v) !== String(this.startValue)){
44571 this.fireEvent('change', this, v, this.startValue);
44574 this.fireEvent("blur", this);
44577 inputEl : function()
44579 return this.el.select('.roo-money-amount-input', true).first();
44582 currencyEl : function()
44584 return this.el.select('.roo-money-currency-input', true).first();
44587 hiddenEl : function()
44589 return this.el.select('input.hidden-number-input',true).first();
44593 * @class Roo.bootstrap.BezierSignature
44594 * @extends Roo.bootstrap.Component
44595 * Bootstrap BezierSignature class
44596 * This script refer to:
44597 * Title: Signature Pad
44599 * Availability: https://github.com/szimek/signature_pad
44602 * Create a new BezierSignature
44603 * @param {Object} config The config object
44606 Roo.bootstrap.BezierSignature = function(config){
44607 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44613 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44620 mouse_btn_down: true,
44623 * @cfg {int} canvas height
44625 canvas_height: '200px',
44628 * @cfg {float|function} Radius of a single dot.
44633 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44638 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44643 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44648 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44653 * @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.
44655 bg_color: 'rgba(0, 0, 0, 0)',
44658 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44660 dot_color: 'black',
44663 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44665 velocity_filter_weight: 0.7,
44668 * @cfg {function} Callback when stroke begin.
44673 * @cfg {function} Callback when stroke end.
44677 getAutoCreate : function()
44679 var cls = 'roo-signature column';
44682 cls += ' ' + this.cls;
44692 for(var i = 0; i < col_sizes.length; i++) {
44693 if(this[col_sizes[i]]) {
44694 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44704 cls: 'roo-signature-body',
44708 cls: 'roo-signature-body-canvas',
44709 height: this.canvas_height,
44710 width: this.canvas_width
44717 style: 'display: none'
44725 initEvents: function()
44727 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44729 var canvas = this.canvasEl();
44731 // mouse && touch event swapping...
44732 canvas.dom.style.touchAction = 'none';
44733 canvas.dom.style.msTouchAction = 'none';
44735 this.mouse_btn_down = false;
44736 canvas.on('mousedown', this._handleMouseDown, this);
44737 canvas.on('mousemove', this._handleMouseMove, this);
44738 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44740 if (window.PointerEvent) {
44741 canvas.on('pointerdown', this._handleMouseDown, this);
44742 canvas.on('pointermove', this._handleMouseMove, this);
44743 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44746 if ('ontouchstart' in window) {
44747 canvas.on('touchstart', this._handleTouchStart, this);
44748 canvas.on('touchmove', this._handleTouchMove, this);
44749 canvas.on('touchend', this._handleTouchEnd, this);
44752 Roo.EventManager.onWindowResize(this.resize, this, true);
44754 // file input event
44755 this.fileEl().on('change', this.uploadImage, this);
44762 resize: function(){
44764 var canvas = this.canvasEl().dom;
44765 var ctx = this.canvasElCtx();
44766 var img_data = false;
44768 if(canvas.width > 0) {
44769 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44771 // setting canvas width will clean img data
44774 var style = window.getComputedStyle ?
44775 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44777 var padding_left = parseInt(style.paddingLeft) || 0;
44778 var padding_right = parseInt(style.paddingRight) || 0;
44780 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44783 ctx.putImageData(img_data, 0, 0);
44787 _handleMouseDown: function(e)
44789 if (e.browserEvent.which === 1) {
44790 this.mouse_btn_down = true;
44791 this.strokeBegin(e);
44795 _handleMouseMove: function (e)
44797 if (this.mouse_btn_down) {
44798 this.strokeMoveUpdate(e);
44802 _handleMouseUp: function (e)
44804 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44805 this.mouse_btn_down = false;
44810 _handleTouchStart: function (e) {
44812 e.preventDefault();
44813 if (e.browserEvent.targetTouches.length === 1) {
44814 // var touch = e.browserEvent.changedTouches[0];
44815 // this.strokeBegin(touch);
44817 this.strokeBegin(e); // assume e catching the correct xy...
44821 _handleTouchMove: function (e) {
44822 e.preventDefault();
44823 // var touch = event.targetTouches[0];
44824 // _this._strokeMoveUpdate(touch);
44825 this.strokeMoveUpdate(e);
44828 _handleTouchEnd: function (e) {
44829 var wasCanvasTouched = e.target === this.canvasEl().dom;
44830 if (wasCanvasTouched) {
44831 e.preventDefault();
44832 // var touch = event.changedTouches[0];
44833 // _this._strokeEnd(touch);
44838 reset: function () {
44839 this._lastPoints = [];
44840 this._lastVelocity = 0;
44841 this._lastWidth = (this.min_width + this.max_width) / 2;
44842 this.canvasElCtx().fillStyle = this.dot_color;
44845 strokeMoveUpdate: function(e)
44847 this.strokeUpdate(e);
44849 if (this.throttle) {
44850 this.throttleStroke(this.strokeUpdate, this.throttle);
44853 this.strokeUpdate(e);
44857 strokeBegin: function(e)
44859 var newPointGroup = {
44860 color: this.dot_color,
44864 if (typeof this.onBegin === 'function') {
44868 this.curve_data.push(newPointGroup);
44870 this.strokeUpdate(e);
44873 strokeUpdate: function(e)
44875 var rect = this.canvasEl().dom.getBoundingClientRect();
44876 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44877 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44878 var lastPoints = lastPointGroup.points;
44879 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44880 var isLastPointTooClose = lastPoint
44881 ? point.distanceTo(lastPoint) <= this.min_distance
44883 var color = lastPointGroup.color;
44884 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44885 var curve = this.addPoint(point);
44887 this.drawDot({color: color, point: point});
44890 this.drawCurve({color: color, curve: curve});
44900 strokeEnd: function(e)
44902 this.strokeUpdate(e);
44903 if (typeof this.onEnd === 'function') {
44908 addPoint: function (point) {
44909 var _lastPoints = this._lastPoints;
44910 _lastPoints.push(point);
44911 if (_lastPoints.length > 2) {
44912 if (_lastPoints.length === 3) {
44913 _lastPoints.unshift(_lastPoints[0]);
44915 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44916 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44917 _lastPoints.shift();
44923 calculateCurveWidths: function (startPoint, endPoint) {
44924 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44925 (1 - this.velocity_filter_weight) * this._lastVelocity;
44927 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44930 start: this._lastWidth
44933 this._lastVelocity = velocity;
44934 this._lastWidth = newWidth;
44938 drawDot: function (_a) {
44939 var color = _a.color, point = _a.point;
44940 var ctx = this.canvasElCtx();
44941 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44943 this.drawCurveSegment(point.x, point.y, width);
44945 ctx.fillStyle = color;
44949 drawCurve: function (_a) {
44950 var color = _a.color, curve = _a.curve;
44951 var ctx = this.canvasElCtx();
44952 var widthDelta = curve.endWidth - curve.startWidth;
44953 var drawSteps = Math.floor(curve.length()) * 2;
44955 ctx.fillStyle = color;
44956 for (var i = 0; i < drawSteps; i += 1) {
44957 var t = i / drawSteps;
44963 var x = uuu * curve.startPoint.x;
44964 x += 3 * uu * t * curve.control1.x;
44965 x += 3 * u * tt * curve.control2.x;
44966 x += ttt * curve.endPoint.x;
44967 var y = uuu * curve.startPoint.y;
44968 y += 3 * uu * t * curve.control1.y;
44969 y += 3 * u * tt * curve.control2.y;
44970 y += ttt * curve.endPoint.y;
44971 var width = curve.startWidth + ttt * widthDelta;
44972 this.drawCurveSegment(x, y, width);
44978 drawCurveSegment: function (x, y, width) {
44979 var ctx = this.canvasElCtx();
44981 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44982 this.is_empty = false;
44987 var ctx = this.canvasElCtx();
44988 var canvas = this.canvasEl().dom;
44989 ctx.fillStyle = this.bg_color;
44990 ctx.clearRect(0, 0, canvas.width, canvas.height);
44991 ctx.fillRect(0, 0, canvas.width, canvas.height);
44992 this.curve_data = [];
44994 this.is_empty = true;
44999 return this.el.select('input',true).first();
45002 canvasEl: function()
45004 return this.el.select('canvas',true).first();
45007 canvasElCtx: function()
45009 return this.el.select('canvas',true).first().dom.getContext('2d');
45012 getImage: function(type)
45014 if(this.is_empty) {
45019 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45022 drawFromImage: function(img_src)
45024 var img = new Image();
45026 img.onload = function(){
45027 this.canvasElCtx().drawImage(img, 0, 0);
45032 this.is_empty = false;
45035 selectImage: function()
45037 this.fileEl().dom.click();
45040 uploadImage: function(e)
45042 var reader = new FileReader();
45044 reader.onload = function(e){
45045 var img = new Image();
45046 img.onload = function(){
45048 this.canvasElCtx().drawImage(img, 0, 0);
45050 img.src = e.target.result;
45053 reader.readAsDataURL(e.target.files[0]);
45056 // Bezier Point Constructor
45057 Point: (function () {
45058 function Point(x, y, time) {
45061 this.time = time || Date.now();
45063 Point.prototype.distanceTo = function (start) {
45064 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45066 Point.prototype.equals = function (other) {
45067 return this.x === other.x && this.y === other.y && this.time === other.time;
45069 Point.prototype.velocityFrom = function (start) {
45070 return this.time !== start.time
45071 ? this.distanceTo(start) / (this.time - start.time)
45078 // Bezier Constructor
45079 Bezier: (function () {
45080 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45081 this.startPoint = startPoint;
45082 this.control2 = control2;
45083 this.control1 = control1;
45084 this.endPoint = endPoint;
45085 this.startWidth = startWidth;
45086 this.endWidth = endWidth;
45088 Bezier.fromPoints = function (points, widths, scope) {
45089 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45090 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45091 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45093 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45094 var dx1 = s1.x - s2.x;
45095 var dy1 = s1.y - s2.y;
45096 var dx2 = s2.x - s3.x;
45097 var dy2 = s2.y - s3.y;
45098 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45099 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45100 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45101 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45102 var dxm = m1.x - m2.x;
45103 var dym = m1.y - m2.y;
45104 var k = l2 / (l1 + l2);
45105 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45106 var tx = s2.x - cm.x;
45107 var ty = s2.y - cm.y;
45109 c1: new scope.Point(m1.x + tx, m1.y + ty),
45110 c2: new scope.Point(m2.x + tx, m2.y + ty)
45113 Bezier.prototype.length = function () {
45118 for (var i = 0; i <= steps; i += 1) {
45120 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45121 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45123 var xdiff = cx - px;
45124 var ydiff = cy - py;
45125 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45132 Bezier.prototype.point = function (t, start, c1, c2, end) {
45133 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45134 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45135 + (3.0 * c2 * (1.0 - t) * t * t)
45136 + (end * t * t * t);
45141 throttleStroke: function(fn, wait) {
45142 if (wait === void 0) { wait = 250; }
45144 var timeout = null;
45148 var later = function () {
45149 previous = Date.now();
45151 result = fn.apply(storedContext, storedArgs);
45153 storedContext = null;
45157 return function wrapper() {
45159 for (var _i = 0; _i < arguments.length; _i++) {
45160 args[_i] = arguments[_i];
45162 var now = Date.now();
45163 var remaining = wait - (now - previous);
45164 storedContext = this;
45166 if (remaining <= 0 || remaining > wait) {
45168 clearTimeout(timeout);
45172 result = fn.apply(storedContext, storedArgs);
45174 storedContext = null;
45178 else if (!timeout) {
45179 timeout = window.setTimeout(later, remaining);
45189 // old names for form elements
45190 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45191 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45192 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45193 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45194 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45195 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45196 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45197 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45198 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45199 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45200 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45201 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45202 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45203 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45204 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45205 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45206 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45207 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45208 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45209 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45210 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45211 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45212 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45213 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45214 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45215 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45217 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45218 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45220 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45221 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45223 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45224 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45225 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45226 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator