2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
22 * Ext JS Library 1.1.1
23 * Copyright(c) 2006-2007, Ext JS, LLC.
25 * Originally Released Under LGPL - original licence link has changed is not relivant.
28 * <script type="text/javascript">
34 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
35 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
36 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
39 * @param {Object} config The config object
41 Roo.Shadow = function(config){
42 Roo.apply(this, config);
43 if(typeof this.mode != "string"){
44 this.mode = this.defaultMode;
46 var o = this.offset, a = {h: 0};
47 var rad = Math.floor(this.offset/2);
48 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
54 a.l -= this.offset + rad;
55 a.t -= this.offset + rad;
66 a.l -= (this.offset - rad);
67 a.t -= this.offset + rad;
69 a.w -= (this.offset - rad)*2;
80 a.l -= (this.offset - rad);
81 a.t -= (this.offset - rad);
83 a.w -= (this.offset + rad + 1);
84 a.h -= (this.offset + rad);
93 Roo.Shadow.prototype = {
96 * The shadow display mode. Supports the following options:<br />
97 * sides: Shadow displays on both sides and bottom only<br />
98 * frame: Shadow displays equally on all four sides<br />
99 * drop: Traditional bottom-right drop shadow (default)
103 * @cfg {String} offset
104 * The number of pixels to offset the shadow from the element (defaults to 4)
112 * Displays the shadow under the target element
113 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
115 show : function(target){
116 target = Roo.get(target);
118 this.el = Roo.Shadow.Pool.pull();
119 if(this.el.dom.nextSibling != target.dom){
120 this.el.insertBefore(target);
123 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
125 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
128 target.getLeft(true),
133 this.el.dom.style.display = "block";
137 * Returns true if the shadow is visible, else false
139 isVisible : function(){
140 return this.el ? true : false;
144 * Direct alignment when values are already available. Show must be called at least once before
145 * calling this method to ensure it is initialized.
146 * @param {Number} left The target element left position
147 * @param {Number} top The target element top position
148 * @param {Number} width The target element width
149 * @param {Number} height The target element height
151 realign : function(l, t, w, h){
155 var a = this.adjusts, d = this.el.dom, s = d.style;
157 s.left = (l+a.l)+"px";
158 s.top = (t+a.t)+"px";
159 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
161 if(s.width != sws || s.height != shs){
165 var cn = d.childNodes;
166 var sww = Math.max(0, (sw-12))+"px";
167 cn[0].childNodes[1].style.width = sww;
168 cn[1].childNodes[1].style.width = sww;
169 cn[2].childNodes[1].style.width = sww;
170 cn[1].style.height = Math.max(0, (sh-12))+"px";
180 this.el.dom.style.display = "none";
181 Roo.Shadow.Pool.push(this.el);
187 * Adjust the z-index of this shadow
188 * @param {Number} zindex The new z-index
190 setZIndex : function(z){
193 this.el.setStyle("z-index", z);
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
201 var markup = Roo.isIE ?
202 '<div class="x-ie-shadow"></div>' :
203 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
208 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209 sh.autoBoxAdjust = false;
221 * base class for bootstrap elements.
225 Roo.bootstrap = Roo.bootstrap || {};
227 * @class Roo.bootstrap.Component
228 * @extends Roo.Component
230 * @children Roo.bootstrap.Component
231 * Bootstrap Component base class
232 * @cfg {String} cls css class
233 * @cfg {String} style any extra css
234 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
236 * @cfg {string} dataId cutomer id
237 * @cfg {string} name Specifies name attribute
238 * @cfg {string} tooltip Text for the tooltip
239 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
240 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
243 * Do not use directly - it does not do anything..
244 * @param {Object} config The config object
249 Roo.bootstrap.Component = function(config){
250 Roo.bootstrap.Component.superclass.constructor.call(this, config);
254 * @event childrenrendered
255 * Fires when the children have been rendered..
256 * @param {Roo.bootstrap.Component} this
258 "childrenrendered" : true
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
270 allowDomMove : false, // to stop relocations in parent onRender...
280 * Initialize Events for the element
282 initEvents : function() { },
288 can_build_overlaid : true,
290 container_method : false,
297 // returns the parent component..
298 return Roo.ComponentMgr.get(this.parentId)
304 onRender : function(ct, position)
306 // Roo.log("Call onRender: " + this.xtype);
308 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
311 if (this.el.attr('xtype')) {
312 this.el.attr('xtypex', this.el.attr('xtype'));
313 this.el.dom.removeAttribute('xtype');
323 var cfg = Roo.apply({}, this.getAutoCreate());
325 cfg.id = this.id || Roo.id();
327 // fill in the extra attributes
328 if (this.xattr && typeof(this.xattr) =='object') {
329 for (var i in this.xattr) {
330 cfg[i] = this.xattr[i];
335 cfg.dataId = this.dataId;
339 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
342 if (this.style) { // fixme needs to support more complex style data.
343 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347 cfg.name = this.name;
350 this.el = ct.createChild(cfg, position);
353 this.tooltipEl().attr('tooltip', this.tooltip);
356 if(this.tabIndex !== undefined){
357 this.el.dom.setAttribute('tabIndex', this.tabIndex);
364 * Fetch the element to add children to
365 * @return {Roo.Element} defaults to this.el
367 getChildContainer : function()
371 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
373 return Roo.get(document.body);
377 * Fetch the element to display the tooltip on.
378 * @return {Roo.Element} defaults to this.el
380 tooltipEl : function()
385 addxtype : function(tree,cntr)
389 cn = Roo.factory(tree);
390 //Roo.log(['addxtype', cn]);
392 cn.parentType = this.xtype; //??
393 cn.parentId = this.id;
395 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396 if (typeof(cn.container_method) == 'string') {
397 cntr = cn.container_method;
401 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
403 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
405 var build_from_html = Roo.XComponent.build_from_html;
407 var is_body = (tree.xtype == 'Body') ;
409 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
411 var self_cntr_el = Roo.get(this[cntr](false));
413 // do not try and build conditional elements
414 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420 return this.addxtypeChild(tree,cntr, is_body);
423 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
426 return this.addxtypeChild(Roo.apply({}, tree),cntr);
429 Roo.log('skipping render');
435 if (!build_from_html) {
439 // this i think handles overlaying multiple children of the same type
440 // with the sam eelement.. - which might be buggy..
442 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
448 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
459 addxtypeChild : function (tree, cntr, is_body)
461 Roo.debug && Roo.log('addxtypeChild:' + cntr);
463 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
466 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467 (typeof(tree['flexy:foreach']) != 'undefined');
471 skip_children = false;
472 // render the element if it's not BODY.
475 // if parent was disabled, then do not try and create the children..
476 if(!this[cntr](true)){
481 cn = Roo.factory(tree);
483 cn.parentType = this.xtype; //??
484 cn.parentId = this.id;
486 var build_from_html = Roo.XComponent.build_from_html;
489 // does the container contain child eleemnts with 'xtype' attributes.
490 // that match this xtype..
491 // note - when we render we create these as well..
492 // so we should check to see if body has xtype set.
493 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
495 var self_cntr_el = Roo.get(this[cntr](false));
496 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
498 //Roo.log(Roo.XComponent.build_from_html);
499 //Roo.log("got echild:");
502 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503 // and are not displayed -this causes this to use up the wrong element when matching.
504 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
507 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
514 //echild.dom.removeAttribute('xtype');
516 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517 Roo.debug && Roo.log(self_cntr_el);
518 Roo.debug && Roo.log(echild);
519 Roo.debug && Roo.log(cn);
525 // if object has flexy:if - then it may or may not be rendered.
526 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
527 // skip a flexy if element.
528 Roo.debug && Roo.log('skipping render');
529 Roo.debug && Roo.log(tree);
531 Roo.debug && Roo.log('skipping all children');
532 skip_children = true;
537 // actually if flexy:foreach is found, we really want to create
538 // multiple copies here...
540 //Roo.log(this[cntr]());
541 // some elements do not have render methods.. like the layouts...
543 if(this[cntr](true) === false){
548 cn.render && cn.render(this[cntr](true));
551 // then add the element..
558 if (typeof (tree.menu) != 'undefined') {
559 tree.menu.parentType = cn.xtype;
560 tree.menu.triggerEl = cn.el;
561 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
565 if (!tree.items || !tree.items.length) {
567 //Roo.log(["no children", this]);
572 var items = tree.items;
575 //Roo.log(items.length);
577 if (!skip_children) {
578 for(var i =0;i < items.length;i++) {
579 // Roo.log(['add child', items[i]]);
580 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
586 //Roo.log("fire childrenrendered");
588 cn.fireEvent('childrenrendered', this);
594 * Set the element that will be used to show or hide
596 setVisibilityEl : function(el)
598 this.visibilityEl = el;
602 * Get the element that will be used to show or hide
604 getVisibilityEl : function()
606 if (typeof(this.visibilityEl) == 'object') {
607 return this.visibilityEl;
610 if (typeof(this.visibilityEl) == 'string') {
611 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
618 * Show a component - removes 'hidden' class
622 if(!this.getVisibilityEl()){
626 this.getVisibilityEl().removeClass(['hidden','d-none']);
628 this.fireEvent('show', this);
633 * Hide a component - adds 'hidden' class
637 if(!this.getVisibilityEl()){
641 this.getVisibilityEl().addClass(['hidden','d-none']);
643 this.fireEvent('hide', this);
656 * @class Roo.bootstrap.Element
657 * @extends Roo.bootstrap.Component
658 * @children Roo.bootstrap.Component
659 * Bootstrap Element class (basically a DIV used to make random stuff )
661 * @cfg {String} html contents of the element
662 * @cfg {String} tag tag of the element
663 * @cfg {String} cls class of the element
664 * @cfg {Boolean} preventDefault (true|false) default false
665 * @cfg {Boolean} clickable (true|false) default false
666 * @cfg {String} role default blank - set to button to force cursor pointer
670 * Create a new Element
671 * @param {Object} config The config object
674 Roo.bootstrap.Element = function(config){
675 Roo.bootstrap.Element.superclass.constructor.call(this, config);
681 * When a element is chick
682 * @param {Roo.bootstrap.Element} this
683 * @param {Roo.EventObject} e
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
696 preventDefault: false,
701 getAutoCreate : function(){
705 // cls: this.cls, double assign in parent class Component.js :: onRender
708 if (this.role !== false) {
709 cfg.role = this.role;
715 initEvents: function()
717 Roo.bootstrap.Element.superclass.initEvents.call(this);
720 this.el.on('click', this.onClick, this);
726 onClick : function(e)
728 if(this.preventDefault){
732 this.fireEvent('click', this, e); // why was this double click before?
740 getValue : function()
742 return this.el.dom.innerHTML;
745 setValue : function(value)
747 this.el.dom.innerHTML = value;
762 * @class Roo.bootstrap.DropTarget
763 * @extends Roo.bootstrap.Element
764 * Bootstrap DropTarget class
766 * @cfg {string} name dropable name
769 * Create a new Dropable Area
770 * @param {Object} config The config object
773 Roo.bootstrap.DropTarget = function(config){
774 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
780 * When a element is chick
781 * @param {Roo.bootstrap.Element} this
782 * @param {Roo.EventObject} e
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
791 getAutoCreate : function(){
796 initEvents: function()
798 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
802 drop : this.dragDrop.createDelegate(this),
803 enter : this.dragEnter.createDelegate(this),
804 out : this.dragOut.createDelegate(this),
805 over : this.dragOver.createDelegate(this)
809 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
812 dragDrop : function(source,e,data)
814 // user has to decide how to impliment this.
817 //this.fireEvent('drop', this, source, e ,data);
821 dragEnter : function(n, dd, e, data)
823 // probably want to resize the element to match the dropped element..
825 this.originalSize = this.el.getSize();
826 this.el.setSize( n.el.getSize());
827 this.dropZone.DDM.refreshCache(this.name);
828 Roo.log([n, dd, e, data]);
831 dragOut : function(value)
833 // resize back to normal
835 this.el.setSize(this.originalSize);
836 this.dropZone.resetConstraints();
839 dragOver : function()
856 * @class Roo.bootstrap.Body
857 * @extends Roo.bootstrap.Component
858 * @children Roo.bootstrap.Component
859 * @parent none builder
860 * Bootstrap Body class
864 * @param {Object} config The config object
867 Roo.bootstrap.Body = function(config){
869 config = config || {};
871 Roo.bootstrap.Body.superclass.constructor.call(this, config);
872 this.el = Roo.get(config.el ? config.el : document.body );
873 if (this.cls && this.cls.length) {
874 Roo.get(document.body).addClass(this.cls);
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
880 is_body : true,// just to make sure it's constructed?
885 onRender : function(ct, position)
887 /* Roo.log("Roo.bootstrap.Body - onRender");
888 if (this.cls && this.cls.length) {
889 Roo.get(document.body).addClass(this.cls);
908 * @class Roo.bootstrap.ButtonGroup
909 * @extends Roo.bootstrap.Component
910 * Bootstrap ButtonGroup class
911 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913 * @cfg {String} size lg | sm | xs (default empty normal)
914 * @cfg {String} align vertical | justified (default none)
915 * @cfg {String} direction up | down (default down)
916 * @cfg {Boolean} toolbar false | true
917 * @cfg {Boolean} btn true | false
922 * @param {Object} config The config object
925 Roo.bootstrap.ButtonGroup = function(config){
926 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
937 getAutoCreate : function(){
943 cfg.html = this.html || cfg.html;
954 if (['vertical','justified'].indexOf(this.align)!==-1) {
955 cfg.cls = 'btn-group-' + this.align;
957 if (this.align == 'justified') {
958 console.log(this.items);
962 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963 cfg.cls += ' btn-group-' + this.size;
966 if (this.direction == 'up') {
967 cfg.cls += ' dropup' ;
973 * Add a button to the group (similar to NavItem API.)
975 addItem : function(cfg)
977 var cn = new Roo.bootstrap.Button(cfg);
979 cn.parentId = this.id;
980 cn.onRender(this.el, null);
994 * @class Roo.bootstrap.Button
995 * @extends Roo.bootstrap.Component
996 * Bootstrap Button class
997 * @cfg {String} html The button content
998 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001 * @cfg {String} size (lg|sm|xs)
1002 * @cfg {String} tag (a|input|submit)
1003 * @cfg {String} href empty or href
1004 * @cfg {Boolean} disabled default false;
1005 * @cfg {Boolean} isClose default false;
1006 * @cfg {String} glyphicon depricated - use fa
1007 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008 * @cfg {String} badge text for badge
1009 * @cfg {String} theme (default|glow)
1010 * @cfg {Boolean} inverse dark themed version
1011 * @cfg {Boolean} toggle is it a slidy toggle button
1012 * @cfg {Boolean} pressed default null - if the button ahs active state
1013 * @cfg {String} ontext text for on slidy toggle state
1014 * @cfg {String} offtext text for off slidy toggle state
1015 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1016 * @cfg {Boolean} removeClass remove the standard class..
1017 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1018 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1022 * Create a new button
1023 * @param {Object} config The config object
1027 Roo.bootstrap.Button = function(config){
1028 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1034 * When a button is pressed
1035 * @param {Roo.bootstrap.Button} btn
1036 * @param {Roo.EventObject} e
1041 * When a button is double clicked
1042 * @param {Roo.bootstrap.Button} btn
1043 * @param {Roo.EventObject} e
1048 * After the button has been toggles
1049 * @param {Roo.bootstrap.Button} btn
1050 * @param {Roo.EventObject} e
1051 * @param {boolean} pressed (also available as button.pressed)
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1078 preventDefault: true,
1087 getAutoCreate : function(){
1095 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097 this.tag = 'button';
1101 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103 if (this.toggle == true) {
1106 cls: 'slider-frame roo-button',
1110 'data-on-text':'ON',
1111 'data-off-text':'OFF',
1112 cls: 'slider-button',
1117 // why are we validating the weights?
1118 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119 cfg.cls += ' ' + this.weight;
1126 cfg.cls += ' close';
1128 cfg["aria-hidden"] = true;
1130 cfg.html = "×";
1136 if (this.theme==='default') {
1137 cfg.cls = 'btn roo-button';
1139 //if (this.parentType != 'Navbar') {
1140 this.weight = this.weight.length ? this.weight : 'default';
1142 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146 cfg.cls += ' btn-' + outline + weight;
1147 if (this.weight == 'default') {
1149 cfg.cls += ' btn-' + this.weight;
1152 } else if (this.theme==='glow') {
1155 cfg.cls = 'btn-glow roo-button';
1157 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159 cfg.cls += ' ' + this.weight;
1165 this.cls += ' inverse';
1169 if (this.active || this.pressed === true) {
1170 cfg.cls += ' active';
1173 if (this.disabled) {
1174 cfg.disabled = 'disabled';
1178 Roo.log('changing to ul' );
1180 this.glyphicon = 'caret';
1181 if (Roo.bootstrap.version == 4) {
1182 this.fa = 'caret-down';
1187 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189 //gsRoo.log(this.parentType);
1190 if (this.parentType === 'Navbar' && !this.parent().bar) {
1191 Roo.log('changing to li?');
1200 href : this.href || '#'
1203 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1204 cfg.cls += ' dropdown';
1211 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1213 if (this.glyphicon) {
1214 cfg.html = ' ' + cfg.html;
1219 cls: 'glyphicon glyphicon-' + this.glyphicon
1224 cfg.html = ' ' + cfg.html;
1229 cls: 'fa fas fa-' + this.fa
1239 // cfg.cls='btn roo-button';
1243 var value = cfg.html;
1248 cls: 'glyphicon glyphicon-' + this.glyphicon,
1255 cls: 'fa fas fa-' + this.fa,
1260 var bw = this.badge_weight.length ? this.badge_weight :
1261 (this.weight.length ? this.weight : 'secondary');
1262 bw = bw == 'default' ? 'secondary' : bw;
1268 cls: 'badge badge-' + bw,
1277 cfg.cls += ' dropdown';
1278 cfg.html = typeof(cfg.html) != 'undefined' ?
1279 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1282 if (cfg.tag !== 'a' && this.href !== '') {
1283 throw "Tag must be a to set href.";
1284 } else if (this.href.length > 0) {
1285 cfg.href = this.href;
1288 if(this.removeClass){
1293 cfg.target = this.target;
1298 initEvents: function() {
1299 // Roo.log('init events?');
1300 // Roo.log(this.el.dom);
1303 if (typeof (this.menu) != 'undefined') {
1304 this.menu.parentType = this.xtype;
1305 this.menu.triggerEl = this.el;
1306 this.addxtype(Roo.apply({}, this.menu));
1310 if (this.el.hasClass('roo-button')) {
1311 this.el.on('click', this.onClick, this);
1312 this.el.on('dblclick', this.onDblClick, this);
1314 this.el.select('.roo-button').on('click', this.onClick, this);
1315 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1319 if(this.removeClass){
1320 this.el.on('click', this.onClick, this);
1323 if (this.group === true) {
1324 if (this.pressed === false || this.pressed === true) {
1327 this.pressed = false;
1328 this.setActive(this.pressed);
1333 this.el.enableDisplayMode();
1336 onClick : function(e)
1338 if (this.disabled) {
1342 Roo.log('button on click ');
1343 if(this.preventDefault){
1352 this.setActive(true);
1353 var pi = this.parent().items;
1354 for (var i = 0;i < pi.length;i++) {
1355 if (this == pi[i]) {
1358 if (pi[i].el.hasClass('roo-button')) {
1359 pi[i].setActive(false);
1362 this.fireEvent('click', this, e);
1366 if (this.pressed === true || this.pressed === false) {
1367 this.toggleActive(e);
1371 this.fireEvent('click', this, e);
1373 onDblClick: function(e)
1375 if (this.disabled) {
1378 if(this.preventDefault){
1381 this.fireEvent('dblclick', this, e);
1384 * Enables this button
1388 this.disabled = false;
1389 this.el.removeClass('disabled');
1390 this.el.dom.removeAttribute("disabled");
1394 * Disable this button
1396 disable : function()
1398 this.disabled = true;
1399 this.el.addClass('disabled');
1400 this.el.attr("disabled", "disabled")
1403 * sets the active state on/off,
1404 * @param {Boolean} state (optional) Force a particular state
1406 setActive : function(v) {
1408 this.el[v ? 'addClass' : 'removeClass']('active');
1412 * toggles the current active state
1414 toggleActive : function(e)
1416 this.setActive(!this.pressed); // this modifies pressed...
1417 this.fireEvent('toggle', this, e, this.pressed);
1420 * get the current active state
1421 * @return {boolean} true if it's active
1423 isActive : function()
1425 return this.el.hasClass('active');
1428 * set the text of the first selected button
1430 setText : function(str)
1432 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1435 * get the text of the first selected button
1437 getText : function()
1439 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1442 setWeight : function(str)
1444 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447 var outline = this.outline ? 'outline-' : '';
1448 if (str == 'default') {
1449 this.el.addClass('btn-default btn-outline-secondary');
1452 this.el.addClass('btn-' + outline + str);
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459 Roo.bootstrap.Button.weights = [
1479 * @class Roo.bootstrap.Column
1480 * @extends Roo.bootstrap.Component
1481 * @children Roo.bootstrap.Component
1482 * Bootstrap Column class
1483 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1493 * @cfg {Boolean} hidden (true|false) hide the element
1494 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495 * @cfg {String} fa (ban|check|...) font awesome icon
1496 * @cfg {Number} fasize (1|2|....) font awsome size
1498 * @cfg {String} icon (info-sign|check|...) glyphicon name
1500 * @cfg {String} html content of column.
1503 * Create a new Column
1504 * @param {Object} config The config object
1507 Roo.bootstrap.Column = function(config){
1508 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1529 getAutoCreate : function(){
1530 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538 var sizes = ['xs','sm','md','lg'];
1539 sizes.map(function(size ,ix){
1540 //Roo.log( size + ':' + settings[size]);
1542 if (settings[size+'off'] !== false) {
1543 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1546 if (settings[size] === false) {
1550 if (!settings[size]) { // 0 = hidden
1551 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553 for (var i = ix; i > -1; i--) {
1554 cfg.cls += ' d-' + sizes[i] + '-none';
1560 cfg.cls += ' col-' + size + '-' + settings[size] + (
1561 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1567 cfg.cls += ' hidden';
1570 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571 cfg.cls +=' alert alert-' + this.alert;
1575 if (this.html.length) {
1576 cfg.html = this.html;
1580 if (this.fasize > 1) {
1581 fasize = ' fa-' + this.fasize + 'x';
1583 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1588 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1607 * @class Roo.bootstrap.Container
1608 * @extends Roo.bootstrap.Component
1609 * @children Roo.bootstrap.Component
1611 * Bootstrap Container class
1612 * @cfg {Boolean} jumbotron is it a jumbotron element
1613 * @cfg {String} html content of element
1614 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1616 * @cfg {String} header content of header (for panel)
1617 * @cfg {String} footer content of footer (for panel)
1618 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619 * @cfg {String} tag (header|aside|section) type of HTML tag.
1620 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621 * @cfg {String} fa font awesome icon
1622 * @cfg {String} icon (info-sign|check|...) glyphicon name
1623 * @cfg {Boolean} hidden (true|false) hide the element
1624 * @cfg {Boolean} expandable (true|false) default false
1625 * @cfg {Boolean} expanded (true|false) default true
1626 * @cfg {String} rheader contet on the right of header
1627 * @cfg {Boolean} clickable (true|false) default false
1631 * Create a new Container
1632 * @param {Object} config The config object
1635 Roo.bootstrap.Container = function(config){
1636 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1642 * After the panel has been expand
1644 * @param {Roo.bootstrap.Container} this
1649 * After the panel has been collapsed
1651 * @param {Roo.bootstrap.Container} this
1656 * When a element is chick
1657 * @param {Roo.bootstrap.Container} this
1658 * @param {Roo.EventObject} e
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1682 getChildContainer : function() {
1688 if (this.panel.length) {
1689 return this.el.select('.panel-body',true).first();
1696 getAutoCreate : function(){
1699 tag : this.tag || 'div',
1703 if (this.jumbotron) {
1704 cfg.cls = 'jumbotron';
1709 // - this is applied by the parent..
1711 // cfg.cls = this.cls + '';
1714 if (this.sticky.length) {
1716 var bd = Roo.get(document.body);
1717 if (!bd.hasClass('bootstrap-sticky')) {
1718 bd.addClass('bootstrap-sticky');
1719 Roo.select('html',true).setStyle('height', '100%');
1722 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726 if (this.well.length) {
1727 switch (this.well) {
1730 cfg.cls +=' well well-' +this.well;
1739 cfg.cls += ' hidden';
1743 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744 cfg.cls +=' alert alert-' + this.alert;
1749 if (this.panel.length) {
1750 cfg.cls += ' panel panel-' + this.panel;
1752 if (this.header.length) {
1756 if(this.expandable){
1758 cfg.cls = cfg.cls + ' expandable';
1762 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1770 cls : 'panel-title',
1771 html : (this.expandable ? ' ' : '') + this.header
1775 cls: 'panel-header-right',
1781 cls : 'panel-heading',
1782 style : this.expandable ? 'cursor: pointer' : '',
1790 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1795 if (this.footer.length) {
1797 cls : 'panel-footer',
1806 body.html = this.html || cfg.html;
1807 // prefix with the icons..
1809 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1812 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1817 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818 cfg.cls = 'container';
1824 initEvents: function()
1826 if(this.expandable){
1827 var headerEl = this.headerEl();
1830 headerEl.on('click', this.onToggleClick, this);
1835 this.el.on('click', this.onClick, this);
1840 onToggleClick : function()
1842 var headerEl = this.headerEl();
1858 if(this.fireEvent('expand', this)) {
1860 this.expanded = true;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864 this.el.select('.panel-body',true).first().removeClass('hide');
1866 var toggleEl = this.toggleEl();
1872 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1877 collapse : function()
1879 if(this.fireEvent('collapse', this)) {
1881 this.expanded = false;
1883 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884 this.el.select('.panel-body',true).first().addClass('hide');
1886 var toggleEl = this.toggleEl();
1892 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896 toggleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902 return this.el.select('.panel-heading .fa',true).first();
1905 headerEl : function()
1907 if(!this.el || !this.panel.length || !this.header.length){
1911 return this.el.select('.panel-heading',true).first()
1916 if(!this.el || !this.panel.length){
1920 return this.el.select('.panel-body',true).first()
1923 titleEl : function()
1925 if(!this.el || !this.panel.length || !this.header.length){
1929 return this.el.select('.panel-title',true).first();
1932 setTitle : function(v)
1934 var titleEl = this.titleEl();
1940 titleEl.dom.innerHTML = v;
1943 getTitle : function()
1946 var titleEl = this.titleEl();
1952 return titleEl.dom.innerHTML;
1955 setRightTitle : function(v)
1957 var t = this.el.select('.panel-header-right',true).first();
1963 t.dom.innerHTML = v;
1966 onClick : function(e)
1970 this.fireEvent('click', this, e);
1975 * @class Roo.bootstrap.Card
1976 * @extends Roo.bootstrap.Component
1977 * @children Roo.bootstrap.Component
1979 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1982 * possible... may not be implemented..
1983 * @cfg {String} header_image src url of image.
1984 * @cfg {String|Object} header
1985 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1988 * @cfg {String} title
1989 * @cfg {String} subtitle
1990 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991 * @cfg {String} footer
1993 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995 * @cfg {String} margin (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003 * @cfg {String} padding (0|1|2|3|4|5)
2004 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006 * @cfg {String} padding_left (0|1|2|3|4|5)
2007 * @cfg {String} padding_right (0|1|2|3|4|5)
2008 * @cfg {String} padding_x (0|1|2|3|4|5)
2009 * @cfg {String} padding_y (0|1|2|3|4|5)
2011 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @config {Boolean} dragable if this card can be dragged.
2018 * @config {String} drag_group group for drag
2019 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2020 * @config {String} drop_group group for drag
2022 * @config {Boolean} collapsable can the body be collapsed.
2023 * @config {Boolean} collapsed is the body collapsed when rendered...
2024 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025 * @config {Boolean} rotated is the body rotated when rendered...
2028 * Create a new Container
2029 * @param {Object} config The config object
2032 Roo.bootstrap.Card = function(config){
2033 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2039 * When a element a card is dropped
2040 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044 * @param {String} position 'above' or 'below'
2045 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2051 * When a element a card is rotate
2052 * @param {Roo.bootstrap.Card} this
2053 * @param {Roo.Element} n the node being dropped?
2054 * @param {Boolean} rotate status
2059 * When a card element is dragged over ready to drop (return false to block dropable)
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Object} data from dragdrop
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2074 margin: '', /// may be better in component?
2104 collapsable : false,
2113 childContainer : false,
2114 dropEl : false, /// the dom placeholde element that indicates drop location.
2115 containerEl: false, // body container
2116 bodyEl: false, // card-body
2117 headerContainerEl : false, //
2119 header_imageEl : false,
2122 layoutCls : function()
2126 Roo.log(this.margin_bottom.length);
2127 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2133 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2138 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144 // more generic support?
2152 // Roo.log("Call onRender: " + this.xtype);
2153 /* We are looking at something like this.
2155 <img src="..." class="card-img-top" alt="...">
2156 <div class="card-body">
2157 <h5 class="card-title">Card title</h5>
2158 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160 >> this bit is really the body...
2161 <div> << we will ad dthis in hopefully it will not break shit.
2163 ** card text does not actually have any styling...
2165 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2168 <a href="#" class="card-link">Card link</a>
2171 <div class="card-footer">
2172 <small class="text-muted">Last updated 3 mins ago</small>
2176 getAutoCreate : function(){
2184 if (this.weight.length && this.weight != 'light') {
2185 cfg.cls += ' text-white';
2187 cfg.cls += ' text-dark'; // need as it's nested..
2189 if (this.weight.length) {
2190 cfg.cls += ' bg-' + this.weight;
2193 cfg.cls += ' ' + this.layoutCls();
2196 var hdr_ctr = false;
2197 if (this.header.length) {
2199 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2214 if (this.collapsable) {
2217 cls : 'd-block user-select-none',
2221 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226 hdr.cn.push(hdr_ctr);
2231 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236 if (this.header_image.length) {
2239 cls : 'card-img-top',
2240 src: this.header_image // escape?
2245 cls : 'card-img-top d-none'
2251 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2255 if (this.collapsable || this.rotateable) {
2258 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2265 if (this.title.length) {
2269 src: this.title // escape?
2273 if (this.subtitle.length) {
2277 src: this.subtitle // escape?
2283 cls : 'roo-card-body-ctr'
2286 if (this.html.length) {
2292 // fixme ? handle objects?
2294 if (this.footer.length) {
2297 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302 cfg.cn.push({cls : 'card-footer d-none'});
2311 getCardHeader : function()
2313 var ret = this.el.select('.card-header',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardFooter : function()
2322 var ret = this.el.select('.card-footer',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardImageTop : function()
2331 var ret = this.header_imageEl;
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2339 getChildContainer : function()
2345 return this.el.select('.roo-card-body-ctr',true).first();
2348 initEvents: function()
2350 this.bodyEl = this.el.select('.card-body',true).first();
2351 this.containerEl = this.getChildContainer();
2353 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354 containerScroll: true,
2355 ddGroup: this.drag_group || 'default_card_drag_group'
2357 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359 if (this.dropable) {
2360 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361 containerScroll: true,
2362 ddGroup: this.drop_group || 'default_card_drag_group'
2364 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371 if (this.collapsable) {
2372 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374 if (this.rotateable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379 this.footerEl = this.el.select('.card-footer',true).first();
2380 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382 this.headerEl = this.el.select('.card-header',true).first();
2385 this.el.addClass('roo-card-rotated');
2386 this.fireEvent('rotate', this, true);
2388 this.header_imageEl = this.el.select('.card-img-top',true).first();
2389 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392 getDragData : function(e)
2394 var target = this.getEl();
2396 //this.handleSelection(e);
2401 nodes: this.getEl(),
2406 dragData.ddel = target.dom ; // the div element
2407 Roo.log(target.getWidth( ));
2408 dragData.ddel.style.width = target.getWidth() + 'px';
2415 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2416 * whole Element becomes the target, and this causes the drop gesture to append.
2418 * Returns an object:
2421 position : 'below' or 'above'
2422 card : relateive to card OBJECT (or true for no cards listed)
2423 items_n : relative to nth item in list
2424 card_n : relative to nth card in list
2429 getTargetFromEvent : function(e, dragged_card_el)
2431 var target = e.getTarget();
2432 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433 target = target.parentNode;
2444 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445 // see if target is one of the 'cards'...
2448 //Roo.log(this.items.length);
2451 var last_card_n = 0;
2453 for (var i = 0;i< this.items.length;i++) {
2455 if (!this.items[i].el.hasClass('card')) {
2458 pos = this.getDropPoint(e, this.items[i].el.dom);
2460 cards_len = ret.cards.length;
2461 //Roo.log(this.items[i].el.dom.id);
2462 ret.cards.push(this.items[i]);
2464 if (ret.card_n < 0 && pos == 'above') {
2465 ret.position = cards_len > 0 ? 'below' : pos;
2466 ret.items_n = i > 0 ? i - 1 : 0;
2467 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2468 ret.card = ret.cards[ret.card_n];
2471 if (!ret.cards.length) {
2473 ret.position = 'below';
2477 // could not find a card.. stick it at the end..
2478 if (ret.card_n < 0) {
2479 ret.card_n = last_card_n;
2480 ret.card = ret.cards[last_card_n];
2481 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482 ret.position = 'below';
2485 if (this.items[ret.items_n].el == dragged_card_el) {
2489 if (ret.position == 'below') {
2490 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492 if (card_after && card_after.el == dragged_card_el) {
2499 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501 if (card_before && card_before.el == dragged_card_el) {
2508 onNodeEnter : function(n, dd, e, data){
2511 onNodeOver : function(n, dd, e, data)
2514 var target_info = this.getTargetFromEvent(e,data.source.el);
2515 if (target_info === false) {
2516 this.dropPlaceHolder('hide');
2519 Roo.log(['getTargetFromEvent', target_info ]);
2522 if (this.fireEvent('cardover', this, [ data ]) === false) {
2526 this.dropPlaceHolder('show', target_info,data);
2530 onNodeOut : function(n, dd, e, data){
2531 this.dropPlaceHolder('hide');
2534 onNodeDrop : function(n, dd, e, data)
2537 // call drop - return false if
2539 // this could actually fail - if the Network drops..
2540 // we will ignore this at present..- client should probably reload
2541 // the whole set of cards if stuff like that fails.
2544 var info = this.getTargetFromEvent(e,data.source.el);
2545 if (info === false) {
2548 this.dropPlaceHolder('hide');
2552 this.acceptCard(data.source, info.position, info.card, info.items_n);
2556 firstChildCard : function()
2558 for (var i = 0;i< this.items.length;i++) {
2560 if (!this.items[i].el.hasClass('card')) {
2563 return this.items[i];
2565 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2572 acceptCard : function(move_card, position, next_to_card )
2574 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580 move_card.parent().removeCard(move_card);
2583 var dom = move_card.el.dom;
2584 dom.style.width = ''; // clear with - which is set by drag.
2586 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587 var cardel = next_to_card.el.dom;
2589 if (position == 'above' ) {
2590 cardel.parentNode.insertBefore(dom, cardel);
2591 } else if (cardel.nextSibling) {
2592 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594 cardel.parentNode.append(dom);
2597 // card container???
2598 this.containerEl.dom.append(dom);
2601 //FIXME HANDLE card = true
2603 // add this to the correct place in items.
2605 // remove Card from items.
2608 if (this.items.length) {
2610 //Roo.log([info.items_n, info.position, this.items.length]);
2611 for (var i =0; i < this.items.length; i++) {
2612 if (i == to_items_n && position == 'above') {
2613 nitems.push(move_card);
2615 nitems.push(this.items[i]);
2616 if (i == to_items_n && position == 'below') {
2617 nitems.push(move_card);
2620 this.items = nitems;
2621 Roo.log(this.items);
2623 this.items.push(move_card);
2626 move_card.parentId = this.id;
2632 removeCard : function(c)
2634 this.items = this.items.filter(function(e) { return e != c });
2637 dom.parentNode.removeChild(dom);
2638 dom.style.width = ''; // clear with - which is set by drag.
2643 /** Decide whether to drop above or below a View node. */
2644 getDropPoint : function(e, n, dd)
2649 if (n == this.containerEl.dom) {
2652 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653 var c = t + (b - t) / 2;
2654 var y = Roo.lib.Event.getPageY(e);
2661 onToggleCollapse : function(e)
2663 if (this.collapsed) {
2664 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665 this.collapsableEl.addClass('show');
2666 this.collapsed = false;
2669 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670 this.collapsableEl.removeClass('show');
2671 this.collapsed = true;
2676 onToggleRotate : function(e)
2678 this.collapsableEl.removeClass('show');
2679 this.footerEl.removeClass('d-none');
2680 this.el.removeClass('roo-card-rotated');
2681 this.el.removeClass('d-none');
2684 this.collapsableEl.addClass('show');
2685 this.rotated = false;
2686 this.fireEvent('rotate', this, this.rotated);
2689 this.el.addClass('roo-card-rotated');
2690 this.footerEl.addClass('d-none');
2691 this.el.select('.roo-collapsable').removeClass('show');
2693 this.rotated = true;
2694 this.fireEvent('rotate', this, this.rotated);
2698 dropPlaceHolder: function (action, info, data)
2700 if (this.dropEl === false) {
2701 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705 this.dropEl.removeClass(['d-none', 'd-block']);
2706 if (action == 'hide') {
2708 this.dropEl.addClass('d-none');
2711 // FIXME - info.card == true!!!
2712 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714 if (info.card !== true) {
2715 var cardel = info.card.el.dom;
2717 if (info.position == 'above') {
2718 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719 } else if (cardel.nextSibling) {
2720 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722 cardel.parentNode.append(this.dropEl.dom);
2725 // card container???
2726 this.containerEl.dom.append(this.dropEl.dom);
2729 this.dropEl.addClass('d-block roo-card-dropzone');
2731 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2738 setHeaderText: function(html)
2741 if (this.headerContainerEl) {
2742 this.headerContainerEl.dom.innerHTML = html;
2745 onHeaderImageLoad : function(ev, he)
2747 if (!this.header_image_fit_square) {
2751 var hw = he.naturalHeight / he.naturalWidth;
2754 //var w = he.dom.naturalWidth;
2757 he.style.position = 'relative';
2759 var nw = (ww * (1/hw));
2760 Roo.get(he).setSize( ww * (1/hw), ww);
2761 he.style.left = ((ww - nw)/ 2) + 'px';
2762 he.style.position = 'relative';
2773 * Card header - holder for the card header elements.
2778 * @class Roo.bootstrap.CardHeader
2779 * @extends Roo.bootstrap.Element
2780 * @parent Roo.bootstrap.Card
2781 * @children Roo.bootstrap.Component
2782 * Bootstrap CardHeader class
2784 * Create a new Card Header - that you can embed children into
2785 * @param {Object} config The config object
2788 Roo.bootstrap.CardHeader = function(config){
2789 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2795 container_method : 'getCardHeader'
2808 * Card footer - holder for the card footer elements.
2813 * @class Roo.bootstrap.CardFooter
2814 * @extends Roo.bootstrap.Element
2815 * @parent Roo.bootstrap.Card
2816 * @children Roo.bootstrap.Component
2817 * Bootstrap CardFooter class
2820 * Create a new Card Footer - that you can embed children into
2821 * @param {Object} config The config object
2824 Roo.bootstrap.CardFooter = function(config){
2825 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2831 container_method : 'getCardFooter'
2844 * Card header - holder for the card header elements.
2849 * @class Roo.bootstrap.CardImageTop
2850 * @extends Roo.bootstrap.Element
2851 * @parent Roo.bootstrap.Card
2852 * @children Roo.bootstrap.Component
2853 * Bootstrap CardImageTop class
2856 * Create a new Card Image Top container
2857 * @param {Object} config The config object
2860 Roo.bootstrap.CardImageTop = function(config){
2861 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2867 container_method : 'getCardImageTop'
2882 * @class Roo.bootstrap.ButtonUploader
2883 * @extends Roo.bootstrap.Button
2884 * Bootstrap Button Uploader class - it's a button which when you add files to it
2887 * @cfg {Number} errorTimeout default 3000
2888 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2889 * @cfg {Array} html The button text.
2890 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2893 * Create a new CardUploader
2894 * @param {Object} config The config object
2897 Roo.bootstrap.ButtonUploader = function(config){
2901 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2907 * @event beforeselect
2908 * When button is pressed, before show upload files dialog is shown
2909 * @param {Roo.bootstrap.UploaderButton} this
2912 'beforeselect' : true,
2914 * @event fired when files have been selected,
2915 * When a the download link is clicked
2916 * @param {Roo.bootstrap.UploaderButton} this
2917 * @param {Array} Array of files that have been uploaded
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2927 errorTimeout : 3000,
2931 fileCollection : false,
2936 getAutoCreate : function()
2941 cls : 'd-none roo-card-upload-selector'
2944 if (this.multiple) {
2945 im.multiple = 'multiple';
2951 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2961 initEvents : function()
2964 Roo.bootstrap.Button.prototype.initEvents.call(this);
2970 this.urlAPI = (window.createObjectURL && window) ||
2971 (window.URL && URL.revokeObjectURL && URL) ||
2972 (window.webkitURL && webkitURL);
2977 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979 this.selectorEl.on('change', this.onFileSelected, this);
2986 onClick : function(e)
2990 if ( this.fireEvent('beforeselect', this) === false) {
2994 this.selectorEl.dom.click();
2998 onFileSelected : function(e)
3002 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3005 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006 this.selectorEl.dom.value = '';// hopefully reset..
3008 this.fireEvent('uploaded', this, files );
3016 * addCard - add an Attachment to the uploader
3017 * @param data - the data about the image to upload
3021 title : "Title of file",
3022 is_uploaded : false,
3023 src : "http://.....",
3024 srcfile : { the File upload object },
3025 mimetype : file.type,
3028 .. any other data...
3053 * @class Roo.bootstrap.Img
3054 * @extends Roo.bootstrap.Component
3055 * Bootstrap Img class
3056 * @cfg {Boolean} imgResponsive false | true
3057 * @cfg {String} border rounded | circle | thumbnail
3058 * @cfg {String} src image source
3059 * @cfg {String} alt image alternative text
3060 * @cfg {String} href a tag href
3061 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062 * @cfg {String} xsUrl xs image source
3063 * @cfg {String} smUrl sm image source
3064 * @cfg {String} mdUrl md image source
3065 * @cfg {String} lgUrl lg image source
3066 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3069 * Create a new Input
3070 * @param {Object} config The config object
3073 Roo.bootstrap.Img = function(config){
3074 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3080 * The img click event for the img.
3081 * @param {Roo.EventObject} e
3086 * The when any image loads
3087 * @param {Roo.EventObject} e
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3095 imgResponsive: true,
3104 backgroundContain : false,
3106 getAutoCreate : function()
3108 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109 return this.createSingleImg();
3114 cls: 'roo-image-responsive-group',
3119 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121 if(!_this[size + 'Url']){
3127 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128 html: _this.html || cfg.html,
3129 src: _this[size + 'Url']
3132 img.cls += ' roo-image-responsive-' + size;
3134 var s = ['xs', 'sm', 'md', 'lg'];
3136 s.splice(s.indexOf(size), 1);
3138 Roo.each(s, function(ss){
3139 img.cls += ' hidden-' + ss;
3142 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143 cfg.cls += ' img-' + _this.border;
3147 cfg.alt = _this.alt;
3160 a.target = _this.target;
3164 cfg.cn.push((_this.href) ? a : img);
3171 createSingleImg : function()
3175 cls: (this.imgResponsive) ? 'img-responsive' : '',
3177 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3180 if (this.backgroundContain) {
3181 cfg.cls += ' background-contain';
3184 cfg.html = this.html || cfg.html;
3186 if (this.backgroundContain) {
3187 cfg.style="background-image: url(" + this.src + ')';
3189 cfg.src = this.src || cfg.src;
3192 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193 cfg.cls += ' img-' + this.border;
3210 a.target = this.target;
3215 return (this.href) ? a : cfg;
3218 initEvents: function()
3221 this.el.on('click', this.onClick, this);
3223 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224 this.el.on('load', this.onImageLoad, this);
3226 // not sure if this works.. not tested
3227 this.el.select('img', true).on('load', this.onImageLoad, this);
3232 onClick : function(e)
3234 Roo.log('img onclick');
3235 this.fireEvent('click', this, e);
3237 onImageLoad: function(e)
3239 Roo.log('img load');
3240 this.fireEvent('load', this, e);
3244 * Sets the url of the image - used to update it
3245 * @param {String} url the url of the image
3248 setSrc : function(url)
3252 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253 if (this.backgroundContain) {
3254 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3256 this.el.dom.src = url;
3261 this.el.select('img', true).first().dom.src = url;
3277 * @class Roo.bootstrap.Link
3278 * @extends Roo.bootstrap.Component
3279 * @children Roo.bootstrap.Component
3280 * Bootstrap Link Class (eg. '<a href>')
3282 * @cfg {String} alt image alternative text
3283 * @cfg {String} href a tag href
3284 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285 * @cfg {String} html the content of the link.
3286 * @cfg {String} anchor name for the anchor link
3287 * @cfg {String} fa - favicon
3289 * @cfg {Boolean} preventDefault (true | false) default false
3293 * Create a new Input
3294 * @param {Object} config The config object
3297 Roo.bootstrap.Link = function(config){
3298 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3304 * The img click event for the img.
3305 * @param {Roo.EventObject} e
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3315 preventDefault: false,
3321 getAutoCreate : function()
3323 var html = this.html || '';
3325 if (this.fa !== false) {
3326 html = '<i class="fa fa-' + this.fa + '"></i>';
3331 // anchor's do not require html/href...
3332 if (this.anchor === false) {
3334 cfg.href = this.href || '#';
3336 cfg.name = this.anchor;
3337 if (this.html !== false || this.fa !== false) {
3340 if (this.href !== false) {
3341 cfg.href = this.href;
3345 if(this.alt !== false){
3350 if(this.target !== false) {
3351 cfg.target = this.target;
3357 initEvents: function() {
3359 if(!this.href || this.preventDefault){
3360 this.el.on('click', this.onClick, this);
3364 onClick : function(e)
3366 if(this.preventDefault){
3369 //Roo.log('img onclick');
3370 this.fireEvent('click', this, e);
3383 * @class Roo.bootstrap.Header
3384 * @extends Roo.bootstrap.Component
3385 * @children Roo.bootstrap.Component
3386 * Bootstrap Header class
3389 * @cfg {String} html content of header
3390 * @cfg {Number} level (1|2|3|4|5|6) default 1
3393 * Create a new Header
3394 * @param {Object} config The config object
3398 Roo.bootstrap.Header = function(config){
3399 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3410 getAutoCreate : function(){
3415 tag: 'h' + (1 *this.level),
3416 html: this.html || ''
3427 * @class Roo.bootstrap.MenuMgr
3429 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3432 Roo.bootstrap.menu.Manager = function(){
3433 var menus, active, groups = {}, attached = false, lastShow = new Date();
3435 // private - called when first menu is created
3438 active = new Roo.util.MixedCollection();
3439 Roo.get(document).addKeyListener(27, function(){
3440 if(active.length > 0){
3448 if(active && active.length > 0){
3449 var c = active.clone();
3459 if(active.length < 1){
3460 Roo.get(document).un("mouseup", onMouseDown);
3468 var last = active.last();
3469 lastShow = new Date();
3472 Roo.get(document).on("mouseup", onMouseDown);
3477 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478 m.parentMenu.activeChild = m;
3479 }else if(last && last.isVisible()){
3480 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3485 function onBeforeHide(m){
3487 m.activeChild.hide();
3489 if(m.autoHideTimer){
3490 clearTimeout(m.autoHideTimer);
3491 delete m.autoHideTimer;
3496 function onBeforeShow(m){
3497 var pm = m.parentMenu;
3498 if(!pm && !m.allowOtherMenus){
3500 }else if(pm && pm.activeChild && active != m){
3501 pm.activeChild.hide();
3505 // private this should really trigger on mouseup..
3506 function onMouseDown(e){
3507 Roo.log("on Mouse Up");
3509 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510 Roo.log("MenuManager hideAll");
3519 function onBeforeCheck(mi, state){
3521 var g = groups[mi.group];
3522 for(var i = 0, l = g.length; i < l; i++){
3524 g[i].setChecked(false);
3533 * Hides all menus that are currently visible
3535 hideAll : function(){
3540 register : function(menu){
3544 menus[menu.id] = menu;
3545 menu.on("beforehide", onBeforeHide);
3546 menu.on("hide", onHide);
3547 menu.on("beforeshow", onBeforeShow);
3548 menu.on("show", onShow);
3550 if(g && menu.events["checkchange"]){
3554 groups[g].push(menu);
3555 menu.on("checkchange", onCheck);
3560 * Returns a {@link Roo.menu.Menu} object
3561 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562 * be used to generate and return a new Menu instance.
3564 get : function(menu){
3565 if(typeof menu == "string"){ // menu id
3567 }else if(menu.events){ // menu instance
3570 /*else if(typeof menu.length == 'number'){ // array of menu items?
3571 return new Roo.bootstrap.Menu({items:menu});
3572 }else{ // otherwise, must be a config
3573 return new Roo.bootstrap.Menu(menu);
3580 unregister : function(menu){
3581 delete menus[menu.id];
3582 menu.un("beforehide", onBeforeHide);
3583 menu.un("hide", onHide);
3584 menu.un("beforeshow", onBeforeShow);
3585 menu.un("show", onShow);
3587 if(g && menu.events["checkchange"]){
3588 groups[g].remove(menu);
3589 menu.un("checkchange", onCheck);
3594 registerCheckable : function(menuItem){
3595 var g = menuItem.group;
3600 groups[g].push(menuItem);
3601 menuItem.on("beforecheckchange", onBeforeCheck);
3606 unregisterCheckable : function(menuItem){
3607 var g = menuItem.group;
3609 groups[g].remove(menuItem);
3610 menuItem.un("beforecheckchange", onBeforeCheck);
3616 * @class Roo.bootstrap.menu.Menu
3617 * @extends Roo.bootstrap.Component
3619 * @children Roo.bootstrap.menu.Item
3621 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3623 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624 * @cfg {bool} hidden if the menu should be hidden when rendered.
3625 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3626 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3628 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3632 * @param {Object} config The config objectQ
3636 Roo.bootstrap.menu.Menu = function(config){
3638 if (config.type == 'treeview') {
3639 // normally menu's are drawn attached to the document to handle layering etc..
3640 // however treeview (used by the docs menu is drawn into the parent element)
3641 this.container_method = 'getChildContainer';
3644 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645 if (this.registerMenu && this.type != 'treeview') {
3646 Roo.bootstrap.menu.Manager.register(this);
3653 * Fires before this menu is displayed (return false to block)
3654 * @param {Roo.menu.Menu} this
3659 * Fires before this menu is hidden (return false to block)
3660 * @param {Roo.menu.Menu} this
3665 * Fires after this menu is displayed
3666 * @param {Roo.menu.Menu} this
3671 * Fires after this menu is hidden
3672 * @param {Roo.menu.Menu} this
3677 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678 * @param {Roo.menu.Menu} this
3679 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * @param {Roo.EventObject} e
3685 * Fires when the mouse is hovering over this menu
3686 * @param {Roo.menu.Menu} this
3687 * @param {Roo.EventObject} e
3688 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3693 * Fires when the mouse exits this menu
3694 * @param {Roo.menu.Menu} this
3695 * @param {Roo.EventObject} e
3696 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3701 * Fires when a menu item contained in this menu is clicked
3702 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703 * @param {Roo.EventObject} e
3707 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3714 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3717 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719 registerMenu : true,
3721 menuItems :false, // stores the menu items..
3731 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733 hideTrigger : false,
3738 getChildContainer : function() {
3742 getAutoCreate : function(){
3744 //if (['right'].indexOf(this.align)!==-1) {
3745 // cfg.cn[1].cls += ' pull-right'
3750 cls : 'dropdown-menu shadow' ,
3751 style : 'z-index:1000'
3755 if (this.type === 'submenu') {
3756 cfg.cls = 'submenu active';
3758 if (this.type === 'treeview') {
3759 cfg.cls = 'treeview-menu';
3764 initEvents : function() {
3766 // Roo.log("ADD event");
3767 // Roo.log(this.triggerEl.dom);
3768 if (this.triggerEl) {
3770 this.triggerEl.on('click', this.onTriggerClick, this);
3772 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774 if (!this.hideTrigger) {
3775 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776 // dropdown toggle on the 'a' in BS4?
3777 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779 this.triggerEl.addClass('dropdown-toggle');
3785 this.el.on('touchstart' , this.onTouch, this);
3787 this.el.on('click' , this.onClick, this);
3789 this.el.on("mouseover", this.onMouseOver, this);
3790 this.el.on("mouseout", this.onMouseOut, this);
3794 findTargetItem : function(e)
3796 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3800 //Roo.log(t); Roo.log(t.id);
3802 //Roo.log(this.menuitems);
3803 return this.menuitems.get(t.id);
3805 //return this.items.get(t.menuItemId);
3811 onTouch : function(e)
3813 Roo.log("menu.onTouch");
3814 //e.stopEvent(); this make the user popdown broken
3818 onClick : function(e)
3820 Roo.log("menu.onClick");
3822 var t = this.findTargetItem(e);
3823 if(!t || t.isContainer){
3828 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3829 if(t == this.activeItem && t.shouldDeactivate(e)){
3830 this.activeItem.deactivate();
3831 delete this.activeItem;
3835 this.setActiveItem(t, true);
3843 Roo.log('pass click event');
3847 this.fireEvent("click", this, t, e);
3851 if(!t.href.length || t.href == '#'){
3852 (function() { _this.hide(); }).defer(100);
3857 onMouseOver : function(e){
3858 var t = this.findTargetItem(e);
3861 // if(t.canActivate && !t.disabled){
3862 // this.setActiveItem(t, true);
3866 this.fireEvent("mouseover", this, e, t);
3868 isVisible : function(){
3869 return !this.hidden;
3871 onMouseOut : function(e){
3872 var t = this.findTargetItem(e);
3875 // if(t == this.activeItem && t.shouldDeactivate(e)){
3876 // this.activeItem.deactivate();
3877 // delete this.activeItem;
3880 this.fireEvent("mouseout", this, e, t);
3885 * Displays this menu relative to another element
3886 * @param {String/HTMLElement/Roo.Element} element The element to align to
3887 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888 * the element (defaults to this.defaultAlign)
3889 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891 show : function(el, pos, parentMenu)
3893 if (false === this.fireEvent("beforeshow", this)) {
3894 Roo.log("show canceled");
3897 this.parentMenu = parentMenu;
3901 this.el.addClass('show'); // show otherwise we do not know how big we are..
3903 var xy = this.el.getAlignToXY(el, pos);
3905 // bl-tl << left align below
3906 // tl-bl << left align
3908 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909 // if it goes to far to the right.. -> align left.
3910 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3913 // was left align - go right?
3914 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3917 // goes down the bottom
3918 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920 var a = this.align.replace('?', '').split('-');
3921 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3925 this.showAt( xy , parentMenu, false);
3928 * Displays this menu at a specific xy position
3929 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932 showAt : function(xy, parentMenu, /* private: */_e){
3933 this.parentMenu = parentMenu;
3938 this.fireEvent("beforeshow", this);
3939 //xy = this.el.adjustForConstraints(xy);
3943 this.hideMenuItems();
3944 this.hidden = false;
3945 if (this.triggerEl) {
3946 this.triggerEl.addClass('open');
3949 this.el.addClass('show');
3953 // reassign x when hitting right
3955 // reassign y when hitting bottom
3957 // but the list may align on trigger left or trigger top... should it be a properity?
3959 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3964 this.fireEvent("show", this);
3970 this.doFocus.defer(50, this);
3974 doFocus : function(){
3976 this.focusEl.focus();
3981 * Hides this menu and optionally all parent menus
3982 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984 hide : function(deep)
3986 if (false === this.fireEvent("beforehide", this)) {
3987 Roo.log("hide canceled");
3990 this.hideMenuItems();
3991 if(this.el && this.isVisible()){
3993 if(this.activeItem){
3994 this.activeItem.deactivate();
3995 this.activeItem = null;
3997 if (this.triggerEl) {
3998 this.triggerEl.removeClass('open');
4001 this.el.removeClass('show');
4003 this.fireEvent("hide", this);
4005 if(deep === true && this.parentMenu){
4006 this.parentMenu.hide(true);
4010 onTriggerClick : function(e)
4012 Roo.log('trigger click');
4014 var target = e.getTarget();
4016 Roo.log(target.nodeName.toLowerCase());
4018 if(target.nodeName.toLowerCase() === 'i'){
4024 onTriggerPress : function(e)
4026 Roo.log('trigger press');
4027 //Roo.log(e.getTarget());
4028 // Roo.log(this.triggerEl.dom);
4030 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031 var pel = Roo.get(e.getTarget());
4032 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033 Roo.log('is treeview or dropdown?');
4037 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4041 if (this.isVisible()) {
4047 this.show(this.triggerEl, this.align, false);
4050 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4057 hideMenuItems : function()
4059 Roo.log("hide Menu Items");
4064 this.el.select('.open',true).each(function(aa) {
4066 aa.removeClass('open');
4070 addxtypeChild : function (tree, cntr) {
4071 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073 this.menuitems.add(comp);
4085 this.getEl().dom.innerHTML = '';
4086 this.menuitems.clear();
4092 * @class Roo.bootstrap.menu.Item
4093 * @extends Roo.bootstrap.Component
4094 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095 * @parent Roo.bootstrap.menu.Menu
4097 * Bootstrap MenuItem class
4099 * @cfg {String} html the menu label
4100 * @cfg {String} href the link
4101 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103 * @cfg {Boolean} active used on sidebars to highlight active itesm
4104 * @cfg {String} fa favicon to show on left of menu item.
4105 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4109 * Create a new MenuItem
4110 * @param {Object} config The config object
4114 Roo.bootstrap.menu.Item = function(config){
4115 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4120 * The raw click event for the entire grid.
4121 * @param {Roo.bootstrap.menu.Item} this
4122 * @param {Roo.EventObject} e
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4132 preventDefault: false,
4133 isContainer : false,
4137 getAutoCreate : function(){
4139 if(this.isContainer){
4142 cls: 'dropdown-menu-item '
4152 cls : 'dropdown-item',
4157 if (this.fa !== false) {
4160 cls : 'fa fa-' + this.fa
4169 cls: 'dropdown-menu-item',
4172 if (this.parent().type == 'treeview') {
4173 cfg.cls = 'treeview-menu';
4176 cfg.cls += ' active';
4181 anc.href = this.href || cfg.cn[0].href ;
4182 ctag.html = this.html || cfg.cn[0].html ;
4186 initEvents: function()
4188 if (this.parent().type == 'treeview') {
4189 this.el.select('a').on('click', this.onClick, this);
4193 this.menu.parentType = this.xtype;
4194 this.menu.triggerEl = this.el;
4195 this.menu = this.addxtype(Roo.apply({}, this.menu));
4199 onClick : function(e)
4201 Roo.log('item on click ');
4203 if(this.preventDefault){
4206 //this.parent().hideMenuItems();
4208 this.fireEvent('click', this, e);
4222 * @class Roo.bootstrap.menu.Separator
4223 * @extends Roo.bootstrap.Component
4225 * @parent Roo.bootstrap.menu.Menu
4226 * Bootstrap Separator class
4229 * Create a new Separator
4230 * @param {Object} config The config object
4234 Roo.bootstrap.menu.Separator = function(config){
4235 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4240 getAutoCreate : function(){
4243 cls: 'dropdown-divider divider'
4259 * @class Roo.bootstrap.Modal
4260 * @extends Roo.bootstrap.Component
4261 * @parent none builder
4262 * @children Roo.bootstrap.Component
4263 * Bootstrap Modal class
4264 * @cfg {String} title Title of dialog
4265 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4267 * @cfg {Boolean} specificTitle default false
4268 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270 * @cfg {Boolean} animate default true
4271 * @cfg {Boolean} allow_close default true
4272 * @cfg {Boolean} fitwindow default false
4273 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276 * @cfg {String} size (sm|lg|xl) default empty
4277 * @cfg {Number} max_width set the max width of modal
4278 * @cfg {Boolean} editableTitle can the title be edited
4283 * Create a new Modal Dialog
4284 * @param {Object} config The config object
4287 Roo.bootstrap.Modal = function(config){
4288 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4293 * The raw btnclick event for the button
4294 * @param {Roo.EventObject} e
4299 * Fire when dialog resize
4300 * @param {Roo.bootstrap.Modal} this
4301 * @param {Roo.EventObject} e
4305 * @event titlechanged
4306 * Fire when the editable title has been changed
4307 * @param {Roo.bootstrap.Modal} this
4308 * @param {Roo.EventObject} value
4310 "titlechanged" : true
4313 this.buttons = this.buttons || [];
4316 this.tmpl = Roo.factory(this.tmpl);
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4323 title : 'test dialog',
4333 specificTitle: false,
4335 buttonPosition: 'right',
4357 editableTitle : false,
4359 onRender : function(ct, position)
4361 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364 var cfg = Roo.apply({}, this.getAutoCreate());
4367 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4369 //if (!cfg.name.length) {
4373 cfg.cls += ' ' + this.cls;
4376 cfg.style = this.style;
4378 this.el = Roo.get(document.body).createChild(cfg, position);
4380 //var type = this.el.dom.type;
4383 if(this.tabIndex !== undefined){
4384 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387 this.dialogEl = this.el.select('.modal-dialog',true).first();
4388 this.bodyEl = this.el.select('.modal-body',true).first();
4389 this.closeEl = this.el.select('.modal-header .close', true).first();
4390 this.headerEl = this.el.select('.modal-header',true).first();
4391 this.titleEl = this.el.select('.modal-title',true).first();
4392 this.footerEl = this.el.select('.modal-footer',true).first();
4394 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4396 //this.el.addClass("x-dlg-modal");
4398 if (this.buttons.length) {
4399 Roo.each(this.buttons, function(bb) {
4400 var b = Roo.apply({}, bb);
4401 b.xns = b.xns || Roo.bootstrap;
4402 b.xtype = b.xtype || 'Button';
4403 if (typeof(b.listeners) == 'undefined') {
4404 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4407 var btn = Roo.factory(b);
4409 btn.render(this.getButtonContainer());
4413 // render the children.
4416 if(typeof(this.items) != 'undefined'){
4417 var items = this.items;
4420 for(var i =0;i < items.length;i++) {
4421 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425 this.items = nitems;
4427 // where are these used - they used to be body/close/footer
4431 //this.el.addClass([this.fieldClass, this.cls]);
4435 getAutoCreate : function()
4437 // we will default to modal-body-overflow - might need to remove or make optional later.
4439 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4440 html : this.html || ''
4445 cls : 'modal-title',
4449 if(this.specificTitle){ // WTF is this?
4454 if (this.allow_close && Roo.bootstrap.version == 3) {
4464 if (this.editableTitle) {
4466 cls: 'form-control roo-editable-title d-none',
4472 if (this.allow_close && Roo.bootstrap.version == 4) {
4482 if(this.size.length){
4483 size = 'modal-' + this.size;
4486 var footer = Roo.bootstrap.version == 3 ?
4488 cls : 'modal-footer',
4492 cls: 'btn-' + this.buttonPosition
4497 { // BS4 uses mr-auto on left buttons....
4498 cls : 'modal-footer'
4509 cls: "modal-dialog " + size,
4512 cls : "modal-content",
4515 cls : 'modal-header',
4530 modal.cls += ' fade';
4536 getChildContainer : function() {
4541 getButtonContainer : function() {
4543 return Roo.bootstrap.version == 4 ?
4544 this.el.select('.modal-footer',true).first()
4545 : this.el.select('.modal-footer div',true).first();
4548 initEvents : function()
4550 if (this.allow_close) {
4551 this.closeEl.on('click', this.hide, this);
4553 Roo.EventManager.onWindowResize(this.resize, this, true);
4554 if (this.editableTitle) {
4555 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4556 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557 this.headerEditEl.on('keyup', function(e) {
4558 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559 this.toggleHeaderInput(false)
4562 this.headerEditEl.on('blur', function(e) {
4563 this.toggleHeaderInput(false)
4572 this.maskEl.setSize(
4573 Roo.lib.Dom.getViewWidth(true),
4574 Roo.lib.Dom.getViewHeight(true)
4577 if (this.fitwindow) {
4579 this.dialogEl.setStyle( { 'max-width' : '100%' });
4581 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4587 if(this.max_width !== 0) {
4589 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592 this.setSize(w, this.height);
4596 if(this.max_height) {
4597 this.setSize(w,Math.min(
4599 Roo.lib.Dom.getViewportHeight(true) - 60
4605 if(!this.fit_content) {
4606 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4610 this.setSize(w, Math.min(
4612 this.headerEl.getHeight() +
4613 this.footerEl.getHeight() +
4614 this.getChildHeight(this.bodyEl.dom.childNodes),
4615 Roo.lib.Dom.getViewportHeight(true) - 60)
4621 setSize : function(w,h)
4632 if (!this.rendered) {
4635 this.toggleHeaderInput(false);
4636 //this.el.setStyle('display', 'block');
4637 this.el.removeClass('hideing');
4638 this.el.dom.style.display='block';
4640 Roo.get(document.body).addClass('modal-open');
4642 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4645 this.el.addClass('show');
4646 this.el.addClass('in');
4649 this.el.addClass('show');
4650 this.el.addClass('in');
4653 // not sure how we can show data in here..
4655 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4658 Roo.get(document.body).addClass("x-body-masked");
4660 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4661 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4662 this.maskEl.dom.style.display = 'block';
4663 this.maskEl.addClass('show');
4668 this.fireEvent('show', this);
4670 // set zindex here - otherwise it appears to be ignored...
4671 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4674 this.items.forEach( function(e) {
4675 e.layout ? e.layout() : false;
4683 if(this.fireEvent("beforehide", this) !== false){
4685 this.maskEl.removeClass('show');
4687 this.maskEl.dom.style.display = '';
4688 Roo.get(document.body).removeClass("x-body-masked");
4689 this.el.removeClass('in');
4690 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4692 if(this.animate){ // why
4693 this.el.addClass('hideing');
4694 this.el.removeClass('show');
4696 if (!this.el.hasClass('hideing')) {
4697 return; // it's been shown again...
4700 this.el.dom.style.display='';
4702 Roo.get(document.body).removeClass('modal-open');
4703 this.el.removeClass('hideing');
4707 this.el.removeClass('show');
4708 this.el.dom.style.display='';
4709 Roo.get(document.body).removeClass('modal-open');
4712 this.fireEvent('hide', this);
4715 isVisible : function()
4718 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4722 addButton : function(str, cb)
4726 var b = Roo.apply({}, { html : str } );
4727 b.xns = b.xns || Roo.bootstrap;
4728 b.xtype = b.xtype || 'Button';
4729 if (typeof(b.listeners) == 'undefined') {
4730 b.listeners = { click : cb.createDelegate(this) };
4733 var btn = Roo.factory(b);
4735 btn.render(this.getButtonContainer());
4741 setDefaultButton : function(btn)
4743 //this.el.select('.modal-footer').()
4746 resizeTo: function(w,h)
4748 this.dialogEl.setWidth(w);
4750 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4752 this.bodyEl.setHeight(h - diff);
4754 this.fireEvent('resize', this);
4757 setContentSize : function(w, h)
4761 onButtonClick: function(btn,e)
4764 this.fireEvent('btnclick', btn.name, e);
4767 * Set the title of the Dialog
4768 * @param {String} str new Title
4770 setTitle: function(str) {
4771 this.titleEl.dom.innerHTML = str;
4775 * Set the body of the Dialog
4776 * @param {String} str new Title
4778 setBody: function(str) {
4779 this.bodyEl.dom.innerHTML = str;
4782 * Set the body of the Dialog using the template
4783 * @param {Obj} data - apply this data to the template and replace the body contents.
4785 applyBody: function(obj)
4788 Roo.log("Error - using apply Body without a template");
4791 this.tmpl.overwrite(this.bodyEl, obj);
4794 getChildHeight : function(child_nodes)
4798 child_nodes.length == 0
4803 var child_height = 0;
4805 for(var i = 0; i < child_nodes.length; i++) {
4808 * for modal with tabs...
4809 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4811 var layout_childs = child_nodes[i].childNodes;
4813 for(var j = 0; j < layout_childs.length; j++) {
4815 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4817 var layout_body_childs = layout_childs[j].childNodes;
4819 for(var k = 0; k < layout_body_childs.length; k++) {
4821 if(layout_body_childs[k].classList.contains('navbar')) {
4822 child_height += layout_body_childs[k].offsetHeight;
4826 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4828 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4830 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4832 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4833 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4848 child_height += child_nodes[i].offsetHeight;
4849 // Roo.log(child_nodes[i].offsetHeight);
4852 return child_height;
4854 toggleHeaderInput : function(is_edit)
4856 if (!this.editableTitle) {
4857 return; // not editable.
4859 if (is_edit && this.is_header_editing) {
4860 return; // already editing..
4864 this.headerEditEl.dom.value = this.title;
4865 this.headerEditEl.removeClass('d-none');
4866 this.headerEditEl.dom.focus();
4867 this.titleEl.addClass('d-none');
4869 this.is_header_editing = true;
4872 // flip back to not editing.
4873 this.title = this.headerEditEl.dom.value;
4874 this.headerEditEl.addClass('d-none');
4875 this.titleEl.removeClass('d-none');
4876 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4877 this.is_header_editing = false;
4878 this.fireEvent('titlechanged', this, this.title);
4887 Roo.apply(Roo.bootstrap.Modal, {
4889 * Button config that displays a single OK button
4898 * Button config that displays Yes and No buttons
4914 * Button config that displays OK and Cancel buttons
4929 * Button config that displays Yes, No and Cancel buttons
4954 * messagebox - can be used as a replace
4958 * @class Roo.MessageBox
4959 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4963 Roo.Msg.alert('Status', 'Changes saved successfully.');
4965 // Prompt for user data:
4966 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4968 // process text value...
4972 // Show a dialog using config options:
4974 title:'Save Changes?',
4975 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4976 buttons: Roo.Msg.YESNOCANCEL,
4983 Roo.bootstrap.MessageBox = function(){
4984 var dlg, opt, mask, waitTimer;
4985 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4986 var buttons, activeTextEl, bwidth;
4990 var handleButton = function(button){
4992 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4996 var handleHide = function(){
4998 dlg.el.removeClass(opt.cls);
5001 // Roo.TaskMgr.stop(waitTimer);
5002 // waitTimer = null;
5007 var updateButtons = function(b){
5010 buttons["ok"].hide();
5011 buttons["cancel"].hide();
5012 buttons["yes"].hide();
5013 buttons["no"].hide();
5014 dlg.footerEl.hide();
5018 dlg.footerEl.show();
5019 for(var k in buttons){
5020 if(typeof buttons[k] != "function"){
5023 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5024 width += buttons[k].el.getWidth()+15;
5034 var handleEsc = function(d, k, e){
5035 if(opt && opt.closable !== false){
5045 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5046 * @return {Roo.BasicDialog} The BasicDialog element
5048 getDialog : function(){
5050 dlg = new Roo.bootstrap.Modal( {
5053 //constraintoviewport:false,
5055 //collapsible : false,
5060 //buttonAlign:"center",
5061 closeClick : function(){
5062 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5065 handleButton("cancel");
5070 dlg.on("hide", handleHide);
5072 //dlg.addKeyListener(27, handleEsc);
5074 this.buttons = buttons;
5075 var bt = this.buttonText;
5076 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5077 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5078 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5079 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5081 bodyEl = dlg.bodyEl.createChild({
5083 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5084 '<textarea class="roo-mb-textarea"></textarea>' +
5085 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5087 msgEl = bodyEl.dom.firstChild;
5088 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5089 textboxEl.enableDisplayMode();
5090 textboxEl.addKeyListener([10,13], function(){
5091 if(dlg.isVisible() && opt && opt.buttons){
5094 }else if(opt.buttons.yes){
5095 handleButton("yes");
5099 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5100 textareaEl.enableDisplayMode();
5101 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5102 progressEl.enableDisplayMode();
5104 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5105 var pf = progressEl.dom.firstChild;
5107 pp = Roo.get(pf.firstChild);
5108 pp.setHeight(pf.offsetHeight);
5116 * Updates the message box body text
5117 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5118 * the XHTML-compliant non-breaking space character '&#160;')
5119 * @return {Roo.MessageBox} This message box
5121 updateText : function(text)
5123 if(!dlg.isVisible() && !opt.width){
5124 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5125 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5127 msgEl.innerHTML = text || ' ';
5129 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5130 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5132 Math.min(opt.width || cw , this.maxWidth),
5133 Math.max(opt.minWidth || this.minWidth, bwidth)
5136 activeTextEl.setWidth(w);
5138 if(dlg.isVisible()){
5139 dlg.fixedcenter = false;
5141 // to big, make it scroll. = But as usual stupid IE does not support
5144 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5145 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5146 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5148 bodyEl.dom.style.height = '';
5149 bodyEl.dom.style.overflowY = '';
5152 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5154 bodyEl.dom.style.overflowX = '';
5157 dlg.setContentSize(w, bodyEl.getHeight());
5158 if(dlg.isVisible()){
5159 dlg.fixedcenter = true;
5165 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5166 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5167 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5168 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5169 * @return {Roo.MessageBox} This message box
5171 updateProgress : function(value, text){
5173 this.updateText(text);
5176 if (pp) { // weird bug on my firefox - for some reason this is not defined
5177 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5178 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5184 * Returns true if the message box is currently displayed
5185 * @return {Boolean} True if the message box is visible, else false
5187 isVisible : function(){
5188 return dlg && dlg.isVisible();
5192 * Hides the message box if it is displayed
5195 if(this.isVisible()){
5201 * Displays a new message box, or reinitializes an existing message box, based on the config options
5202 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5203 * The following config object properties are supported:
5205 Property Type Description
5206 ---------- --------------- ------------------------------------------------------------------------------------
5207 animEl String/Element An id or Element from which the message box should animate as it opens and
5208 closes (defaults to undefined)
5209 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5210 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5211 closable Boolean False to hide the top-right close button (defaults to true). Note that
5212 progress and wait dialogs will ignore this property and always hide the
5213 close button as they can only be closed programmatically.
5214 cls String A custom CSS class to apply to the message box element
5215 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5216 displayed (defaults to 75)
5217 fn Function A callback function to execute after closing the dialog. The arguments to the
5218 function will be btn (the name of the button that was clicked, if applicable,
5219 e.g. "ok"), and text (the value of the active text field, if applicable).
5220 Progress and wait dialogs will ignore this option since they do not respond to
5221 user actions and can only be closed programmatically, so any required function
5222 should be called by the same code after it closes the dialog.
5223 icon String A CSS class that provides a background image to be used as an icon for
5224 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5225 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5226 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5227 modal Boolean False to allow user interaction with the page while the message box is
5228 displayed (defaults to true)
5229 msg String A string that will replace the existing message box body text (defaults
5230 to the XHTML-compliant non-breaking space character ' ')
5231 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5232 progress Boolean True to display a progress bar (defaults to false)
5233 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5234 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5235 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5236 title String The title text
5237 value String The string value to set into the active textbox element if displayed
5238 wait Boolean True to display a progress bar (defaults to false)
5239 width Number The width of the dialog in pixels
5246 msg: 'Please enter your address:',
5248 buttons: Roo.MessageBox.OKCANCEL,
5251 animEl: 'addAddressBtn'
5254 * @param {Object} config Configuration options
5255 * @return {Roo.MessageBox} This message box
5257 show : function(options)
5260 // this causes nightmares if you show one dialog after another
5261 // especially on callbacks..
5263 if(this.isVisible()){
5266 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5267 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5268 Roo.log("New Dialog Message:" + options.msg )
5269 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5270 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5273 var d = this.getDialog();
5275 d.setTitle(opt.title || " ");
5276 d.closeEl.setDisplayed(opt.closable !== false);
5277 activeTextEl = textboxEl;
5278 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5283 textareaEl.setHeight(typeof opt.multiline == "number" ?
5284 opt.multiline : this.defaultTextHeight);
5285 activeTextEl = textareaEl;
5294 progressEl.setDisplayed(opt.progress === true);
5296 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5298 this.updateProgress(0);
5299 activeTextEl.dom.value = opt.value || "";
5301 dlg.setDefaultButton(activeTextEl);
5303 var bs = opt.buttons;
5307 }else if(bs && bs.yes){
5308 db = buttons["yes"];
5310 dlg.setDefaultButton(db);
5312 bwidth = updateButtons(opt.buttons);
5313 this.updateText(opt.msg);
5315 d.el.addClass(opt.cls);
5317 d.proxyDrag = opt.proxyDrag === true;
5318 d.modal = opt.modal !== false;
5319 d.mask = opt.modal !== false ? mask : false;
5321 // force it to the end of the z-index stack so it gets a cursor in FF
5322 document.body.appendChild(dlg.el.dom);
5323 d.animateTarget = null;
5324 d.show(options.animEl);
5330 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5331 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5332 * and closing the message box when the process is complete.
5333 * @param {String} title The title bar text
5334 * @param {String} msg The message box body text
5335 * @return {Roo.MessageBox} This message box
5337 progress : function(title, msg){
5344 minWidth: this.minProgressWidth,
5351 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5352 * If a callback function is passed it will be called after the user clicks the button, and the
5353 * id of the button that was clicked will be passed as the only parameter to the callback
5354 * (could also be the top-right close button).
5355 * @param {String} title The title bar text
5356 * @param {String} msg The message box body text
5357 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5358 * @param {Object} scope (optional) The scope of the callback function
5359 * @return {Roo.MessageBox} This message box
5361 alert : function(title, msg, fn, scope)
5376 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5377 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5378 * You are responsible for closing the message box when the process is complete.
5379 * @param {String} msg The message box body text
5380 * @param {String} title (optional) The title bar text
5381 * @return {Roo.MessageBox} This message box
5383 wait : function(msg, title){
5394 waitTimer = Roo.TaskMgr.start({
5396 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5404 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5405 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5406 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5407 * @param {String} title The title bar text
5408 * @param {String} msg The message box body text
5409 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5410 * @param {Object} scope (optional) The scope of the callback function
5411 * @return {Roo.MessageBox} This message box
5413 confirm : function(title, msg, fn, scope){
5417 buttons: this.YESNO,
5426 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5427 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5428 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5429 * (could also be the top-right close button) and the text that was entered will be passed as the two
5430 * parameters to the callback.
5431 * @param {String} title The title bar text
5432 * @param {String} msg The message box body text
5433 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5434 * @param {Object} scope (optional) The scope of the callback function
5435 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5436 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5437 * @return {Roo.MessageBox} This message box
5439 prompt : function(title, msg, fn, scope, multiline){
5443 buttons: this.OKCANCEL,
5448 multiline: multiline,
5455 * Button config that displays a single OK button
5460 * Button config that displays Yes and No buttons
5463 YESNO : {yes:true, no:true},
5465 * Button config that displays OK and Cancel buttons
5468 OKCANCEL : {ok:true, cancel:true},
5470 * Button config that displays Yes, No and Cancel buttons
5473 YESNOCANCEL : {yes:true, no:true, cancel:true},
5476 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5479 defaultTextHeight : 75,
5481 * The maximum width in pixels of the message box (defaults to 600)
5486 * The minimum width in pixels of the message box (defaults to 100)
5491 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5492 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5495 minProgressWidth : 250,
5497 * An object containing the default button text strings that can be overriden for localized language support.
5498 * Supported properties are: ok, cancel, yes and no.
5499 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5512 * Shorthand for {@link Roo.MessageBox}
5514 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5515 Roo.Msg = Roo.Msg || Roo.MessageBox;
5524 * @class Roo.bootstrap.nav.Bar
5525 * @extends Roo.bootstrap.Component
5527 * Bootstrap Navbar class
5530 * Create a new Navbar
5531 * @param {Object} config The config object
5535 Roo.bootstrap.nav.Bar = function(config){
5536 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5540 * @event beforetoggle
5541 * Fire before toggle the menu
5542 * @param {Roo.EventObject} e
5544 "beforetoggle" : true
5548 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5557 getAutoCreate : function(){
5560 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5564 initEvents :function ()
5566 //Roo.log(this.el.select('.navbar-toggle',true));
5567 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5574 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5576 var size = this.el.getSize();
5577 this.maskEl.setSize(size.width, size.height);
5578 this.maskEl.enableDisplayMode("block");
5587 getChildContainer : function()
5589 if (this.el && this.el.select('.collapse').getCount()) {
5590 return this.el.select('.collapse',true).first();
5605 onToggle : function()
5608 if(this.fireEvent('beforetoggle', this) === false){
5611 var ce = this.el.select('.navbar-collapse',true).first();
5613 if (!ce.hasClass('show')) {
5623 * Expand the navbar pulldown
5625 expand : function ()
5628 var ce = this.el.select('.navbar-collapse',true).first();
5629 if (ce.hasClass('collapsing')) {
5632 ce.dom.style.height = '';
5634 ce.addClass('in'); // old...
5635 ce.removeClass('collapse');
5636 ce.addClass('show');
5637 var h = ce.getHeight();
5639 ce.removeClass('show');
5640 // at this point we should be able to see it..
5641 ce.addClass('collapsing');
5643 ce.setHeight(0); // resize it ...
5644 ce.on('transitionend', function() {
5645 //Roo.log('done transition');
5646 ce.removeClass('collapsing');
5647 ce.addClass('show');
5648 ce.removeClass('collapse');
5650 ce.dom.style.height = '';
5651 }, this, { single: true} );
5653 ce.dom.scrollTop = 0;
5656 * Collapse the navbar pulldown
5658 collapse : function()
5660 var ce = this.el.select('.navbar-collapse',true).first();
5662 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5663 // it's collapsed or collapsing..
5666 ce.removeClass('in'); // old...
5667 ce.setHeight(ce.getHeight());
5668 ce.removeClass('show');
5669 ce.addClass('collapsing');
5671 ce.on('transitionend', function() {
5672 ce.dom.style.height = '';
5673 ce.removeClass('collapsing');
5674 ce.addClass('collapse');
5675 }, this, { single: true} );
5695 * @class Roo.bootstrap.nav.Simplebar
5696 * @extends Roo.bootstrap.nav.Bar
5697 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5698 * Bootstrap Sidebar class
5700 * @cfg {Boolean} inverse is inverted color
5702 * @cfg {String} type (nav | pills | tabs)
5703 * @cfg {Boolean} arrangement stacked | justified
5704 * @cfg {String} align (left | right) alignment
5706 * @cfg {Boolean} main (true|false) main nav bar? default false
5707 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5709 * @cfg {String} tag (header|footer|nav|div) default is nav
5711 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5715 * Create a new Sidebar
5716 * @param {Object} config The config object
5720 Roo.bootstrap.nav.Simplebar = function(config){
5721 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5724 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5740 getAutoCreate : function(){
5744 tag : this.tag || 'div',
5745 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5747 if (['light','white'].indexOf(this.weight) > -1) {
5748 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5750 cfg.cls += ' bg-' + this.weight;
5753 cfg.cls += ' navbar-inverse';
5757 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5759 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5768 cls: 'nav nav-' + this.xtype,
5774 this.type = this.type || 'nav';
5775 if (['tabs','pills'].indexOf(this.type) != -1) {
5776 cfg.cn[0].cls += ' nav-' + this.type
5780 if (this.type!=='nav') {
5781 Roo.log('nav type must be nav/tabs/pills')
5783 cfg.cn[0].cls += ' navbar-nav'
5789 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5790 cfg.cn[0].cls += ' nav-' + this.arrangement;
5794 if (this.align === 'right') {
5795 cfg.cn[0].cls += ' navbar-right';
5820 * navbar-expand-md fixed-top
5824 * @class Roo.bootstrap.nav.Headerbar
5825 * @extends Roo.bootstrap.nav.Simplebar
5826 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5827 * Bootstrap Sidebar class
5829 * @cfg {String} brand what is brand
5830 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5831 * @cfg {String} brand_href href of the brand
5832 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5833 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5834 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5835 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5838 * Create a new Sidebar
5839 * @param {Object} config The config object
5843 Roo.bootstrap.nav.Headerbar = function(config){
5844 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5848 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5855 desktopCenter : false,
5858 getAutoCreate : function(){
5861 tag: this.nav || 'nav',
5862 cls: 'navbar navbar-expand-md',
5868 if (this.desktopCenter) {
5869 cn.push({cls : 'container', cn : []});
5877 cls: 'navbar-toggle navbar-toggler',
5878 'data-toggle': 'collapse',
5883 html: 'Toggle navigation'
5887 cls: 'icon-bar navbar-toggler-icon'
5900 cn.push( Roo.bootstrap.version == 4 ? btn : {
5902 cls: 'navbar-header',
5911 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5915 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5917 if (['light','white'].indexOf(this.weight) > -1) {
5918 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5920 cfg.cls += ' bg-' + this.weight;
5923 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5924 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5926 // tag can override this..
5928 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5931 if (this.brand !== '') {
5932 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5933 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5935 href: this.brand_href ? this.brand_href : '#',
5936 cls: 'navbar-brand',
5944 cfg.cls += ' main-nav';
5952 getHeaderChildContainer : function()
5954 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5955 return this.el.select('.navbar-header',true).first();
5958 return this.getChildContainer();
5961 getChildContainer : function()
5964 return this.el.select('.roo-navbar-collapse',true).first();
5969 initEvents : function()
5971 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5973 if (this.autohide) {
5978 Roo.get(document).on('scroll',function(e) {
5979 var ns = Roo.get(document).getScroll().top;
5980 var os = prevScroll;
5984 ft.removeClass('slideDown');
5985 ft.addClass('slideUp');
5988 ft.removeClass('slideUp');
5989 ft.addClass('slideDown');
6010 * @class Roo.bootstrap.nav.Sidebar
6011 * @extends Roo.bootstrap.nav.Bar
6012 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6013 * Bootstrap Sidebar class
6016 * Create a new Sidebar
6017 * @param {Object} config The config object
6021 Roo.bootstrap.nav.Sidebar = function(config){
6022 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6025 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6027 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6029 getAutoCreate : function(){
6034 cls: 'sidebar sidebar-nav'
6056 * @class Roo.bootstrap.nav.Group
6057 * @extends Roo.bootstrap.Component
6058 * @children Roo.bootstrap.nav.Item
6059 * Bootstrap NavGroup class
6060 * @cfg {String} align (left|right)
6061 * @cfg {Boolean} inverse
6062 * @cfg {String} type (nav|pills|tab) default nav
6063 * @cfg {String} navId - reference Id for navbar.
6064 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6067 * Create a new nav group
6068 * @param {Object} config The config object
6071 Roo.bootstrap.nav.Group = function(config){
6072 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6075 Roo.bootstrap.nav.Group.register(this);
6079 * Fires when the active item changes
6080 * @param {Roo.bootstrap.nav.Group} this
6081 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6082 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6089 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6101 getAutoCreate : function()
6103 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6109 if (Roo.bootstrap.version == 4) {
6110 if (['tabs','pills'].indexOf(this.type) != -1) {
6111 cfg.cls += ' nav-' + this.type;
6113 // trying to remove so header bar can right align top?
6114 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6115 // do not use on header bar...
6116 cfg.cls += ' navbar-nav';
6121 if (['tabs','pills'].indexOf(this.type) != -1) {
6122 cfg.cls += ' nav-' + this.type
6124 if (this.type !== 'nav') {
6125 Roo.log('nav type must be nav/tabs/pills')
6127 cfg.cls += ' navbar-nav'
6131 if (this.parent() && this.parent().sidebar) {
6134 cls: 'dashboard-menu sidebar-menu'
6140 if (this.form === true) {
6143 cls: 'navbar-form form-inline'
6145 //nav navbar-right ml-md-auto
6146 if (this.align === 'right') {
6147 cfg.cls += ' navbar-right ml-md-auto';
6149 cfg.cls += ' navbar-left';
6153 if (this.align === 'right') {
6154 cfg.cls += ' navbar-right ml-md-auto';
6156 cfg.cls += ' mr-auto';
6160 cfg.cls += ' navbar-inverse';
6168 * sets the active Navigation item
6169 * @param {Roo.bootstrap.nav.Item} the new current navitem
6171 setActiveItem : function(item)
6174 Roo.each(this.navItems, function(v){
6179 v.setActive(false, true);
6186 item.setActive(true, true);
6187 this.fireEvent('changed', this, item, prev);
6192 * gets the active Navigation item
6193 * @return {Roo.bootstrap.nav.Item} the current navitem
6195 getActive : function()
6199 Roo.each(this.navItems, function(v){
6210 indexOfNav : function()
6214 Roo.each(this.navItems, function(v,i){
6225 * adds a Navigation item
6226 * @param {Roo.bootstrap.nav.Item} the navitem to add
6228 addItem : function(cfg)
6230 if (this.form && Roo.bootstrap.version == 4) {
6233 var cn = new Roo.bootstrap.nav.Item(cfg);
6235 cn.parentId = this.id;
6236 cn.onRender(this.el, null);
6240 * register a Navigation item
6241 * @param {Roo.bootstrap.nav.Item} the navitem to add
6243 register : function(item)
6245 this.navItems.push( item);
6246 item.navId = this.navId;
6251 * clear all the Navigation item
6254 clearAll : function()
6257 this.el.dom.innerHTML = '';
6260 getNavItem: function(tabId)
6263 Roo.each(this.navItems, function(e) {
6264 if (e.tabId == tabId) {
6274 setActiveNext : function()
6276 var i = this.indexOfNav(this.getActive());
6277 if (i > this.navItems.length) {
6280 this.setActiveItem(this.navItems[i+1]);
6282 setActivePrev : function()
6284 var i = this.indexOfNav(this.getActive());
6288 this.setActiveItem(this.navItems[i-1]);
6290 clearWasActive : function(except) {
6291 Roo.each(this.navItems, function(e) {
6292 if (e.tabId != except.tabId && e.was_active) {
6293 e.was_active = false;
6300 getWasActive : function ()
6303 Roo.each(this.navItems, function(e) {
6318 Roo.apply(Roo.bootstrap.nav.Group, {
6322 * register a Navigation Group
6323 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6325 register : function(navgrp)
6327 this.groups[navgrp.navId] = navgrp;
6331 * fetch a Navigation Group based on the navigation ID
6332 * @param {string} the navgroup to add
6333 * @returns {Roo.bootstrap.nav.Group} the navgroup
6335 get: function(navId) {
6336 if (typeof(this.groups[navId]) == 'undefined') {
6338 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6340 return this.groups[navId] ;
6348 * @class Roo.bootstrap.nav.Item
6349 * @extends Roo.bootstrap.Component
6350 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6351 * @parent Roo.bootstrap.nav.Group
6353 * Bootstrap Navbar.NavItem class
6355 * @cfg {String} href link to
6356 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6357 * @cfg {Boolean} button_outline show and outlined button
6358 * @cfg {String} html content of button
6359 * @cfg {String} badge text inside badge
6360 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6361 * @cfg {String} glyphicon DEPRICATED - use fa
6362 * @cfg {String} icon DEPRICATED - use fa
6363 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6364 * @cfg {Boolean} active Is item active
6365 * @cfg {Boolean} disabled Is item disabled
6366 * @cfg {String} linkcls Link Class
6367 * @cfg {Boolean} preventDefault (true | false) default false
6368 * @cfg {String} tabId the tab that this item activates.
6369 * @cfg {String} tagtype (a|span) render as a href or span?
6370 * @cfg {Boolean} animateRef (true|false) link to element default false
6371 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6374 * Create a new Navbar Item
6375 * @param {Object} config The config object
6377 Roo.bootstrap.nav.Item = function(config){
6378 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6383 * The raw click event for the entire grid.
6384 * @param {Roo.EventObject} e
6389 * Fires when the active item active state changes
6390 * @param {Roo.bootstrap.nav.Item} this
6391 * @param {boolean} state the new state
6397 * Fires when scroll to element
6398 * @param {Roo.bootstrap.nav.Item} this
6399 * @param {Object} options
6400 * @param {Roo.EventObject} e
6408 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6417 preventDefault : false,
6425 button_outline : false,
6429 getAutoCreate : function(){
6436 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6439 cfg.cls += ' active' ;
6441 if (this.disabled) {
6442 cfg.cls += ' disabled';
6446 if (this.button_weight.length) {
6447 cfg.tag = this.href ? 'a' : 'button';
6448 cfg.html = this.html || '';
6449 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6451 cfg.href = this.href;
6454 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6456 cfg.cls += " nav-html";
6459 // menu .. should add dropdown-menu class - so no need for carat..
6461 if (this.badge !== '') {
6463 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6468 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6472 href : this.href || "#",
6473 html: this.html || '',
6477 if (this.tagtype == 'a') {
6478 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6482 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6483 } else if (this.fa) {
6484 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6485 } else if(this.glyphicon) {
6486 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6488 cfg.cn[0].cls += " nav-html";
6492 cfg.cn[0].html += " <span class='caret'></span>";
6496 if (this.badge !== '') {
6497 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6505 onRender : function(ct, position)
6507 // Roo.log("Call onRender: " + this.xtype);
6508 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6512 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6513 this.navLink = this.el.select('.nav-link',true).first();
6514 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6519 initEvents: function()
6521 if (typeof (this.menu) != 'undefined') {
6522 this.menu.parentType = this.xtype;
6523 this.menu.triggerEl = this.el;
6524 this.menu = this.addxtype(Roo.apply({}, this.menu));
6527 this.el.on('click', this.onClick, this);
6529 //if(this.tagtype == 'span'){
6530 // this.el.select('span',true).on('click', this.onClick, this);
6533 // at this point parent should be available..
6534 this.parent().register(this);
6537 onClick : function(e)
6539 if (e.getTarget('.dropdown-menu-item')) {
6540 // did you click on a menu itemm.... - then don't trigger onclick..
6545 this.preventDefault ||
6548 Roo.log("NavItem - prevent Default?");
6552 if (this.disabled) {
6556 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6557 if (tg && tg.transition) {
6558 Roo.log("waiting for the transitionend");
6564 //Roo.log("fire event clicked");
6565 if(this.fireEvent('click', this, e) === false){
6569 if(this.tagtype == 'span'){
6573 //Roo.log(this.href);
6574 var ael = this.el.select('a',true).first();
6577 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6578 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6579 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6580 return; // ignore... - it's a 'hash' to another page.
6582 Roo.log("NavItem - prevent Default?");
6584 this.scrollToElement(e);
6588 var p = this.parent();
6590 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6591 if (typeof(p.setActiveItem) !== 'undefined') {
6592 p.setActiveItem(this);
6596 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6597 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6598 // remove the collapsed menu expand...
6599 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6603 isActive: function () {
6606 setActive : function(state, fire, is_was_active)
6608 if (this.active && !state && this.navId) {
6609 this.was_active = true;
6610 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6612 nv.clearWasActive(this);
6616 this.active = state;
6619 this.el.removeClass('active');
6620 this.navLink ? this.navLink.removeClass('active') : false;
6621 } else if (!this.el.hasClass('active')) {
6623 this.el.addClass('active');
6624 if (Roo.bootstrap.version == 4 && this.navLink ) {
6625 this.navLink.addClass('active');
6630 this.fireEvent('changed', this, state);
6633 // show a panel if it's registered and related..
6635 if (!this.navId || !this.tabId || !state || is_was_active) {
6639 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6643 var pan = tg.getPanelByName(this.tabId);
6647 // if we can not flip to new panel - go back to old nav highlight..
6648 if (false == tg.showPanel(pan)) {
6649 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6651 var onav = nv.getWasActive();
6653 onav.setActive(true, false, true);
6662 // this should not be here...
6663 setDisabled : function(state)
6665 this.disabled = state;
6667 this.el.removeClass('disabled');
6668 } else if (!this.el.hasClass('disabled')) {
6669 this.el.addClass('disabled');
6675 * Fetch the element to display the tooltip on.
6676 * @return {Roo.Element} defaults to this.el
6678 tooltipEl : function()
6680 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6683 scrollToElement : function(e)
6685 var c = document.body;
6688 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6690 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6691 c = document.documentElement;
6694 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6700 var o = target.calcOffsetsTo(c);
6707 this.fireEvent('scrollto', this, options, e);
6709 Roo.get(c).scrollTo('top', options.value, true);
6714 * Set the HTML (text content) of the item
6715 * @param {string} html content for the nav item
6717 setHtml : function(html)
6720 this.htmlEl.dom.innerHTML = html;
6732 * <span> icon </span>
6733 * <span> text </span>
6734 * <span>badge </span>
6738 * @class Roo.bootstrap.nav.SidebarItem
6739 * @extends Roo.bootstrap.nav.Item
6740 * Bootstrap Navbar.NavSidebarItem class
6742 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6743 * {Boolean} open is the menu open
6744 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6745 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6746 * {String} buttonSize (sm|md|lg)the extra classes for the button
6747 * {Boolean} showArrow show arrow next to the text (default true)
6749 * Create a new Navbar Button
6750 * @param {Object} config The config object
6752 Roo.bootstrap.nav.SidebarItem = function(config){
6753 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6758 * The raw click event for the entire grid.
6759 * @param {Roo.EventObject} e
6764 * Fires when the active item active state changes
6765 * @param {Roo.bootstrap.nav.SidebarItem} this
6766 * @param {boolean} state the new state
6774 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6776 badgeWeight : 'default',
6782 buttonWeight : 'default',
6788 getAutoCreate : function(){
6793 href : this.href || '#',
6799 if(this.buttonView){
6802 href : this.href || '#',
6803 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6816 cfg.cls += ' active';
6819 if (this.disabled) {
6820 cfg.cls += ' disabled';
6823 cfg.cls += ' open x-open';
6826 if (this.glyphicon || this.icon) {
6827 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6828 a.cn.push({ tag : 'i', cls : c }) ;
6831 if(!this.buttonView){
6834 html : this.html || ''
6841 if (this.badge !== '') {
6842 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6848 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6851 a.cls += ' dropdown-toggle treeview' ;
6857 initEvents : function()
6859 if (typeof (this.menu) != 'undefined') {
6860 this.menu.parentType = this.xtype;
6861 this.menu.triggerEl = this.el;
6862 this.menu = this.addxtype(Roo.apply({}, this.menu));
6865 this.el.on('click', this.onClick, this);
6867 if(this.badge !== ''){
6868 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6873 onClick : function(e)
6880 if(this.preventDefault){
6884 this.fireEvent('click', this, e);
6887 disable : function()
6889 this.setDisabled(true);
6894 this.setDisabled(false);
6897 setDisabled : function(state)
6899 if(this.disabled == state){
6903 this.disabled = state;
6906 this.el.addClass('disabled');
6910 this.el.removeClass('disabled');
6915 setActive : function(state)
6917 if(this.active == state){
6921 this.active = state;
6924 this.el.addClass('active');
6928 this.el.removeClass('active');
6933 isActive: function ()
6938 setBadge : function(str)
6944 this.badgeEl.dom.innerHTML = str;
6961 * @class Roo.bootstrap.nav.ProgressBar
6962 * @extends Roo.bootstrap.Component
6963 * @children Roo.bootstrap.nav.ProgressBarItem
6964 * Bootstrap NavProgressBar class
6967 * Create a new nav progress bar - a bar indicating step along a process
6968 * @param {Object} config The config object
6971 Roo.bootstrap.nav.ProgressBar = function(config){
6972 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6974 this.bullets = this.bullets || [];
6976 // Roo.bootstrap.nav.ProgressBar.register(this);
6980 * Fires when the active item changes
6981 * @param {Roo.bootstrap.nav.ProgressBar} this
6982 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6983 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
6990 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
6992 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
6993 * Bullets for the Nav Progress bar for the toolbar
6998 getAutoCreate : function()
7000 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7004 cls : 'roo-navigation-bar-group',
7008 cls : 'roo-navigation-top-bar'
7012 cls : 'roo-navigation-bullets-bar',
7016 cls : 'roo-navigation-bar'
7023 cls : 'roo-navigation-bottom-bar'
7033 initEvents: function()
7038 onRender : function(ct, position)
7040 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7042 if(this.bullets.length){
7043 Roo.each(this.bullets, function(b){
7052 addItem : function(cfg)
7054 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7056 item.parentId = this.id;
7057 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7060 var top = new Roo.bootstrap.Element({
7062 cls : 'roo-navigation-bar-text'
7065 var bottom = new Roo.bootstrap.Element({
7067 cls : 'roo-navigation-bar-text'
7070 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7071 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7073 var topText = new Roo.bootstrap.Element({
7075 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7078 var bottomText = new Roo.bootstrap.Element({
7080 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7083 topText.onRender(top.el, null);
7084 bottomText.onRender(bottom.el, null);
7087 item.bottomEl = bottom;
7090 this.barItems.push(item);
7095 getActive : function()
7099 Roo.each(this.barItems, function(v){
7101 if (!v.isActive()) {
7113 setActiveItem : function(item)
7117 Roo.each(this.barItems, function(v){
7118 if (v.rid == item.rid) {
7128 item.setActive(true);
7130 this.fireEvent('changed', this, item, prev);
7133 getBarItem: function(rid)
7137 Roo.each(this.barItems, function(e) {
7149 indexOfItem : function(item)
7153 Roo.each(this.barItems, function(v, i){
7155 if (v.rid != item.rid) {
7166 setActiveNext : function()
7168 var i = this.indexOfItem(this.getActive());
7170 if (i > this.barItems.length) {
7174 this.setActiveItem(this.barItems[i+1]);
7177 setActivePrev : function()
7179 var i = this.indexOfItem(this.getActive());
7185 this.setActiveItem(this.barItems[i-1]);
7190 if(!this.barItems.length){
7194 var width = 100 / this.barItems.length;
7196 Roo.each(this.barItems, function(i){
7197 i.el.setStyle('width', width + '%');
7198 i.topEl.el.setStyle('width', width + '%');
7199 i.bottomEl.el.setStyle('width', width + '%');
7213 * @class Roo.bootstrap.nav.ProgressBarItem
7214 * @extends Roo.bootstrap.Component
7215 * Bootstrap NavProgressBarItem class
7216 * @cfg {String} rid the reference id
7217 * @cfg {Boolean} active (true|false) Is item active default false
7218 * @cfg {Boolean} disabled (true|false) Is item active default false
7219 * @cfg {String} html
7220 * @cfg {String} position (top|bottom) text position default bottom
7221 * @cfg {String} icon show icon instead of number
7224 * Create a new NavProgressBarItem
7225 * @param {Object} config The config object
7227 Roo.bootstrap.nav.ProgressBarItem = function(config){
7228 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7233 * The raw click event for the entire grid.
7234 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7235 * @param {Roo.EventObject} e
7242 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7248 position : 'bottom',
7251 getAutoCreate : function()
7253 var iconCls = 'roo-navigation-bar-item-icon';
7255 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7259 cls: 'roo-navigation-bar-item',
7269 cfg.cls += ' active';
7272 cfg.cls += ' disabled';
7278 disable : function()
7280 this.setDisabled(true);
7285 this.setDisabled(false);
7288 initEvents: function()
7290 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7292 this.iconEl.on('click', this.onClick, this);
7295 onClick : function(e)
7303 if(this.fireEvent('click', this, e) === false){
7307 this.parent().setActiveItem(this);
7310 isActive: function ()
7315 setActive : function(state)
7317 if(this.active == state){
7321 this.active = state;
7324 this.el.addClass('active');
7328 this.el.removeClass('active');
7333 setDisabled : function(state)
7335 if(this.disabled == state){
7339 this.disabled = state;
7342 this.el.addClass('disabled');
7346 this.el.removeClass('disabled');
7349 tooltipEl : function()
7351 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7362 Roo.namespace('Roo.bootstrap.breadcrumb');
7366 * @class Roo.bootstrap.breadcrumb.Nav
7367 * @extends Roo.bootstrap.Component
7368 * Bootstrap Breadcrumb Nav Class
7370 * @children Roo.bootstrap.breadcrumb.Item
7373 * Create a new breadcrumb.Nav
7374 * @param {Object} config The config object
7378 Roo.bootstrap.breadcrumb.Nav = function(config){
7379 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7384 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7386 getAutoCreate : function()
7403 initEvents: function()
7405 this.olEl = this.el.select('ol',true).first();
7407 getChildContainer : function()
7423 * @class Roo.bootstrap.breadcrumb.Nav
7424 * @extends Roo.bootstrap.Component
7425 * @children Roo.bootstrap.Component
7426 * @parent Roo.bootstrap.breadcrumb.Nav
7427 * Bootstrap Breadcrumb Nav Class
7430 * @cfg {String} html the content of the link.
7431 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7432 * @cfg {Boolean} active is it active
7436 * Create a new breadcrumb.Nav
7437 * @param {Object} config The config object
7440 Roo.bootstrap.breadcrumb.Item = function(config){
7441 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7446 * The img click event for the img.
7447 * @param {Roo.EventObject} e
7454 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7459 getAutoCreate : function()
7464 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7466 if (this.href !== false) {
7473 cfg.html = this.html;
7479 initEvents: function()
7482 this.el.select('a', true).first().on('click',this.onClick, this)
7486 onClick : function(e)
7489 this.fireEvent('click',this, e);
7502 * @class Roo.bootstrap.Row
7503 * @extends Roo.bootstrap.Component
7504 * @children Roo.bootstrap.Component
7505 * Bootstrap Row class (contains columns...)
7509 * @param {Object} config The config object
7512 Roo.bootstrap.Row = function(config){
7513 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7516 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7518 getAutoCreate : function(){
7537 * @class Roo.bootstrap.Pagination
7538 * @extends Roo.bootstrap.Component
7539 * @children Roo.bootstrap.Pagination
7540 * Bootstrap Pagination class
7542 * @cfg {String} size (xs|sm|md|lg|xl)
7543 * @cfg {Boolean} inverse
7546 * Create a new Pagination
7547 * @param {Object} config The config object
7550 Roo.bootstrap.Pagination = function(config){
7551 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7554 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7560 getAutoCreate : function(){
7566 cfg.cls += ' inverse';
7572 cfg.cls += " " + this.cls;
7590 * @class Roo.bootstrap.PaginationItem
7591 * @extends Roo.bootstrap.Component
7592 * Bootstrap PaginationItem class
7593 * @cfg {String} html text
7594 * @cfg {String} href the link
7595 * @cfg {Boolean} preventDefault (true | false) default true
7596 * @cfg {Boolean} active (true | false) default false
7597 * @cfg {Boolean} disabled default false
7601 * Create a new PaginationItem
7602 * @param {Object} config The config object
7606 Roo.bootstrap.PaginationItem = function(config){
7607 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7612 * The raw click event for the entire grid.
7613 * @param {Roo.EventObject} e
7619 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7623 preventDefault: true,
7628 getAutoCreate : function(){
7634 href : this.href ? this.href : '#',
7635 html : this.html ? this.html : ''
7645 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7649 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7655 initEvents: function() {
7657 this.el.on('click', this.onClick, this);
7660 onClick : function(e)
7662 Roo.log('PaginationItem on click ');
7663 if(this.preventDefault){
7671 this.fireEvent('click', this, e);
7687 * @class Roo.bootstrap.Slider
7688 * @extends Roo.bootstrap.Component
7689 * Bootstrap Slider class
7692 * Create a new Slider
7693 * @param {Object} config The config object
7696 Roo.bootstrap.Slider = function(config){
7697 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7700 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7702 getAutoCreate : function(){
7706 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7710 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7722 * Ext JS Library 1.1.1
7723 * Copyright(c) 2006-2007, Ext JS, LLC.
7725 * Originally Released Under LGPL - original licence link has changed is not relivant.
7728 * <script type="text/javascript">
7731 * @extends Roo.dd.DDProxy
7732 * @class Roo.grid.SplitDragZone
7733 * Support for Column Header resizing
7735 * @param {Object} config
7738 // This is a support class used internally by the Grid components
7739 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7741 this.view = grid.getView();
7742 this.proxy = this.view.resizeProxy;
7743 Roo.grid.SplitDragZone.superclass.constructor.call(
7746 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7748 dragElId : Roo.id(this.proxy.dom),
7753 this.setHandleElId(Roo.id(hd));
7754 if (hd2 !== false) {
7755 this.setOuterHandleElId(Roo.id(hd2));
7758 this.scroll = false;
7760 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7761 fly: Roo.Element.fly,
7763 b4StartDrag : function(x, y){
7764 this.view.headersDisabled = true;
7765 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7766 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7768 this.proxy.setHeight(h);
7770 // for old system colWidth really stored the actual width?
7771 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7772 // which in reality did not work.. - it worked only for fixed sizes
7773 // for resizable we need to use actual sizes.
7774 var w = this.cm.getColumnWidth(this.cellIndex);
7775 if (!this.view.mainWrap) {
7777 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7782 // this was w-this.grid.minColumnWidth;
7783 // doesnt really make sense? - w = thie curren width or the rendered one?
7784 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7785 this.resetConstraints();
7786 this.setXConstraint(minw, 1000);
7787 this.setYConstraint(0, 0);
7788 this.minX = x - minw;
7789 this.maxX = x + 1000;
7791 if (!this.view.mainWrap) { // this is Bootstrap code..
7792 this.getDragEl().style.display='block';
7795 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7799 handleMouseDown : function(e){
7800 ev = Roo.EventObject.setEvent(e);
7801 var t = this.fly(ev.getTarget());
7802 if(t.hasClass("x-grid-split")){
7803 this.cellIndex = this.view.getCellIndex(t.dom);
7805 this.cm = this.grid.colModel;
7806 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7807 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7812 endDrag : function(e){
7813 this.view.headersDisabled = false;
7814 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7815 var diff = endX - this.startPos;
7817 var w = this.cm.getColumnWidth(this.cellIndex);
7818 if (!this.view.mainWrap) {
7821 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7824 autoOffset : function(){
7829 * Ext JS Library 1.1.1
7830 * Copyright(c) 2006-2007, Ext JS, LLC.
7832 * Originally Released Under LGPL - original licence link has changed is not relivant.
7835 * <script type="text/javascript">
7839 * @class Roo.grid.AbstractSelectionModel
7840 * @extends Roo.util.Observable
7842 * Abstract base class for grid SelectionModels. It provides the interface that should be
7843 * implemented by descendant classes. This class should not be directly instantiated.
7846 Roo.grid.AbstractSelectionModel = function(){
7847 this.locked = false;
7848 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7851 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7852 /** @ignore Called by the grid automatically. Do not call directly. */
7853 init : function(grid){
7859 * Locks the selections.
7866 * Unlocks the selections.
7868 unlock : function(){
7869 this.locked = false;
7873 * Returns true if the selections are locked.
7876 isLocked : function(){
7881 * Ext JS Library 1.1.1
7882 * Copyright(c) 2006-2007, Ext JS, LLC.
7884 * Originally Released Under LGPL - original licence link has changed is not relivant.
7887 * <script type="text/javascript">
7890 * @extends Roo.grid.AbstractSelectionModel
7891 * @class Roo.grid.RowSelectionModel
7892 * The default SelectionModel used by {@link Roo.grid.Grid}.
7893 * It supports multiple selections and keyboard selection/navigation.
7895 * @param {Object} config
7897 Roo.grid.RowSelectionModel = function(config){
7898 Roo.apply(this, config);
7899 this.selections = new Roo.util.MixedCollection(false, function(o){
7904 this.lastActive = false;
7908 * @event selectionchange
7909 * Fires when the selection changes
7910 * @param {SelectionModel} this
7912 "selectionchange" : true,
7914 * @event afterselectionchange
7915 * Fires after the selection changes (eg. by key press or clicking)
7916 * @param {SelectionModel} this
7918 "afterselectionchange" : true,
7920 * @event beforerowselect
7921 * Fires when a row is selected being selected, return false to cancel.
7922 * @param {SelectionModel} this
7923 * @param {Number} rowIndex The selected index
7924 * @param {Boolean} keepExisting False if other selections will be cleared
7926 "beforerowselect" : true,
7929 * Fires when a row is selected.
7930 * @param {SelectionModel} this
7931 * @param {Number} rowIndex The selected index
7932 * @param {Roo.data.Record} r The record
7936 * @event rowdeselect
7937 * Fires when a row is deselected.
7938 * @param {SelectionModel} this
7939 * @param {Number} rowIndex The selected index
7941 "rowdeselect" : true
7943 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7944 this.locked = false;
7947 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7949 * @cfg {Boolean} singleSelect
7950 * True to allow selection of only one row at a time (defaults to false)
7952 singleSelect : false,
7955 initEvents : function(){
7957 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7958 this.grid.on("mousedown", this.handleMouseDown, this);
7959 }else{ // allow click to work like normal
7960 this.grid.on("rowclick", this.handleDragableRowClick, this);
7962 // bootstrap does not have a view..
7963 var view = this.grid.view ? this.grid.view : this.grid;
7964 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7967 this.selectPrevious(e.shiftKey);
7968 }else if(this.last !== false && this.lastActive !== false){
7969 var last = this.last;
7970 this.selectRange(this.last, this.lastActive-1);
7971 view.focusRow(this.lastActive);
7976 this.selectFirstRow();
7978 this.fireEvent("afterselectionchange", this);
7980 "down" : function(e){
7982 this.selectNext(e.shiftKey);
7983 }else if(this.last !== false && this.lastActive !== false){
7984 var last = this.last;
7985 this.selectRange(this.last, this.lastActive+1);
7986 view.focusRow(this.lastActive);
7991 this.selectFirstRow();
7993 this.fireEvent("afterselectionchange", this);
7999 view.on("refresh", this.onRefresh, this);
8000 view.on("rowupdated", this.onRowUpdated, this);
8001 view.on("rowremoved", this.onRemove, this);
8005 onRefresh : function(){
8006 var ds = this.grid.ds, i, v = this.grid.view;
8007 var s = this.selections;
8009 if((i = ds.indexOfId(r.id)) != -1){
8011 s.add(ds.getAt(i)); // updating the selection relate data
8019 onRemove : function(v, index, r){
8020 this.selections.remove(r);
8024 onRowUpdated : function(v, index, r){
8025 if(this.isSelected(r)){
8026 v.onRowSelect(index);
8032 * @param {Array} records The records to select
8033 * @param {Boolean} keepExisting (optional) True to keep existing selections
8035 selectRecords : function(records, keepExisting){
8037 this.clearSelections();
8039 var ds = this.grid.ds;
8040 for(var i = 0, len = records.length; i < len; i++){
8041 this.selectRow(ds.indexOf(records[i]), true);
8046 * Gets the number of selected rows.
8049 getCount : function(){
8050 return this.selections.length;
8054 * Selects the first row in the grid.
8056 selectFirstRow : function(){
8061 * Select the last row.
8062 * @param {Boolean} keepExisting (optional) True to keep existing selections
8064 selectLastRow : function(keepExisting){
8065 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8069 * Selects the row immediately following the last selected row.
8070 * @param {Boolean} keepExisting (optional) True to keep existing selections
8072 selectNext : function(keepExisting){
8073 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8074 this.selectRow(this.last+1, keepExisting);
8075 var view = this.grid.view ? this.grid.view : this.grid;
8076 view.focusRow(this.last);
8081 * Selects the row that precedes the last selected row.
8082 * @param {Boolean} keepExisting (optional) True to keep existing selections
8084 selectPrevious : function(keepExisting){
8086 this.selectRow(this.last-1, keepExisting);
8087 var view = this.grid.view ? this.grid.view : this.grid;
8088 view.focusRow(this.last);
8093 * Returns the selected records
8094 * @return {Array} Array of selected records
8096 getSelections : function(){
8097 return [].concat(this.selections.items);
8101 * Returns the first selected record.
8104 getSelected : function(){
8105 return this.selections.itemAt(0);
8110 * Clears all selections.
8112 clearSelections : function(fast){
8117 var ds = this.grid.ds;
8118 var s = this.selections;
8120 this.deselectRow(ds.indexOfId(r.id));
8124 this.selections.clear();
8133 selectAll : function(){
8137 this.selections.clear();
8138 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8139 this.selectRow(i, true);
8144 * Returns True if there is a selection.
8147 hasSelection : function(){
8148 return this.selections.length > 0;
8152 * Returns True if the specified row is selected.
8153 * @param {Number/Record} record The record or index of the record to check
8156 isSelected : function(index){
8157 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8158 return (r && this.selections.key(r.id) ? true : false);
8162 * Returns True if the specified record id is selected.
8163 * @param {String} id The id of record to check
8166 isIdSelected : function(id){
8167 return (this.selections.key(id) ? true : false);
8171 handleMouseDown : function(e, t)
8173 var view = this.grid.view ? this.grid.view : this.grid;
8175 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8178 if(e.shiftKey && this.last !== false){
8179 var last = this.last;
8180 this.selectRange(last, rowIndex, e.ctrlKey);
8181 this.last = last; // reset the last
8182 view.focusRow(rowIndex);
8184 var isSelected = this.isSelected(rowIndex);
8185 if(e.button !== 0 && isSelected){
8186 view.focusRow(rowIndex);
8187 }else if(e.ctrlKey && isSelected){
8188 this.deselectRow(rowIndex);
8189 }else if(!isSelected){
8190 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8191 view.focusRow(rowIndex);
8194 this.fireEvent("afterselectionchange", this);
8197 handleDragableRowClick : function(grid, rowIndex, e)
8199 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8200 this.selectRow(rowIndex, false);
8201 var view = this.grid.view ? this.grid.view : this.grid;
8202 view.focusRow(rowIndex);
8203 this.fireEvent("afterselectionchange", this);
8208 * Selects multiple rows.
8209 * @param {Array} rows Array of the indexes of the row to select
8210 * @param {Boolean} keepExisting (optional) True to keep existing selections
8212 selectRows : function(rows, keepExisting){
8214 this.clearSelections();
8216 for(var i = 0, len = rows.length; i < len; i++){
8217 this.selectRow(rows[i], true);
8222 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8223 * @param {Number} startRow The index of the first row in the range
8224 * @param {Number} endRow The index of the last row in the range
8225 * @param {Boolean} keepExisting (optional) True to retain existing selections
8227 selectRange : function(startRow, endRow, keepExisting){
8232 this.clearSelections();
8234 if(startRow <= endRow){
8235 for(var i = startRow; i <= endRow; i++){
8236 this.selectRow(i, true);
8239 for(var i = startRow; i >= endRow; i--){
8240 this.selectRow(i, true);
8246 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8247 * @param {Number} startRow The index of the first row in the range
8248 * @param {Number} endRow The index of the last row in the range
8250 deselectRange : function(startRow, endRow, preventViewNotify){
8254 for(var i = startRow; i <= endRow; i++){
8255 this.deselectRow(i, preventViewNotify);
8261 * @param {Number} row The index of the row to select
8262 * @param {Boolean} keepExisting (optional) True to keep existing selections
8264 selectRow : function(index, keepExisting, preventViewNotify){
8265 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8268 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8269 if(!keepExisting || this.singleSelect){
8270 this.clearSelections();
8272 var r = this.grid.ds.getAt(index);
8273 this.selections.add(r);
8274 this.last = this.lastActive = index;
8275 if(!preventViewNotify){
8276 var view = this.grid.view ? this.grid.view : this.grid;
8277 view.onRowSelect(index);
8279 this.fireEvent("rowselect", this, index, r);
8280 this.fireEvent("selectionchange", this);
8286 * @param {Number} row The index of the row to deselect
8288 deselectRow : function(index, preventViewNotify){
8292 if(this.last == index){
8295 if(this.lastActive == index){
8296 this.lastActive = false;
8298 var r = this.grid.ds.getAt(index);
8299 this.selections.remove(r);
8300 if(!preventViewNotify){
8301 var view = this.grid.view ? this.grid.view : this.grid;
8302 view.onRowDeselect(index);
8304 this.fireEvent("rowdeselect", this, index);
8305 this.fireEvent("selectionchange", this);
8309 restoreLast : function(){
8311 this.last = this._last;
8316 acceptsNav : function(row, col, cm){
8317 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8321 onEditorKey : function(field, e){
8322 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8327 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8329 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8331 }else if(k == e.ENTER && !e.ctrlKey){
8335 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8337 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8339 }else if(k == e.ESC){
8343 g.startEditing(newCell[0], newCell[1]);
8348 * Ext JS Library 1.1.1
8349 * Copyright(c) 2006-2007, Ext JS, LLC.
8351 * Originally Released Under LGPL - original licence link has changed is not relivant.
8354 * <script type="text/javascript">
8359 * @class Roo.grid.ColumnModel
8360 * @extends Roo.util.Observable
8361 * This is the default implementation of a ColumnModel used by the Grid. It defines
8362 * the columns in the grid.
8365 var colModel = new Roo.grid.ColumnModel([
8366 {header: "Ticker", width: 60, sortable: true, locked: true},
8367 {header: "Company Name", width: 150, sortable: true},
8368 {header: "Market Cap.", width: 100, sortable: true},
8369 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8370 {header: "Employees", width: 100, sortable: true, resizable: false}
8375 * The config options listed for this class are options which may appear in each
8376 * individual column definition.
8377 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8379 * @param {Object} config An Array of column config objects. See this class's
8380 * config objects for details.
8382 Roo.grid.ColumnModel = function(config){
8384 * The config passed into the constructor
8386 this.config = []; //config;
8389 // if no id, create one
8390 // if the column does not have a dataIndex mapping,
8391 // map it to the order it is in the config
8392 for(var i = 0, len = config.length; i < len; i++){
8393 this.addColumn(config[i]);
8398 * The width of columns which have no width specified (defaults to 100)
8401 this.defaultWidth = 100;
8404 * Default sortable of columns which have no sortable specified (defaults to false)
8407 this.defaultSortable = false;
8411 * @event widthchange
8412 * Fires when the width of a column changes.
8413 * @param {ColumnModel} this
8414 * @param {Number} columnIndex The column index
8415 * @param {Number} newWidth The new width
8417 "widthchange": true,
8419 * @event headerchange
8420 * Fires when the text of a header changes.
8421 * @param {ColumnModel} this
8422 * @param {Number} columnIndex The column index
8423 * @param {Number} newText The new header text
8425 "headerchange": true,
8427 * @event hiddenchange
8428 * Fires when a column is hidden or "unhidden".
8429 * @param {ColumnModel} this
8430 * @param {Number} columnIndex The column index
8431 * @param {Boolean} hidden true if hidden, false otherwise
8433 "hiddenchange": true,
8435 * @event columnmoved
8436 * Fires when a column is moved.
8437 * @param {ColumnModel} this
8438 * @param {Number} oldIndex
8439 * @param {Number} newIndex
8441 "columnmoved" : true,
8443 * @event columlockchange
8444 * Fires when a column's locked state is changed
8445 * @param {ColumnModel} this
8446 * @param {Number} colIndex
8447 * @param {Boolean} locked true if locked
8449 "columnlockchange" : true
8451 Roo.grid.ColumnModel.superclass.constructor.call(this);
8453 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8455 * @cfg {String} header The header text to display in the Grid view.
8458 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8461 * @cfg {String} smHeader Header at Bootsrap Small width
8464 * @cfg {String} mdHeader Header at Bootsrap Medium width
8467 * @cfg {String} lgHeader Header at Bootsrap Large width
8470 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8473 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8474 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8475 * specified, the column's index is used as an index into the Record's data Array.
8478 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8479 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8482 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8483 * Defaults to the value of the {@link #defaultSortable} property.
8484 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8487 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8490 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8493 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8496 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8499 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8500 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8501 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8502 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8505 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8508 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8511 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8514 * @cfg {String} cursor (Optional)
8517 * @cfg {String} tooltip (Optional)
8520 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8523 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8526 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8529 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8532 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8535 * Returns the id of the column at the specified index.
8536 * @param {Number} index The column index
8537 * @return {String} the id
8539 getColumnId : function(index){
8540 return this.config[index].id;
8544 * Returns the column for a specified id.
8545 * @param {String} id The column id
8546 * @return {Object} the column
8548 getColumnById : function(id){
8549 return this.lookup[id];
8554 * Returns the column Object for a specified dataIndex.
8555 * @param {String} dataIndex The column dataIndex
8556 * @return {Object|Boolean} the column or false if not found
8558 getColumnByDataIndex: function(dataIndex){
8559 var index = this.findColumnIndex(dataIndex);
8560 return index > -1 ? this.config[index] : false;
8564 * Returns the index for a specified column id.
8565 * @param {String} id The column id
8566 * @return {Number} the index, or -1 if not found
8568 getIndexById : function(id){
8569 for(var i = 0, len = this.config.length; i < len; i++){
8570 if(this.config[i].id == id){
8578 * Returns the index for a specified column dataIndex.
8579 * @param {String} dataIndex The column dataIndex
8580 * @return {Number} the index, or -1 if not found
8583 findColumnIndex : function(dataIndex){
8584 for(var i = 0, len = this.config.length; i < len; i++){
8585 if(this.config[i].dataIndex == dataIndex){
8593 moveColumn : function(oldIndex, newIndex){
8594 var c = this.config[oldIndex];
8595 this.config.splice(oldIndex, 1);
8596 this.config.splice(newIndex, 0, c);
8597 this.dataMap = null;
8598 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8601 isLocked : function(colIndex){
8602 return this.config[colIndex].locked === true;
8605 setLocked : function(colIndex, value, suppressEvent){
8606 if(this.isLocked(colIndex) == value){
8609 this.config[colIndex].locked = value;
8611 this.fireEvent("columnlockchange", this, colIndex, value);
8615 getTotalLockedWidth : function(){
8617 for(var i = 0; i < this.config.length; i++){
8618 if(this.isLocked(i) && !this.isHidden(i)){
8619 this.totalWidth += this.getColumnWidth(i);
8625 getLockedCount : function(){
8626 for(var i = 0, len = this.config.length; i < len; i++){
8627 if(!this.isLocked(i)){
8632 return this.config.length;
8636 * Returns the number of columns.
8639 getColumnCount : function(visibleOnly){
8640 if(visibleOnly === true){
8642 for(var i = 0, len = this.config.length; i < len; i++){
8643 if(!this.isHidden(i)){
8649 return this.config.length;
8653 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8654 * @param {Function} fn
8655 * @param {Object} scope (optional)
8656 * @return {Array} result
8658 getColumnsBy : function(fn, scope){
8660 for(var i = 0, len = this.config.length; i < len; i++){
8661 var c = this.config[i];
8662 if(fn.call(scope||this, c, i) === true){
8670 * Returns true if the specified column is sortable.
8671 * @param {Number} col The column index
8674 isSortable : function(col){
8675 if(typeof this.config[col].sortable == "undefined"){
8676 return this.defaultSortable;
8678 return this.config[col].sortable;
8682 * Returns the rendering (formatting) function defined for the column.
8683 * @param {Number} col The column index.
8684 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8686 getRenderer : function(col){
8687 if(!this.config[col].renderer){
8688 return Roo.grid.ColumnModel.defaultRenderer;
8690 return this.config[col].renderer;
8694 * Sets the rendering (formatting) function for a column.
8695 * @param {Number} col The column index
8696 * @param {Function} fn The function to use to process the cell's raw data
8697 * to return HTML markup for the grid view. The render function is called with
8698 * the following parameters:<ul>
8699 * <li>Data value.</li>
8700 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8701 * <li>css A CSS style string to apply to the table cell.</li>
8702 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8703 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8704 * <li>Row index</li>
8705 * <li>Column index</li>
8706 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8708 setRenderer : function(col, fn){
8709 this.config[col].renderer = fn;
8713 * Returns the width for the specified column.
8714 * @param {Number} col The column index
8715 * @param (optional) {String} gridSize bootstrap width size.
8718 getColumnWidth : function(col, gridSize)
8720 var cfg = this.config[col];
8722 if (typeof(gridSize) == 'undefined') {
8723 return cfg.width * 1 || this.defaultWidth;
8725 if (gridSize === false) { // if we set it..
8726 return cfg.width || false;
8728 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8730 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8731 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8734 return cfg[ sizes[i] ];
8741 * Sets the width for a column.
8742 * @param {Number} col The column index
8743 * @param {Number} width The new width
8745 setColumnWidth : function(col, width, suppressEvent){
8746 this.config[col].width = width;
8747 this.totalWidth = null;
8749 this.fireEvent("widthchange", this, col, width);
8754 * Returns the total width of all columns.
8755 * @param {Boolean} includeHidden True to include hidden column widths
8758 getTotalWidth : function(includeHidden){
8759 if(!this.totalWidth){
8760 this.totalWidth = 0;
8761 for(var i = 0, len = this.config.length; i < len; i++){
8762 if(includeHidden || !this.isHidden(i)){
8763 this.totalWidth += this.getColumnWidth(i);
8767 return this.totalWidth;
8771 * Returns the header for the specified column.
8772 * @param {Number} col The column index
8775 getColumnHeader : function(col){
8776 return this.config[col].header;
8780 * Sets the header for a column.
8781 * @param {Number} col The column index
8782 * @param {String} header The new header
8784 setColumnHeader : function(col, header){
8785 this.config[col].header = header;
8786 this.fireEvent("headerchange", this, col, header);
8790 * Returns the tooltip for the specified column.
8791 * @param {Number} col The column index
8794 getColumnTooltip : function(col){
8795 return this.config[col].tooltip;
8798 * Sets the tooltip for a column.
8799 * @param {Number} col The column index
8800 * @param {String} tooltip The new tooltip
8802 setColumnTooltip : function(col, tooltip){
8803 this.config[col].tooltip = tooltip;
8807 * Returns the dataIndex for the specified column.
8808 * @param {Number} col The column index
8811 getDataIndex : function(col){
8812 return this.config[col].dataIndex;
8816 * Sets the dataIndex for a column.
8817 * @param {Number} col The column index
8818 * @param {Number} dataIndex The new dataIndex
8820 setDataIndex : function(col, dataIndex){
8821 this.config[col].dataIndex = dataIndex;
8827 * Returns true if the cell is editable.
8828 * @param {Number} colIndex The column index
8829 * @param {Number} rowIndex The row index - this is nto actually used..?
8832 isCellEditable : function(colIndex, rowIndex){
8833 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8837 * Returns the editor defined for the cell/column.
8838 * return false or null to disable editing.
8839 * @param {Number} colIndex The column index
8840 * @param {Number} rowIndex The row index
8843 getCellEditor : function(colIndex, rowIndex){
8844 return this.config[colIndex].editor;
8848 * Sets if a column is editable.
8849 * @param {Number} col The column index
8850 * @param {Boolean} editable True if the column is editable
8852 setEditable : function(col, editable){
8853 this.config[col].editable = editable;
8858 * Returns true if the column is hidden.
8859 * @param {Number} colIndex The column index
8862 isHidden : function(colIndex){
8863 return this.config[colIndex].hidden;
8868 * Returns true if the column width cannot be changed
8870 isFixed : function(colIndex){
8871 return this.config[colIndex].fixed;
8875 * Returns true if the column can be resized
8878 isResizable : function(colIndex){
8879 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8882 * Sets if a column is hidden.
8883 * @param {Number} colIndex The column index
8884 * @param {Boolean} hidden True if the column is hidden
8886 setHidden : function(colIndex, hidden){
8887 this.config[colIndex].hidden = hidden;
8888 this.totalWidth = null;
8889 this.fireEvent("hiddenchange", this, colIndex, hidden);
8893 * Sets the editor for a column.
8894 * @param {Number} col The column index
8895 * @param {Object} editor The editor object
8897 setEditor : function(col, editor){
8898 this.config[col].editor = editor;
8901 * Add a column (experimental...) - defaults to adding to the end..
8902 * @param {Object} config
8904 addColumn : function(c)
8907 var i = this.config.length;
8910 if(typeof c.dataIndex == "undefined"){
8913 if(typeof c.renderer == "string"){
8914 c.renderer = Roo.util.Format[c.renderer];
8916 if(typeof c.id == "undefined"){
8919 if(c.editor && c.editor.xtype){
8920 c.editor = Roo.factory(c.editor, Roo.grid);
8922 if(c.editor && c.editor.isFormField){
8923 c.editor = new Roo.grid.GridEditor(c.editor);
8925 this.lookup[c.id] = c;
8930 Roo.grid.ColumnModel.defaultRenderer = function(value)
8932 if(typeof value == "object") {
8935 if(typeof value == "string" && value.length < 1){
8939 return String.format("{0}", value);
8942 // Alias for backwards compatibility
8943 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8946 * Ext JS Library 1.1.1
8947 * Copyright(c) 2006-2007, Ext JS, LLC.
8949 * Originally Released Under LGPL - original licence link has changed is not relivant.
8952 * <script type="text/javascript">
8956 * @class Roo.LoadMask
8957 * A simple utility class for generically masking elements while loading data. If the element being masked has
8958 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8959 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8960 * element's UpdateManager load indicator and will be destroyed after the initial load.
8962 * Create a new LoadMask
8963 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8964 * @param {Object} config The config object
8966 Roo.LoadMask = function(el, config){
8967 this.el = Roo.get(el);
8968 Roo.apply(this, config);
8970 this.store.on('beforeload', this.onBeforeLoad, this);
8971 this.store.on('load', this.onLoad, this);
8972 this.store.on('loadexception', this.onLoadException, this);
8973 this.removeMask = false;
8975 var um = this.el.getUpdateManager();
8976 um.showLoadIndicator = false; // disable the default indicator
8977 um.on('beforeupdate', this.onBeforeLoad, this);
8978 um.on('update', this.onLoad, this);
8979 um.on('failure', this.onLoad, this);
8980 this.removeMask = true;
8984 Roo.LoadMask.prototype = {
8986 * @cfg {Boolean} removeMask
8987 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8988 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
8993 * The text to display in a centered loading message box (defaults to 'Loading...')
8997 * @cfg {String} msgCls
8998 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9000 msgCls : 'x-mask-loading',
9003 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9009 * Disables the mask to prevent it from being displayed
9011 disable : function(){
9012 this.disabled = true;
9016 * Enables the mask so that it can be displayed
9018 enable : function(){
9019 this.disabled = false;
9022 onLoadException : function()
9026 if (typeof(arguments[3]) != 'undefined') {
9027 Roo.MessageBox.alert("Error loading",arguments[3]);
9031 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9032 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9039 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9044 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9048 onBeforeLoad : function(){
9050 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9055 destroy : function(){
9057 this.store.un('beforeload', this.onBeforeLoad, this);
9058 this.store.un('load', this.onLoad, this);
9059 this.store.un('loadexception', this.onLoadException, this);
9061 var um = this.el.getUpdateManager();
9062 um.un('beforeupdate', this.onBeforeLoad, this);
9063 um.un('update', this.onLoad, this);
9064 um.un('failure', this.onLoad, this);
9068 * @class Roo.bootstrap.Table
9070 * @extends Roo.bootstrap.Component
9071 * @children Roo.bootstrap.TableBody
9072 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9073 * Similar to Roo.grid.Grid
9075 var table = Roo.factory({
9077 xns : Roo.bootstrap,
9078 autoSizeColumns: true,
9085 sortInfo : { direction : 'ASC', field: 'name' },
9087 xtype : 'HttpProxy',
9090 url : 'https://example.com/some.data.url.json'
9093 xtype : 'JsonReader',
9095 fields : [ 'id', 'name', whatever' ],
9102 xtype : 'ColumnModel',
9106 dataIndex : 'is_in_group',
9109 renderer : function(v, x , r) {
9111 return String.format("{0}", v)
9117 xtype : 'RowSelectionModel',
9118 xns : Roo.bootstrap.Table
9119 // you can add listeners to catch selection change here....
9125 grid.render(Roo.get("some-div"));
9128 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9133 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9134 * @cfg {Roo.data.Store} store The data store to use
9135 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9137 * @cfg {String} cls table class
9140 * @cfg {boolean} striped Should the rows be alternative striped
9141 * @cfg {boolean} bordered Add borders to the table
9142 * @cfg {boolean} hover Add hover highlighting
9143 * @cfg {boolean} condensed Format condensed
9144 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9145 * also adds table-responsive (see bootstrap docs for details)
9146 * @cfg {Boolean} loadMask (true|false) default false
9147 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9148 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9149 * @cfg {Boolean} rowSelection (true|false) default false
9150 * @cfg {Boolean} cellSelection (true|false) default false
9151 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
9152 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9153 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9154 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9155 * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
9156 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9159 * Create a new Table
9160 * @param {Object} config The config object
9163 Roo.bootstrap.Table = function(config)
9165 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9168 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9169 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9170 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9171 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9173 this.view = this; // compat with grid.
9175 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9177 this.sm.grid = this;
9178 this.selModel = Roo.factory(this.sm, Roo.grid);
9179 this.sm = this.selModel;
9180 this.sm.xmodule = this.xmodule || false;
9183 if (this.cm && typeof(this.cm.config) == 'undefined') {
9184 this.colModel = new Roo.grid.ColumnModel(this.cm);
9185 this.cm = this.colModel;
9186 this.cm.xmodule = this.xmodule || false;
9189 this.store= Roo.factory(this.store, Roo.data);
9190 this.ds = this.store;
9191 this.ds.xmodule = this.xmodule || false;
9194 if (this.footer && this.store) {
9195 this.footer.dataSource = this.ds;
9196 this.footer = Roo.factory(this.footer);
9203 * Fires when a cell is clicked
9204 * @param {Roo.bootstrap.Table} this
9205 * @param {Roo.Element} el
9206 * @param {Number} rowIndex
9207 * @param {Number} columnIndex
9208 * @param {Roo.EventObject} e
9212 * @event celldblclick
9213 * Fires when a cell is double clicked
9214 * @param {Roo.bootstrap.Table} this
9215 * @param {Roo.Element} el
9216 * @param {Number} rowIndex
9217 * @param {Number} columnIndex
9218 * @param {Roo.EventObject} e
9220 "celldblclick" : true,
9223 * Fires when a row is clicked
9224 * @param {Roo.bootstrap.Table} this
9225 * @param {Roo.Element} el
9226 * @param {Number} rowIndex
9227 * @param {Roo.EventObject} e
9231 * @event rowdblclick
9232 * Fires when a row is double clicked
9233 * @param {Roo.bootstrap.Table} this
9234 * @param {Roo.Element} el
9235 * @param {Number} rowIndex
9236 * @param {Roo.EventObject} e
9238 "rowdblclick" : true,
9241 * Fires when a mouseover occur
9242 * @param {Roo.bootstrap.Table} this
9243 * @param {Roo.Element} el
9244 * @param {Number} rowIndex
9245 * @param {Number} columnIndex
9246 * @param {Roo.EventObject} e
9251 * Fires when a mouseout occur
9252 * @param {Roo.bootstrap.Table} this
9253 * @param {Roo.Element} el
9254 * @param {Number} rowIndex
9255 * @param {Number} columnIndex
9256 * @param {Roo.EventObject} e
9261 * Fires when a row is rendered, so you can change add a style to it.
9262 * @param {Roo.bootstrap.Table} this
9263 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9267 * @event rowsrendered
9268 * Fires when all the rows have been rendered
9269 * @param {Roo.bootstrap.Table} this
9271 'rowsrendered' : true,
9273 * @event contextmenu
9274 * The raw contextmenu event for the entire grid.
9275 * @param {Roo.EventObject} e
9277 "contextmenu" : true,
9279 * @event rowcontextmenu
9280 * Fires when a row is right clicked
9281 * @param {Roo.bootstrap.Table} this
9282 * @param {Number} rowIndex
9283 * @param {Roo.EventObject} e
9285 "rowcontextmenu" : true,
9287 * @event cellcontextmenu
9288 * Fires when a cell is right clicked
9289 * @param {Roo.bootstrap.Table} this
9290 * @param {Number} rowIndex
9291 * @param {Number} cellIndex
9292 * @param {Roo.EventObject} e
9294 "cellcontextmenu" : true,
9296 * @event headercontextmenu
9297 * Fires when a header is right clicked
9298 * @param {Roo.bootstrap.Table} this
9299 * @param {Number} columnIndex
9300 * @param {Roo.EventObject} e
9302 "headercontextmenu" : true,
9305 * The raw mousedown event for the entire grid.
9306 * @param {Roo.EventObject} e
9313 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9329 enableColumnResize: true,
9331 rowSelection : false,
9332 cellSelection : false,
9335 minColumnWidth : 50,
9337 // Roo.Element - the tbody
9338 bodyEl: false, // <tbody> Roo.Element - thead element
9339 headEl: false, // <thead> Roo.Element - thead element
9340 resizeProxy : false, // proxy element for dragging?
9344 container: false, // used by gridpanel...
9350 auto_hide_footer : false,
9352 view: false, // actually points to this..
9354 getAutoCreate : function()
9356 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9363 // this get's auto added by panel.Grid
9364 if (this.scrollBody) {
9365 cfg.cls += ' table-body-fixed';
9368 cfg.cls += ' table-striped';
9372 cfg.cls += ' table-hover';
9374 if (this.bordered) {
9375 cfg.cls += ' table-bordered';
9377 if (this.condensed) {
9378 cfg.cls += ' table-condensed';
9381 if (this.responsive) {
9382 cfg.cls += ' table-responsive';
9386 cfg.cls+= ' ' +this.cls;
9392 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9395 if(this.store || this.cm){
9396 if(this.headerShow){
9397 cfg.cn.push(this.renderHeader());
9400 cfg.cn.push(this.renderBody());
9402 if(this.footerShow){
9403 cfg.cn.push(this.renderFooter());
9405 // where does this come from?
9406 //cfg.cls+= ' TableGrid';
9409 return { cn : [ cfg ] };
9412 initEvents : function()
9414 if(!this.store || !this.cm){
9417 if (this.selModel) {
9418 this.selModel.initEvents();
9422 //Roo.log('initEvents with ds!!!!');
9424 this.bodyEl = this.el.select('tbody', true).first();
9425 this.headEl = this.el.select('thead', true).first();
9426 this.mainFoot = this.el.select('tfoot', true).first();
9431 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9432 e.on('click', this.sort, this);
9436 // why is this done????? = it breaks dialogs??
9437 //this.parent().el.setStyle('position', 'relative');
9441 this.footer.parentId = this.id;
9442 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9445 this.el.select('tfoot tr td').first().addClass('hide');
9450 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9453 this.store.on('load', this.onLoad, this);
9454 this.store.on('beforeload', this.onBeforeLoad, this);
9455 this.store.on('update', this.onUpdate, this);
9456 this.store.on('add', this.onAdd, this);
9457 this.store.on("clear", this.clear, this);
9459 this.el.on("contextmenu", this.onContextMenu, this);
9462 this.cm.on("headerchange", this.onHeaderChange, this);
9463 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9465 //?? does bodyEl get replaced on render?
9466 this.bodyEl.on("click", this.onClick, this);
9467 this.bodyEl.on("dblclick", this.onDblClick, this);
9468 this.bodyEl.on('scroll', this.onBodyScroll, this);
9470 // guessing mainbody will work - this relays usually caught by selmodel at present.
9471 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9474 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9477 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9478 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9483 // Compatibility with grid - we implement all the view features at present.
9484 getView : function()
9489 initCSS : function()
9493 var cm = this.cm, styles = [];
9494 this.CSS.removeStyleSheet(this.id + '-cssrules');
9495 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9496 // we can honour xs/sm/md/xl as widths...
9497 // we first have to decide what widht we are currently at...
9498 var sz = Roo.getGridSize();
9502 var cols = []; // visable cols.
9504 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9505 var w = cm.getColumnWidth(i, false);
9507 cols.push( { rel : false, abs : 0 });
9511 cols.push( { rel : false, abs : w });
9513 last = i; // not really..
9516 var w = cm.getColumnWidth(i, sz);
9521 cols.push( { rel : w, abs : false });
9524 var avail = this.bodyEl.dom.clientWidth - total_abs;
9526 var unitWidth = Math.floor(avail / total);
9527 var rem = avail - (unitWidth * total);
9529 var hidden, width, pos = 0 , splithide , left;
9530 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9532 hidden = 'display:none;';
9534 width = 'width:0px;';
9536 if(!cm.isHidden(i)){
9540 // we can honour xs/sm/md/xl ?
9541 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9543 hidden = 'display:none;';
9545 // width should return a small number...
9547 w+=rem; // add the remaining with..
9550 left = "left:" + (pos -4) + "px;";
9551 width = "width:" + w+ "px;";
9554 if (this.responsive) {
9557 hidden = cm.isHidden(i) ? 'display:none;' : '';
9558 splithide = 'display: none;';
9561 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9564 splithide = 'display:none;';
9567 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9568 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9573 //Roo.log(styles.join(''));
9574 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9580 onContextMenu : function(e, t)
9582 this.processEvent("contextmenu", e);
9585 processEvent : function(name, e)
9587 if (name != 'touchstart' ) {
9588 this.fireEvent(name, e);
9591 var t = e.getTarget();
9593 var cell = Roo.get(t);
9599 if(cell.findParent('tfoot', false, true)){
9603 if(cell.findParent('thead', false, true)){
9605 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9606 cell = Roo.get(t).findParent('th', false, true);
9608 Roo.log("failed to find th in thead?");
9609 Roo.log(e.getTarget());
9614 var cellIndex = cell.dom.cellIndex;
9616 var ename = name == 'touchstart' ? 'click' : name;
9617 this.fireEvent("header" + ename, this, cellIndex, e);
9622 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9623 cell = Roo.get(t).findParent('td', false, true);
9625 Roo.log("failed to find th in tbody?");
9626 Roo.log(e.getTarget());
9631 var row = cell.findParent('tr', false, true);
9632 var cellIndex = cell.dom.cellIndex;
9633 var rowIndex = row.dom.rowIndex - 1;
9637 this.fireEvent("row" + name, this, rowIndex, e);
9641 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9647 onMouseover : function(e, el)
9649 var cell = Roo.get(el);
9655 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9656 cell = cell.findParent('td', false, true);
9659 var row = cell.findParent('tr', false, true);
9660 var cellIndex = cell.dom.cellIndex;
9661 var rowIndex = row.dom.rowIndex - 1; // start from 0
9663 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9667 onMouseout : function(e, el)
9669 var cell = Roo.get(el);
9675 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9676 cell = cell.findParent('td', false, true);
9679 var row = cell.findParent('tr', false, true);
9680 var cellIndex = cell.dom.cellIndex;
9681 var rowIndex = row.dom.rowIndex - 1; // start from 0
9683 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9687 onClick : function(e, el)
9689 var cell = Roo.get(el);
9691 if(!cell || (!this.cellSelection && !this.rowSelection)){
9695 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9696 cell = cell.findParent('td', false, true);
9699 if(!cell || typeof(cell) == 'undefined'){
9703 var row = cell.findParent('tr', false, true);
9705 if(!row || typeof(row) == 'undefined'){
9709 var cellIndex = cell.dom.cellIndex;
9710 var rowIndex = this.getRowIndex(row);
9712 // why??? - should these not be based on SelectionModel?
9713 //if(this.cellSelection){
9714 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9717 //if(this.rowSelection){
9718 this.fireEvent('rowclick', this, row, rowIndex, e);
9723 onDblClick : function(e,el)
9725 var cell = Roo.get(el);
9727 if(!cell || (!this.cellSelection && !this.rowSelection)){
9731 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9732 cell = cell.findParent('td', false, true);
9735 if(!cell || typeof(cell) == 'undefined'){
9739 var row = cell.findParent('tr', false, true);
9741 if(!row || typeof(row) == 'undefined'){
9745 var cellIndex = cell.dom.cellIndex;
9746 var rowIndex = this.getRowIndex(row);
9748 if(this.cellSelection){
9749 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9752 if(this.rowSelection){
9753 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9756 findRowIndex : function(el)
9758 var cell = Roo.get(el);
9762 var row = cell.findParent('tr', false, true);
9764 if(!row || typeof(row) == 'undefined'){
9767 return this.getRowIndex(row);
9769 sort : function(e,el)
9771 var col = Roo.get(el);
9773 if(!col.hasClass('sortable')){
9777 var sort = col.attr('sort');
9780 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9784 this.store.sortInfo = {field : sort, direction : dir};
9787 Roo.log("calling footer first");
9788 this.footer.onClick('first');
9791 this.store.load({ params : { start : 0 } });
9795 renderHeader : function()
9803 this.totalWidth = 0;
9805 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9807 var config = cm.config[i];
9811 cls : 'x-hcol-' + i,
9814 html: cm.getColumnHeader(i)
9817 var tooltip = cm.getColumnTooltip(i);
9819 c.tooltip = tooltip;
9825 if(typeof(config.sortable) != 'undefined' && config.sortable){
9826 c.cls += ' sortable';
9827 c.html = '<i class="fa"></i>' + c.html;
9830 // could use BS4 hidden-..-down
9832 if(typeof(config.lgHeader) != 'undefined'){
9833 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9836 if(typeof(config.mdHeader) != 'undefined'){
9837 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9840 if(typeof(config.smHeader) != 'undefined'){
9841 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9844 if(typeof(config.xsHeader) != 'undefined'){
9845 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9852 if(typeof(config.tooltip) != 'undefined'){
9853 c.tooltip = config.tooltip;
9856 if(typeof(config.colspan) != 'undefined'){
9857 c.colspan = config.colspan;
9860 // hidden is handled by CSS now
9862 if(typeof(config.dataIndex) != 'undefined'){
9863 c.sort = config.dataIndex;
9868 if(typeof(config.align) != 'undefined' && config.align.length){
9869 c.style += ' text-align:' + config.align + ';';
9872 /* width is done in CSS
9873 *if(typeof(config.width) != 'undefined'){
9874 c.style += ' width:' + config.width + 'px;';
9875 this.totalWidth += config.width;
9877 this.totalWidth += 100; // assume minimum of 100 per column?
9881 if(typeof(config.cls) != 'undefined'){
9882 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9884 // this is the bit that doesnt reall work at all...
9886 if (this.responsive) {
9889 ['xs','sm','md','lg'].map(function(size){
9891 if(typeof(config[size]) == 'undefined'){
9895 if (!config[size]) { // 0 = hidden
9896 // BS 4 '0' is treated as hide that column and below.
9897 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9901 c.cls += ' col-' + size + '-' + config[size] + (
9902 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9910 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9921 renderBody : function()
9931 colspan : this.cm.getColumnCount()
9941 renderFooter : function()
9951 colspan : this.cm.getColumnCount()
9965 // Roo.log('ds onload');
9970 var ds = this.store;
9972 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9973 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9974 if (_this.store.sortInfo) {
9976 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9977 e.select('i', true).addClass(['fa-arrow-up']);
9980 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9981 e.select('i', true).addClass(['fa-arrow-down']);
9986 var tbody = this.bodyEl;
9988 if(ds.getCount() > 0){
9989 ds.data.each(function(d,rowIndex){
9990 var row = this.renderRow(cm, ds, rowIndex);
9992 tbody.createChild(row);
9996 if(row.cellObjects.length){
9997 Roo.each(row.cellObjects, function(r){
9998 _this.renderCellObject(r);
10005 var tfoot = this.el.select('tfoot', true).first();
10007 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10009 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10011 var total = this.ds.getTotalCount();
10013 if(this.footer.pageSize < total){
10014 this.mainFoot.show();
10018 Roo.each(this.el.select('tbody td', true).elements, function(e){
10019 e.on('mouseover', _this.onMouseover, _this);
10022 Roo.each(this.el.select('tbody td', true).elements, function(e){
10023 e.on('mouseout', _this.onMouseout, _this);
10025 this.fireEvent('rowsrendered', this);
10029 this.initCSS(); /// resize cols
10035 onUpdate : function(ds,record)
10037 this.refreshRow(record);
10041 onRemove : function(ds, record, index, isUpdate){
10042 if(isUpdate !== true){
10043 this.fireEvent("beforerowremoved", this, index, record);
10045 var bt = this.bodyEl.dom;
10047 var rows = this.el.select('tbody > tr', true).elements;
10049 if(typeof(rows[index]) != 'undefined'){
10050 bt.removeChild(rows[index].dom);
10053 // if(bt.rows[index]){
10054 // bt.removeChild(bt.rows[index]);
10057 if(isUpdate !== true){
10058 //this.stripeRows(index);
10059 //this.syncRowHeights(index, index);
10061 this.fireEvent("rowremoved", this, index, record);
10065 onAdd : function(ds, records, rowIndex)
10067 //Roo.log('on Add called');
10068 // - note this does not handle multiple adding very well..
10069 var bt = this.bodyEl.dom;
10070 for (var i =0 ; i < records.length;i++) {
10071 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10072 //Roo.log(records[i]);
10073 //Roo.log(this.store.getAt(rowIndex+i));
10074 this.insertRow(this.store, rowIndex + i, false);
10081 refreshRow : function(record){
10082 var ds = this.store, index;
10083 if(typeof record == 'number'){
10085 record = ds.getAt(index);
10087 index = ds.indexOf(record);
10089 return; // should not happen - but seems to
10092 this.insertRow(ds, index, true);
10094 this.onRemove(ds, record, index+1, true);
10096 //this.syncRowHeights(index, index);
10098 this.fireEvent("rowupdated", this, index, record);
10100 // private - called by RowSelection
10101 onRowSelect : function(rowIndex){
10102 var row = this.getRowDom(rowIndex);
10103 row.addClass(['bg-info','info']);
10105 // private - called by RowSelection
10106 onRowDeselect : function(rowIndex)
10108 if (rowIndex < 0) {
10111 var row = this.getRowDom(rowIndex);
10112 row.removeClass(['bg-info','info']);
10115 * Focuses the specified row.
10116 * @param {Number} row The row index
10118 focusRow : function(row)
10120 //Roo.log('GridView.focusRow');
10121 var x = this.bodyEl.dom.scrollLeft;
10122 this.focusCell(row, 0, false);
10123 this.bodyEl.dom.scrollLeft = x;
10127 * Focuses the specified cell.
10128 * @param {Number} row The row index
10129 * @param {Number} col The column index
10130 * @param {Boolean} hscroll false to disable horizontal scrolling
10132 focusCell : function(row, col, hscroll)
10134 //Roo.log('GridView.focusCell');
10135 var el = this.ensureVisible(row, col, hscroll);
10136 // not sure what focusEL achives = it's a <a> pos relative
10137 //this.focusEl.alignTo(el, "tl-tl");
10139 // this.focusEl.focus();
10141 // this.focusEl.focus.defer(1, this.focusEl);
10146 * Scrolls the specified cell into view
10147 * @param {Number} row The row index
10148 * @param {Number} col The column index
10149 * @param {Boolean} hscroll false to disable horizontal scrolling
10151 ensureVisible : function(row, col, hscroll)
10153 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10154 //return null; //disable for testing.
10155 if(typeof row != "number"){
10156 row = row.rowIndex;
10158 if(row < 0 && row >= this.ds.getCount()){
10161 col = (col !== undefined ? col : 0);
10163 while(cm.isHidden(col)){
10167 var el = this.getCellDom(row, col);
10171 var c = this.bodyEl.dom;
10173 var ctop = parseInt(el.offsetTop, 10);
10174 var cleft = parseInt(el.offsetLeft, 10);
10175 var cbot = ctop + el.offsetHeight;
10176 var cright = cleft + el.offsetWidth;
10178 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10179 var ch = 0; //?? header is not withing the area?
10180 var stop = parseInt(c.scrollTop, 10);
10181 var sleft = parseInt(c.scrollLeft, 10);
10182 var sbot = stop + ch;
10183 var sright = sleft + c.clientWidth;
10185 Roo.log('GridView.ensureVisible:' +
10187 ' c.clientHeight:' + c.clientHeight +
10188 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10196 c.scrollTop = ctop;
10197 //Roo.log("set scrolltop to ctop DISABLE?");
10198 }else if(cbot > sbot){
10199 //Roo.log("set scrolltop to cbot-ch");
10200 c.scrollTop = cbot-ch;
10203 if(hscroll !== false){
10205 c.scrollLeft = cleft;
10206 }else if(cright > sright){
10207 c.scrollLeft = cright-c.clientWidth;
10215 insertRow : function(dm, rowIndex, isUpdate){
10218 this.fireEvent("beforerowsinserted", this, rowIndex);
10220 //var s = this.getScrollState();
10221 var row = this.renderRow(this.cm, this.store, rowIndex);
10222 // insert before rowIndex..
10223 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10227 if(row.cellObjects.length){
10228 Roo.each(row.cellObjects, function(r){
10229 _this.renderCellObject(r);
10234 this.fireEvent("rowsinserted", this, rowIndex);
10235 //this.syncRowHeights(firstRow, lastRow);
10236 //this.stripeRows(firstRow);
10243 getRowDom : function(rowIndex)
10245 var rows = this.el.select('tbody > tr', true).elements;
10247 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10250 getCellDom : function(rowIndex, colIndex)
10252 var row = this.getRowDom(rowIndex);
10253 if (row === false) {
10256 var cols = row.select('td', true).elements;
10257 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10261 // returns the object tree for a tr..
10264 renderRow : function(cm, ds, rowIndex)
10266 var d = ds.getAt(rowIndex);
10270 cls : 'x-row-' + rowIndex,
10274 var cellObjects = [];
10276 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10277 var config = cm.config[i];
10279 var renderer = cm.getRenderer(i);
10283 if(typeof(renderer) !== 'undefined'){
10284 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10286 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10287 // and are rendered into the cells after the row is rendered - using the id for the element.
10289 if(typeof(value) === 'object'){
10299 rowIndex : rowIndex,
10304 this.fireEvent('rowclass', this, rowcfg);
10308 // this might end up displaying HTML?
10309 // this is too messy... - better to only do it on columsn you know are going to be too long
10310 //tooltip : (typeof(value) === 'object') ? '' : value,
10311 cls : rowcfg.rowClass + ' x-col-' + i,
10313 html: (typeof(value) === 'object') ? '' : value
10320 if(typeof(config.colspan) != 'undefined'){
10321 td.colspan = config.colspan;
10326 if(typeof(config.align) != 'undefined' && config.align.length){
10327 td.style += ' text-align:' + config.align + ';';
10329 if(typeof(config.valign) != 'undefined' && config.valign.length){
10330 td.style += ' vertical-align:' + config.valign + ';';
10333 if(typeof(config.width) != 'undefined'){
10334 td.style += ' width:' + config.width + 'px;';
10338 if(typeof(config.cursor) != 'undefined'){
10339 td.style += ' cursor:' + config.cursor + ';';
10342 if(typeof(config.cls) != 'undefined'){
10343 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10345 if (this.responsive) {
10346 ['xs','sm','md','lg'].map(function(size){
10348 if(typeof(config[size]) == 'undefined'){
10354 if (!config[size]) { // 0 = hidden
10355 // BS 4 '0' is treated as hide that column and below.
10356 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10360 td.cls += ' col-' + size + '-' + config[size] + (
10361 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10371 row.cellObjects = cellObjects;
10379 onBeforeLoad : function()
10388 this.el.select('tbody', true).first().dom.innerHTML = '';
10391 * Show or hide a row.
10392 * @param {Number} rowIndex to show or hide
10393 * @param {Boolean} state hide
10395 setRowVisibility : function(rowIndex, state)
10397 var bt = this.bodyEl.dom;
10399 var rows = this.el.select('tbody > tr', true).elements;
10401 if(typeof(rows[rowIndex]) == 'undefined'){
10404 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10409 getSelectionModel : function(){
10410 if(!this.selModel){
10411 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10413 return this.selModel;
10416 * Render the Roo.bootstrap object from renderder
10418 renderCellObject : function(r)
10422 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10424 var t = r.cfg.render(r.container);
10427 Roo.each(r.cfg.cn, function(c){
10429 container: t.getChildContainer(),
10432 _this.renderCellObject(child);
10437 * get the Row Index from a dom element.
10438 * @param {Roo.Element} row The row to look for
10439 * @returns {Number} the row
10441 getRowIndex : function(row)
10445 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10456 * get the header TH element for columnIndex
10457 * @param {Number} columnIndex
10458 * @returns {Roo.Element}
10460 getHeaderIndex: function(colIndex)
10462 var cols = this.headEl.select('th', true).elements;
10463 return cols[colIndex];
10466 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10467 * @param {domElement} cell to look for
10468 * @returns {Number} the column
10470 getCellIndex : function(cell)
10472 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10474 return parseInt(id[1], 10);
10479 * Returns the grid's underlying element = used by panel.Grid
10480 * @return {Element} The element
10482 getGridEl : function(){
10486 * Forces a resize - used by panel.Grid
10487 * @return {Element} The element
10489 autoSize : function()
10491 //var ctr = Roo.get(this.container.dom.parentElement);
10492 var ctr = Roo.get(this.el.dom);
10494 var thd = this.getGridEl().select('thead',true).first();
10495 var tbd = this.getGridEl().select('tbody', true).first();
10496 var tfd = this.getGridEl().select('tfoot', true).first();
10498 var cw = ctr.getWidth();
10499 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10503 tbd.setWidth(ctr.getWidth());
10504 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10505 // this needs fixing for various usage - currently only hydra job advers I think..
10507 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10509 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10512 cw = Math.max(cw, this.totalWidth);
10513 this.getGridEl().select('tbody tr',true).setWidth(cw);
10516 // resize 'expandable coloumn?
10518 return; // we doe not have a view in this design..
10521 onBodyScroll: function()
10523 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10525 this.headEl.setStyle({
10526 'position' : 'relative',
10527 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10533 var scrollHeight = this.bodyEl.dom.scrollHeight;
10535 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10537 var height = this.bodyEl.getHeight();
10539 if(scrollHeight - height == scrollTop) {
10541 var total = this.ds.getTotalCount();
10543 if(this.footer.cursor + this.footer.pageSize < total){
10545 this.footer.ds.load({
10547 start : this.footer.cursor + this.footer.pageSize,
10548 limit : this.footer.pageSize
10557 onColumnSplitterMoved : function(i, diff)
10559 this.userResized = true;
10561 var cm = this.colModel;
10563 var w = this.getHeaderIndex(i).getWidth() + diff;
10566 cm.setColumnWidth(i, w, true);
10568 //var cid = cm.getColumnId(i); << not used in this version?
10569 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10571 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10572 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10573 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10575 //this.updateSplitters();
10576 //this.layout(); << ??
10577 this.fireEvent("columnresize", i, w);
10579 onHeaderChange : function()
10581 var header = this.renderHeader();
10582 var table = this.el.select('table', true).first();
10584 this.headEl.remove();
10585 this.headEl = table.createChild(header, this.bodyEl, false);
10587 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10588 e.on('click', this.sort, this);
10591 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10592 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10597 onHiddenChange : function(colModel, colIndex, hidden)
10600 this.cm.setHidden()
10601 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10602 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10604 this.CSS.updateRule(thSelector, "display", "");
10605 this.CSS.updateRule(tdSelector, "display", "");
10608 this.CSS.updateRule(thSelector, "display", "none");
10609 this.CSS.updateRule(tdSelector, "display", "none");
10612 // onload calls initCSS()
10613 this.onHeaderChange();
10617 setColumnWidth: function(col_index, width)
10619 // width = "md-2 xs-2..."
10620 if(!this.colModel.config[col_index]) {
10624 var w = width.split(" ");
10626 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10628 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10631 for(var j = 0; j < w.length; j++) {
10637 var size_cls = w[j].split("-");
10639 if(!Number.isInteger(size_cls[1] * 1)) {
10643 if(!this.colModel.config[col_index][size_cls[0]]) {
10647 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10651 h_row[0].classList.replace(
10652 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10653 "col-"+size_cls[0]+"-"+size_cls[1]
10656 for(var i = 0; i < rows.length; i++) {
10658 var size_cls = w[j].split("-");
10660 if(!Number.isInteger(size_cls[1] * 1)) {
10664 if(!this.colModel.config[col_index][size_cls[0]]) {
10668 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10672 rows[i].classList.replace(
10673 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10674 "col-"+size_cls[0]+"-"+size_cls[1]
10678 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10683 // currently only used to find the split on drag..
10684 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10689 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10690 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10699 * @class Roo.bootstrap.TableCell
10700 * @extends Roo.bootstrap.Component
10701 * @children Roo.bootstrap.Component
10702 * @parent Roo.bootstrap.TableRow
10703 * Bootstrap TableCell class
10705 * @cfg {String} html cell contain text
10706 * @cfg {String} cls cell class
10707 * @cfg {String} tag cell tag (td|th) default td
10708 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10709 * @cfg {String} align Aligns the content in a cell
10710 * @cfg {String} axis Categorizes cells
10711 * @cfg {String} bgcolor Specifies the background color of a cell
10712 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10713 * @cfg {Number} colspan Specifies the number of columns a cell should span
10714 * @cfg {String} headers Specifies one or more header cells a cell is related to
10715 * @cfg {Number} height Sets the height of a cell
10716 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10717 * @cfg {Number} rowspan Sets the number of rows a cell should span
10718 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10719 * @cfg {String} valign Vertical aligns the content in a cell
10720 * @cfg {Number} width Specifies the width of a cell
10723 * Create a new TableCell
10724 * @param {Object} config The config object
10727 Roo.bootstrap.TableCell = function(config){
10728 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10731 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10751 getAutoCreate : function(){
10752 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10759 cfg.tag = this.tag;
10772 cfg.align=this.align
10777 if (this.bgcolor) {
10778 cfg.bgcolor=this.bgcolor
10780 if (this.charoff) {
10781 cfg.charoff=this.charoff
10783 if (this.colspan) {
10784 cfg.colspan=this.colspan
10786 if (this.headers) {
10787 cfg.headers=this.headers
10790 cfg.height=this.height
10793 cfg.nowrap=this.nowrap
10795 if (this.rowspan) {
10796 cfg.rowspan=this.rowspan
10799 cfg.scope=this.scope
10802 cfg.valign=this.valign
10805 cfg.width=this.width
10824 * @class Roo.bootstrap.TableRow
10825 * @extends Roo.bootstrap.Component
10826 * @children Roo.bootstrap.TableCell
10827 * @parent Roo.bootstrap.TableBody
10828 * Bootstrap TableRow class
10829 * @cfg {String} cls row class
10830 * @cfg {String} align Aligns the content in a table row
10831 * @cfg {String} bgcolor Specifies a background color for a table row
10832 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10833 * @cfg {String} valign Vertical aligns the content in a table row
10836 * Create a new TableRow
10837 * @param {Object} config The config object
10840 Roo.bootstrap.TableRow = function(config){
10841 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10844 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10852 getAutoCreate : function(){
10853 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10860 cfg.cls = this.cls;
10863 cfg.align = this.align;
10866 cfg.bgcolor = this.bgcolor;
10869 cfg.charoff = this.charoff;
10872 cfg.valign = this.valign;
10890 * @class Roo.bootstrap.TableBody
10891 * @extends Roo.bootstrap.Component
10892 * @children Roo.bootstrap.TableRow
10893 * @parent Roo.bootstrap.Table
10894 * Bootstrap TableBody class
10895 * @cfg {String} cls element class
10896 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10897 * @cfg {String} align Aligns the content inside the element
10898 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10899 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10902 * Create a new TableBody
10903 * @param {Object} config The config object
10906 Roo.bootstrap.TableBody = function(config){
10907 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10910 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10918 getAutoCreate : function(){
10919 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10929 cfg.tag = this.tag;
10933 cfg.align = this.align;
10936 cfg.charoff = this.charoff;
10939 cfg.valign = this.valign;
10946 // initEvents : function()
10949 // if(!this.store){
10953 // this.store = Roo.factory(this.store, Roo.data);
10954 // this.store.on('load', this.onLoad, this);
10956 // this.store.load();
10960 // onLoad: function ()
10962 // this.fireEvent('load', this);
10972 * Ext JS Library 1.1.1
10973 * Copyright(c) 2006-2007, Ext JS, LLC.
10975 * Originally Released Under LGPL - original licence link has changed is not relivant.
10978 * <script type="text/javascript">
10981 // as we use this in bootstrap.
10982 Roo.namespace('Roo.form');
10984 * @class Roo.form.Action
10985 * Internal Class used to handle form actions
10987 * @param {Roo.form.BasicForm} el The form element or its id
10988 * @param {Object} config Configuration options
10993 // define the action interface
10994 Roo.form.Action = function(form, options){
10996 this.options = options || {};
10999 * Client Validation Failed
11002 Roo.form.Action.CLIENT_INVALID = 'client';
11004 * Server Validation Failed
11007 Roo.form.Action.SERVER_INVALID = 'server';
11009 * Connect to Server Failed
11012 Roo.form.Action.CONNECT_FAILURE = 'connect';
11014 * Reading Data from Server Failed
11017 Roo.form.Action.LOAD_FAILURE = 'load';
11019 Roo.form.Action.prototype = {
11021 failureType : undefined,
11022 response : undefined,
11023 result : undefined,
11025 // interface method
11026 run : function(options){
11030 // interface method
11031 success : function(response){
11035 // interface method
11036 handleResponse : function(response){
11040 // default connection failure
11041 failure : function(response){
11043 this.response = response;
11044 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11045 this.form.afterAction(this, false);
11048 processResponse : function(response){
11049 this.response = response;
11050 if(!response.responseText){
11053 this.result = this.handleResponse(response);
11054 return this.result;
11057 // utility functions used internally
11058 getUrl : function(appendParams){
11059 var url = this.options.url || this.form.url || this.form.el.dom.action;
11061 var p = this.getParams();
11063 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11069 getMethod : function(){
11070 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11073 getParams : function(){
11074 var bp = this.form.baseParams;
11075 var p = this.options.params;
11077 if(typeof p == "object"){
11078 p = Roo.urlEncode(Roo.applyIf(p, bp));
11079 }else if(typeof p == 'string' && bp){
11080 p += '&' + Roo.urlEncode(bp);
11083 p = Roo.urlEncode(bp);
11088 createCallback : function(){
11090 success: this.success,
11091 failure: this.failure,
11093 timeout: (this.form.timeout*1000),
11094 upload: this.form.fileUpload ? this.success : undefined
11099 Roo.form.Action.Submit = function(form, options){
11100 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11103 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11106 haveProgress : false,
11107 uploadComplete : false,
11109 // uploadProgress indicator.
11110 uploadProgress : function()
11112 if (!this.form.progressUrl) {
11116 if (!this.haveProgress) {
11117 Roo.MessageBox.progress("Uploading", "Uploading");
11119 if (this.uploadComplete) {
11120 Roo.MessageBox.hide();
11124 this.haveProgress = true;
11126 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11128 var c = new Roo.data.Connection();
11130 url : this.form.progressUrl,
11135 success : function(req){
11136 //console.log(data);
11140 rdata = Roo.decode(req.responseText)
11142 Roo.log("Invalid data from server..");
11146 if (!rdata || !rdata.success) {
11148 Roo.MessageBox.alert(Roo.encode(rdata));
11151 var data = rdata.data;
11153 if (this.uploadComplete) {
11154 Roo.MessageBox.hide();
11159 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11160 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11163 this.uploadProgress.defer(2000,this);
11166 failure: function(data) {
11167 Roo.log('progress url failed ');
11178 // run get Values on the form, so it syncs any secondary forms.
11179 this.form.getValues();
11181 var o = this.options;
11182 var method = this.getMethod();
11183 var isPost = method == 'POST';
11184 if(o.clientValidation === false || this.form.isValid()){
11186 if (this.form.progressUrl) {
11187 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11188 (new Date() * 1) + '' + Math.random());
11193 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11194 form:this.form.el.dom,
11195 url:this.getUrl(!isPost),
11197 params:isPost ? this.getParams() : null,
11198 isUpload: this.form.fileUpload,
11199 formData : this.form.formData
11202 this.uploadProgress();
11204 }else if (o.clientValidation !== false){ // client validation failed
11205 this.failureType = Roo.form.Action.CLIENT_INVALID;
11206 this.form.afterAction(this, false);
11210 success : function(response)
11212 this.uploadComplete= true;
11213 if (this.haveProgress) {
11214 Roo.MessageBox.hide();
11218 var result = this.processResponse(response);
11219 if(result === true || result.success){
11220 this.form.afterAction(this, true);
11224 this.form.markInvalid(result.errors);
11225 this.failureType = Roo.form.Action.SERVER_INVALID;
11227 this.form.afterAction(this, false);
11229 failure : function(response)
11231 this.uploadComplete= true;
11232 if (this.haveProgress) {
11233 Roo.MessageBox.hide();
11236 this.response = response;
11237 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11238 this.form.afterAction(this, false);
11241 handleResponse : function(response){
11242 if(this.form.errorReader){
11243 var rs = this.form.errorReader.read(response);
11246 for(var i = 0, len = rs.records.length; i < len; i++) {
11247 var r = rs.records[i];
11248 errors[i] = r.data;
11251 if(errors.length < 1){
11255 success : rs.success,
11261 ret = Roo.decode(response.responseText);
11265 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11275 Roo.form.Action.Load = function(form, options){
11276 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11277 this.reader = this.form.reader;
11280 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11285 Roo.Ajax.request(Roo.apply(
11286 this.createCallback(), {
11287 method:this.getMethod(),
11288 url:this.getUrl(false),
11289 params:this.getParams()
11293 success : function(response){
11295 var result = this.processResponse(response);
11296 if(result === true || !result.success || !result.data){
11297 this.failureType = Roo.form.Action.LOAD_FAILURE;
11298 this.form.afterAction(this, false);
11301 this.form.clearInvalid();
11302 this.form.setValues(result.data);
11303 this.form.afterAction(this, true);
11306 handleResponse : function(response){
11307 if(this.form.reader){
11308 var rs = this.form.reader.read(response);
11309 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11311 success : rs.success,
11315 return Roo.decode(response.responseText);
11319 Roo.form.Action.ACTION_TYPES = {
11320 'load' : Roo.form.Action.Load,
11321 'submit' : Roo.form.Action.Submit
11330 * @class Roo.bootstrap.form.Form
11331 * @extends Roo.bootstrap.Component
11332 * @children Roo.bootstrap.Component
11333 * Bootstrap Form class
11334 * @cfg {String} method GET | POST (default POST)
11335 * @cfg {String} labelAlign top | left (default top)
11336 * @cfg {String} align left | right - for navbars
11337 * @cfg {Boolean} loadMask load mask when submit (default true)
11341 * Create a new Form
11342 * @param {Object} config The config object
11346 Roo.bootstrap.form.Form = function(config){
11348 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11350 Roo.bootstrap.form.Form.popover.apply();
11354 * @event clientvalidation
11355 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11356 * @param {Form} this
11357 * @param {Boolean} valid true if the form has passed client-side validation
11359 clientvalidation: true,
11361 * @event beforeaction
11362 * Fires before any action is performed. Return false to cancel the action.
11363 * @param {Form} this
11364 * @param {Action} action The action to be performed
11366 beforeaction: true,
11368 * @event actionfailed
11369 * Fires when an action fails.
11370 * @param {Form} this
11371 * @param {Action} action The action that failed
11373 actionfailed : true,
11375 * @event actioncomplete
11376 * Fires when an action is completed.
11377 * @param {Form} this
11378 * @param {Action} action The action that completed
11380 actioncomplete : true
11384 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11387 * @cfg {String} method
11388 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11392 * @cfg {String} url
11393 * The URL to use for form actions if one isn't supplied in the action options.
11396 * @cfg {Boolean} fileUpload
11397 * Set to true if this form is a file upload.
11401 * @cfg {Object} baseParams
11402 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11406 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11410 * @cfg {Sting} align (left|right) for navbar forms
11415 activeAction : null,
11418 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11419 * element by passing it or its id or mask the form itself by passing in true.
11422 waitMsgTarget : false,
11427 * @cfg {Boolean} errorMask (true|false) default false
11432 * @cfg {Number} maskOffset Default 100
11437 * @cfg {Boolean} maskBody
11441 getAutoCreate : function(){
11445 method : this.method || 'POST',
11446 id : this.id || Roo.id(),
11449 if (this.parent().xtype.match(/^Nav/)) {
11450 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11454 if (this.labelAlign == 'left' ) {
11455 cfg.cls += ' form-horizontal';
11461 initEvents : function()
11463 this.el.on('submit', this.onSubmit, this);
11464 // this was added as random key presses on the form where triggering form submit.
11465 this.el.on('keypress', function(e) {
11466 if (e.getCharCode() != 13) {
11469 // we might need to allow it for textareas.. and some other items.
11470 // check e.getTarget().
11472 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11476 Roo.log("keypress blocked");
11478 e.preventDefault();
11484 onSubmit : function(e){
11489 * Returns true if client-side validation on the form is successful.
11492 isValid : function(){
11493 var items = this.getItems();
11495 var target = false;
11497 items.each(function(f){
11503 Roo.log('invalid field: ' + f.name);
11507 if(!target && f.el.isVisible(true)){
11513 if(this.errorMask && !valid){
11514 Roo.bootstrap.form.Form.popover.mask(this, target);
11521 * Returns true if any fields in this form have changed since their original load.
11524 isDirty : function(){
11526 var items = this.getItems();
11527 items.each(function(f){
11537 * Performs a predefined action (submit or load) or custom actions you define on this form.
11538 * @param {String} actionName The name of the action type
11539 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11540 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11541 * accept other config options):
11543 Property Type Description
11544 ---------------- --------------- ----------------------------------------------------------------------------------
11545 url String The url for the action (defaults to the form's url)
11546 method String The form method to use (defaults to the form's method, or POST if not defined)
11547 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11548 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11549 validate the form on the client (defaults to false)
11551 * @return {BasicForm} this
11553 doAction : function(action, options){
11554 if(typeof action == 'string'){
11555 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11557 if(this.fireEvent('beforeaction', this, action) !== false){
11558 this.beforeAction(action);
11559 action.run.defer(100, action);
11565 beforeAction : function(action){
11566 var o = action.options;
11571 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11573 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11576 // not really supported yet.. ??
11578 //if(this.waitMsgTarget === true){
11579 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11580 //}else if(this.waitMsgTarget){
11581 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11582 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11584 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11590 afterAction : function(action, success){
11591 this.activeAction = null;
11592 var o = action.options;
11597 Roo.get(document.body).unmask();
11603 //if(this.waitMsgTarget === true){
11604 // this.el.unmask();
11605 //}else if(this.waitMsgTarget){
11606 // this.waitMsgTarget.unmask();
11608 // Roo.MessageBox.updateProgress(1);
11609 // Roo.MessageBox.hide();
11616 Roo.callback(o.success, o.scope, [this, action]);
11617 this.fireEvent('actioncomplete', this, action);
11621 // failure condition..
11622 // we have a scenario where updates need confirming.
11623 // eg. if a locking scenario exists..
11624 // we look for { errors : { needs_confirm : true }} in the response.
11626 (typeof(action.result) != 'undefined') &&
11627 (typeof(action.result.errors) != 'undefined') &&
11628 (typeof(action.result.errors.needs_confirm) != 'undefined')
11631 Roo.log("not supported yet");
11634 Roo.MessageBox.confirm(
11635 "Change requires confirmation",
11636 action.result.errorMsg,
11641 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11651 Roo.callback(o.failure, o.scope, [this, action]);
11652 // show an error message if no failed handler is set..
11653 if (!this.hasListener('actionfailed')) {
11654 Roo.log("need to add dialog support");
11656 Roo.MessageBox.alert("Error",
11657 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11658 action.result.errorMsg :
11659 "Saving Failed, please check your entries or try again"
11664 this.fireEvent('actionfailed', this, action);
11669 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11670 * @param {String} id The value to search for
11673 findField : function(id){
11674 var items = this.getItems();
11675 var field = items.get(id);
11677 items.each(function(f){
11678 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11685 return field || null;
11688 * Mark fields in this form invalid in bulk.
11689 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11690 * @return {BasicForm} this
11692 markInvalid : function(errors){
11693 if(errors instanceof Array){
11694 for(var i = 0, len = errors.length; i < len; i++){
11695 var fieldError = errors[i];
11696 var f = this.findField(fieldError.id);
11698 f.markInvalid(fieldError.msg);
11704 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11705 field.markInvalid(errors[id]);
11709 //Roo.each(this.childForms || [], function (f) {
11710 // f.markInvalid(errors);
11717 * Set values for fields in this form in bulk.
11718 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11719 * @return {BasicForm} this
11721 setValues : function(values){
11722 if(values instanceof Array){ // array of objects
11723 for(var i = 0, len = values.length; i < len; i++){
11725 var f = this.findField(v.id);
11727 f.setValue(v.value);
11728 if(this.trackResetOnLoad){
11729 f.originalValue = f.getValue();
11733 }else{ // object hash
11736 if(typeof values[id] != 'function' && (field = this.findField(id))){
11738 if (field.setFromData &&
11739 field.valueField &&
11740 field.displayField &&
11741 // combos' with local stores can
11742 // be queried via setValue()
11743 // to set their value..
11744 (field.store && !field.store.isLocal)
11748 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11749 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11750 field.setFromData(sd);
11752 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11754 field.setFromData(values);
11757 field.setValue(values[id]);
11761 if(this.trackResetOnLoad){
11762 field.originalValue = field.getValue();
11768 //Roo.each(this.childForms || [], function (f) {
11769 // f.setValues(values);
11776 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11777 * they are returned as an array.
11778 * @param {Boolean} asString
11781 getValues : function(asString){
11782 //if (this.childForms) {
11783 // copy values from the child forms
11784 // Roo.each(this.childForms, function (f) {
11785 // this.setValues(f.getValues());
11791 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11792 if(asString === true){
11795 return Roo.urlDecode(fs);
11799 * Returns the fields in this form as an object with key/value pairs.
11800 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11803 getFieldValues : function(with_hidden)
11805 var items = this.getItems();
11807 items.each(function(f){
11809 if (!f.getName()) {
11813 var v = f.getValue();
11815 if (f.inputType =='radio') {
11816 if (typeof(ret[f.getName()]) == 'undefined') {
11817 ret[f.getName()] = ''; // empty..
11820 if (!f.el.dom.checked) {
11824 v = f.el.dom.value;
11828 if(f.xtype == 'MoneyField'){
11829 ret[f.currencyName] = f.getCurrency();
11832 // not sure if this supported any more..
11833 if ((typeof(v) == 'object') && f.getRawValue) {
11834 v = f.getRawValue() ; // dates..
11836 // combo boxes where name != hiddenName...
11837 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11838 ret[f.name] = f.getRawValue();
11840 ret[f.getName()] = v;
11847 * Clears all invalid messages in this form.
11848 * @return {BasicForm} this
11850 clearInvalid : function(){
11851 var items = this.getItems();
11853 items.each(function(f){
11861 * Resets this form.
11862 * @return {BasicForm} this
11864 reset : function(){
11865 var items = this.getItems();
11866 items.each(function(f){
11870 Roo.each(this.childForms || [], function (f) {
11878 getItems : function()
11880 var r=new Roo.util.MixedCollection(false, function(o){
11881 return o.id || (o.id = Roo.id());
11883 var iter = function(el) {
11890 Roo.each(el.items,function(e) {
11899 hideFields : function(items)
11901 Roo.each(items, function(i){
11903 var f = this.findField(i);
11914 showFields : function(items)
11916 Roo.each(items, function(i){
11918 var f = this.findField(i);
11931 Roo.apply(Roo.bootstrap.form.Form, {
11947 intervalID : false,
11953 if(this.isApplied){
11958 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11959 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11960 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11961 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11964 this.maskEl.top.enableDisplayMode("block");
11965 this.maskEl.left.enableDisplayMode("block");
11966 this.maskEl.bottom.enableDisplayMode("block");
11967 this.maskEl.right.enableDisplayMode("block");
11969 this.toolTip = new Roo.bootstrap.Tooltip({
11970 cls : 'roo-form-error-popover',
11972 'left' : ['r-l', [-2,0], 'right'],
11973 'right' : ['l-r', [2,0], 'left'],
11974 'bottom' : ['tl-bl', [0,2], 'top'],
11975 'top' : [ 'bl-tl', [0,-2], 'bottom']
11979 this.toolTip.render(Roo.get(document.body));
11981 this.toolTip.el.enableDisplayMode("block");
11983 Roo.get(document.body).on('click', function(){
11987 Roo.get(document.body).on('touchstart', function(){
11991 this.isApplied = true
11994 mask : function(form, target)
11998 this.target = target;
12000 if(!this.form.errorMask || !target.el){
12004 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12006 Roo.log(scrollable);
12008 var ot = this.target.el.calcOffsetsTo(scrollable);
12010 var scrollTo = ot[1] - this.form.maskOffset;
12012 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12014 scrollable.scrollTo('top', scrollTo);
12016 var box = this.target.el.getBox();
12018 var zIndex = Roo.bootstrap.Modal.zIndex++;
12021 this.maskEl.top.setStyle('position', 'absolute');
12022 this.maskEl.top.setStyle('z-index', zIndex);
12023 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12024 this.maskEl.top.setLeft(0);
12025 this.maskEl.top.setTop(0);
12026 this.maskEl.top.show();
12028 this.maskEl.left.setStyle('position', 'absolute');
12029 this.maskEl.left.setStyle('z-index', zIndex);
12030 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12031 this.maskEl.left.setLeft(0);
12032 this.maskEl.left.setTop(box.y - this.padding);
12033 this.maskEl.left.show();
12035 this.maskEl.bottom.setStyle('position', 'absolute');
12036 this.maskEl.bottom.setStyle('z-index', zIndex);
12037 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12038 this.maskEl.bottom.setLeft(0);
12039 this.maskEl.bottom.setTop(box.bottom + this.padding);
12040 this.maskEl.bottom.show();
12042 this.maskEl.right.setStyle('position', 'absolute');
12043 this.maskEl.right.setStyle('z-index', zIndex);
12044 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12045 this.maskEl.right.setLeft(box.right + this.padding);
12046 this.maskEl.right.setTop(box.y - this.padding);
12047 this.maskEl.right.show();
12049 this.toolTip.bindEl = this.target.el;
12051 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12053 var tip = this.target.blankText;
12055 if(this.target.getValue() !== '' ) {
12057 if (this.target.invalidText.length) {
12058 tip = this.target.invalidText;
12059 } else if (this.target.regexText.length){
12060 tip = this.target.regexText;
12064 this.toolTip.show(tip);
12066 this.intervalID = window.setInterval(function() {
12067 Roo.bootstrap.form.Form.popover.unmask();
12070 window.onwheel = function(){ return false;};
12072 (function(){ this.isMasked = true; }).defer(500, this);
12076 unmask : function()
12078 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12082 this.maskEl.top.setStyle('position', 'absolute');
12083 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12084 this.maskEl.top.hide();
12086 this.maskEl.left.setStyle('position', 'absolute');
12087 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12088 this.maskEl.left.hide();
12090 this.maskEl.bottom.setStyle('position', 'absolute');
12091 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12092 this.maskEl.bottom.hide();
12094 this.maskEl.right.setStyle('position', 'absolute');
12095 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12096 this.maskEl.right.hide();
12098 this.toolTip.hide();
12100 this.toolTip.el.hide();
12102 window.onwheel = function(){ return true;};
12104 if(this.intervalID){
12105 window.clearInterval(this.intervalID);
12106 this.intervalID = false;
12109 this.isMasked = false;
12119 * Ext JS Library 1.1.1
12120 * Copyright(c) 2006-2007, Ext JS, LLC.
12122 * Originally Released Under LGPL - original licence link has changed is not relivant.
12125 * <script type="text/javascript">
12128 * @class Roo.form.VTypes
12129 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12132 Roo.form.VTypes = function(){
12133 // closure these in so they are only created once.
12134 var alpha = /^[a-zA-Z_]+$/;
12135 var alphanum = /^[a-zA-Z0-9_]+$/;
12136 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12137 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12139 // All these messages and functions are configurable
12142 * The function used to validate email addresses
12143 * @param {String} value The email address
12145 'email' : function(v){
12146 return email.test(v);
12149 * The error text to display when the email validation function returns false
12152 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12154 * The keystroke filter mask to be applied on email input
12157 'emailMask' : /[a-z0-9_\.\-@]/i,
12160 * The function used to validate URLs
12161 * @param {String} value The URL
12163 'url' : function(v){
12164 return url.test(v);
12167 * The error text to display when the url validation function returns false
12170 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12173 * The function used to validate alpha values
12174 * @param {String} value The value
12176 'alpha' : function(v){
12177 return alpha.test(v);
12180 * The error text to display when the alpha validation function returns false
12183 'alphaText' : 'This field should only contain letters and _',
12185 * The keystroke filter mask to be applied on alpha input
12188 'alphaMask' : /[a-z_]/i,
12191 * The function used to validate alphanumeric values
12192 * @param {String} value The value
12194 'alphanum' : function(v){
12195 return alphanum.test(v);
12198 * The error text to display when the alphanumeric validation function returns false
12201 'alphanumText' : 'This field should only contain letters, numbers and _',
12203 * The keystroke filter mask to be applied on alphanumeric input
12206 'alphanumMask' : /[a-z0-9_]/i
12216 * @class Roo.bootstrap.form.Input
12217 * @extends Roo.bootstrap.Component
12218 * Bootstrap Input class
12219 * @cfg {Boolean} disabled is it disabled
12220 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12221 * @cfg {String} name name of the input
12222 * @cfg {string} fieldLabel - the label associated
12223 * @cfg {string} placeholder - placeholder to put in text.
12224 * @cfg {string} before - input group add on before
12225 * @cfg {string} after - input group add on after
12226 * @cfg {string} size - (lg|sm) or leave empty..
12227 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12228 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12229 * @cfg {Number} md colspan out of 12 for computer-sized screens
12230 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12231 * @cfg {string} value default value of the input
12232 * @cfg {Number} labelWidth set the width of label
12233 * @cfg {Number} labellg set the width of label (1-12)
12234 * @cfg {Number} labelmd set the width of label (1-12)
12235 * @cfg {Number} labelsm set the width of label (1-12)
12236 * @cfg {Number} labelxs set the width of label (1-12)
12237 * @cfg {String} labelAlign (top|left)
12238 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12239 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12240 * @cfg {String} indicatorpos (left|right) default left
12241 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12242 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12243 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12244 * @cfg {Roo.bootstrap.Button} before Button to show before
12245 * @cfg {Roo.bootstrap.Button} afterButton to show before
12246 * @cfg {String} align (left|center|right) Default left
12247 * @cfg {Boolean} forceFeedback (true|false) Default false
12250 * Create a new Input
12251 * @param {Object} config The config object
12254 Roo.bootstrap.form.Input = function(config){
12256 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12261 * Fires when this field receives input focus.
12262 * @param {Roo.form.Field} this
12267 * Fires when this field loses input focus.
12268 * @param {Roo.form.Field} this
12272 * @event specialkey
12273 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12274 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12275 * @param {Roo.form.Field} this
12276 * @param {Roo.EventObject} e The event object
12281 * Fires just before the field blurs if the field value has changed.
12282 * @param {Roo.form.Field} this
12283 * @param {Mixed} newValue The new value
12284 * @param {Mixed} oldValue The original value
12289 * Fires after the field has been marked as invalid.
12290 * @param {Roo.form.Field} this
12291 * @param {String} msg The validation message
12296 * Fires after the field has been validated with no errors.
12297 * @param {Roo.form.Field} this
12302 * Fires after the key up
12303 * @param {Roo.form.Field} this
12304 * @param {Roo.EventObject} e The event Object
12309 * Fires after the user pastes into input
12310 * @param {Roo.form.Field} this
12311 * @param {Roo.EventObject} e The event Object
12317 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12319 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12320 automatic validation (defaults to "keyup").
12322 validationEvent : "keyup",
12324 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12326 validateOnBlur : true,
12328 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12330 validationDelay : 250,
12332 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12334 focusClass : "x-form-focus", // not needed???
12338 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12340 invalidClass : "has-warning",
12343 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12345 validClass : "has-success",
12348 * @cfg {Boolean} hasFeedback (true|false) default true
12350 hasFeedback : true,
12353 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12355 invalidFeedbackClass : "glyphicon-warning-sign",
12358 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12360 validFeedbackClass : "glyphicon-ok",
12363 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12365 selectOnFocus : false,
12368 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12372 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12377 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12379 disableKeyFilter : false,
12382 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12386 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12390 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12392 blankText : "Please complete this mandatory field",
12395 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12399 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12401 maxLength : Number.MAX_VALUE,
12403 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12405 minLengthText : "The minimum length for this field is {0}",
12407 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12409 maxLengthText : "The maximum length for this field is {0}",
12413 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12414 * If available, this function will be called only after the basic validators all return true, and will be passed the
12415 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12419 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12420 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12421 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12425 * @cfg {String} regexText -- Depricated - use Invalid Text
12430 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12436 autocomplete: false,
12440 inputType : 'text',
12443 placeholder: false,
12448 preventMark: false,
12449 isFormField : true,
12452 labelAlign : false,
12455 formatedValue : false,
12456 forceFeedback : false,
12458 indicatorpos : 'left',
12468 parentLabelAlign : function()
12471 while (parent.parent()) {
12472 parent = parent.parent();
12473 if (typeof(parent.labelAlign) !='undefined') {
12474 return parent.labelAlign;
12481 getAutoCreate : function()
12483 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12489 if(this.inputType != 'hidden'){
12490 cfg.cls = 'form-group' //input-group
12496 type : this.inputType,
12497 value : this.value,
12498 cls : 'form-control',
12499 placeholder : this.placeholder || '',
12500 autocomplete : this.autocomplete || 'new-password'
12502 if (this.inputType == 'file') {
12503 input.style = 'overflow:hidden'; // why not in CSS?
12506 if(this.capture.length){
12507 input.capture = this.capture;
12510 if(this.accept.length){
12511 input.accept = this.accept + "/*";
12515 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12518 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12519 input.maxLength = this.maxLength;
12522 if (this.disabled) {
12523 input.disabled=true;
12526 if (this.readOnly) {
12527 input.readonly=true;
12531 input.name = this.name;
12535 input.cls += ' input-' + this.size;
12539 ['xs','sm','md','lg'].map(function(size){
12540 if (settings[size]) {
12541 cfg.cls += ' col-' + size + '-' + settings[size];
12545 var inputblock = input;
12549 cls: 'glyphicon form-control-feedback'
12552 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12555 cls : 'has-feedback',
12563 if (this.before || this.after) {
12566 cls : 'input-group',
12570 if (this.before && typeof(this.before) == 'string') {
12572 inputblock.cn.push({
12574 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12578 if (this.before && typeof(this.before) == 'object') {
12579 this.before = Roo.factory(this.before);
12581 inputblock.cn.push({
12583 cls : 'roo-input-before input-group-prepend input-group-' +
12584 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12588 inputblock.cn.push(input);
12590 if (this.after && typeof(this.after) == 'string') {
12591 inputblock.cn.push({
12593 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12597 if (this.after && typeof(this.after) == 'object') {
12598 this.after = Roo.factory(this.after);
12600 inputblock.cn.push({
12602 cls : 'roo-input-after input-group-append input-group-' +
12603 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12607 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12608 inputblock.cls += ' has-feedback';
12609 inputblock.cn.push(feedback);
12614 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12615 tooltip : 'This field is required'
12617 if (this.allowBlank ) {
12618 indicator.style = this.allowBlank ? ' display:none' : '';
12620 if (align ==='left' && this.fieldLabel.length) {
12622 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12629 cls : 'control-label col-form-label',
12630 html : this.fieldLabel
12641 var labelCfg = cfg.cn[1];
12642 var contentCfg = cfg.cn[2];
12644 if(this.indicatorpos == 'right'){
12649 cls : 'control-label col-form-label',
12653 html : this.fieldLabel
12667 labelCfg = cfg.cn[0];
12668 contentCfg = cfg.cn[1];
12672 if(this.labelWidth > 12){
12673 labelCfg.style = "width: " + this.labelWidth + 'px';
12676 if(this.labelWidth < 13 && this.labelmd == 0){
12677 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12680 if(this.labellg > 0){
12681 labelCfg.cls += ' col-lg-' + this.labellg;
12682 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12685 if(this.labelmd > 0){
12686 labelCfg.cls += ' col-md-' + this.labelmd;
12687 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12690 if(this.labelsm > 0){
12691 labelCfg.cls += ' col-sm-' + this.labelsm;
12692 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12695 if(this.labelxs > 0){
12696 labelCfg.cls += ' col-xs-' + this.labelxs;
12697 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12701 } else if ( this.fieldLabel.length) {
12708 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12709 tooltip : 'This field is required',
12710 style : this.allowBlank ? ' display:none' : ''
12714 //cls : 'input-group-addon',
12715 html : this.fieldLabel
12723 if(this.indicatorpos == 'right'){
12728 //cls : 'input-group-addon',
12729 html : this.fieldLabel
12734 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12735 tooltip : 'This field is required',
12736 style : this.allowBlank ? ' display:none' : ''
12756 if (this.parentType === 'Navbar' && this.parent().bar) {
12757 cfg.cls += ' navbar-form';
12760 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12761 // on BS4 we do this only if not form
12762 cfg.cls += ' navbar-form';
12770 * return the real input element.
12772 inputEl: function ()
12774 return this.el.select('input.form-control',true).first();
12777 tooltipEl : function()
12779 return this.inputEl();
12782 indicatorEl : function()
12784 if (Roo.bootstrap.version == 4) {
12785 return false; // not enabled in v4 yet.
12788 var indicator = this.el.select('i.roo-required-indicator',true).first();
12798 setDisabled : function(v)
12800 var i = this.inputEl().dom;
12802 i.removeAttribute('disabled');
12806 i.setAttribute('disabled','true');
12808 initEvents : function()
12811 this.inputEl().on("keydown" , this.fireKey, this);
12812 this.inputEl().on("focus", this.onFocus, this);
12813 this.inputEl().on("blur", this.onBlur, this);
12815 this.inputEl().relayEvent('keyup', this);
12816 this.inputEl().relayEvent('paste', this);
12818 this.indicator = this.indicatorEl();
12820 if(this.indicator){
12821 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12824 // reference to original value for reset
12825 this.originalValue = this.getValue();
12826 //Roo.form.TextField.superclass.initEvents.call(this);
12827 if(this.validationEvent == 'keyup'){
12828 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12829 this.inputEl().on('keyup', this.filterValidation, this);
12831 else if(this.validationEvent !== false){
12832 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12835 if(this.selectOnFocus){
12836 this.on("focus", this.preFocus, this);
12839 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12840 this.inputEl().on("keypress", this.filterKeys, this);
12842 this.inputEl().relayEvent('keypress', this);
12845 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12846 this.el.on("click", this.autoSize, this);
12849 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12850 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12853 if (typeof(this.before) == 'object') {
12854 this.before.render(this.el.select('.roo-input-before',true).first());
12856 if (typeof(this.after) == 'object') {
12857 this.after.render(this.el.select('.roo-input-after',true).first());
12860 this.inputEl().on('change', this.onChange, this);
12863 filterValidation : function(e){
12864 if(!e.isNavKeyPress()){
12865 this.validationTask.delay(this.validationDelay);
12869 * Validates the field value
12870 * @return {Boolean} True if the value is valid, else false
12872 validate : function(){
12873 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12874 if(this.disabled || this.validateValue(this.getRawValue())){
12879 this.markInvalid();
12885 * Validates a value according to the field's validation rules and marks the field as invalid
12886 * if the validation fails
12887 * @param {Mixed} value The value to validate
12888 * @return {Boolean} True if the value is valid, else false
12890 validateValue : function(value)
12892 if(this.getVisibilityEl().hasClass('hidden')){
12896 if(value.length < 1) { // if it's blank
12897 if(this.allowBlank){
12903 if(value.length < this.minLength){
12906 if(value.length > this.maxLength){
12910 var vt = Roo.form.VTypes;
12911 if(!vt[this.vtype](value, this)){
12915 if(typeof this.validator == "function"){
12916 var msg = this.validator(value);
12920 if (typeof(msg) == 'string') {
12921 this.invalidText = msg;
12925 if(this.regex && !this.regex.test(value)){
12933 fireKey : function(e){
12934 //Roo.log('field ' + e.getKey());
12935 if(e.isNavKeyPress()){
12936 this.fireEvent("specialkey", this, e);
12939 focus : function (selectText){
12941 this.inputEl().focus();
12942 if(selectText === true){
12943 this.inputEl().dom.select();
12949 onFocus : function(){
12950 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12951 // this.el.addClass(this.focusClass);
12953 if(!this.hasFocus){
12954 this.hasFocus = true;
12955 this.startValue = this.getValue();
12956 this.fireEvent("focus", this);
12960 beforeBlur : Roo.emptyFn,
12964 onBlur : function(){
12966 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12967 //this.el.removeClass(this.focusClass);
12969 this.hasFocus = false;
12970 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12973 var v = this.getValue();
12974 if(String(v) !== String(this.startValue)){
12975 this.fireEvent('change', this, v, this.startValue);
12977 this.fireEvent("blur", this);
12980 onChange : function(e)
12982 var v = this.getValue();
12983 if(String(v) !== String(this.startValue)){
12984 this.fireEvent('change', this, v, this.startValue);
12990 * Resets the current field value to the originally loaded value and clears any validation messages
12992 reset : function(){
12993 this.setValue(this.originalValue);
12997 * Returns the name of the field
12998 * @return {Mixed} name The name field
13000 getName: function(){
13004 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13005 * @return {Mixed} value The field value
13007 getValue : function(){
13009 var v = this.inputEl().getValue();
13014 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13015 * @return {Mixed} value The field value
13017 getRawValue : function(){
13018 var v = this.inputEl().getValue();
13024 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13025 * @param {Mixed} value The value to set
13027 setRawValue : function(v){
13028 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13031 selectText : function(start, end){
13032 var v = this.getRawValue();
13034 start = start === undefined ? 0 : start;
13035 end = end === undefined ? v.length : end;
13036 var d = this.inputEl().dom;
13037 if(d.setSelectionRange){
13038 d.setSelectionRange(start, end);
13039 }else if(d.createTextRange){
13040 var range = d.createTextRange();
13041 range.moveStart("character", start);
13042 range.moveEnd("character", v.length-end);
13049 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13050 * @param {Mixed} value The value to set
13052 setValue : function(v){
13055 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13061 processValue : function(value){
13062 if(this.stripCharsRe){
13063 var newValue = value.replace(this.stripCharsRe, '');
13064 if(newValue !== value){
13065 this.setRawValue(newValue);
13072 preFocus : function(){
13074 if(this.selectOnFocus){
13075 this.inputEl().dom.select();
13078 filterKeys : function(e){
13079 var k = e.getKey();
13080 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13083 var c = e.getCharCode(), cc = String.fromCharCode(c);
13084 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13087 if(!this.maskRe.test(cc)){
13092 * Clear any invalid styles/messages for this field
13094 clearInvalid : function(){
13096 if(!this.el || this.preventMark){ // not rendered
13101 this.el.removeClass([this.invalidClass, 'is-invalid']);
13103 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13105 var feedback = this.el.select('.form-control-feedback', true).first();
13108 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13113 if(this.indicator){
13114 this.indicator.removeClass('visible');
13115 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13118 this.fireEvent('valid', this);
13122 * Mark this field as valid
13124 markValid : function()
13126 if(!this.el || this.preventMark){ // not rendered...
13130 this.el.removeClass([this.invalidClass, this.validClass]);
13131 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13133 var feedback = this.el.select('.form-control-feedback', true).first();
13136 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13139 if(this.indicator){
13140 this.indicator.removeClass('visible');
13141 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13149 if(this.allowBlank && !this.getRawValue().length){
13152 if (Roo.bootstrap.version == 3) {
13153 this.el.addClass(this.validClass);
13155 this.inputEl().addClass('is-valid');
13158 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13160 var feedback = this.el.select('.form-control-feedback', true).first();
13163 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13164 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13169 this.fireEvent('valid', this);
13173 * Mark this field as invalid
13174 * @param {String} msg The validation message
13176 markInvalid : function(msg)
13178 if(!this.el || this.preventMark){ // not rendered
13182 this.el.removeClass([this.invalidClass, this.validClass]);
13183 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13185 var feedback = this.el.select('.form-control-feedback', true).first();
13188 this.el.select('.form-control-feedback', true).first().removeClass(
13189 [this.invalidFeedbackClass, this.validFeedbackClass]);
13196 if(this.allowBlank && !this.getRawValue().length){
13200 if(this.indicator){
13201 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13202 this.indicator.addClass('visible');
13204 if (Roo.bootstrap.version == 3) {
13205 this.el.addClass(this.invalidClass);
13207 this.inputEl().addClass('is-invalid');
13212 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13214 var feedback = this.el.select('.form-control-feedback', true).first();
13217 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13219 if(this.getValue().length || this.forceFeedback){
13220 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13227 this.fireEvent('invalid', this, msg);
13230 SafariOnKeyDown : function(event)
13232 // this is a workaround for a password hang bug on chrome/ webkit.
13233 if (this.inputEl().dom.type != 'password') {
13237 var isSelectAll = false;
13239 if(this.inputEl().dom.selectionEnd > 0){
13240 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13242 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13243 event.preventDefault();
13248 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13250 event.preventDefault();
13251 // this is very hacky as keydown always get's upper case.
13253 var cc = String.fromCharCode(event.getCharCode());
13254 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13258 adjustWidth : function(tag, w){
13259 tag = tag.toLowerCase();
13260 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13261 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13262 if(tag == 'input'){
13265 if(tag == 'textarea'){
13268 }else if(Roo.isOpera){
13269 if(tag == 'input'){
13272 if(tag == 'textarea'){
13280 setFieldLabel : function(v)
13282 if(!this.rendered){
13286 if(this.indicatorEl()){
13287 var ar = this.el.select('label > span',true);
13289 if (ar.elements.length) {
13290 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13291 this.fieldLabel = v;
13295 var br = this.el.select('label',true);
13297 if(br.elements.length) {
13298 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13299 this.fieldLabel = v;
13303 Roo.log('Cannot Found any of label > span || label in input');
13307 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13308 this.fieldLabel = v;
13323 * @class Roo.bootstrap.form.TextArea
13324 * @extends Roo.bootstrap.form.Input
13325 * Bootstrap TextArea class
13326 * @cfg {Number} cols Specifies the visible width of a text area
13327 * @cfg {Number} rows Specifies the visible number of lines in a text area
13328 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13329 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13330 * @cfg {string} html text
13333 * Create a new TextArea
13334 * @param {Object} config The config object
13337 Roo.bootstrap.form.TextArea = function(config){
13338 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13342 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13352 getAutoCreate : function(){
13354 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13360 if(this.inputType != 'hidden'){
13361 cfg.cls = 'form-group' //input-group
13369 value : this.value || '',
13370 html: this.html || '',
13371 cls : 'form-control',
13372 placeholder : this.placeholder || ''
13376 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13377 input.maxLength = this.maxLength;
13381 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13385 input.cols = this.cols;
13388 if (this.readOnly) {
13389 input.readonly = true;
13393 input.name = this.name;
13397 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13401 ['xs','sm','md','lg'].map(function(size){
13402 if (settings[size]) {
13403 cfg.cls += ' col-' + size + '-' + settings[size];
13407 var inputblock = input;
13409 if(this.hasFeedback && !this.allowBlank){
13413 cls: 'glyphicon form-control-feedback'
13417 cls : 'has-feedback',
13426 if (this.before || this.after) {
13429 cls : 'input-group',
13433 inputblock.cn.push({
13435 cls : 'input-group-addon',
13440 inputblock.cn.push(input);
13442 if(this.hasFeedback && !this.allowBlank){
13443 inputblock.cls += ' has-feedback';
13444 inputblock.cn.push(feedback);
13448 inputblock.cn.push({
13450 cls : 'input-group-addon',
13457 if (align ==='left' && this.fieldLabel.length) {
13462 cls : 'control-label',
13463 html : this.fieldLabel
13474 if(this.labelWidth > 12){
13475 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13478 if(this.labelWidth < 13 && this.labelmd == 0){
13479 this.labelmd = this.labelWidth;
13482 if(this.labellg > 0){
13483 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13484 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13487 if(this.labelmd > 0){
13488 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13489 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13492 if(this.labelsm > 0){
13493 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13494 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13497 if(this.labelxs > 0){
13498 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13499 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13502 } else if ( this.fieldLabel.length) {
13507 //cls : 'input-group-addon',
13508 html : this.fieldLabel
13526 if (this.disabled) {
13527 input.disabled=true;
13534 * return the real textarea element.
13536 inputEl: function ()
13538 return this.el.select('textarea.form-control',true).first();
13542 * Clear any invalid styles/messages for this field
13544 clearInvalid : function()
13547 if(!this.el || this.preventMark){ // not rendered
13551 var label = this.el.select('label', true).first();
13552 var icon = this.el.select('i.fa-star', true).first();
13557 this.el.removeClass( this.validClass);
13558 this.inputEl().removeClass('is-invalid');
13560 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13562 var feedback = this.el.select('.form-control-feedback', true).first();
13565 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13570 this.fireEvent('valid', this);
13574 * Mark this field as valid
13576 markValid : function()
13578 if(!this.el || this.preventMark){ // not rendered
13582 this.el.removeClass([this.invalidClass, this.validClass]);
13583 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13585 var feedback = this.el.select('.form-control-feedback', true).first();
13588 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13591 if(this.disabled || this.allowBlank){
13595 var label = this.el.select('label', true).first();
13596 var icon = this.el.select('i.fa-star', true).first();
13601 if (Roo.bootstrap.version == 3) {
13602 this.el.addClass(this.validClass);
13604 this.inputEl().addClass('is-valid');
13608 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13610 var feedback = this.el.select('.form-control-feedback', true).first();
13613 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13614 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13619 this.fireEvent('valid', this);
13623 * Mark this field as invalid
13624 * @param {String} msg The validation message
13626 markInvalid : function(msg)
13628 if(!this.el || this.preventMark){ // not rendered
13632 this.el.removeClass([this.invalidClass, this.validClass]);
13633 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13635 var feedback = this.el.select('.form-control-feedback', true).first();
13638 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13641 if(this.disabled || this.allowBlank){
13645 var label = this.el.select('label', true).first();
13646 var icon = this.el.select('i.fa-star', true).first();
13648 if(!this.getValue().length && label && !icon){
13649 this.el.createChild({
13651 cls : 'text-danger fa fa-lg fa-star',
13652 tooltip : 'This field is required',
13653 style : 'margin-right:5px;'
13657 if (Roo.bootstrap.version == 3) {
13658 this.el.addClass(this.invalidClass);
13660 this.inputEl().addClass('is-invalid');
13663 // fixme ... this may be depricated need to test..
13664 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13666 var feedback = this.el.select('.form-control-feedback', true).first();
13669 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13671 if(this.getValue().length || this.forceFeedback){
13672 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13679 this.fireEvent('invalid', this, msg);
13687 * trigger field - base class for combo..
13692 * @class Roo.bootstrap.form.TriggerField
13693 * @extends Roo.bootstrap.form.Input
13694 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13695 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13696 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13697 * for which you can provide a custom implementation. For example:
13699 var trigger = new Roo.bootstrap.form.TriggerField();
13700 trigger.onTriggerClick = myTriggerFn;
13701 trigger.applyTo('my-field');
13704 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13705 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13706 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13707 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13708 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13711 * Create a new TriggerField.
13712 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13713 * to the base TextField)
13715 Roo.bootstrap.form.TriggerField = function(config){
13716 this.mimicing = false;
13717 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13720 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13722 * @cfg {String} triggerClass A CSS class to apply to the trigger
13725 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13730 * @cfg {Boolean} removable (true|false) special filter default false
13734 /** @cfg {Boolean} grow @hide */
13735 /** @cfg {Number} growMin @hide */
13736 /** @cfg {Number} growMax @hide */
13742 autoSize: Roo.emptyFn,
13746 deferHeight : true,
13749 actionMode : 'wrap',
13754 getAutoCreate : function(){
13756 var align = this.labelAlign || this.parentLabelAlign();
13761 cls: 'form-group' //input-group
13768 type : this.inputType,
13769 cls : 'form-control',
13770 autocomplete: 'new-password',
13771 placeholder : this.placeholder || ''
13775 input.name = this.name;
13778 input.cls += ' input-' + this.size;
13781 if (this.disabled) {
13782 input.disabled=true;
13785 var inputblock = input;
13787 if(this.hasFeedback && !this.allowBlank){
13791 cls: 'glyphicon form-control-feedback'
13794 if(this.removable && !this.editable ){
13796 cls : 'has-feedback',
13802 cls : 'roo-combo-removable-btn close'
13809 cls : 'has-feedback',
13818 if(this.removable && !this.editable ){
13820 cls : 'roo-removable',
13826 cls : 'roo-combo-removable-btn close'
13833 if (this.before || this.after) {
13836 cls : 'input-group',
13840 inputblock.cn.push({
13842 cls : 'input-group-addon input-group-prepend input-group-text',
13847 inputblock.cn.push(input);
13849 if(this.hasFeedback && !this.allowBlank){
13850 inputblock.cls += ' has-feedback';
13851 inputblock.cn.push(feedback);
13855 inputblock.cn.push({
13857 cls : 'input-group-addon input-group-append input-group-text',
13866 var ibwrap = inputblock;
13871 cls: 'roo-select2-choices',
13875 cls: 'roo-select2-search-field',
13887 cls: 'roo-select2-container input-group',
13892 cls: 'form-hidden-field'
13898 if(!this.multiple && this.showToggleBtn){
13904 if (this.caret != false) {
13907 cls: 'fa fa-' + this.caret
13914 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13916 Roo.bootstrap.version == 3 ? caret : '',
13919 cls: 'combobox-clear',
13933 combobox.cls += ' roo-select2-container-multi';
13937 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13938 tooltip : 'This field is required'
13940 if (Roo.bootstrap.version == 4) {
13943 style : 'display:none'
13948 if (align ==='left' && this.fieldLabel.length) {
13950 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13957 cls : 'control-label',
13958 html : this.fieldLabel
13970 var labelCfg = cfg.cn[1];
13971 var contentCfg = cfg.cn[2];
13973 if(this.indicatorpos == 'right'){
13978 cls : 'control-label',
13982 html : this.fieldLabel
13996 labelCfg = cfg.cn[0];
13997 contentCfg = cfg.cn[1];
14000 if(this.labelWidth > 12){
14001 labelCfg.style = "width: " + this.labelWidth + 'px';
14004 if(this.labelWidth < 13 && this.labelmd == 0){
14005 this.labelmd = this.labelWidth;
14008 if(this.labellg > 0){
14009 labelCfg.cls += ' col-lg-' + this.labellg;
14010 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14013 if(this.labelmd > 0){
14014 labelCfg.cls += ' col-md-' + this.labelmd;
14015 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14018 if(this.labelsm > 0){
14019 labelCfg.cls += ' col-sm-' + this.labelsm;
14020 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14023 if(this.labelxs > 0){
14024 labelCfg.cls += ' col-xs-' + this.labelxs;
14025 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14028 } else if ( this.fieldLabel.length) {
14029 // Roo.log(" label");
14034 //cls : 'input-group-addon',
14035 html : this.fieldLabel
14043 if(this.indicatorpos == 'right'){
14051 html : this.fieldLabel
14065 // Roo.log(" no label && no align");
14072 ['xs','sm','md','lg'].map(function(size){
14073 if (settings[size]) {
14074 cfg.cls += ' col-' + size + '-' + settings[size];
14085 onResize : function(w, h){
14086 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14087 // if(typeof w == 'number'){
14088 // var x = w - this.trigger.getWidth();
14089 // this.inputEl().setWidth(this.adjustWidth('input', x));
14090 // this.trigger.setStyle('left', x+'px');
14095 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14098 getResizeEl : function(){
14099 return this.inputEl();
14103 getPositionEl : function(){
14104 return this.inputEl();
14108 alignErrorIcon : function(){
14109 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14113 initEvents : function(){
14117 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14118 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14119 if(!this.multiple && this.showToggleBtn){
14120 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14121 if(this.hideTrigger){
14122 this.trigger.setDisplayed(false);
14124 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14128 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14131 if(this.removable && !this.editable && !this.tickable){
14132 var close = this.closeTriggerEl();
14135 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14136 close.on('click', this.removeBtnClick, this, close);
14140 //this.trigger.addClassOnOver('x-form-trigger-over');
14141 //this.trigger.addClassOnClick('x-form-trigger-click');
14144 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14148 closeTriggerEl : function()
14150 var close = this.el.select('.roo-combo-removable-btn', true).first();
14151 return close ? close : false;
14154 removeBtnClick : function(e, h, el)
14156 e.preventDefault();
14158 if(this.fireEvent("remove", this) !== false){
14160 this.fireEvent("afterremove", this)
14164 createList : function()
14166 this.list = Roo.get(document.body).createChild({
14167 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14168 cls: 'typeahead typeahead-long dropdown-menu shadow',
14169 style: 'display:none'
14172 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14177 initTrigger : function(){
14182 onDestroy : function(){
14184 this.trigger.removeAllListeners();
14185 // this.trigger.remove();
14188 // this.wrap.remove();
14190 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14194 onFocus : function(){
14195 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14197 if(!this.mimicing){
14198 this.wrap.addClass('x-trigger-wrap-focus');
14199 this.mimicing = true;
14200 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14201 if(this.monitorTab){
14202 this.el.on("keydown", this.checkTab, this);
14209 checkTab : function(e){
14210 if(e.getKey() == e.TAB){
14211 this.triggerBlur();
14216 onBlur : function(){
14221 mimicBlur : function(e, t){
14223 if(!this.wrap.contains(t) && this.validateBlur()){
14224 this.triggerBlur();
14230 triggerBlur : function(){
14231 this.mimicing = false;
14232 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14233 if(this.monitorTab){
14234 this.el.un("keydown", this.checkTab, this);
14236 //this.wrap.removeClass('x-trigger-wrap-focus');
14237 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14241 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14242 validateBlur : function(e, t){
14247 onDisable : function(){
14248 this.inputEl().dom.disabled = true;
14249 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14251 // this.wrap.addClass('x-item-disabled');
14256 onEnable : function(){
14257 this.inputEl().dom.disabled = false;
14258 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14260 // this.el.removeClass('x-item-disabled');
14265 onShow : function(){
14266 var ae = this.getActionEl();
14269 ae.dom.style.display = '';
14270 ae.dom.style.visibility = 'visible';
14276 onHide : function(){
14277 var ae = this.getActionEl();
14278 ae.dom.style.display = 'none';
14282 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14283 * by an implementing function.
14285 * @param {EventObject} e
14287 onTriggerClick : Roo.emptyFn
14295 * @class Roo.bootstrap.form.CardUploader
14296 * @extends Roo.bootstrap.Button
14297 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14298 * @cfg {Number} errorTimeout default 3000
14299 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14300 * @cfg {Array} html The button text.
14304 * Create a new CardUploader
14305 * @param {Object} config The config object
14308 Roo.bootstrap.form.CardUploader = function(config){
14312 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14315 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14323 * When a image is clicked on - and needs to display a slideshow or similar..
14324 * @param {Roo.bootstrap.Card} this
14325 * @param {Object} The image information data
14331 * When a the download link is clicked
14332 * @param {Roo.bootstrap.Card} this
14333 * @param {Object} The image information data contains
14340 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14343 errorTimeout : 3000,
14347 fileCollection : false,
14350 getAutoCreate : function()
14354 cls :'form-group' ,
14359 //cls : 'input-group-addon',
14360 html : this.fieldLabel
14368 value : this.value,
14369 cls : 'd-none form-control'
14374 multiple : 'multiple',
14376 cls : 'd-none roo-card-upload-selector'
14380 cls : 'roo-card-uploader-button-container w-100 mb-2'
14383 cls : 'card-columns roo-card-uploader-container'
14393 getChildContainer : function() /// what children are added to.
14395 return this.containerEl;
14398 getButtonContainer : function() /// what children are added to.
14400 return this.el.select(".roo-card-uploader-button-container").first();
14403 initEvents : function()
14406 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14410 xns: Roo.bootstrap,
14413 container_method : 'getButtonContainer' ,
14414 html : this.html, // fix changable?
14417 'click' : function(btn, e) {
14426 this.urlAPI = (window.createObjectURL && window) ||
14427 (window.URL && URL.revokeObjectURL && URL) ||
14428 (window.webkitURL && webkitURL);
14433 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14435 this.selectorEl.on('change', this.onFileSelected, this);
14438 this.images.forEach(function(img) {
14441 this.images = false;
14443 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14449 onClick : function(e)
14451 e.preventDefault();
14453 this.selectorEl.dom.click();
14457 onFileSelected : function(e)
14459 e.preventDefault();
14461 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14465 Roo.each(this.selectorEl.dom.files, function(file){
14466 this.addFile(file);
14475 addFile : function(file)
14478 if(typeof(file) === 'string'){
14479 throw "Add file by name?"; // should not happen
14483 if(!file || !this.urlAPI){
14493 var url = _this.urlAPI.createObjectURL( file);
14496 id : Roo.bootstrap.form.CardUploader.ID--,
14497 is_uploaded : false,
14501 mimetype : file.type,
14509 * addCard - add an Attachment to the uploader
14510 * @param data - the data about the image to upload
14514 title : "Title of file",
14515 is_uploaded : false,
14516 src : "http://.....",
14517 srcfile : { the File upload object },
14518 mimetype : file.type,
14521 .. any other data...
14527 addCard : function (data)
14529 // hidden input element?
14530 // if the file is not an image...
14531 //then we need to use something other that and header_image
14536 xns : Roo.bootstrap,
14537 xtype : 'CardFooter',
14540 xns : Roo.bootstrap,
14546 xns : Roo.bootstrap,
14548 html : String.format("<small>{0}</small>", data.title),
14549 cls : 'col-10 text-left',
14554 click : function() {
14556 t.fireEvent( "download", t, data );
14562 xns : Roo.bootstrap,
14564 style: 'max-height: 28px; ',
14570 click : function() {
14571 t.removeCard(data.id)
14583 var cn = this.addxtype(
14586 xns : Roo.bootstrap,
14589 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14590 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14591 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14596 initEvents : function() {
14597 Roo.bootstrap.Card.prototype.initEvents.call(this);
14599 this.imgEl = this.el.select('.card-img-top').first();
14601 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14602 this.imgEl.set({ 'pointer' : 'cursor' });
14605 this.getCardFooter().addClass('p-1');
14612 // dont' really need ot update items.
14613 // this.items.push(cn);
14614 this.fileCollection.add(cn);
14616 if (!data.srcfile) {
14617 this.updateInput();
14622 var reader = new FileReader();
14623 reader.addEventListener("load", function() {
14624 data.srcdata = reader.result;
14627 reader.readAsDataURL(data.srcfile);
14632 removeCard : function(id)
14635 var card = this.fileCollection.get(id);
14636 card.data.is_deleted = 1;
14637 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14638 //this.fileCollection.remove(card);
14639 //this.items = this.items.filter(function(e) { return e != card });
14640 // dont' really need ot update items.
14641 card.el.dom.parentNode.removeChild(card.el.dom);
14642 this.updateInput();
14648 this.fileCollection.each(function(card) {
14649 if (card.el.dom && card.el.dom.parentNode) {
14650 card.el.dom.parentNode.removeChild(card.el.dom);
14653 this.fileCollection.clear();
14654 this.updateInput();
14657 updateInput : function()
14660 this.fileCollection.each(function(e) {
14664 this.inputEl().dom.value = JSON.stringify(data);
14674 Roo.bootstrap.form.CardUploader.ID = -1;/*
14676 * Ext JS Library 1.1.1
14677 * Copyright(c) 2006-2007, Ext JS, LLC.
14679 * Originally Released Under LGPL - original licence link has changed is not relivant.
14682 * <script type="text/javascript">
14687 * @class Roo.data.SortTypes
14689 * Defines the default sorting (casting?) comparison functions used when sorting data.
14691 Roo.data.SortTypes = {
14693 * Default sort that does nothing
14694 * @param {Mixed} s The value being converted
14695 * @return {Mixed} The comparison value
14697 none : function(s){
14702 * The regular expression used to strip tags
14706 stripTagsRE : /<\/?[^>]+>/gi,
14709 * Strips all HTML tags to sort on text only
14710 * @param {Mixed} s The value being converted
14711 * @return {String} The comparison value
14713 asText : function(s){
14714 return String(s).replace(this.stripTagsRE, "");
14718 * Strips all HTML tags to sort on text only - Case insensitive
14719 * @param {Mixed} s The value being converted
14720 * @return {String} The comparison value
14722 asUCText : function(s){
14723 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14727 * Case insensitive string
14728 * @param {Mixed} s The value being converted
14729 * @return {String} The comparison value
14731 asUCString : function(s) {
14732 return String(s).toUpperCase();
14737 * @param {Mixed} s The value being converted
14738 * @return {Number} The comparison value
14740 asDate : function(s) {
14744 if(s instanceof Date){
14745 return s.getTime();
14747 return Date.parse(String(s));
14752 * @param {Mixed} s The value being converted
14753 * @return {Float} The comparison value
14755 asFloat : function(s) {
14756 var val = parseFloat(String(s).replace(/,/g, ""));
14765 * @param {Mixed} s The value being converted
14766 * @return {Number} The comparison value
14768 asInt : function(s) {
14769 var val = parseInt(String(s).replace(/,/g, ""));
14777 * Ext JS Library 1.1.1
14778 * Copyright(c) 2006-2007, Ext JS, LLC.
14780 * Originally Released Under LGPL - original licence link has changed is not relivant.
14783 * <script type="text/javascript">
14787 * @class Roo.data.Record
14788 * Instances of this class encapsulate both record <em>definition</em> information, and record
14789 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14790 * to access Records cached in an {@link Roo.data.Store} object.<br>
14792 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14793 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14796 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14798 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14799 * {@link #create}. The parameters are the same.
14800 * @param {Array} data An associative Array of data values keyed by the field name.
14801 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14802 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14803 * not specified an integer id is generated.
14805 Roo.data.Record = function(data, id){
14806 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14811 * Generate a constructor for a specific record layout.
14812 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14813 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14814 * Each field definition object may contain the following properties: <ul>
14815 * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14816 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14817 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14818 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14819 * is being used, then this is a string containing the javascript expression to reference the data relative to
14820 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14821 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14822 * this may be omitted.</p></li>
14823 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14824 * <ul><li>auto (Default, implies no conversion)</li>
14829 * <li>date</li></ul></p></li>
14830 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14831 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14832 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14833 * by the Reader into an object that will be stored in the Record. It is passed the
14834 * following parameters:<ul>
14835 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14837 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14839 * <br>usage:<br><pre><code>
14840 var TopicRecord = Roo.data.Record.create(
14841 {name: 'title', mapping: 'topic_title'},
14842 {name: 'author', mapping: 'username'},
14843 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14844 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14845 {name: 'lastPoster', mapping: 'user2'},
14846 {name: 'excerpt', mapping: 'post_text'}
14849 var myNewRecord = new TopicRecord({
14850 title: 'Do my job please',
14853 lastPost: new Date(),
14854 lastPoster: 'Animal',
14855 excerpt: 'No way dude!'
14857 myStore.add(myNewRecord);
14862 Roo.data.Record.create = function(o){
14863 var f = function(){
14864 f.superclass.constructor.apply(this, arguments);
14866 Roo.extend(f, Roo.data.Record);
14867 var p = f.prototype;
14868 p.fields = new Roo.util.MixedCollection(false, function(field){
14871 for(var i = 0, len = o.length; i < len; i++){
14872 p.fields.add(new Roo.data.Field(o[i]));
14874 f.getField = function(name){
14875 return p.fields.get(name);
14880 Roo.data.Record.AUTO_ID = 1000;
14881 Roo.data.Record.EDIT = 'edit';
14882 Roo.data.Record.REJECT = 'reject';
14883 Roo.data.Record.COMMIT = 'commit';
14885 Roo.data.Record.prototype = {
14887 * Readonly flag - true if this record has been modified.
14896 join : function(store){
14897 this.store = store;
14901 * Set the named field to the specified value.
14902 * @param {String} name The name of the field to set.
14903 * @param {Object} value The value to set the field to.
14905 set : function(name, value){
14906 if(this.data[name] == value){
14910 if(!this.modified){
14911 this.modified = {};
14913 if(typeof this.modified[name] == 'undefined'){
14914 this.modified[name] = this.data[name];
14916 this.data[name] = value;
14917 if(!this.editing && this.store){
14918 this.store.afterEdit(this);
14923 * Get the value of the named field.
14924 * @param {String} name The name of the field to get the value of.
14925 * @return {Object} The value of the field.
14927 get : function(name){
14928 return this.data[name];
14932 beginEdit : function(){
14933 this.editing = true;
14934 this.modified = {};
14938 cancelEdit : function(){
14939 this.editing = false;
14940 delete this.modified;
14944 endEdit : function(){
14945 this.editing = false;
14946 if(this.dirty && this.store){
14947 this.store.afterEdit(this);
14952 * Usually called by the {@link Roo.data.Store} which owns the Record.
14953 * Rejects all changes made to the Record since either creation, or the last commit operation.
14954 * Modified fields are reverted to their original values.
14956 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14957 * of reject operations.
14959 reject : function(){
14960 var m = this.modified;
14962 if(typeof m[n] != "function"){
14963 this.data[n] = m[n];
14966 this.dirty = false;
14967 delete this.modified;
14968 this.editing = false;
14970 this.store.afterReject(this);
14975 * Usually called by the {@link Roo.data.Store} which owns the Record.
14976 * Commits all changes made to the Record since either creation, or the last commit operation.
14978 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14979 * of commit operations.
14981 commit : function(){
14982 this.dirty = false;
14983 delete this.modified;
14984 this.editing = false;
14986 this.store.afterCommit(this);
14991 hasError : function(){
14992 return this.error != null;
14996 clearError : function(){
15001 * Creates a copy of this record.
15002 * @param {String} id (optional) A new record id if you don't want to use this record's id
15005 copy : function(newId) {
15006 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15010 * Ext JS Library 1.1.1
15011 * Copyright(c) 2006-2007, Ext JS, LLC.
15013 * Originally Released Under LGPL - original licence link has changed is not relivant.
15016 * <script type="text/javascript">
15022 * @class Roo.data.Store
15023 * @extends Roo.util.Observable
15024 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15025 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15027 * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
15028 * has no knowledge of the format of the data returned by the Proxy.<br>
15030 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15031 * instances from the data object. These records are cached and made available through accessor functions.
15033 * Creates a new Store.
15034 * @param {Object} config A config object containing the objects needed for the Store to access data,
15035 * and read the data into Records.
15037 Roo.data.Store = function(config){
15038 this.data = new Roo.util.MixedCollection(false);
15039 this.data.getKey = function(o){
15042 this.baseParams = {};
15044 this.paramNames = {
15049 "multisort" : "_multisort"
15052 if(config && config.data){
15053 this.inlineData = config.data;
15054 delete config.data;
15057 Roo.apply(this, config);
15059 if(this.reader){ // reader passed
15060 this.reader = Roo.factory(this.reader, Roo.data);
15061 this.reader.xmodule = this.xmodule || false;
15062 if(!this.recordType){
15063 this.recordType = this.reader.recordType;
15065 if(this.reader.onMetaChange){
15066 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15070 if(this.recordType){
15071 this.fields = this.recordType.prototype.fields;
15073 this.modified = [];
15077 * @event datachanged
15078 * Fires when the data cache has changed, and a widget which is using this Store
15079 * as a Record cache should refresh its view.
15080 * @param {Store} this
15082 datachanged : true,
15084 * @event metachange
15085 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15086 * @param {Store} this
15087 * @param {Object} meta The JSON metadata
15092 * Fires when Records have been added to the Store
15093 * @param {Store} this
15094 * @param {Roo.data.Record[]} records The array of Records added
15095 * @param {Number} index The index at which the record(s) were added
15100 * Fires when a Record has been removed from the Store
15101 * @param {Store} this
15102 * @param {Roo.data.Record} record The Record that was removed
15103 * @param {Number} index The index at which the record was removed
15108 * Fires when a Record has been updated
15109 * @param {Store} this
15110 * @param {Roo.data.Record} record The Record that was updated
15111 * @param {String} operation The update operation being performed. Value may be one of:
15113 Roo.data.Record.EDIT
15114 Roo.data.Record.REJECT
15115 Roo.data.Record.COMMIT
15121 * Fires when the data cache has been cleared.
15122 * @param {Store} this
15126 * @event beforeload
15127 * Fires before a request is made for a new data object. If the beforeload handler returns false
15128 * the load action will be canceled.
15129 * @param {Store} this
15130 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15134 * @event beforeloadadd
15135 * Fires after a new set of Records has been loaded.
15136 * @param {Store} this
15137 * @param {Roo.data.Record[]} records The Records that were loaded
15138 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15140 beforeloadadd : true,
15143 * Fires after a new set of Records has been loaded, before they are added to the store.
15144 * @param {Store} this
15145 * @param {Roo.data.Record[]} records The Records that were loaded
15146 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15147 * @params {Object} return from reader
15151 * @event loadexception
15152 * Fires if an exception occurs in the Proxy during loading.
15153 * Called with the signature of the Proxy's "loadexception" event.
15154 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15157 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15158 * @param {Object} load options
15159 * @param {Object} jsonData from your request (normally this contains the Exception)
15161 loadexception : true
15165 this.proxy = Roo.factory(this.proxy, Roo.data);
15166 this.proxy.xmodule = this.xmodule || false;
15167 this.relayEvents(this.proxy, ["loadexception"]);
15169 this.sortToggle = {};
15170 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15172 Roo.data.Store.superclass.constructor.call(this);
15174 if(this.inlineData){
15175 this.loadData(this.inlineData);
15176 delete this.inlineData;
15180 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15182 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15183 * without a remote query - used by combo/forms at present.
15187 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15190 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15193 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15194 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15197 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15198 * on any HTTP request
15201 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15204 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15208 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15209 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15211 remoteSort : false,
15214 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15215 * loaded or when a record is removed. (defaults to false).
15217 pruneModifiedRecords : false,
15220 lastOptions : null,
15223 * Add Records to the Store and fires the add event.
15224 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15226 add : function(records){
15227 records = [].concat(records);
15228 for(var i = 0, len = records.length; i < len; i++){
15229 records[i].join(this);
15231 var index = this.data.length;
15232 this.data.addAll(records);
15233 this.fireEvent("add", this, records, index);
15237 * Remove a Record from the Store and fires the remove event.
15238 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15240 remove : function(record){
15241 var index = this.data.indexOf(record);
15242 this.data.removeAt(index);
15244 if(this.pruneModifiedRecords){
15245 this.modified.remove(record);
15247 this.fireEvent("remove", this, record, index);
15251 * Remove all Records from the Store and fires the clear event.
15253 removeAll : function(){
15255 if(this.pruneModifiedRecords){
15256 this.modified = [];
15258 this.fireEvent("clear", this);
15262 * Inserts Records to the Store at the given index and fires the add event.
15263 * @param {Number} index The start index at which to insert the passed Records.
15264 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15266 insert : function(index, records){
15267 records = [].concat(records);
15268 for(var i = 0, len = records.length; i < len; i++){
15269 this.data.insert(index, records[i]);
15270 records[i].join(this);
15272 this.fireEvent("add", this, records, index);
15276 * Get the index within the cache of the passed Record.
15277 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15278 * @return {Number} The index of the passed Record. Returns -1 if not found.
15280 indexOf : function(record){
15281 return this.data.indexOf(record);
15285 * Get the index within the cache of the Record with the passed id.
15286 * @param {String} id The id of the Record to find.
15287 * @return {Number} The index of the Record. Returns -1 if not found.
15289 indexOfId : function(id){
15290 return this.data.indexOfKey(id);
15294 * Get the Record with the specified id.
15295 * @param {String} id The id of the Record to find.
15296 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15298 getById : function(id){
15299 return this.data.key(id);
15303 * Get the Record at the specified index.
15304 * @param {Number} index The index of the Record to find.
15305 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15307 getAt : function(index){
15308 return this.data.itemAt(index);
15312 * Returns a range of Records between specified indices.
15313 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15314 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15315 * @return {Roo.data.Record[]} An array of Records
15317 getRange : function(start, end){
15318 return this.data.getRange(start, end);
15322 storeOptions : function(o){
15323 o = Roo.apply({}, o);
15326 this.lastOptions = o;
15330 * Loads the Record cache from the configured Proxy using the configured Reader.
15332 * If using remote paging, then the first load call must specify the <em>start</em>
15333 * and <em>limit</em> properties in the options.params property to establish the initial
15334 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15336 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15337 * and this call will return before the new data has been loaded. Perform any post-processing
15338 * in a callback function, or in a "load" event handler.</strong>
15340 * @param {Object} options An object containing properties which control loading options:<ul>
15341 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15342 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15343 * passed the following arguments:<ul>
15344 * <li>r : Roo.data.Record[]</li>
15345 * <li>options: Options object from the load call</li>
15346 * <li>success: Boolean success indicator</li></ul></li>
15347 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15348 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15351 load : function(options){
15352 options = options || {};
15353 if(this.fireEvent("beforeload", this, options) !== false){
15354 this.storeOptions(options);
15355 var p = Roo.apply(options.params || {}, this.baseParams);
15356 // if meta was not loaded from remote source.. try requesting it.
15357 if (!this.reader.metaFromRemote) {
15358 p._requestMeta = 1;
15360 if(this.sortInfo && this.remoteSort){
15361 var pn = this.paramNames;
15362 p[pn["sort"]] = this.sortInfo.field;
15363 p[pn["dir"]] = this.sortInfo.direction;
15365 if (this.multiSort) {
15366 var pn = this.paramNames;
15367 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15370 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15375 * Reloads the Record cache from the configured Proxy using the configured Reader and
15376 * the options from the last load operation performed.
15377 * @param {Object} options (optional) An object containing properties which may override the options
15378 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15379 * the most recently used options are reused).
15381 reload : function(options){
15382 this.load(Roo.applyIf(options||{}, this.lastOptions));
15386 // Called as a callback by the Reader during a load operation.
15387 loadRecords : function(o, options, success){
15388 if(!o || success === false){
15389 if(success !== false){
15390 this.fireEvent("load", this, [], options, o);
15392 if(options.callback){
15393 options.callback.call(options.scope || this, [], options, false);
15397 // if data returned failure - throw an exception.
15398 if (o.success === false) {
15399 // show a message if no listener is registered.
15400 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15401 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15403 // loadmask wil be hooked into this..
15404 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15407 var r = o.records, t = o.totalRecords || r.length;
15409 this.fireEvent("beforeloadadd", this, r, options, o);
15411 if(!options || options.add !== true){
15412 if(this.pruneModifiedRecords){
15413 this.modified = [];
15415 for(var i = 0, len = r.length; i < len; i++){
15419 this.data = this.snapshot;
15420 delete this.snapshot;
15423 this.data.addAll(r);
15424 this.totalLength = t;
15426 this.fireEvent("datachanged", this);
15428 this.totalLength = Math.max(t, this.data.length+r.length);
15432 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15434 var e = new Roo.data.Record({});
15436 e.set(this.parent.displayField, this.parent.emptyTitle);
15437 e.set(this.parent.valueField, '');
15442 this.fireEvent("load", this, r, options, o);
15443 if(options.callback){
15444 options.callback.call(options.scope || this, r, options, true);
15450 * Loads data from a passed data block. A Reader which understands the format of the data
15451 * must have been configured in the constructor.
15452 * @param {Object} data The data block from which to read the Records. The format of the data expected
15453 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15454 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15456 loadData : function(o, append){
15457 var r = this.reader.readRecords(o);
15458 this.loadRecords(r, {add: append}, true);
15462 * using 'cn' the nested child reader read the child array into it's child stores.
15463 * @param {Object} rec The record with a 'children array
15465 loadDataFromChildren : function(rec)
15467 this.loadData(this.reader.toLoadData(rec));
15472 * Gets the number of cached records.
15474 * <em>If using paging, this may not be the total size of the dataset. If the data object
15475 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15476 * the data set size</em>
15478 getCount : function(){
15479 return this.data.length || 0;
15483 * Gets the total number of records in the dataset as returned by the server.
15485 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15486 * the dataset size</em>
15488 getTotalCount : function(){
15489 return this.totalLength || 0;
15493 * Returns the sort state of the Store as an object with two properties:
15495 field {String} The name of the field by which the Records are sorted
15496 direction {String} The sort order, "ASC" or "DESC"
15499 getSortState : function(){
15500 return this.sortInfo;
15504 applySort : function(){
15505 if(this.sortInfo && !this.remoteSort){
15506 var s = this.sortInfo, f = s.field;
15507 var st = this.fields.get(f).sortType;
15508 var fn = function(r1, r2){
15509 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15510 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15512 this.data.sort(s.direction, fn);
15513 if(this.snapshot && this.snapshot != this.data){
15514 this.snapshot.sort(s.direction, fn);
15520 * Sets the default sort column and order to be used by the next load operation.
15521 * @param {String} fieldName The name of the field to sort by.
15522 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15524 setDefaultSort : function(field, dir){
15525 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15529 * Sort the Records.
15530 * If remote sorting is used, the sort is performed on the server, and the cache is
15531 * reloaded. If local sorting is used, the cache is sorted internally.
15532 * @param {String} fieldName The name of the field to sort by.
15533 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15535 sort : function(fieldName, dir){
15536 var f = this.fields.get(fieldName);
15538 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15540 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15541 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15546 this.sortToggle[f.name] = dir;
15547 this.sortInfo = {field: f.name, direction: dir};
15548 if(!this.remoteSort){
15550 this.fireEvent("datachanged", this);
15552 this.load(this.lastOptions);
15557 * Calls the specified function for each of the Records in the cache.
15558 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15559 * Returning <em>false</em> aborts and exits the iteration.
15560 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15562 each : function(fn, scope){
15563 this.data.each(fn, scope);
15567 * Gets all records modified since the last commit. Modified records are persisted across load operations
15568 * (e.g., during paging).
15569 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15571 getModifiedRecords : function(){
15572 return this.modified;
15576 createFilterFn : function(property, value, anyMatch){
15577 if(!value.exec){ // not a regex
15578 value = String(value);
15579 if(value.length == 0){
15582 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15584 return function(r){
15585 return value.test(r.data[property]);
15590 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15591 * @param {String} property A field on your records
15592 * @param {Number} start The record index to start at (defaults to 0)
15593 * @param {Number} end The last record index to include (defaults to length - 1)
15594 * @return {Number} The sum
15596 sum : function(property, start, end){
15597 var rs = this.data.items, v = 0;
15598 start = start || 0;
15599 end = (end || end === 0) ? end : rs.length-1;
15601 for(var i = start; i <= end; i++){
15602 v += (rs[i].data[property] || 0);
15608 * Filter the records by a specified property.
15609 * @param {String} field A field on your records
15610 * @param {String/RegExp} value Either a string that the field
15611 * should start with or a RegExp to test against the field
15612 * @param {Boolean} anyMatch True to match any part not just the beginning
15614 filter : function(property, value, anyMatch){
15615 var fn = this.createFilterFn(property, value, anyMatch);
15616 return fn ? this.filterBy(fn) : this.clearFilter();
15620 * Filter by a function. The specified function will be called with each
15621 * record in this data source. If the function returns true the record is included,
15622 * otherwise it is filtered.
15623 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15624 * @param {Object} scope (optional) The scope of the function (defaults to this)
15626 filterBy : function(fn, scope){
15627 this.snapshot = this.snapshot || this.data;
15628 this.data = this.queryBy(fn, scope||this);
15629 this.fireEvent("datachanged", this);
15633 * Query the records by a specified property.
15634 * @param {String} field A field on your records
15635 * @param {String/RegExp} value Either a string that the field
15636 * should start with or a RegExp to test against the field
15637 * @param {Boolean} anyMatch True to match any part not just the beginning
15638 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15640 query : function(property, value, anyMatch){
15641 var fn = this.createFilterFn(property, value, anyMatch);
15642 return fn ? this.queryBy(fn) : this.data.clone();
15646 * Query by a function. The specified function will be called with each
15647 * record in this data source. If the function returns true the record is included
15649 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15650 * @param {Object} scope (optional) The scope of the function (defaults to this)
15651 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15653 queryBy : function(fn, scope){
15654 var data = this.snapshot || this.data;
15655 return data.filterBy(fn, scope||this);
15659 * Collects unique values for a particular dataIndex from this store.
15660 * @param {String} dataIndex The property to collect
15661 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15662 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15663 * @return {Array} An array of the unique values
15665 collect : function(dataIndex, allowNull, bypassFilter){
15666 var d = (bypassFilter === true && this.snapshot) ?
15667 this.snapshot.items : this.data.items;
15668 var v, sv, r = [], l = {};
15669 for(var i = 0, len = d.length; i < len; i++){
15670 v = d[i].data[dataIndex];
15672 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15681 * Revert to a view of the Record cache with no filtering applied.
15682 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15684 clearFilter : function(suppressEvent){
15685 if(this.snapshot && this.snapshot != this.data){
15686 this.data = this.snapshot;
15687 delete this.snapshot;
15688 if(suppressEvent !== true){
15689 this.fireEvent("datachanged", this);
15695 afterEdit : function(record){
15696 if(this.modified.indexOf(record) == -1){
15697 this.modified.push(record);
15699 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15703 afterReject : function(record){
15704 this.modified.remove(record);
15705 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15709 afterCommit : function(record){
15710 this.modified.remove(record);
15711 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15715 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15716 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15718 commitChanges : function(){
15719 var m = this.modified.slice(0);
15720 this.modified = [];
15721 for(var i = 0, len = m.length; i < len; i++){
15727 * Cancel outstanding changes on all changed records.
15729 rejectChanges : function(){
15730 var m = this.modified.slice(0);
15731 this.modified = [];
15732 for(var i = 0, len = m.length; i < len; i++){
15737 onMetaChange : function(meta, rtype, o){
15738 this.recordType = rtype;
15739 this.fields = rtype.prototype.fields;
15740 delete this.snapshot;
15741 this.sortInfo = meta.sortInfo || this.sortInfo;
15742 this.modified = [];
15743 this.fireEvent('metachange', this, this.reader.meta);
15746 moveIndex : function(data, type)
15748 var index = this.indexOf(data);
15750 var newIndex = index + type;
15754 this.insert(newIndex, data);
15759 * Ext JS Library 1.1.1
15760 * Copyright(c) 2006-2007, Ext JS, LLC.
15762 * Originally Released Under LGPL - original licence link has changed is not relivant.
15765 * <script type="text/javascript">
15769 * @class Roo.data.SimpleStore
15770 * @extends Roo.data.Store
15771 * Small helper class to make creating Stores from Array data easier.
15772 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15773 * @cfg {Array} fields An array of field definition objects, or field name strings.
15774 * @cfg {Object} an existing reader (eg. copied from another store)
15775 * @cfg {Array} data The multi-dimensional array of data
15776 * @cfg {Roo.data.DataProxy} proxy [not-required]
15777 * @cfg {Roo.data.Reader} reader [not-required]
15779 * @param {Object} config
15781 Roo.data.SimpleStore = function(config)
15783 Roo.data.SimpleStore.superclass.constructor.call(this, {
15785 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15788 Roo.data.Record.create(config.fields)
15790 proxy : new Roo.data.MemoryProxy(config.data)
15794 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15796 * Ext JS Library 1.1.1
15797 * Copyright(c) 2006-2007, Ext JS, LLC.
15799 * Originally Released Under LGPL - original licence link has changed is not relivant.
15802 * <script type="text/javascript">
15807 * @extends Roo.data.Store
15808 * @class Roo.data.JsonStore
15809 * Small helper class to make creating Stores for JSON data easier. <br/>
15811 var store = new Roo.data.JsonStore({
15812 url: 'get-images.php',
15814 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15817 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15818 * JsonReader and HttpProxy (unless inline data is provided).</b>
15819 * @cfg {Array} fields An array of field definition objects, or field name strings.
15821 * @param {Object} config
15823 Roo.data.JsonStore = function(c){
15824 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15825 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15826 reader: new Roo.data.JsonReader(c, c.fields)
15829 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15831 * Ext JS Library 1.1.1
15832 * Copyright(c) 2006-2007, Ext JS, LLC.
15834 * Originally Released Under LGPL - original licence link has changed is not relivant.
15837 * <script type="text/javascript">
15841 Roo.data.Field = function(config){
15842 if(typeof config == "string"){
15843 config = {name: config};
15845 Roo.apply(this, config);
15848 this.type = "auto";
15851 var st = Roo.data.SortTypes;
15852 // named sortTypes are supported, here we look them up
15853 if(typeof this.sortType == "string"){
15854 this.sortType = st[this.sortType];
15857 // set default sortType for strings and dates
15858 if(!this.sortType){
15861 this.sortType = st.asUCString;
15864 this.sortType = st.asDate;
15867 this.sortType = st.none;
15872 var stripRe = /[\$,%]/g;
15874 // prebuilt conversion function for this field, instead of
15875 // switching every time we're reading a value
15877 var cv, dateFormat = this.dateFormat;
15882 cv = function(v){ return v; };
15885 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15889 return v !== undefined && v !== null && v !== '' ?
15890 parseInt(String(v).replace(stripRe, ""), 10) : '';
15895 return v !== undefined && v !== null && v !== '' ?
15896 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15901 cv = function(v){ return v === true || v === "true" || v == 1; };
15908 if(v instanceof Date){
15912 if(dateFormat == "timestamp"){
15913 return new Date(v*1000);
15915 return Date.parseDate(v, dateFormat);
15917 var parsed = Date.parse(v);
15918 return parsed ? new Date(parsed) : null;
15927 Roo.data.Field.prototype = {
15935 * Ext JS Library 1.1.1
15936 * Copyright(c) 2006-2007, Ext JS, LLC.
15938 * Originally Released Under LGPL - original licence link has changed is not relivant.
15941 * <script type="text/javascript">
15944 // Base class for reading structured data from a data source. This class is intended to be
15945 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15948 * @class Roo.data.DataReader
15950 * Base class for reading structured data from a data source. This class is intended to be
15951 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15954 Roo.data.DataReader = function(meta, recordType){
15958 this.recordType = recordType instanceof Array ?
15959 Roo.data.Record.create(recordType) : recordType;
15962 Roo.data.DataReader.prototype = {
15965 readerType : 'Data',
15967 * Create an empty record
15968 * @param {Object} data (optional) - overlay some values
15969 * @return {Roo.data.Record} record created.
15971 newRow : function(d) {
15973 this.recordType.prototype.fields.each(function(c) {
15975 case 'int' : da[c.name] = 0; break;
15976 case 'date' : da[c.name] = new Date(); break;
15977 case 'float' : da[c.name] = 0.0; break;
15978 case 'boolean' : da[c.name] = false; break;
15979 default : da[c.name] = ""; break;
15983 return new this.recordType(Roo.apply(da, d));
15989 * Ext JS Library 1.1.1
15990 * Copyright(c) 2006-2007, Ext JS, LLC.
15992 * Originally Released Under LGPL - original licence link has changed is not relivant.
15995 * <script type="text/javascript">
15999 * @class Roo.data.DataProxy
16000 * @extends Roo.util.Observable
16002 * This class is an abstract base class for implementations which provide retrieval of
16003 * unformatted data objects.<br>
16005 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16006 * (of the appropriate type which knows how to parse the data object) to provide a block of
16007 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16009 * Custom implementations must implement the load method as described in
16010 * {@link Roo.data.HttpProxy#load}.
16012 Roo.data.DataProxy = function(){
16015 * @event beforeload
16016 * Fires before a network request is made to retrieve a data object.
16017 * @param {Object} This DataProxy object.
16018 * @param {Object} params The params parameter to the load function.
16023 * Fires before the load method's callback is called.
16024 * @param {Object} This DataProxy object.
16025 * @param {Object} o The data object.
16026 * @param {Object} arg The callback argument object passed to the load function.
16030 * @event loadexception
16031 * Fires if an Exception occurs during data retrieval.
16032 * @param {Object} This DataProxy object.
16033 * @param {Object} o The data object.
16034 * @param {Object} arg The callback argument object passed to the load function.
16035 * @param {Object} e The Exception.
16037 loadexception : true
16039 Roo.data.DataProxy.superclass.constructor.call(this);
16042 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16045 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16049 * Ext JS Library 1.1.1
16050 * Copyright(c) 2006-2007, Ext JS, LLC.
16052 * Originally Released Under LGPL - original licence link has changed is not relivant.
16055 * <script type="text/javascript">
16058 * @class Roo.data.MemoryProxy
16059 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16060 * to the Reader when its load method is called.
16062 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16064 Roo.data.MemoryProxy = function(data){
16068 Roo.data.MemoryProxy.superclass.constructor.call(this);
16072 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16075 * Load data from the requested source (in this case an in-memory
16076 * data object passed to the constructor), read the data object into
16077 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16078 * process that block using the passed callback.
16079 * @param {Object} params This parameter is not used by the MemoryProxy class.
16080 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16081 * object into a block of Roo.data.Records.
16082 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16083 * The function must be passed <ul>
16084 * <li>The Record block object</li>
16085 * <li>The "arg" argument from the load function</li>
16086 * <li>A boolean success indicator</li>
16088 * @param {Object} scope The scope in which to call the callback
16089 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16091 load : function(params, reader, callback, scope, arg){
16092 params = params || {};
16095 result = reader.readRecords(params.data ? params.data :this.data);
16097 this.fireEvent("loadexception", this, arg, null, e);
16098 callback.call(scope, null, arg, false);
16101 callback.call(scope, result, arg, true);
16105 update : function(params, records){
16110 * Ext JS Library 1.1.1
16111 * Copyright(c) 2006-2007, Ext JS, LLC.
16113 * Originally Released Under LGPL - original licence link has changed is not relivant.
16116 * <script type="text/javascript">
16119 * @class Roo.data.HttpProxy
16120 * @extends Roo.data.DataProxy
16121 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16122 * configured to reference a certain URL.<br><br>
16124 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16125 * from which the running page was served.<br><br>
16127 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16129 * Be aware that to enable the browser to parse an XML document, the server must set
16130 * the Content-Type header in the HTTP response to "text/xml".
16132 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16133 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16134 * will be used to make the request.
16136 Roo.data.HttpProxy = function(conn){
16137 Roo.data.HttpProxy.superclass.constructor.call(this);
16138 // is conn a conn config or a real conn?
16140 this.useAjax = !conn || !conn.events;
16144 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16145 // thse are take from connection...
16148 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16151 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16152 * extra parameters to each request made by this object. (defaults to undefined)
16155 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16156 * to each request made by this object. (defaults to undefined)
16159 * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
16162 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16165 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16171 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16175 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16176 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16177 * a finer-grained basis than the DataProxy events.
16179 getConnection : function(){
16180 return this.useAjax ? Roo.Ajax : this.conn;
16184 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16185 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16186 * process that block using the passed callback.
16187 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16188 * for the request to the remote server.
16189 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16190 * object into a block of Roo.data.Records.
16191 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16192 * The function must be passed <ul>
16193 * <li>The Record block object</li>
16194 * <li>The "arg" argument from the load function</li>
16195 * <li>A boolean success indicator</li>
16197 * @param {Object} scope The scope in which to call the callback
16198 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16200 load : function(params, reader, callback, scope, arg){
16201 if(this.fireEvent("beforeload", this, params) !== false){
16203 params : params || {},
16205 callback : callback,
16210 callback : this.loadResponse,
16214 Roo.applyIf(o, this.conn);
16215 if(this.activeRequest){
16216 Roo.Ajax.abort(this.activeRequest);
16218 this.activeRequest = Roo.Ajax.request(o);
16220 this.conn.request(o);
16223 callback.call(scope||this, null, arg, false);
16228 loadResponse : function(o, success, response){
16229 delete this.activeRequest;
16231 this.fireEvent("loadexception", this, o, response);
16232 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16237 result = o.reader.read(response);
16239 this.fireEvent("loadexception", this, o, response, e);
16240 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16244 this.fireEvent("load", this, o, o.request.arg);
16245 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16249 update : function(dataSet){
16254 updateResponse : function(dataSet){
16259 * Ext JS Library 1.1.1
16260 * Copyright(c) 2006-2007, Ext JS, LLC.
16262 * Originally Released Under LGPL - original licence link has changed is not relivant.
16265 * <script type="text/javascript">
16269 * @class Roo.data.ScriptTagProxy
16270 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16271 * other than the originating domain of the running page.<br><br>
16273 * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
16274 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16276 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16277 * source code that is used as the source inside a <script> tag.<br><br>
16279 * In order for the browser to process the returned data, the server must wrap the data object
16280 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16281 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16282 * depending on whether the callback name was passed:
16285 boolean scriptTag = false;
16286 String cb = request.getParameter("callback");
16289 response.setContentType("text/javascript");
16291 response.setContentType("application/x-json");
16293 Writer out = response.getWriter();
16295 out.write(cb + "(");
16297 out.print(dataBlock.toJsonString());
16304 * @param {Object} config A configuration object.
16306 Roo.data.ScriptTagProxy = function(config){
16307 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16308 Roo.apply(this, config);
16309 this.head = document.getElementsByTagName("head")[0];
16312 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16314 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16316 * @cfg {String} url The URL from which to request the data object.
16319 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16323 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16324 * the server the name of the callback function set up by the load call to process the returned data object.
16325 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16326 * javascript output which calls this named function passing the data object as its only parameter.
16328 callbackParam : "callback",
16330 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16331 * name to the request.
16336 * Load data from the configured URL, read the data object into
16337 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16338 * process that block using the passed callback.
16339 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16340 * for the request to the remote server.
16341 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16342 * object into a block of Roo.data.Records.
16343 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16344 * The function must be passed <ul>
16345 * <li>The Record block object</li>
16346 * <li>The "arg" argument from the load function</li>
16347 * <li>A boolean success indicator</li>
16349 * @param {Object} scope The scope in which to call the callback
16350 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16352 load : function(params, reader, callback, scope, arg){
16353 if(this.fireEvent("beforeload", this, params) !== false){
16355 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16357 var url = this.url;
16358 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16360 url += "&_dc=" + (new Date().getTime());
16362 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16365 cb : "stcCallback"+transId,
16366 scriptId : "stcScript"+transId,
16370 callback : callback,
16376 window[trans.cb] = function(o){
16377 conn.handleResponse(o, trans);
16380 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16382 if(this.autoAbort !== false){
16386 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16388 var script = document.createElement("script");
16389 script.setAttribute("src", url);
16390 script.setAttribute("type", "text/javascript");
16391 script.setAttribute("id", trans.scriptId);
16392 this.head.appendChild(script);
16394 this.trans = trans;
16396 callback.call(scope||this, null, arg, false);
16401 isLoading : function(){
16402 return this.trans ? true : false;
16406 * Abort the current server request.
16408 abort : function(){
16409 if(this.isLoading()){
16410 this.destroyTrans(this.trans);
16415 destroyTrans : function(trans, isLoaded){
16416 this.head.removeChild(document.getElementById(trans.scriptId));
16417 clearTimeout(trans.timeoutId);
16419 window[trans.cb] = undefined;
16421 delete window[trans.cb];
16424 // if hasn't been loaded, wait for load to remove it to prevent script error
16425 window[trans.cb] = function(){
16426 window[trans.cb] = undefined;
16428 delete window[trans.cb];
16435 handleResponse : function(o, trans){
16436 this.trans = false;
16437 this.destroyTrans(trans, true);
16440 result = trans.reader.readRecords(o);
16442 this.fireEvent("loadexception", this, o, trans.arg, e);
16443 trans.callback.call(trans.scope||window, null, trans.arg, false);
16446 this.fireEvent("load", this, o, trans.arg);
16447 trans.callback.call(trans.scope||window, result, trans.arg, true);
16451 handleFailure : function(trans){
16452 this.trans = false;
16453 this.destroyTrans(trans, false);
16454 this.fireEvent("loadexception", this, null, trans.arg);
16455 trans.callback.call(trans.scope||window, null, trans.arg, false);
16459 * Ext JS Library 1.1.1
16460 * Copyright(c) 2006-2007, Ext JS, LLC.
16462 * Originally Released Under LGPL - original licence link has changed is not relivant.
16465 * <script type="text/javascript">
16469 * @class Roo.data.JsonReader
16470 * @extends Roo.data.DataReader
16471 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16472 * based on mappings in a provided Roo.data.Record constructor.
16474 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16475 * in the reply previously.
16480 var RecordDef = Roo.data.Record.create([
16481 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16482 {name: 'occupation'} // This field will use "occupation" as the mapping.
16484 var myReader = new Roo.data.JsonReader({
16485 totalProperty: "results", // The property which contains the total dataset size (optional)
16486 root: "rows", // The property which contains an Array of row objects
16487 id: "id" // The property within each row object that provides an ID for the record (optional)
16491 * This would consume a JSON file like this:
16493 { 'results': 2, 'rows': [
16494 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16495 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16498 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16499 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16500 * paged from the remote server.
16501 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16502 * @cfg {String} root name of the property which contains the Array of row objects.
16503 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16504 * @cfg {Array} fields Array of field definition objects
16506 * Create a new JsonReader
16507 * @param {Object} meta Metadata configuration options
16508 * @param {Object} recordType Either an Array of field definition objects,
16509 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16511 Roo.data.JsonReader = function(meta, recordType){
16514 // set some defaults:
16515 Roo.applyIf(meta, {
16516 totalProperty: 'total',
16517 successProperty : 'success',
16522 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16524 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16526 readerType : 'Json',
16529 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16530 * Used by Store query builder to append _requestMeta to params.
16533 metaFromRemote : false,
16535 * This method is only used by a DataProxy which has retrieved data from a remote server.
16536 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16537 * @return {Object} data A data block which is used by an Roo.data.Store object as
16538 * a cache of Roo.data.Records.
16540 read : function(response){
16541 var json = response.responseText;
16543 var o = /* eval:var:o */ eval("("+json+")");
16545 throw {message: "JsonReader.read: Json object not found"};
16551 this.metaFromRemote = true;
16552 this.meta = o.metaData;
16553 this.recordType = Roo.data.Record.create(o.metaData.fields);
16554 this.onMetaChange(this.meta, this.recordType, o);
16556 return this.readRecords(o);
16559 // private function a store will implement
16560 onMetaChange : function(meta, recordType, o){
16567 simpleAccess: function(obj, subsc) {
16574 getJsonAccessor: function(){
16576 return function(expr) {
16578 return(re.test(expr))
16579 ? new Function("obj", "return obj." + expr)
16584 return Roo.emptyFn;
16589 * Create a data block containing Roo.data.Records from an XML document.
16590 * @param {Object} o An object which contains an Array of row objects in the property specified
16591 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16592 * which contains the total size of the dataset.
16593 * @return {Object} data A data block which is used by an Roo.data.Store object as
16594 * a cache of Roo.data.Records.
16596 readRecords : function(o){
16598 * After any data loads, the raw JSON data is available for further custom processing.
16602 var s = this.meta, Record = this.recordType,
16603 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16605 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16607 if(s.totalProperty) {
16608 this.getTotal = this.getJsonAccessor(s.totalProperty);
16610 if(s.successProperty) {
16611 this.getSuccess = this.getJsonAccessor(s.successProperty);
16613 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16615 var g = this.getJsonAccessor(s.id);
16616 this.getId = function(rec) {
16618 return (r === undefined || r === "") ? null : r;
16621 this.getId = function(){return null;};
16624 for(var jj = 0; jj < fl; jj++){
16626 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16627 this.ef[jj] = this.getJsonAccessor(map);
16631 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16632 if(s.totalProperty){
16633 var vt = parseInt(this.getTotal(o), 10);
16638 if(s.successProperty){
16639 var vs = this.getSuccess(o);
16640 if(vs === false || vs === 'false'){
16645 for(var i = 0; i < c; i++){
16648 var id = this.getId(n);
16649 for(var j = 0; j < fl; j++){
16651 var v = this.ef[j](n);
16653 Roo.log('missing convert for ' + f.name);
16657 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16659 var record = new Record(values, id);
16661 records[i] = record;
16667 totalRecords : totalRecords
16670 // used when loading children.. @see loadDataFromChildren
16671 toLoadData: function(rec)
16673 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16674 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16675 return { data : data, total : data.length };
16680 * Ext JS Library 1.1.1
16681 * Copyright(c) 2006-2007, Ext JS, LLC.
16683 * Originally Released Under LGPL - original licence link has changed is not relivant.
16686 * <script type="text/javascript">
16690 * @class Roo.data.ArrayReader
16691 * @extends Roo.data.DataReader
16692 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16693 * Each element of that Array represents a row of data fields. The
16694 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16695 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16699 var RecordDef = Roo.data.Record.create([
16700 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16701 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16703 var myReader = new Roo.data.ArrayReader({
16704 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16708 * This would consume an Array like this:
16710 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16714 * Create a new JsonReader
16715 * @param {Object} meta Metadata configuration options.
16716 * @param {Object|Array} recordType Either an Array of field definition objects
16718 * @cfg {Array} fields Array of field definition objects
16719 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16720 * as specified to {@link Roo.data.Record#create},
16721 * or an {@link Roo.data.Record} object
16724 * created using {@link Roo.data.Record#create}.
16726 Roo.data.ArrayReader = function(meta, recordType)
16728 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16731 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16734 * Create a data block containing Roo.data.Records from an XML document.
16735 * @param {Object} o An Array of row objects which represents the dataset.
16736 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16737 * a cache of Roo.data.Records.
16739 readRecords : function(o)
16741 var sid = this.meta ? this.meta.id : null;
16742 var recordType = this.recordType, fields = recordType.prototype.fields;
16745 for(var i = 0; i < root.length; i++){
16748 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16749 for(var j = 0, jlen = fields.length; j < jlen; j++){
16750 var f = fields.items[j];
16751 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16752 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16754 values[f.name] = v;
16756 var record = new recordType(values, id);
16758 records[records.length] = record;
16762 totalRecords : records.length
16765 // used when loading children.. @see loadDataFromChildren
16766 toLoadData: function(rec)
16768 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16769 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16780 * @class Roo.bootstrap.form.ComboBox
16781 * @extends Roo.bootstrap.form.TriggerField
16782 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16783 * @cfg {Boolean} append (true|false) default false
16784 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16785 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16786 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16787 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16788 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16789 * @cfg {Boolean} animate default true
16790 * @cfg {Boolean} emptyResultText only for touch device
16791 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16792 * @cfg {String} emptyTitle default ''
16793 * @cfg {Number} width fixed with? experimental
16795 * Create a new ComboBox.
16796 * @param {Object} config Configuration options
16798 Roo.bootstrap.form.ComboBox = function(config){
16799 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16803 * Fires when the dropdown list is expanded
16804 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16809 * Fires when the dropdown list is collapsed
16810 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16814 * @event beforeselect
16815 * Fires before a list item is selected. Return false to cancel the selection.
16816 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16817 * @param {Roo.data.Record} record The data record returned from the underlying store
16818 * @param {Number} index The index of the selected item in the dropdown list
16820 'beforeselect' : true,
16823 * Fires when a list item is selected
16824 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16825 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16826 * @param {Number} index The index of the selected item in the dropdown list
16830 * @event beforequery
16831 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16832 * The event object passed has these properties:
16833 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16834 * @param {String} query The query
16835 * @param {Boolean} forceAll true to force "all" query
16836 * @param {Boolean} cancel true to cancel the query
16837 * @param {Object} e The query event object
16839 'beforequery': true,
16842 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16843 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16848 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16849 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16850 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16855 * Fires when the remove value from the combobox array
16856 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16860 * @event afterremove
16861 * Fires when the remove value from the combobox array
16862 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16864 'afterremove' : true,
16866 * @event specialfilter
16867 * Fires when specialfilter
16868 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16870 'specialfilter' : true,
16873 * Fires when tick the element
16874 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16878 * @event touchviewdisplay
16879 * Fires when touch view require special display (default is using displayField)
16880 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16881 * @param {Object} cfg set html .
16883 'touchviewdisplay' : true
16888 this.tickItems = [];
16890 this.selectedIndex = -1;
16891 if(this.mode == 'local'){
16892 if(config.queryDelay === undefined){
16893 this.queryDelay = 10;
16895 if(config.minChars === undefined){
16901 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16904 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16905 * rendering into an Roo.Editor, defaults to false)
16908 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16909 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16912 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16915 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16916 * the dropdown list (defaults to undefined, with no header element)
16920 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16924 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16926 listWidth: undefined,
16928 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16929 * mode = 'remote' or 'text' if mode = 'local')
16931 displayField: undefined,
16934 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16935 * mode = 'remote' or 'value' if mode = 'local').
16936 * Note: use of a valueField requires the user make a selection
16937 * in order for a value to be mapped.
16939 valueField: undefined,
16941 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16946 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16947 * field's data value (defaults to the underlying DOM element's name)
16949 hiddenName: undefined,
16951 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16955 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16957 selectedClass: 'active',
16960 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16964 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16965 * anchor positions (defaults to 'tl-bl')
16967 listAlign: 'tl-bl?',
16969 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16973 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
16974 * query specified by the allQuery config option (defaults to 'query')
16976 triggerAction: 'query',
16978 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16979 * (defaults to 4, does not apply if editable = false)
16983 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16984 * delay (typeAheadDelay) if it matches a known value (defaults to false)
16988 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16989 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16993 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16994 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
16998 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
16999 * when editable = true (defaults to false)
17001 selectOnFocus:false,
17003 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17005 queryParam: 'query',
17007 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17008 * when mode = 'remote' (defaults to 'Loading...')
17010 loadingText: 'Loading...',
17012 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17016 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17020 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17021 * traditional select (defaults to true)
17025 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17029 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17033 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17034 * listWidth has a higher value)
17038 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17039 * allow the user to set arbitrary text into the field (defaults to false)
17041 forceSelection:false,
17043 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17044 * if typeAhead = true (defaults to 250)
17046 typeAheadDelay : 250,
17048 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17049 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17051 valueNotFoundText : undefined,
17053 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17055 blockFocus : false,
17058 * @cfg {Boolean} disableClear Disable showing of clear button.
17060 disableClear : false,
17062 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17064 alwaysQuery : false,
17067 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17072 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17074 invalidClass : "has-warning",
17077 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17079 validClass : "has-success",
17082 * @cfg {Boolean} specialFilter (true|false) special filter default false
17084 specialFilter : false,
17087 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17089 mobileTouchView : true,
17092 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17094 useNativeIOS : false,
17097 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17099 mobile_restrict_height : false,
17101 ios_options : false,
17113 btnPosition : 'right',
17114 triggerList : true,
17115 showToggleBtn : true,
17117 emptyResultText: 'Empty',
17118 triggerText : 'Select',
17122 // element that contains real text value.. (when hidden is used..)
17124 getAutoCreate : function()
17129 * Render classic select for iso
17132 if(Roo.isIOS && this.useNativeIOS){
17133 cfg = this.getAutoCreateNativeIOS();
17141 if(Roo.isTouch && this.mobileTouchView){
17142 cfg = this.getAutoCreateTouchView();
17149 if(!this.tickable){
17150 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17155 * ComboBox with tickable selections
17158 var align = this.labelAlign || this.parentLabelAlign();
17161 cls : 'form-group roo-combobox-tickable' //input-group
17164 var btn_text_select = '';
17165 var btn_text_done = '';
17166 var btn_text_cancel = '';
17168 if (this.btn_text_show) {
17169 btn_text_select = 'Select';
17170 btn_text_done = 'Done';
17171 btn_text_cancel = 'Cancel';
17176 cls : 'tickable-buttons',
17181 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17182 //html : this.triggerText
17183 html: btn_text_select
17189 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17191 html: btn_text_done
17197 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17199 html: btn_text_cancel
17205 buttons.cn.unshift({
17207 cls: 'roo-select2-search-field-input'
17213 Roo.each(buttons.cn, function(c){
17215 c.cls += ' btn-' + _this.size;
17218 if (_this.disabled) {
17225 style : 'display: contents',
17230 cls: 'form-hidden-field'
17234 cls: 'roo-select2-choices',
17238 cls: 'roo-select2-search-field',
17249 cls: 'roo-select2-container input-group roo-select2-container-multi',
17255 // cls: 'typeahead typeahead-long dropdown-menu',
17256 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17261 if(this.hasFeedback && !this.allowBlank){
17265 cls: 'glyphicon form-control-feedback'
17268 combobox.cn.push(feedback);
17275 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17276 tooltip : 'This field is required'
17278 if (Roo.bootstrap.version == 4) {
17281 style : 'display:none'
17284 if (align ==='left' && this.fieldLabel.length) {
17286 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17293 cls : 'control-label col-form-label',
17294 html : this.fieldLabel
17306 var labelCfg = cfg.cn[1];
17307 var contentCfg = cfg.cn[2];
17310 if(this.indicatorpos == 'right'){
17316 cls : 'control-label col-form-label',
17320 html : this.fieldLabel
17336 labelCfg = cfg.cn[0];
17337 contentCfg = cfg.cn[1];
17341 if(this.labelWidth > 12){
17342 labelCfg.style = "width: " + this.labelWidth + 'px';
17344 if(this.width * 1 > 0){
17345 contentCfg.style = "width: " + this.width + 'px';
17347 if(this.labelWidth < 13 && this.labelmd == 0){
17348 this.labelmd = this.labelWidth;
17351 if(this.labellg > 0){
17352 labelCfg.cls += ' col-lg-' + this.labellg;
17353 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17356 if(this.labelmd > 0){
17357 labelCfg.cls += ' col-md-' + this.labelmd;
17358 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17361 if(this.labelsm > 0){
17362 labelCfg.cls += ' col-sm-' + this.labelsm;
17363 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17366 if(this.labelxs > 0){
17367 labelCfg.cls += ' col-xs-' + this.labelxs;
17368 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17372 } else if ( this.fieldLabel.length) {
17373 // Roo.log(" label");
17378 //cls : 'input-group-addon',
17379 html : this.fieldLabel
17384 if(this.indicatorpos == 'right'){
17388 //cls : 'input-group-addon',
17389 html : this.fieldLabel
17399 // Roo.log(" no label && no align");
17406 ['xs','sm','md','lg'].map(function(size){
17407 if (settings[size]) {
17408 cfg.cls += ' col-' + size + '-' + settings[size];
17416 _initEventsCalled : false,
17419 initEvents: function()
17421 if (this._initEventsCalled) { // as we call render... prevent looping...
17424 this._initEventsCalled = true;
17427 throw "can not find store for combo";
17430 this.indicator = this.indicatorEl();
17432 this.store = Roo.factory(this.store, Roo.data);
17433 this.store.parent = this;
17435 // if we are building from html. then this element is so complex, that we can not really
17436 // use the rendered HTML.
17437 // so we have to trash and replace the previous code.
17438 if (Roo.XComponent.build_from_html) {
17439 // remove this element....
17440 var e = this.el.dom, k=0;
17441 while (e ) { e = e.previousSibling; ++k;}
17446 this.rendered = false;
17448 this.render(this.parent().getChildContainer(true), k);
17451 if(Roo.isIOS && this.useNativeIOS){
17452 this.initIOSView();
17460 if(Roo.isTouch && this.mobileTouchView){
17461 this.initTouchView();
17466 this.initTickableEvents();
17470 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17472 if(this.hiddenName){
17474 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17476 this.hiddenField.dom.value =
17477 this.hiddenValue !== undefined ? this.hiddenValue :
17478 this.value !== undefined ? this.value : '';
17480 // prevent input submission
17481 this.el.dom.removeAttribute('name');
17482 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17487 // this.el.dom.setAttribute('autocomplete', 'off');
17490 var cls = 'x-combo-list';
17492 //this.list = new Roo.Layer({
17493 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17499 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17500 _this.list.setWidth(lw);
17503 this.list.on('mouseover', this.onViewOver, this);
17504 this.list.on('mousemove', this.onViewMove, this);
17505 this.list.on('scroll', this.onViewScroll, this);
17508 this.list.swallowEvent('mousewheel');
17509 this.assetHeight = 0;
17512 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17513 this.assetHeight += this.header.getHeight();
17516 this.innerList = this.list.createChild({cls:cls+'-inner'});
17517 this.innerList.on('mouseover', this.onViewOver, this);
17518 this.innerList.on('mousemove', this.onViewMove, this);
17519 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17521 if(this.allowBlank && !this.pageSize && !this.disableClear){
17522 this.footer = this.list.createChild({cls:cls+'-ft'});
17523 this.pageTb = new Roo.Toolbar(this.footer);
17527 this.footer = this.list.createChild({cls:cls+'-ft'});
17528 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17529 {pageSize: this.pageSize});
17533 if (this.pageTb && this.allowBlank && !this.disableClear) {
17535 this.pageTb.add(new Roo.Toolbar.Fill(), {
17536 cls: 'x-btn-icon x-btn-clear',
17538 handler: function()
17541 _this.clearValue();
17542 _this.onSelect(false, -1);
17547 this.assetHeight += this.footer.getHeight();
17552 this.tpl = Roo.bootstrap.version == 4 ?
17553 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17554 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17557 this.view = new Roo.View(this.list, this.tpl, {
17558 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17560 //this.view.wrapEl.setDisplayed(false);
17561 this.view.on('click', this.onViewClick, this);
17564 this.store.on('beforeload', this.onBeforeLoad, this);
17565 this.store.on('load', this.onLoad, this);
17566 this.store.on('loadexception', this.onLoadException, this);
17568 if(this.resizable){
17569 this.resizer = new Roo.Resizable(this.list, {
17570 pinned:true, handles:'se'
17572 this.resizer.on('resize', function(r, w, h){
17573 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17574 this.listWidth = w;
17575 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17576 this.restrictHeight();
17578 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17581 if(!this.editable){
17582 this.editable = true;
17583 this.setEditable(false);
17588 if (typeof(this.events.add.listeners) != 'undefined') {
17590 this.addicon = this.wrap.createChild(
17591 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17593 this.addicon.on('click', function(e) {
17594 this.fireEvent('add', this);
17597 if (typeof(this.events.edit.listeners) != 'undefined') {
17599 this.editicon = this.wrap.createChild(
17600 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17601 if (this.addicon) {
17602 this.editicon.setStyle('margin-left', '40px');
17604 this.editicon.on('click', function(e) {
17606 // we fire even if inothing is selected..
17607 this.fireEvent('edit', this, this.lastData );
17613 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17614 "up" : function(e){
17615 this.inKeyMode = true;
17619 "down" : function(e){
17620 if(!this.isExpanded()){
17621 this.onTriggerClick();
17623 this.inKeyMode = true;
17628 "enter" : function(e){
17629 // this.onViewClick();
17633 if(this.fireEvent("specialkey", this, e)){
17634 this.onViewClick(false);
17640 "esc" : function(e){
17644 "tab" : function(e){
17647 if(this.fireEvent("specialkey", this, e)){
17648 this.onViewClick(false);
17656 doRelay : function(foo, bar, hname){
17657 if(hname == 'down' || this.scope.isExpanded()){
17658 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17667 this.queryDelay = Math.max(this.queryDelay || 10,
17668 this.mode == 'local' ? 10 : 250);
17671 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17673 if(this.typeAhead){
17674 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17676 if(this.editable !== false){
17677 this.inputEl().on("keyup", this.onKeyUp, this);
17679 if(this.forceSelection){
17680 this.inputEl().on('blur', this.doForce, this);
17684 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17685 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17689 initTickableEvents: function()
17693 if(this.hiddenName){
17695 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17697 this.hiddenField.dom.value =
17698 this.hiddenValue !== undefined ? this.hiddenValue :
17699 this.value !== undefined ? this.value : '';
17701 // prevent input submission
17702 this.el.dom.removeAttribute('name');
17703 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17708 // this.list = this.el.select('ul.dropdown-menu',true).first();
17710 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17711 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17712 if(this.triggerList){
17713 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17716 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17717 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17719 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17720 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17722 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17723 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17725 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17726 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17727 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17730 this.cancelBtn.hide();
17735 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17736 _this.list.setWidth(lw);
17739 this.list.on('mouseover', this.onViewOver, this);
17740 this.list.on('mousemove', this.onViewMove, this);
17742 this.list.on('scroll', this.onViewScroll, this);
17745 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17746 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17749 this.view = new Roo.View(this.list, this.tpl, {
17754 selectedClass: this.selectedClass
17757 //this.view.wrapEl.setDisplayed(false);
17758 this.view.on('click', this.onViewClick, this);
17762 this.store.on('beforeload', this.onBeforeLoad, this);
17763 this.store.on('load', this.onLoad, this);
17764 this.store.on('loadexception', this.onLoadException, this);
17767 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17768 "up" : function(e){
17769 this.inKeyMode = true;
17773 "down" : function(e){
17774 this.inKeyMode = true;
17778 "enter" : function(e){
17779 if(this.fireEvent("specialkey", this, e)){
17780 this.onViewClick(false);
17786 "esc" : function(e){
17787 this.onTickableFooterButtonClick(e, false, false);
17790 "tab" : function(e){
17791 this.fireEvent("specialkey", this, e);
17793 this.onTickableFooterButtonClick(e, false, false);
17800 doRelay : function(e, fn, key){
17801 if(this.scope.isExpanded()){
17802 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17811 this.queryDelay = Math.max(this.queryDelay || 10,
17812 this.mode == 'local' ? 10 : 250);
17815 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17817 if(this.typeAhead){
17818 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17821 if(this.editable !== false){
17822 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17825 this.indicator = this.indicatorEl();
17827 if(this.indicator){
17828 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17829 this.indicator.hide();
17834 onDestroy : function(){
17836 this.view.setStore(null);
17837 this.view.el.removeAllListeners();
17838 this.view.el.remove();
17839 this.view.purgeListeners();
17842 this.list.dom.innerHTML = '';
17846 this.store.un('beforeload', this.onBeforeLoad, this);
17847 this.store.un('load', this.onLoad, this);
17848 this.store.un('loadexception', this.onLoadException, this);
17850 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17854 fireKey : function(e){
17855 if(e.isNavKeyPress() && !this.list.isVisible()){
17856 this.fireEvent("specialkey", this, e);
17861 onResize: function(w, h)
17865 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17867 // if(typeof w != 'number'){
17868 // // we do not handle it!?!?
17871 // var tw = this.trigger.getWidth();
17872 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17873 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17875 // this.inputEl().setWidth( this.adjustWidth('input', x));
17877 // //this.trigger.setStyle('left', x+'px');
17879 // if(this.list && this.listWidth === undefined){
17880 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17881 // this.list.setWidth(lw);
17882 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17890 * Allow or prevent the user from directly editing the field text. If false is passed,
17891 * the user will only be able to select from the items defined in the dropdown list. This method
17892 * is the runtime equivalent of setting the 'editable' config option at config time.
17893 * @param {Boolean} value True to allow the user to directly edit the field text
17895 setEditable : function(value){
17896 if(value == this.editable){
17899 this.editable = value;
17901 this.inputEl().dom.setAttribute('readOnly', true);
17902 this.inputEl().on('mousedown', this.onTriggerClick, this);
17903 this.inputEl().addClass('x-combo-noedit');
17905 this.inputEl().dom.removeAttribute('readOnly');
17906 this.inputEl().un('mousedown', this.onTriggerClick, this);
17907 this.inputEl().removeClass('x-combo-noedit');
17913 onBeforeLoad : function(combo,opts){
17914 if(!this.hasFocus){
17918 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17920 this.restrictHeight();
17921 this.selectedIndex = -1;
17925 onLoad : function(){
17927 this.hasQuery = false;
17929 if(!this.hasFocus){
17933 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17934 this.loading.hide();
17937 if(this.store.getCount() > 0){
17940 this.restrictHeight();
17941 if(this.lastQuery == this.allQuery){
17942 if(this.editable && !this.tickable){
17943 this.inputEl().dom.select();
17947 !this.selectByValue(this.value, true) &&
17950 !this.store.lastOptions ||
17951 typeof(this.store.lastOptions.add) == 'undefined' ||
17952 this.store.lastOptions.add != true
17955 this.select(0, true);
17958 if(this.autoFocus){
17961 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17962 this.taTask.delay(this.typeAheadDelay);
17966 this.onEmptyResults();
17972 onLoadException : function()
17974 this.hasQuery = false;
17976 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17977 this.loading.hide();
17980 if(this.tickable && this.editable){
17985 // only causes errors at present
17986 //Roo.log(this.store.reader.jsonData);
17987 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17989 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17995 onTypeAhead : function(){
17996 if(this.store.getCount() > 0){
17997 var r = this.store.getAt(0);
17998 var newValue = r.data[this.displayField];
17999 var len = newValue.length;
18000 var selStart = this.getRawValue().length;
18002 if(selStart != len){
18003 this.setRawValue(newValue);
18004 this.selectText(selStart, newValue.length);
18010 onSelect : function(record, index){
18012 if(this.fireEvent('beforeselect', this, record, index) !== false){
18014 this.setFromData(index > -1 ? record.data : false);
18017 this.fireEvent('select', this, record, index);
18022 * Returns the currently selected field value or empty string if no value is set.
18023 * @return {String} value The selected value
18025 getValue : function()
18027 if(Roo.isIOS && this.useNativeIOS){
18028 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18032 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18035 if(this.valueField){
18036 return typeof this.value != 'undefined' ? this.value : '';
18038 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18042 getRawValue : function()
18044 if(Roo.isIOS && this.useNativeIOS){
18045 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18048 var v = this.inputEl().getValue();
18054 * Clears any text/value currently set in the field
18056 clearValue : function(){
18058 if(this.hiddenField){
18059 this.hiddenField.dom.value = '';
18062 this.setRawValue('');
18063 this.lastSelectionText = '';
18064 this.lastData = false;
18066 var close = this.closeTriggerEl();
18077 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18078 * will be displayed in the field. If the value does not match the data value of an existing item,
18079 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18080 * Otherwise the field will be blank (although the value will still be set).
18081 * @param {String} value The value to match
18083 setValue : function(v)
18085 if(Roo.isIOS && this.useNativeIOS){
18086 this.setIOSValue(v);
18096 if(this.valueField){
18097 var r = this.findRecord(this.valueField, v);
18099 text = r.data[this.displayField];
18100 }else if(this.valueNotFoundText !== undefined){
18101 text = this.valueNotFoundText;
18104 this.lastSelectionText = text;
18105 if(this.hiddenField){
18106 this.hiddenField.dom.value = v;
18108 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18111 var close = this.closeTriggerEl();
18114 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18120 * @property {Object} the last set data for the element
18125 * Sets the value of the field based on a object which is related to the record format for the store.
18126 * @param {Object} value the value to set as. or false on reset?
18128 setFromData : function(o){
18135 var dv = ''; // display value
18136 var vv = ''; // value value..
18138 if (this.displayField) {
18139 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18141 // this is an error condition!!!
18142 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18145 if(this.valueField){
18146 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18149 var close = this.closeTriggerEl();
18152 if(dv.length || vv * 1 > 0){
18154 this.blockFocus=true;
18160 if(this.hiddenField){
18161 this.hiddenField.dom.value = vv;
18163 this.lastSelectionText = dv;
18164 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18168 // no hidden field.. - we store the value in 'value', but still display
18169 // display field!!!!
18170 this.lastSelectionText = dv;
18171 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18178 reset : function(){
18179 // overridden so that last data is reset..
18186 this.setValue(this.originalValue);
18187 //this.clearInvalid();
18188 this.lastData = false;
18190 this.view.clearSelections();
18196 findRecord : function(prop, value){
18198 if(this.store.getCount() > 0){
18199 this.store.each(function(r){
18200 if(r.data[prop] == value){
18210 getName: function()
18212 // returns hidden if it's set..
18213 if (!this.rendered) {return ''};
18214 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18218 onViewMove : function(e, t){
18219 this.inKeyMode = false;
18223 onViewOver : function(e, t){
18224 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18227 var item = this.view.findItemFromChild(t);
18230 var index = this.view.indexOf(item);
18231 this.select(index, false);
18236 onViewClick : function(view, doFocus, el, e)
18238 var index = this.view.getSelectedIndexes()[0];
18240 var r = this.store.getAt(index);
18244 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18251 Roo.each(this.tickItems, function(v,k){
18253 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18255 _this.tickItems.splice(k, 1);
18257 if(typeof(e) == 'undefined' && view == false){
18258 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18270 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18271 this.tickItems.push(r.data);
18274 if(typeof(e) == 'undefined' && view == false){
18275 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18282 this.onSelect(r, index);
18284 if(doFocus !== false && !this.blockFocus){
18285 this.inputEl().focus();
18290 restrictHeight : function(){
18291 //this.innerList.dom.style.height = '';
18292 //var inner = this.innerList.dom;
18293 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18294 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18295 //this.list.beginUpdate();
18296 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18297 this.list.alignTo(this.inputEl(), this.listAlign);
18298 this.list.alignTo(this.inputEl(), this.listAlign);
18299 //this.list.endUpdate();
18303 onEmptyResults : function(){
18305 if(this.tickable && this.editable){
18306 this.hasFocus = false;
18307 this.restrictHeight();
18315 * Returns true if the dropdown list is expanded, else false.
18317 isExpanded : function(){
18318 return this.list.isVisible();
18322 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18323 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18324 * @param {String} value The data value of the item to select
18325 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18326 * selected item if it is not currently in view (defaults to true)
18327 * @return {Boolean} True if the value matched an item in the list, else false
18329 selectByValue : function(v, scrollIntoView){
18330 if(v !== undefined && v !== null){
18331 var r = this.findRecord(this.valueField || this.displayField, v);
18333 this.select(this.store.indexOf(r), scrollIntoView);
18341 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18342 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18343 * @param {Number} index The zero-based index of the list item to select
18344 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18345 * selected item if it is not currently in view (defaults to true)
18347 select : function(index, scrollIntoView){
18348 this.selectedIndex = index;
18349 this.view.select(index);
18350 if(scrollIntoView !== false){
18351 var el = this.view.getNode(index);
18353 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18356 this.list.scrollChildIntoView(el, false);
18362 selectNext : function(){
18363 var ct = this.store.getCount();
18365 if(this.selectedIndex == -1){
18367 }else if(this.selectedIndex < ct-1){
18368 this.select(this.selectedIndex+1);
18374 selectPrev : function(){
18375 var ct = this.store.getCount();
18377 if(this.selectedIndex == -1){
18379 }else if(this.selectedIndex != 0){
18380 this.select(this.selectedIndex-1);
18386 onKeyUp : function(e){
18387 if(this.editable !== false && !e.isSpecialKey()){
18388 this.lastKey = e.getKey();
18389 this.dqTask.delay(this.queryDelay);
18394 validateBlur : function(){
18395 return !this.list || !this.list.isVisible();
18399 initQuery : function(){
18401 var v = this.getRawValue();
18403 if(this.tickable && this.editable){
18404 v = this.tickableInputEl().getValue();
18411 doForce : function(){
18412 if(this.inputEl().dom.value.length > 0){
18413 this.inputEl().dom.value =
18414 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18420 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18421 * query allowing the query action to be canceled if needed.
18422 * @param {String} query The SQL query to execute
18423 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18424 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18425 * saved in the current store (defaults to false)
18427 doQuery : function(q, forceAll){
18429 if(q === undefined || q === null){
18434 forceAll: forceAll,
18438 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18443 forceAll = qe.forceAll;
18444 if(forceAll === true || (q.length >= this.minChars)){
18446 this.hasQuery = true;
18448 if(this.lastQuery != q || this.alwaysQuery){
18449 this.lastQuery = q;
18450 if(this.mode == 'local'){
18451 this.selectedIndex = -1;
18453 this.store.clearFilter();
18456 if(this.specialFilter){
18457 this.fireEvent('specialfilter', this);
18462 this.store.filter(this.displayField, q);
18465 this.store.fireEvent("datachanged", this.store);
18472 this.store.baseParams[this.queryParam] = q;
18474 var options = {params : this.getParams(q)};
18477 options.add = true;
18478 options.params.start = this.page * this.pageSize;
18481 this.store.load(options);
18484 * this code will make the page width larger, at the beginning, the list not align correctly,
18485 * we should expand the list on onLoad
18486 * so command out it
18491 this.selectedIndex = -1;
18496 this.loadNext = false;
18500 getParams : function(q){
18502 //p[this.queryParam] = q;
18506 p.limit = this.pageSize;
18512 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18514 collapse : function(){
18515 if(!this.isExpanded()){
18521 this.hasFocus = false;
18525 this.cancelBtn.hide();
18526 this.trigger.show();
18529 this.tickableInputEl().dom.value = '';
18530 this.tickableInputEl().blur();
18535 Roo.get(document).un('mousedown', this.collapseIf, this);
18536 Roo.get(document).un('mousewheel', this.collapseIf, this);
18537 if (!this.editable) {
18538 Roo.get(document).un('keydown', this.listKeyPress, this);
18540 this.fireEvent('collapse', this);
18546 collapseIf : function(e){
18547 var in_combo = e.within(this.el);
18548 var in_list = e.within(this.list);
18549 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18551 if (in_combo || in_list || is_list) {
18552 //e.stopPropagation();
18557 this.onTickableFooterButtonClick(e, false, false);
18565 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18567 expand : function(){
18569 if(this.isExpanded() || !this.hasFocus){
18573 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18574 this.list.setWidth(lw);
18580 this.restrictHeight();
18584 this.tickItems = Roo.apply([], this.item);
18587 this.cancelBtn.show();
18588 this.trigger.hide();
18591 this.tickableInputEl().focus();
18596 Roo.get(document).on('mousedown', this.collapseIf, this);
18597 Roo.get(document).on('mousewheel', this.collapseIf, this);
18598 if (!this.editable) {
18599 Roo.get(document).on('keydown', this.listKeyPress, this);
18602 this.fireEvent('expand', this);
18606 // Implements the default empty TriggerField.onTriggerClick function
18607 onTriggerClick : function(e)
18609 Roo.log('trigger click');
18611 if(this.disabled || !this.triggerList){
18616 this.loadNext = false;
18618 if(this.isExpanded()){
18620 if (!this.blockFocus) {
18621 this.inputEl().focus();
18625 this.hasFocus = true;
18626 if(this.triggerAction == 'all') {
18627 this.doQuery(this.allQuery, true);
18629 this.doQuery(this.getRawValue());
18631 if (!this.blockFocus) {
18632 this.inputEl().focus();
18637 onTickableTriggerClick : function(e)
18644 this.loadNext = false;
18645 this.hasFocus = true;
18647 if(this.triggerAction == 'all') {
18648 this.doQuery(this.allQuery, true);
18650 this.doQuery(this.getRawValue());
18654 onSearchFieldClick : function(e)
18656 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18657 this.onTickableFooterButtonClick(e, false, false);
18661 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18666 this.loadNext = false;
18667 this.hasFocus = true;
18669 if(this.triggerAction == 'all') {
18670 this.doQuery(this.allQuery, true);
18672 this.doQuery(this.getRawValue());
18676 listKeyPress : function(e)
18678 //Roo.log('listkeypress');
18679 // scroll to first matching element based on key pres..
18680 if (e.isSpecialKey()) {
18683 var k = String.fromCharCode(e.getKey()).toUpperCase();
18686 var csel = this.view.getSelectedNodes();
18687 var cselitem = false;
18689 var ix = this.view.indexOf(csel[0]);
18690 cselitem = this.store.getAt(ix);
18691 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18697 this.store.each(function(v) {
18699 // start at existing selection.
18700 if (cselitem.id == v.id) {
18706 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18707 match = this.store.indexOf(v);
18713 if (match === false) {
18714 return true; // no more action?
18717 this.view.select(match);
18718 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18719 sn.scrollIntoView(sn.dom.parentNode, false);
18722 onViewScroll : function(e, t){
18724 if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
18728 this.hasQuery = true;
18730 this.loading = this.list.select('.loading', true).first();
18732 if(this.loading === null){
18733 this.list.createChild({
18735 cls: 'loading roo-select2-more-results roo-select2-active',
18736 html: 'Loading more results...'
18739 this.loading = this.list.select('.loading', true).first();
18741 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18743 this.loading.hide();
18746 this.loading.show();
18751 this.loadNext = true;
18753 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18758 addItem : function(o)
18760 var dv = ''; // display value
18762 if (this.displayField) {
18763 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18765 // this is an error condition!!!
18766 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18773 var choice = this.choices.createChild({
18775 cls: 'roo-select2-search-choice',
18784 cls: 'roo-select2-search-choice-close fa fa-times',
18789 }, this.searchField);
18791 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18793 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18801 this.inputEl().dom.value = '';
18806 onRemoveItem : function(e, _self, o)
18808 e.preventDefault();
18810 this.lastItem = Roo.apply([], this.item);
18812 var index = this.item.indexOf(o.data) * 1;
18815 Roo.log('not this item?!');
18819 this.item.splice(index, 1);
18824 this.fireEvent('remove', this, e);
18830 syncValue : function()
18832 if(!this.item.length){
18839 Roo.each(this.item, function(i){
18840 if(_this.valueField){
18841 value.push(i[_this.valueField]);
18848 this.value = value.join(',');
18850 if(this.hiddenField){
18851 this.hiddenField.dom.value = this.value;
18854 this.store.fireEvent("datachanged", this.store);
18859 clearItem : function()
18861 if(!this.multiple){
18867 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18875 if(this.tickable && !Roo.isTouch){
18876 this.view.refresh();
18880 inputEl: function ()
18882 if(Roo.isIOS && this.useNativeIOS){
18883 return this.el.select('select.roo-ios-select', true).first();
18886 if(Roo.isTouch && this.mobileTouchView){
18887 return this.el.select('input.form-control',true).first();
18891 return this.searchField;
18894 return this.el.select('input.form-control',true).first();
18897 onTickableFooterButtonClick : function(e, btn, el)
18899 e.preventDefault();
18901 this.lastItem = Roo.apply([], this.item);
18903 if(btn && btn.name == 'cancel'){
18904 this.tickItems = Roo.apply([], this.item);
18913 Roo.each(this.tickItems, function(o){
18921 validate : function()
18923 if(this.getVisibilityEl().hasClass('hidden')){
18927 var v = this.getRawValue();
18930 v = this.getValue();
18933 if(this.disabled || this.allowBlank || v.length){
18938 this.markInvalid();
18942 tickableInputEl : function()
18944 if(!this.tickable || !this.editable){
18945 return this.inputEl();
18948 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18952 getAutoCreateTouchView : function()
18957 cls: 'form-group' //input-group
18963 type : this.inputType,
18964 cls : 'form-control x-combo-noedit',
18965 autocomplete: 'new-password',
18966 placeholder : this.placeholder || '',
18971 input.name = this.name;
18975 input.cls += ' input-' + this.size;
18978 if (this.disabled) {
18979 input.disabled = true;
18983 cls : 'roo-combobox-wrap',
18990 inputblock.cls += ' input-group';
18992 inputblock.cn.unshift({
18994 cls : 'input-group-addon input-group-prepend input-group-text',
18999 if(this.removable && !this.multiple){
19000 inputblock.cls += ' roo-removable';
19002 inputblock.cn.push({
19005 cls : 'roo-combo-removable-btn close'
19009 if(this.hasFeedback && !this.allowBlank){
19011 inputblock.cls += ' has-feedback';
19013 inputblock.cn.push({
19015 cls: 'glyphicon form-control-feedback'
19022 inputblock.cls += (this.before) ? '' : ' input-group';
19024 inputblock.cn.push({
19026 cls : 'input-group-addon input-group-append input-group-text',
19032 var ibwrap = inputblock;
19037 cls: 'roo-select2-choices',
19041 cls: 'roo-select2-search-field',
19054 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19059 cls: 'form-hidden-field'
19065 if(!this.multiple && this.showToggleBtn){
19071 if (this.caret != false) {
19074 cls: 'fa fa-' + this.caret
19081 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19083 Roo.bootstrap.version == 3 ? caret : '',
19086 cls: 'combobox-clear',
19100 combobox.cls += ' roo-select2-container-multi';
19103 var required = this.allowBlank ? {
19105 style: 'display: none'
19108 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19109 tooltip : 'This field is required'
19112 var align = this.labelAlign || this.parentLabelAlign();
19114 if (align ==='left' && this.fieldLabel.length) {
19120 cls : 'control-label col-form-label',
19121 html : this.fieldLabel
19125 cls : 'roo-combobox-wrap ',
19132 var labelCfg = cfg.cn[1];
19133 var contentCfg = cfg.cn[2];
19136 if(this.indicatorpos == 'right'){
19141 cls : 'control-label col-form-label',
19145 html : this.fieldLabel
19151 cls : "roo-combobox-wrap ",
19159 labelCfg = cfg.cn[0];
19160 contentCfg = cfg.cn[1];
19165 if(this.labelWidth > 12){
19166 labelCfg.style = "width: " + this.labelWidth + 'px';
19169 if(this.labelWidth < 13 && this.labelmd == 0){
19170 this.labelmd = this.labelWidth;
19173 if(this.labellg > 0){
19174 labelCfg.cls += ' col-lg-' + this.labellg;
19175 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19178 if(this.labelmd > 0){
19179 labelCfg.cls += ' col-md-' + this.labelmd;
19180 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19183 if(this.labelsm > 0){
19184 labelCfg.cls += ' col-sm-' + this.labelsm;
19185 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19188 if(this.labelxs > 0){
19189 labelCfg.cls += ' col-xs-' + this.labelxs;
19190 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19194 } else if ( this.fieldLabel.length) {
19199 cls : 'control-label',
19200 html : this.fieldLabel
19211 if(this.indicatorpos == 'right'){
19215 cls : 'control-label',
19216 html : this.fieldLabel,
19234 var settings = this;
19236 ['xs','sm','md','lg'].map(function(size){
19237 if (settings[size]) {
19238 cfg.cls += ' col-' + size + '-' + settings[size];
19245 initTouchView : function()
19247 this.renderTouchView();
19249 this.touchViewEl.on('scroll', function(){
19250 this.el.dom.scrollTop = 0;
19253 this.originalValue = this.getValue();
19255 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19257 this.inputEl().on("click", this.showTouchView, this);
19258 if (this.triggerEl) {
19259 this.triggerEl.on("click", this.showTouchView, this);
19263 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19264 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19266 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19268 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19269 this.store.on('load', this.onTouchViewLoad, this);
19270 this.store.on('loadexception', this.onTouchViewLoadException, this);
19272 if(this.hiddenName){
19274 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19276 this.hiddenField.dom.value =
19277 this.hiddenValue !== undefined ? this.hiddenValue :
19278 this.value !== undefined ? this.value : '';
19280 this.el.dom.removeAttribute('name');
19281 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19285 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19286 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19289 if(this.removable && !this.multiple){
19290 var close = this.closeTriggerEl();
19292 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19293 close.on('click', this.removeBtnClick, this, close);
19297 * fix the bug in Safari iOS8
19299 this.inputEl().on("focus", function(e){
19300 document.activeElement.blur();
19303 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19310 renderTouchView : function()
19312 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19313 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19315 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19316 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19318 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19319 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19320 this.touchViewBodyEl.setStyle('overflow', 'auto');
19322 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19323 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19325 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19326 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19330 showTouchView : function()
19336 this.touchViewHeaderEl.hide();
19338 if(this.modalTitle.length){
19339 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19340 this.touchViewHeaderEl.show();
19343 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19344 this.touchViewEl.show();
19346 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19348 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19349 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19351 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19353 if(this.modalTitle.length){
19354 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19357 this.touchViewBodyEl.setHeight(bodyHeight);
19361 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19363 this.touchViewEl.addClass(['in','show']);
19366 if(this._touchViewMask){
19367 Roo.get(document.body).addClass("x-body-masked");
19368 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19369 this._touchViewMask.setStyle('z-index', 10000);
19370 this._touchViewMask.addClass('show');
19373 this.doTouchViewQuery();
19377 hideTouchView : function()
19379 this.touchViewEl.removeClass(['in','show']);
19383 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19385 this.touchViewEl.setStyle('display', 'none');
19388 if(this._touchViewMask){
19389 this._touchViewMask.removeClass('show');
19390 Roo.get(document.body).removeClass("x-body-masked");
19394 setTouchViewValue : function()
19401 Roo.each(this.tickItems, function(o){
19406 this.hideTouchView();
19409 doTouchViewQuery : function()
19418 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19422 if(!this.alwaysQuery || this.mode == 'local'){
19423 this.onTouchViewLoad();
19430 onTouchViewBeforeLoad : function(combo,opts)
19436 onTouchViewLoad : function()
19438 if(this.store.getCount() < 1){
19439 this.onTouchViewEmptyResults();
19443 this.clearTouchView();
19445 var rawValue = this.getRawValue();
19447 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19449 this.tickItems = [];
19451 this.store.data.each(function(d, rowIndex){
19452 var row = this.touchViewListGroup.createChild(template);
19454 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19455 row.addClass(d.data.cls);
19458 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19461 html : d.data[this.displayField]
19464 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19465 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19468 row.removeClass('selected');
19469 if(!this.multiple && this.valueField &&
19470 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19473 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19474 row.addClass('selected');
19477 if(this.multiple && this.valueField &&
19478 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19482 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19483 this.tickItems.push(d.data);
19486 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19490 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19492 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19494 if(this.modalTitle.length){
19495 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19498 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19500 if(this.mobile_restrict_height && listHeight < bodyHeight){
19501 this.touchViewBodyEl.setHeight(listHeight);
19506 if(firstChecked && listHeight > bodyHeight){
19507 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19512 onTouchViewLoadException : function()
19514 this.hideTouchView();
19517 onTouchViewEmptyResults : function()
19519 this.clearTouchView();
19521 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19523 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19527 clearTouchView : function()
19529 this.touchViewListGroup.dom.innerHTML = '';
19532 onTouchViewClick : function(e, el, o)
19534 e.preventDefault();
19537 var rowIndex = o.rowIndex;
19539 var r = this.store.getAt(rowIndex);
19541 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19543 if(!this.multiple){
19544 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19545 c.dom.removeAttribute('checked');
19548 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19550 this.setFromData(r.data);
19552 var close = this.closeTriggerEl();
19558 this.hideTouchView();
19560 this.fireEvent('select', this, r, rowIndex);
19565 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19566 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19567 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19571 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19572 this.addItem(r.data);
19573 this.tickItems.push(r.data);
19577 getAutoCreateNativeIOS : function()
19580 cls: 'form-group' //input-group,
19585 cls : 'roo-ios-select'
19589 combobox.name = this.name;
19592 if (this.disabled) {
19593 combobox.disabled = true;
19596 var settings = this;
19598 ['xs','sm','md','lg'].map(function(size){
19599 if (settings[size]) {
19600 cfg.cls += ' col-' + size + '-' + settings[size];
19610 initIOSView : function()
19612 this.store.on('load', this.onIOSViewLoad, this);
19617 onIOSViewLoad : function()
19619 if(this.store.getCount() < 1){
19623 this.clearIOSView();
19625 if(this.allowBlank) {
19627 var default_text = '-- SELECT --';
19629 if(this.placeholder.length){
19630 default_text = this.placeholder;
19633 if(this.emptyTitle.length){
19634 default_text += ' - ' + this.emptyTitle + ' -';
19637 var opt = this.inputEl().createChild({
19640 html : default_text
19644 o[this.valueField] = 0;
19645 o[this.displayField] = default_text;
19647 this.ios_options.push({
19654 this.store.data.each(function(d, rowIndex){
19658 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19659 html = d.data[this.displayField];
19664 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19665 value = d.data[this.valueField];
19674 if(this.value == d.data[this.valueField]){
19675 option['selected'] = true;
19678 var opt = this.inputEl().createChild(option);
19680 this.ios_options.push({
19687 this.inputEl().on('change', function(){
19688 this.fireEvent('select', this);
19693 clearIOSView: function()
19695 this.inputEl().dom.innerHTML = '';
19697 this.ios_options = [];
19700 setIOSValue: function(v)
19704 if(!this.ios_options){
19708 Roo.each(this.ios_options, function(opts){
19710 opts.el.dom.removeAttribute('selected');
19712 if(opts.data[this.valueField] != v){
19716 opts.el.dom.setAttribute('selected', true);
19722 * @cfg {Boolean} grow
19726 * @cfg {Number} growMin
19730 * @cfg {Number} growMax
19739 Roo.apply(Roo.bootstrap.form.ComboBox, {
19743 cls: 'modal-header',
19765 cls: 'list-group-item',
19769 cls: 'roo-combobox-list-group-item-value'
19773 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19787 listItemCheckbox : {
19789 cls: 'list-group-item',
19793 cls: 'roo-combobox-list-group-item-value'
19797 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19813 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19818 cls: 'modal-footer',
19826 cls: 'col-xs-6 text-left',
19829 cls: 'btn btn-danger roo-touch-view-cancel',
19835 cls: 'col-xs-6 text-right',
19838 cls: 'btn btn-success roo-touch-view-ok',
19849 Roo.apply(Roo.bootstrap.form.ComboBox, {
19851 touchViewTemplate : {
19853 cls: 'modal fade roo-combobox-touch-view',
19857 cls: 'modal-dialog',
19858 style : 'position:fixed', // we have to fix position....
19862 cls: 'modal-content',
19864 Roo.bootstrap.form.ComboBox.header,
19865 Roo.bootstrap.form.ComboBox.body,
19866 Roo.bootstrap.form.ComboBox.footer
19875 * Ext JS Library 1.1.1
19876 * Copyright(c) 2006-2007, Ext JS, LLC.
19878 * Originally Released Under LGPL - original licence link has changed is not relivant.
19881 * <script type="text/javascript">
19886 * @extends Roo.util.Observable
19887 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19888 * This class also supports single and multi selection modes. <br>
19889 * Create a data model bound view:
19891 var store = new Roo.data.Store(...);
19893 var view = new Roo.View({
19895 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19897 singleSelect: true,
19898 selectedClass: "ydataview-selected",
19902 // listen for node click?
19903 view.on("click", function(vw, index, node, e){
19904 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19908 dataModel.load("foobar.xml");
19910 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19912 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19913 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19915 * Note: old style constructor is still suported (container, template, config)
19918 * Create a new View
19919 * @param {Object} config The config object
19922 Roo.View = function(config, depreciated_tpl, depreciated_config){
19924 this.parent = false;
19926 if (typeof(depreciated_tpl) == 'undefined') {
19927 // new way.. - universal constructor.
19928 Roo.apply(this, config);
19929 this.el = Roo.get(this.el);
19932 this.el = Roo.get(config);
19933 this.tpl = depreciated_tpl;
19934 Roo.apply(this, depreciated_config);
19936 this.wrapEl = this.el.wrap().wrap();
19937 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19940 if(typeof(this.tpl) == "string"){
19941 this.tpl = new Roo.Template(this.tpl);
19943 // support xtype ctors..
19944 this.tpl = new Roo.factory(this.tpl, Roo);
19948 this.tpl.compile();
19953 * @event beforeclick
19954 * Fires before a click is processed. Returns false to cancel the default action.
19955 * @param {Roo.View} this
19956 * @param {Number} index The index of the target node
19957 * @param {HTMLElement} node The target node
19958 * @param {Roo.EventObject} e The raw event object
19960 "beforeclick" : true,
19963 * Fires when a template node is clicked.
19964 * @param {Roo.View} this
19965 * @param {Number} index The index of the target node
19966 * @param {HTMLElement} node The target node
19967 * @param {Roo.EventObject} e The raw event object
19972 * Fires when a template node is double clicked.
19973 * @param {Roo.View} this
19974 * @param {Number} index The index of the target node
19975 * @param {HTMLElement} node The target node
19976 * @param {Roo.EventObject} e The raw event object
19980 * @event contextmenu
19981 * Fires when a template node is right clicked.
19982 * @param {Roo.View} this
19983 * @param {Number} index The index of the target node
19984 * @param {HTMLElement} node The target node
19985 * @param {Roo.EventObject} e The raw event object
19987 "contextmenu" : true,
19989 * @event selectionchange
19990 * Fires when the selected nodes change.
19991 * @param {Roo.View} this
19992 * @param {Array} selections Array of the selected nodes
19994 "selectionchange" : true,
19997 * @event beforeselect
19998 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19999 * @param {Roo.View} this
20000 * @param {HTMLElement} node The node to be selected
20001 * @param {Array} selections Array of currently selected nodes
20003 "beforeselect" : true,
20005 * @event preparedata
20006 * Fires on every row to render, to allow you to change the data.
20007 * @param {Roo.View} this
20008 * @param {Object} data to be rendered (change this)
20010 "preparedata" : true
20018 "click": this.onClick,
20019 "dblclick": this.onDblClick,
20020 "contextmenu": this.onContextMenu,
20024 this.selections = [];
20026 this.cmp = new Roo.CompositeElementLite([]);
20028 this.store = Roo.factory(this.store, Roo.data);
20029 this.setStore(this.store, true);
20032 if ( this.footer && this.footer.xtype) {
20034 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20036 this.footer.dataSource = this.store;
20037 this.footer.container = fctr;
20038 this.footer = Roo.factory(this.footer, Roo);
20039 fctr.insertFirst(this.el);
20041 // this is a bit insane - as the paging toolbar seems to detach the el..
20042 // dom.parentNode.parentNode.parentNode
20043 // they get detached?
20047 Roo.View.superclass.constructor.call(this);
20052 Roo.extend(Roo.View, Roo.util.Observable, {
20055 * @cfg {Roo.data.Store} store Data store to load data from.
20060 * @cfg {String|Roo.Element} el The container element.
20065 * @cfg {String|Roo.Template} tpl The template used by this View
20069 * @cfg {String} dataName the named area of the template to use as the data area
20070 * Works with domtemplates roo-name="name"
20074 * @cfg {String} selectedClass The css class to add to selected nodes
20076 selectedClass : "x-view-selected",
20078 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20083 * @cfg {String} text to display on mask (default Loading)
20087 * @cfg {Boolean} multiSelect Allow multiple selection
20089 multiSelect : false,
20091 * @cfg {Boolean} singleSelect Allow single selection
20093 singleSelect: false,
20096 * @cfg {Boolean} toggleSelect - selecting
20098 toggleSelect : false,
20101 * @cfg {Boolean} tickable - selecting
20106 * Returns the element this view is bound to.
20107 * @return {Roo.Element}
20109 getEl : function(){
20110 return this.wrapEl;
20116 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20118 refresh : function(){
20119 //Roo.log('refresh');
20122 // if we are using something like 'domtemplate', then
20123 // the what gets used is:
20124 // t.applySubtemplate(NAME, data, wrapping data..)
20125 // the outer template then get' applied with
20126 // the store 'extra data'
20127 // and the body get's added to the
20128 // roo-name="data" node?
20129 // <span class='roo-tpl-{name}'></span> ?????
20133 this.clearSelections();
20134 this.el.update("");
20136 var records = this.store.getRange();
20137 if(records.length < 1) {
20139 // is this valid?? = should it render a template??
20141 this.el.update(this.emptyText);
20145 if (this.dataName) {
20146 this.el.update(t.apply(this.store.meta)); //????
20147 el = this.el.child('.roo-tpl-' + this.dataName);
20150 for(var i = 0, len = records.length; i < len; i++){
20151 var data = this.prepareData(records[i].data, i, records[i]);
20152 this.fireEvent("preparedata", this, data, i, records[i]);
20154 var d = Roo.apply({}, data);
20157 Roo.apply(d, {'roo-id' : Roo.id()});
20161 Roo.each(this.parent.item, function(item){
20162 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20165 Roo.apply(d, {'roo-data-checked' : 'checked'});
20169 html[html.length] = Roo.util.Format.trim(
20171 t.applySubtemplate(this.dataName, d, this.store.meta) :
20178 el.update(html.join(""));
20179 this.nodes = el.dom.childNodes;
20180 this.updateIndexes(0);
20185 * Function to override to reformat the data that is sent to
20186 * the template for each node.
20187 * DEPRICATED - use the preparedata event handler.
20188 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20189 * a JSON object for an UpdateManager bound view).
20191 prepareData : function(data, index, record)
20193 this.fireEvent("preparedata", this, data, index, record);
20197 onUpdate : function(ds, record){
20198 // Roo.log('on update');
20199 this.clearSelections();
20200 var index = this.store.indexOf(record);
20201 var n = this.nodes[index];
20202 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20203 n.parentNode.removeChild(n);
20204 this.updateIndexes(index, index);
20210 onAdd : function(ds, records, index)
20212 //Roo.log(['on Add', ds, records, index] );
20213 this.clearSelections();
20214 if(this.nodes.length == 0){
20218 var n = this.nodes[index];
20219 for(var i = 0, len = records.length; i < len; i++){
20220 var d = this.prepareData(records[i].data, i, records[i]);
20222 this.tpl.insertBefore(n, d);
20225 this.tpl.append(this.el, d);
20228 this.updateIndexes(index);
20231 onRemove : function(ds, record, index){
20232 // Roo.log('onRemove');
20233 this.clearSelections();
20234 var el = this.dataName ?
20235 this.el.child('.roo-tpl-' + this.dataName) :
20238 el.dom.removeChild(this.nodes[index]);
20239 this.updateIndexes(index);
20243 * Refresh an individual node.
20244 * @param {Number} index
20246 refreshNode : function(index){
20247 this.onUpdate(this.store, this.store.getAt(index));
20250 updateIndexes : function(startIndex, endIndex){
20251 var ns = this.nodes;
20252 startIndex = startIndex || 0;
20253 endIndex = endIndex || ns.length - 1;
20254 for(var i = startIndex; i <= endIndex; i++){
20255 ns[i].nodeIndex = i;
20260 * Changes the data store this view uses and refresh the view.
20261 * @param {Store} store
20263 setStore : function(store, initial){
20264 if(!initial && this.store){
20265 this.store.un("datachanged", this.refresh);
20266 this.store.un("add", this.onAdd);
20267 this.store.un("remove", this.onRemove);
20268 this.store.un("update", this.onUpdate);
20269 this.store.un("clear", this.refresh);
20270 this.store.un("beforeload", this.onBeforeLoad);
20271 this.store.un("load", this.onLoad);
20272 this.store.un("loadexception", this.onLoad);
20276 store.on("datachanged", this.refresh, this);
20277 store.on("add", this.onAdd, this);
20278 store.on("remove", this.onRemove, this);
20279 store.on("update", this.onUpdate, this);
20280 store.on("clear", this.refresh, this);
20281 store.on("beforeload", this.onBeforeLoad, this);
20282 store.on("load", this.onLoad, this);
20283 store.on("loadexception", this.onLoad, this);
20291 * onbeforeLoad - masks the loading area.
20294 onBeforeLoad : function(store,opts)
20296 //Roo.log('onBeforeLoad');
20298 this.el.update("");
20300 this.el.mask(this.mask ? this.mask : "Loading" );
20302 onLoad : function ()
20309 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20310 * @param {HTMLElement} node
20311 * @return {HTMLElement} The template node
20313 findItemFromChild : function(node){
20314 var el = this.dataName ?
20315 this.el.child('.roo-tpl-' + this.dataName,true) :
20318 if(!node || node.parentNode == el){
20321 var p = node.parentNode;
20322 while(p && p != el){
20323 if(p.parentNode == el){
20332 onClick : function(e){
20333 var item = this.findItemFromChild(e.getTarget());
20335 var index = this.indexOf(item);
20336 if(this.onItemClick(item, index, e) !== false){
20337 this.fireEvent("click", this, index, item, e);
20340 this.clearSelections();
20345 onContextMenu : function(e){
20346 var item = this.findItemFromChild(e.getTarget());
20348 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20353 onDblClick : function(e){
20354 var item = this.findItemFromChild(e.getTarget());
20356 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20360 onItemClick : function(item, index, e)
20362 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20365 if (this.toggleSelect) {
20366 var m = this.isSelected(item) ? 'unselect' : 'select';
20369 _t[m](item, true, false);
20372 if(this.multiSelect || this.singleSelect){
20373 if(this.multiSelect && e.shiftKey && this.lastSelection){
20374 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20376 this.select(item, this.multiSelect && e.ctrlKey);
20377 this.lastSelection = item;
20380 if(!this.tickable){
20381 e.preventDefault();
20389 * Get the number of selected nodes.
20392 getSelectionCount : function(){
20393 return this.selections.length;
20397 * Get the currently selected nodes.
20398 * @return {Array} An array of HTMLElements
20400 getSelectedNodes : function(){
20401 return this.selections;
20405 * Get the indexes of the selected nodes.
20408 getSelectedIndexes : function(){
20409 var indexes = [], s = this.selections;
20410 for(var i = 0, len = s.length; i < len; i++){
20411 indexes.push(s[i].nodeIndex);
20417 * Clear all selections
20418 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20420 clearSelections : function(suppressEvent){
20421 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20422 this.cmp.elements = this.selections;
20423 this.cmp.removeClass(this.selectedClass);
20424 this.selections = [];
20425 if(!suppressEvent){
20426 this.fireEvent("selectionchange", this, this.selections);
20432 * Returns true if the passed node is selected
20433 * @param {HTMLElement/Number} node The node or node index
20434 * @return {Boolean}
20436 isSelected : function(node){
20437 var s = this.selections;
20441 node = this.getNode(node);
20442 return s.indexOf(node) !== -1;
20447 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20448 * @param {Boolean} keepExisting (optional) true to keep existing selections
20449 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20451 select : function(nodeInfo, keepExisting, suppressEvent){
20452 if(nodeInfo instanceof Array){
20454 this.clearSelections(true);
20456 for(var i = 0, len = nodeInfo.length; i < len; i++){
20457 this.select(nodeInfo[i], true, true);
20461 var node = this.getNode(nodeInfo);
20462 if(!node || this.isSelected(node)){
20463 return; // already selected.
20466 this.clearSelections(true);
20469 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20470 Roo.fly(node).addClass(this.selectedClass);
20471 this.selections.push(node);
20472 if(!suppressEvent){
20473 this.fireEvent("selectionchange", this, this.selections);
20481 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20482 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20483 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20485 unselect : function(nodeInfo, keepExisting, suppressEvent)
20487 if(nodeInfo instanceof Array){
20488 Roo.each(this.selections, function(s) {
20489 this.unselect(s, nodeInfo);
20493 var node = this.getNode(nodeInfo);
20494 if(!node || !this.isSelected(node)){
20495 //Roo.log("not selected");
20496 return; // not selected.
20500 Roo.each(this.selections, function(s) {
20502 Roo.fly(node).removeClass(this.selectedClass);
20509 this.selections= ns;
20510 this.fireEvent("selectionchange", this, this.selections);
20514 * Gets a template node.
20515 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20516 * @return {HTMLElement} The node or null if it wasn't found
20518 getNode : function(nodeInfo){
20519 if(typeof nodeInfo == "string"){
20520 return document.getElementById(nodeInfo);
20521 }else if(typeof nodeInfo == "number"){
20522 return this.nodes[nodeInfo];
20528 * Gets a range template nodes.
20529 * @param {Number} startIndex
20530 * @param {Number} endIndex
20531 * @return {Array} An array of nodes
20533 getNodes : function(start, end){
20534 var ns = this.nodes;
20535 start = start || 0;
20536 end = typeof end == "undefined" ? ns.length - 1 : end;
20539 for(var i = start; i <= end; i++){
20543 for(var i = start; i >= end; i--){
20551 * Finds the index of the passed node
20552 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20553 * @return {Number} The index of the node or -1
20555 indexOf : function(node){
20556 node = this.getNode(node);
20557 if(typeof node.nodeIndex == "number"){
20558 return node.nodeIndex;
20560 var ns = this.nodes;
20561 for(var i = 0, len = ns.length; i < len; i++){
20572 * based on jquery fullcalendar
20576 Roo.bootstrap = Roo.bootstrap || {};
20578 * @class Roo.bootstrap.Calendar
20579 * @extends Roo.bootstrap.Component
20580 * Bootstrap Calendar class
20581 * @cfg {Boolean} loadMask (true|false) default false
20582 * @cfg {Object} header generate the user specific header of the calendar, default false
20585 * Create a new Container
20586 * @param {Object} config The config object
20591 Roo.bootstrap.Calendar = function(config){
20592 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20596 * Fires when a date is selected
20597 * @param {DatePicker} this
20598 * @param {Date} date The selected date
20602 * @event monthchange
20603 * Fires when the displayed month changes
20604 * @param {DatePicker} this
20605 * @param {Date} date The selected month
20607 'monthchange': true,
20609 * @event evententer
20610 * Fires when mouse over an event
20611 * @param {Calendar} this
20612 * @param {event} Event
20614 'evententer': true,
20616 * @event eventleave
20617 * Fires when the mouse leaves an
20618 * @param {Calendar} this
20621 'eventleave': true,
20623 * @event eventclick
20624 * Fires when the mouse click an
20625 * @param {Calendar} this
20634 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20637 * @cfg {Roo.data.Store} store
20638 * The data source for the calendar
20642 * @cfg {Number} startDay
20643 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20651 getAutoCreate : function(){
20654 var fc_button = function(name, corner, style, content ) {
20655 return Roo.apply({},{
20657 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20659 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20662 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20673 style : 'width:100%',
20680 cls : 'fc-header-left',
20682 fc_button('prev', 'left', 'arrow', '‹' ),
20683 fc_button('next', 'right', 'arrow', '›' ),
20684 { tag: 'span', cls: 'fc-header-space' },
20685 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20693 cls : 'fc-header-center',
20697 cls: 'fc-header-title',
20700 html : 'month / year'
20708 cls : 'fc-header-right',
20710 /* fc_button('month', 'left', '', 'month' ),
20711 fc_button('week', '', '', 'week' ),
20712 fc_button('day', 'right', '', 'day' )
20724 header = this.header;
20727 var cal_heads = function() {
20729 // fixme - handle this.
20731 for (var i =0; i < Date.dayNames.length; i++) {
20732 var d = Date.dayNames[i];
20735 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20736 html : d.substring(0,3)
20740 ret[0].cls += ' fc-first';
20741 ret[6].cls += ' fc-last';
20744 var cal_cell = function(n) {
20747 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20752 cls: 'fc-day-number',
20756 cls: 'fc-day-content',
20760 style: 'position: relative;' // height: 17px;
20772 var cal_rows = function() {
20775 for (var r = 0; r < 6; r++) {
20782 for (var i =0; i < Date.dayNames.length; i++) {
20783 var d = Date.dayNames[i];
20784 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20787 row.cn[0].cls+=' fc-first';
20788 row.cn[0].cn[0].style = 'min-height:90px';
20789 row.cn[6].cls+=' fc-last';
20793 ret[0].cls += ' fc-first';
20794 ret[4].cls += ' fc-prev-last';
20795 ret[5].cls += ' fc-last';
20802 cls: 'fc-border-separate',
20803 style : 'width:100%',
20811 cls : 'fc-first fc-last',
20829 cls : 'fc-content',
20830 style : "position: relative;",
20833 cls : 'fc-view fc-view-month fc-grid',
20834 style : 'position: relative',
20835 unselectable : 'on',
20838 cls : 'fc-event-container',
20839 style : 'position:absolute;z-index:8;top:0;left:0;'
20857 initEvents : function()
20860 throw "can not find store for calendar";
20866 style: "text-align:center",
20870 style: "background-color:white;width:50%;margin:250 auto",
20874 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20885 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20887 var size = this.el.select('.fc-content', true).first().getSize();
20888 this.maskEl.setSize(size.width, size.height);
20889 this.maskEl.enableDisplayMode("block");
20890 if(!this.loadMask){
20891 this.maskEl.hide();
20894 this.store = Roo.factory(this.store, Roo.data);
20895 this.store.on('load', this.onLoad, this);
20896 this.store.on('beforeload', this.onBeforeLoad, this);
20900 this.cells = this.el.select('.fc-day',true);
20901 //Roo.log(this.cells);
20902 this.textNodes = this.el.query('.fc-day-number');
20903 this.cells.addClassOnOver('fc-state-hover');
20905 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20906 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20907 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20908 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20910 this.on('monthchange', this.onMonthChange, this);
20912 this.update(new Date().clearTime());
20915 resize : function() {
20916 var sz = this.el.getSize();
20918 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20919 this.el.select('.fc-day-content div',true).setHeight(34);
20924 showPrevMonth : function(e){
20925 this.update(this.activeDate.add("mo", -1));
20927 showToday : function(e){
20928 this.update(new Date().clearTime());
20931 showNextMonth : function(e){
20932 this.update(this.activeDate.add("mo", 1));
20936 showPrevYear : function(){
20937 this.update(this.activeDate.add("y", -1));
20941 showNextYear : function(){
20942 this.update(this.activeDate.add("y", 1));
20947 update : function(date)
20949 var vd = this.activeDate;
20950 this.activeDate = date;
20951 // if(vd && this.el){
20952 // var t = date.getTime();
20953 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20954 // Roo.log('using add remove');
20956 // this.fireEvent('monthchange', this, date);
20958 // this.cells.removeClass("fc-state-highlight");
20959 // this.cells.each(function(c){
20960 // if(c.dateValue == t){
20961 // c.addClass("fc-state-highlight");
20962 // setTimeout(function(){
20963 // try{c.dom.firstChild.focus();}catch(e){}
20973 var days = date.getDaysInMonth();
20975 var firstOfMonth = date.getFirstDateOfMonth();
20976 var startingPos = firstOfMonth.getDay()-this.startDay;
20978 if(startingPos < this.startDay){
20982 var pm = date.add(Date.MONTH, -1);
20983 var prevStart = pm.getDaysInMonth()-startingPos;
20985 this.cells = this.el.select('.fc-day',true);
20986 this.textNodes = this.el.query('.fc-day-number');
20987 this.cells.addClassOnOver('fc-state-hover');
20989 var cells = this.cells.elements;
20990 var textEls = this.textNodes;
20992 Roo.each(cells, function(cell){
20993 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20996 days += startingPos;
20998 // convert everything to numbers so it's fast
20999 var day = 86400000;
21000 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21003 //Roo.log(prevStart);
21005 var today = new Date().clearTime().getTime();
21006 var sel = date.clearTime().getTime();
21007 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21008 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21009 var ddMatch = this.disabledDatesRE;
21010 var ddText = this.disabledDatesText;
21011 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21012 var ddaysText = this.disabledDaysText;
21013 var format = this.format;
21015 var setCellClass = function(cal, cell){
21019 //Roo.log('set Cell Class');
21021 var t = d.getTime();
21025 cell.dateValue = t;
21027 cell.className += " fc-today";
21028 cell.className += " fc-state-highlight";
21029 cell.title = cal.todayText;
21032 // disable highlight in other month..
21033 //cell.className += " fc-state-highlight";
21038 cell.className = " fc-state-disabled";
21039 cell.title = cal.minText;
21043 cell.className = " fc-state-disabled";
21044 cell.title = cal.maxText;
21048 if(ddays.indexOf(d.getDay()) != -1){
21049 cell.title = ddaysText;
21050 cell.className = " fc-state-disabled";
21053 if(ddMatch && format){
21054 var fvalue = d.dateFormat(format);
21055 if(ddMatch.test(fvalue)){
21056 cell.title = ddText.replace("%0", fvalue);
21057 cell.className = " fc-state-disabled";
21061 if (!cell.initialClassName) {
21062 cell.initialClassName = cell.dom.className;
21065 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21070 for(; i < startingPos; i++) {
21071 textEls[i].innerHTML = (++prevStart);
21072 d.setDate(d.getDate()+1);
21074 cells[i].className = "fc-past fc-other-month";
21075 setCellClass(this, cells[i]);
21080 for(; i < days; i++){
21081 intDay = i - startingPos + 1;
21082 textEls[i].innerHTML = (intDay);
21083 d.setDate(d.getDate()+1);
21085 cells[i].className = ''; // "x-date-active";
21086 setCellClass(this, cells[i]);
21090 for(; i < 42; i++) {
21091 textEls[i].innerHTML = (++extraDays);
21092 d.setDate(d.getDate()+1);
21094 cells[i].className = "fc-future fc-other-month";
21095 setCellClass(this, cells[i]);
21098 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21100 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21102 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21103 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21105 if(totalRows != 6){
21106 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21107 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21110 this.fireEvent('monthchange', this, date);
21114 if(!this.internalRender){
21115 var main = this.el.dom.firstChild;
21116 var w = main.offsetWidth;
21117 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21118 Roo.fly(main).setWidth(w);
21119 this.internalRender = true;
21120 // opera does not respect the auto grow header center column
21121 // then, after it gets a width opera refuses to recalculate
21122 // without a second pass
21123 if(Roo.isOpera && !this.secondPass){
21124 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21125 this.secondPass = true;
21126 this.update.defer(10, this, [date]);
21133 findCell : function(dt) {
21134 dt = dt.clearTime().getTime();
21136 this.cells.each(function(c){
21137 //Roo.log("check " +c.dateValue + '?=' + dt);
21138 if(c.dateValue == dt){
21148 findCells : function(ev) {
21149 var s = ev.start.clone().clearTime().getTime();
21151 var e= ev.end.clone().clearTime().getTime();
21154 this.cells.each(function(c){
21155 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21157 if(c.dateValue > e){
21160 if(c.dateValue < s){
21169 // findBestRow: function(cells)
21173 // for (var i =0 ; i < cells.length;i++) {
21174 // ret = Math.max(cells[i].rows || 0,ret);
21181 addItem : function(ev)
21183 // look for vertical location slot in
21184 var cells = this.findCells(ev);
21186 // ev.row = this.findBestRow(cells);
21188 // work out the location.
21192 for(var i =0; i < cells.length; i++) {
21194 cells[i].row = cells[0].row;
21197 cells[i].row = cells[i].row + 1;
21207 if (crow.start.getY() == cells[i].getY()) {
21209 crow.end = cells[i];
21226 cells[0].events.push(ev);
21228 this.calevents.push(ev);
21231 clearEvents: function() {
21233 if(!this.calevents){
21237 Roo.each(this.cells.elements, function(c){
21243 Roo.each(this.calevents, function(e) {
21244 Roo.each(e.els, function(el) {
21245 el.un('mouseenter' ,this.onEventEnter, this);
21246 el.un('mouseleave' ,this.onEventLeave, this);
21251 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21257 renderEvents: function()
21261 this.cells.each(function(c) {
21270 if(c.row != c.events.length){
21271 r = 4 - (4 - (c.row - c.events.length));
21274 c.events = ev.slice(0, r);
21275 c.more = ev.slice(r);
21277 if(c.more.length && c.more.length == 1){
21278 c.events.push(c.more.pop());
21281 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21285 this.cells.each(function(c) {
21287 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21290 for (var e = 0; e < c.events.length; e++){
21291 var ev = c.events[e];
21292 var rows = ev.rows;
21294 for(var i = 0; i < rows.length; i++) {
21296 // how many rows should it span..
21299 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21300 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21302 unselectable : "on",
21305 cls: 'fc-event-inner',
21309 // cls: 'fc-event-time',
21310 // html : cells.length > 1 ? '' : ev.time
21314 cls: 'fc-event-title',
21315 html : String.format('{0}', ev.title)
21322 cls: 'ui-resizable-handle ui-resizable-e',
21323 html : '  '
21330 cfg.cls += ' fc-event-start';
21332 if ((i+1) == rows.length) {
21333 cfg.cls += ' fc-event-end';
21336 var ctr = _this.el.select('.fc-event-container',true).first();
21337 var cg = ctr.createChild(cfg);
21339 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21340 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21342 var r = (c.more.length) ? 1 : 0;
21343 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21344 cg.setWidth(ebox.right - sbox.x -2);
21346 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21347 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21348 cg.on('click', _this.onEventClick, _this, ev);
21359 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21360 style : 'position: absolute',
21361 unselectable : "on",
21364 cls: 'fc-event-inner',
21368 cls: 'fc-event-title',
21376 cls: 'ui-resizable-handle ui-resizable-e',
21377 html : '  '
21383 var ctr = _this.el.select('.fc-event-container',true).first();
21384 var cg = ctr.createChild(cfg);
21386 var sbox = c.select('.fc-day-content',true).first().getBox();
21387 var ebox = c.select('.fc-day-content',true).first().getBox();
21389 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21390 cg.setWidth(ebox.right - sbox.x -2);
21392 cg.on('click', _this.onMoreEventClick, _this, c.more);
21402 onEventEnter: function (e, el,event,d) {
21403 this.fireEvent('evententer', this, el, event);
21406 onEventLeave: function (e, el,event,d) {
21407 this.fireEvent('eventleave', this, el, event);
21410 onEventClick: function (e, el,event,d) {
21411 this.fireEvent('eventclick', this, el, event);
21414 onMonthChange: function () {
21418 onMoreEventClick: function(e, el, more)
21422 this.calpopover.placement = 'right';
21423 this.calpopover.setTitle('More');
21425 this.calpopover.setContent('');
21427 var ctr = this.calpopover.el.select('.popover-content', true).first();
21429 Roo.each(more, function(m){
21431 cls : 'fc-event-hori fc-event-draggable',
21434 var cg = ctr.createChild(cfg);
21436 cg.on('click', _this.onEventClick, _this, m);
21439 this.calpopover.show(el);
21444 onLoad: function ()
21446 this.calevents = [];
21449 if(this.store.getCount() > 0){
21450 this.store.data.each(function(d){
21453 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21454 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21455 time : d.data.start_time,
21456 title : d.data.title,
21457 description : d.data.description,
21458 venue : d.data.venue
21463 this.renderEvents();
21465 if(this.calevents.length && this.loadMask){
21466 this.maskEl.hide();
21470 onBeforeLoad: function()
21472 this.clearEvents();
21474 this.maskEl.show();
21488 * @class Roo.bootstrap.Popover
21489 * @extends Roo.bootstrap.Component
21492 * @children Roo.bootstrap.Component
21493 * Bootstrap Popover class
21494 * @cfg {String} html contents of the popover (or false to use children..)
21495 * @cfg {String} title of popover (or false to hide)
21496 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21497 * @cfg {String} trigger click || hover (or false to trigger manually)
21498 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21499 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21500 * - if false and it has a 'parent' then it will be automatically added to that element
21501 * - if string - Roo.get will be called
21502 * @cfg {Number} delay - delay before showing
21505 * Create a new Popover
21506 * @param {Object} config The config object
21509 Roo.bootstrap.Popover = function(config){
21510 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21516 * After the popover show
21518 * @param {Roo.bootstrap.Popover} this
21523 * After the popover hide
21525 * @param {Roo.bootstrap.Popover} this
21531 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21536 placement : 'right',
21537 trigger : 'hover', // hover
21543 can_build_overlaid : false,
21545 maskEl : false, // the mask element
21548 alignEl : false, // when show is called with an element - this get's stored.
21550 getChildContainer : function()
21552 return this.contentEl;
21555 getPopoverHeader : function()
21557 this.title = true; // flag not to hide it..
21558 this.headerEl.addClass('p-0');
21559 return this.headerEl
21563 getAutoCreate : function(){
21566 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21567 style: 'display:block',
21573 cls : 'popover-inner ',
21577 cls: 'popover-title popover-header',
21578 html : this.title === false ? '' : this.title
21581 cls : 'popover-content popover-body ' + (this.cls || ''),
21582 html : this.html || ''
21593 * @param {string} the title
21595 setTitle: function(str)
21599 this.headerEl.dom.innerHTML = str;
21604 * @param {string} the body content
21606 setContent: function(str)
21609 if (this.contentEl) {
21610 this.contentEl.dom.innerHTML = str;
21614 // as it get's added to the bottom of the page.
21615 onRender : function(ct, position)
21617 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21622 var cfg = Roo.apply({}, this.getAutoCreate());
21626 cfg.cls += ' ' + this.cls;
21629 cfg.style = this.style;
21631 //Roo.log("adding to ");
21632 this.el = Roo.get(document.body).createChild(cfg, position);
21633 // Roo.log(this.el);
21636 this.contentEl = this.el.select('.popover-content',true).first();
21637 this.headerEl = this.el.select('.popover-title',true).first();
21640 if(typeof(this.items) != 'undefined'){
21641 var items = this.items;
21644 for(var i =0;i < items.length;i++) {
21645 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21649 this.items = nitems;
21651 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21652 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21659 resizeMask : function()
21661 this.maskEl.setSize(
21662 Roo.lib.Dom.getViewWidth(true),
21663 Roo.lib.Dom.getViewHeight(true)
21667 initEvents : function()
21671 Roo.bootstrap.Popover.register(this);
21674 this.arrowEl = this.el.select('.arrow',true).first();
21675 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21676 this.el.enableDisplayMode('block');
21680 if (this.over === false && !this.parent()) {
21683 if (this.triggers === false) {
21688 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21689 var triggers = this.trigger ? this.trigger.split(' ') : [];
21690 Roo.each(triggers, function(trigger) {
21692 if (trigger == 'click') {
21693 on_el.on('click', this.toggle, this);
21694 } else if (trigger != 'manual') {
21695 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21696 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21698 on_el.on(eventIn ,this.enter, this);
21699 on_el.on(eventOut, this.leave, this);
21709 toggle : function () {
21710 this.hoverState == 'in' ? this.leave() : this.enter();
21713 enter : function () {
21715 clearTimeout(this.timeout);
21717 this.hoverState = 'in';
21719 if (!this.delay || !this.delay.show) {
21724 this.timeout = setTimeout(function () {
21725 if (_t.hoverState == 'in') {
21728 }, this.delay.show)
21731 leave : function() {
21732 clearTimeout(this.timeout);
21734 this.hoverState = 'out';
21736 if (!this.delay || !this.delay.hide) {
21741 this.timeout = setTimeout(function () {
21742 if (_t.hoverState == 'out') {
21745 }, this.delay.hide)
21749 * update the position of the dialog
21750 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21755 doAlign : function()
21758 if (this.alignEl) {
21759 this.updatePosition(this.placement, true);
21762 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21763 var es = this.el.getSize();
21764 var x = Roo.lib.Dom.getViewWidth()/2;
21765 var y = Roo.lib.Dom.getViewHeight()/2;
21766 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21778 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21779 * @param {string} (left|right|top|bottom) position
21781 show : function (on_el, placement)
21783 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21784 on_el = on_el || false; // default to false
21787 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21788 on_el = this.parent().el;
21789 } else if (this.over) {
21790 on_el = Roo.get(this.over);
21795 this.alignEl = Roo.get( on_el );
21798 this.render(document.body);
21804 if (this.title === false) {
21805 this.headerEl.hide();
21810 this.el.dom.style.display = 'block';
21814 //var arrow = this.el.select('.arrow',true).first();
21815 //arrow.set(align[2],
21817 this.el.addClass('in');
21821 this.hoverState = 'in';
21824 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21825 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21826 this.maskEl.dom.style.display = 'block';
21827 this.maskEl.addClass('show');
21829 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21831 this.fireEvent('show', this);
21835 * fire this manually after loading a grid in the table for example
21836 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21837 * @param {Boolean} try and move it if we cant get right position.
21839 updatePosition : function(placement, try_move)
21841 // allow for calling with no parameters
21842 placement = placement ? placement : this.placement;
21843 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21845 this.el.removeClass([
21846 'fade','top','bottom', 'left', 'right','in',
21847 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21849 this.el.addClass(placement + ' bs-popover-' + placement);
21851 if (!this.alignEl ) {
21855 switch (placement) {
21857 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21858 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21859 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21860 //normal display... or moved up/down.
21861 this.el.setXY(offset);
21862 var xy = this.alignEl.getAnchorXY('tr', false);
21864 this.arrowEl.setXY(xy);
21867 // continue through...
21868 return this.updatePosition('left', false);
21872 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21873 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21874 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21875 //normal display... or moved up/down.
21876 this.el.setXY(offset);
21877 var xy = this.alignEl.getAnchorXY('tl', false);
21878 xy[0]-=10;xy[1]+=5; // << fix me
21879 this.arrowEl.setXY(xy);
21883 return this.updatePosition('right', false);
21886 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21887 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21888 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21889 //normal display... or moved up/down.
21890 this.el.setXY(offset);
21891 var xy = this.alignEl.getAnchorXY('t', false);
21892 xy[1]-=10; // << fix me
21893 this.arrowEl.setXY(xy);
21897 return this.updatePosition('bottom', false);
21900 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21901 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21902 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21903 //normal display... or moved up/down.
21904 this.el.setXY(offset);
21905 var xy = this.alignEl.getAnchorXY('b', false);
21906 xy[1]+=2; // << fix me
21907 this.arrowEl.setXY(xy);
21911 return this.updatePosition('top', false);
21922 this.el.setXY([0,0]);
21923 this.el.removeClass('in');
21925 this.hoverState = null;
21926 this.maskEl.hide(); // always..
21927 this.fireEvent('hide', this);
21933 Roo.apply(Roo.bootstrap.Popover, {
21936 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21937 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21938 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21939 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21944 clickHander : false,
21948 onMouseDown : function(e)
21950 if (this.popups.length && !e.getTarget(".roo-popover")) {
21951 /// what is nothing is showing..
21960 register : function(popup)
21962 if (!Roo.bootstrap.Popover.clickHandler) {
21963 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21965 // hide other popups.
21966 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21967 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21968 this.hideAll(); //<< why?
21969 //this.popups.push(popup);
21971 hideAll : function()
21973 this.popups.forEach(function(p) {
21977 onShow : function() {
21978 Roo.bootstrap.Popover.popups.push(this);
21980 onHide : function() {
21981 Roo.bootstrap.Popover.popups.remove(this);
21986 * @class Roo.bootstrap.PopoverNav
21987 * @extends Roo.bootstrap.nav.Simplebar
21988 * @parent Roo.bootstrap.Popover
21989 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
21991 * Bootstrap Popover header navigation class
21992 * FIXME? should this go under nav?
21996 * Create a new Popover Header Navigation
21997 * @param {Object} config The config object
22000 Roo.bootstrap.PopoverNav = function(config){
22001 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22004 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22007 container_method : 'getPopoverHeader'
22025 * @class Roo.bootstrap.Progress
22026 * @extends Roo.bootstrap.Component
22027 * @children Roo.bootstrap.ProgressBar
22028 * Bootstrap Progress class
22029 * @cfg {Boolean} striped striped of the progress bar
22030 * @cfg {Boolean} active animated of the progress bar
22034 * Create a new Progress
22035 * @param {Object} config The config object
22038 Roo.bootstrap.Progress = function(config){
22039 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22042 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22047 getAutoCreate : function(){
22055 cfg.cls += ' progress-striped';
22059 cfg.cls += ' active';
22078 * @class Roo.bootstrap.ProgressBar
22079 * @extends Roo.bootstrap.Component
22080 * Bootstrap ProgressBar class
22081 * @cfg {Number} aria_valuenow aria-value now
22082 * @cfg {Number} aria_valuemin aria-value min
22083 * @cfg {Number} aria_valuemax aria-value max
22084 * @cfg {String} label label for the progress bar
22085 * @cfg {String} panel (success | info | warning | danger )
22086 * @cfg {String} role role of the progress bar
22087 * @cfg {String} sr_only text
22091 * Create a new ProgressBar
22092 * @param {Object} config The config object
22095 Roo.bootstrap.ProgressBar = function(config){
22096 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22099 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22103 aria_valuemax : 100,
22109 getAutoCreate : function()
22114 cls: 'progress-bar',
22115 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22127 cfg.role = this.role;
22130 if(this.aria_valuenow){
22131 cfg['aria-valuenow'] = this.aria_valuenow;
22134 if(this.aria_valuemin){
22135 cfg['aria-valuemin'] = this.aria_valuemin;
22138 if(this.aria_valuemax){
22139 cfg['aria-valuemax'] = this.aria_valuemax;
22142 if(this.label && !this.sr_only){
22143 cfg.html = this.label;
22147 cfg.cls += ' progress-bar-' + this.panel;
22153 update : function(aria_valuenow)
22155 this.aria_valuenow = aria_valuenow;
22157 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22165 * @class Roo.bootstrap.TabGroup
22166 * @extends Roo.bootstrap.Column
22167 * @children Roo.bootstrap.TabPanel
22168 * Bootstrap Column class
22169 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22170 * @cfg {Boolean} carousel true to make the group behave like a carousel
22171 * @cfg {Boolean} bullets show bullets for the panels
22172 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22173 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22174 * @cfg {Boolean} showarrow (true|false) show arrow default true
22177 * Create a new TabGroup
22178 * @param {Object} config The config object
22181 Roo.bootstrap.TabGroup = function(config){
22182 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22184 this.navId = Roo.id();
22187 Roo.bootstrap.TabGroup.register(this);
22191 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22194 transition : false,
22199 slideOnTouch : false,
22202 getAutoCreate : function()
22204 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22206 cfg.cls += ' tab-content';
22208 if (this.carousel) {
22209 cfg.cls += ' carousel slide';
22212 cls : 'carousel-inner',
22216 if(this.bullets && !Roo.isTouch){
22219 cls : 'carousel-bullets',
22223 if(this.bullets_cls){
22224 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22231 cfg.cn[0].cn.push(bullets);
22234 if(this.showarrow){
22235 cfg.cn[0].cn.push({
22237 class : 'carousel-arrow',
22241 class : 'carousel-prev',
22245 class : 'fa fa-chevron-left'
22251 class : 'carousel-next',
22255 class : 'fa fa-chevron-right'
22268 initEvents: function()
22270 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22271 // this.el.on("touchstart", this.onTouchStart, this);
22274 if(this.autoslide){
22277 this.slideFn = window.setInterval(function() {
22278 _this.showPanelNext();
22282 if(this.showarrow){
22283 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22284 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22290 // onTouchStart : function(e, el, o)
22292 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22296 // this.showPanelNext();
22300 getChildContainer : function()
22302 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22306 * register a Navigation item
22307 * @param {Roo.bootstrap.nav.Item} the navitem to add
22309 register : function(item)
22311 this.tabs.push( item);
22312 item.navId = this.navId; // not really needed..
22317 getActivePanel : function()
22320 Roo.each(this.tabs, function(t) {
22330 getPanelByName : function(n)
22333 Roo.each(this.tabs, function(t) {
22334 if (t.tabId == n) {
22342 indexOfPanel : function(p)
22345 Roo.each(this.tabs, function(t,i) {
22346 if (t.tabId == p.tabId) {
22355 * show a specific panel
22356 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22357 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22359 showPanel : function (pan)
22361 if(this.transition || typeof(pan) == 'undefined'){
22362 Roo.log("waiting for the transitionend");
22366 if (typeof(pan) == 'number') {
22367 pan = this.tabs[pan];
22370 if (typeof(pan) == 'string') {
22371 pan = this.getPanelByName(pan);
22374 var cur = this.getActivePanel();
22377 Roo.log('pan or acitve pan is undefined');
22381 if (pan.tabId == this.getActivePanel().tabId) {
22385 if (false === cur.fireEvent('beforedeactivate')) {
22389 if(this.bullets > 0 && !Roo.isTouch){
22390 this.setActiveBullet(this.indexOfPanel(pan));
22393 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22395 //class="carousel-item carousel-item-next carousel-item-left"
22397 this.transition = true;
22398 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22399 var lr = dir == 'next' ? 'left' : 'right';
22400 pan.el.addClass(dir); // or prev
22401 pan.el.addClass('carousel-item-' + dir); // or prev
22402 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22403 cur.el.addClass(lr); // or right
22404 pan.el.addClass(lr);
22405 cur.el.addClass('carousel-item-' +lr); // or right
22406 pan.el.addClass('carousel-item-' +lr);
22410 cur.el.on('transitionend', function() {
22411 Roo.log("trans end?");
22413 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22414 pan.setActive(true);
22416 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22417 cur.setActive(false);
22419 _this.transition = false;
22421 }, this, { single: true } );
22426 cur.setActive(false);
22427 pan.setActive(true);
22432 showPanelNext : function()
22434 var i = this.indexOfPanel(this.getActivePanel());
22436 if (i >= this.tabs.length - 1 && !this.autoslide) {
22440 if (i >= this.tabs.length - 1 && this.autoslide) {
22444 this.showPanel(this.tabs[i+1]);
22447 showPanelPrev : function()
22449 var i = this.indexOfPanel(this.getActivePanel());
22451 if (i < 1 && !this.autoslide) {
22455 if (i < 1 && this.autoslide) {
22456 i = this.tabs.length;
22459 this.showPanel(this.tabs[i-1]);
22463 addBullet: function()
22465 if(!this.bullets || Roo.isTouch){
22468 var ctr = this.el.select('.carousel-bullets',true).first();
22469 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22470 var bullet = ctr.createChild({
22471 cls : 'bullet bullet-' + i
22472 },ctr.dom.lastChild);
22477 bullet.on('click', (function(e, el, o, ii, t){
22479 e.preventDefault();
22481 this.showPanel(ii);
22483 if(this.autoslide && this.slideFn){
22484 clearInterval(this.slideFn);
22485 this.slideFn = window.setInterval(function() {
22486 _this.showPanelNext();
22490 }).createDelegate(this, [i, bullet], true));
22495 setActiveBullet : function(i)
22501 Roo.each(this.el.select('.bullet', true).elements, function(el){
22502 el.removeClass('selected');
22505 var bullet = this.el.select('.bullet-' + i, true).first();
22511 bullet.addClass('selected');
22522 Roo.apply(Roo.bootstrap.TabGroup, {
22526 * register a Navigation Group
22527 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22529 register : function(navgrp)
22531 this.groups[navgrp.navId] = navgrp;
22535 * fetch a Navigation Group based on the navigation ID
22536 * if one does not exist , it will get created.
22537 * @param {string} the navgroup to add
22538 * @returns {Roo.bootstrap.nav.Group} the navgroup
22540 get: function(navId) {
22541 if (typeof(this.groups[navId]) == 'undefined') {
22542 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22544 return this.groups[navId] ;
22559 * @class Roo.bootstrap.TabPanel
22560 * @extends Roo.bootstrap.Component
22561 * @children Roo.bootstrap.Component
22562 * Bootstrap TabPanel class
22563 * @cfg {Boolean} active panel active
22564 * @cfg {String} html panel content
22565 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22566 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22567 * @cfg {String} href click to link..
22568 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22572 * Create a new TabPanel
22573 * @param {Object} config The config object
22576 Roo.bootstrap.TabPanel = function(config){
22577 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22581 * Fires when the active status changes
22582 * @param {Roo.bootstrap.TabPanel} this
22583 * @param {Boolean} state the new state
22588 * @event beforedeactivate
22589 * Fires before a tab is de-activated - can be used to do validation on a form.
22590 * @param {Roo.bootstrap.TabPanel} this
22591 * @return {Boolean} false if there is an error
22594 'beforedeactivate': true
22597 this.tabId = this.tabId || Roo.id();
22601 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22608 touchSlide : false,
22609 getAutoCreate : function(){
22614 // item is needed for carousel - not sure if it has any effect otherwise
22615 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22616 html: this.html || ''
22620 cfg.cls += ' active';
22624 cfg.tabId = this.tabId;
22632 initEvents: function()
22634 var p = this.parent();
22636 this.navId = this.navId || p.navId;
22638 if (typeof(this.navId) != 'undefined') {
22639 // not really needed.. but just in case.. parent should be a NavGroup.
22640 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22644 var i = tg.tabs.length - 1;
22646 if(this.active && tg.bullets > 0 && i < tg.bullets){
22647 tg.setActiveBullet(i);
22651 this.el.on('click', this.onClick, this);
22653 if(Roo.isTouch && this.touchSlide){
22654 this.el.on("touchstart", this.onTouchStart, this);
22655 this.el.on("touchmove", this.onTouchMove, this);
22656 this.el.on("touchend", this.onTouchEnd, this);
22661 onRender : function(ct, position)
22663 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22666 setActive : function(state)
22668 Roo.log("panel - set active " + this.tabId + "=" + state);
22670 this.active = state;
22672 this.el.removeClass('active');
22674 } else if (!this.el.hasClass('active')) {
22675 this.el.addClass('active');
22678 this.fireEvent('changed', this, state);
22681 onClick : function(e)
22683 e.preventDefault();
22685 if(!this.href.length){
22689 window.location.href = this.href;
22698 onTouchStart : function(e)
22700 this.swiping = false;
22702 this.startX = e.browserEvent.touches[0].clientX;
22703 this.startY = e.browserEvent.touches[0].clientY;
22706 onTouchMove : function(e)
22708 this.swiping = true;
22710 this.endX = e.browserEvent.touches[0].clientX;
22711 this.endY = e.browserEvent.touches[0].clientY;
22714 onTouchEnd : function(e)
22721 var tabGroup = this.parent();
22723 if(this.endX > this.startX){ // swiping right
22724 tabGroup.showPanelPrev();
22728 if(this.startX > this.endX){ // swiping left
22729 tabGroup.showPanelNext();
22748 * @class Roo.bootstrap.form.DateField
22749 * @extends Roo.bootstrap.form.Input
22750 * Bootstrap DateField class
22751 * @cfg {Number} weekStart default 0
22752 * @cfg {String} viewMode default empty, (months|years)
22753 * @cfg {String} minViewMode default empty, (months|years)
22754 * @cfg {Number} startDate default -Infinity
22755 * @cfg {Number} endDate default Infinity
22756 * @cfg {Boolean} todayHighlight default false
22757 * @cfg {Boolean} todayBtn default false
22758 * @cfg {Boolean} calendarWeeks default false
22759 * @cfg {Object} daysOfWeekDisabled default empty
22760 * @cfg {Boolean} singleMode default false (true | false)
22762 * @cfg {Boolean} keyboardNavigation default true
22763 * @cfg {String} language default en
22766 * Create a new DateField
22767 * @param {Object} config The config object
22770 Roo.bootstrap.form.DateField = function(config){
22771 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22775 * Fires when this field show.
22776 * @param {Roo.bootstrap.form.DateField} this
22777 * @param {Mixed} date The date value
22782 * Fires when this field hide.
22783 * @param {Roo.bootstrap.form.DateField} this
22784 * @param {Mixed} date The date value
22789 * Fires when select a date.
22790 * @param {Roo.bootstrap.form.DateField} this
22791 * @param {Mixed} date The date value
22795 * @event beforeselect
22796 * Fires when before select a date.
22797 * @param {Roo.bootstrap.form.DateField} this
22798 * @param {Mixed} date The date value
22800 beforeselect : true
22804 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22807 * @cfg {String} format
22808 * The default date format string which can be overriden for localization support. The format must be
22809 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22813 * @cfg {String} altFormats
22814 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22815 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22817 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22825 todayHighlight : false,
22831 keyboardNavigation: true,
22833 calendarWeeks: false,
22835 startDate: -Infinity,
22839 daysOfWeekDisabled: [],
22843 singleMode : false,
22845 UTCDate: function()
22847 return new Date(Date.UTC.apply(Date, arguments));
22850 UTCToday: function()
22852 var today = new Date();
22853 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22856 getDate: function() {
22857 var d = this.getUTCDate();
22858 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22861 getUTCDate: function() {
22865 setDate: function(d) {
22866 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22869 setUTCDate: function(d) {
22871 this.setValue(this.formatDate(this.date));
22874 onRender: function(ct, position)
22877 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22879 this.language = this.language || 'en';
22880 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22881 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22883 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22884 this.format = this.format || 'm/d/y';
22885 this.isInline = false;
22886 this.isInput = true;
22887 this.component = this.el.select('.add-on', true).first() || false;
22888 this.component = (this.component && this.component.length === 0) ? false : this.component;
22889 this.hasInput = this.component && this.inputEl().length;
22891 if (typeof(this.minViewMode === 'string')) {
22892 switch (this.minViewMode) {
22894 this.minViewMode = 1;
22897 this.minViewMode = 2;
22900 this.minViewMode = 0;
22905 if (typeof(this.viewMode === 'string')) {
22906 switch (this.viewMode) {
22919 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22921 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22923 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22925 this.picker().on('mousedown', this.onMousedown, this);
22926 this.picker().on('click', this.onClick, this);
22928 this.picker().addClass('datepicker-dropdown');
22930 this.startViewMode = this.viewMode;
22932 if(this.singleMode){
22933 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22934 v.setVisibilityMode(Roo.Element.DISPLAY);
22938 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22939 v.setStyle('width', '189px');
22943 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22944 if(!this.calendarWeeks){
22949 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22950 v.attr('colspan', function(i, val){
22951 return parseInt(val) + 1;
22956 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22958 this.setStartDate(this.startDate);
22959 this.setEndDate(this.endDate);
22961 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22968 if(this.isInline) {
22973 picker : function()
22975 return this.pickerEl;
22976 // return this.el.select('.datepicker', true).first();
22979 fillDow: function()
22981 var dowCnt = this.weekStart;
22990 if(this.calendarWeeks){
22998 while (dowCnt < this.weekStart + 7) {
23002 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23006 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23009 fillMonths: function()
23012 var months = this.picker().select('>.datepicker-months td', true).first();
23014 months.dom.innerHTML = '';
23020 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23023 months.createChild(month);
23030 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;
23032 if (this.date < this.startDate) {
23033 this.viewDate = new Date(this.startDate);
23034 } else if (this.date > this.endDate) {
23035 this.viewDate = new Date(this.endDate);
23037 this.viewDate = new Date(this.date);
23045 var d = new Date(this.viewDate),
23046 year = d.getUTCFullYear(),
23047 month = d.getUTCMonth(),
23048 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23049 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23050 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23051 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23052 currentDate = this.date && this.date.valueOf(),
23053 today = this.UTCToday();
23055 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23057 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23059 // this.picker.select('>tfoot th.today').
23060 // .text(dates[this.language].today)
23061 // .toggle(this.todayBtn !== false);
23063 this.updateNavArrows();
23066 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23068 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23070 prevMonth.setUTCDate(day);
23072 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23074 var nextMonth = new Date(prevMonth);
23076 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23078 nextMonth = nextMonth.valueOf();
23080 var fillMonths = false;
23082 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23084 while(prevMonth.valueOf() <= nextMonth) {
23087 if (prevMonth.getUTCDay() === this.weekStart) {
23089 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23097 if(this.calendarWeeks){
23098 // ISO 8601: First week contains first thursday.
23099 // ISO also states week starts on Monday, but we can be more abstract here.
23101 // Start of current week: based on weekstart/current date
23102 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23103 // Thursday of this week
23104 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23105 // First Thursday of year, year from thursday
23106 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23107 // Calendar week: ms between thursdays, div ms per day, div 7 days
23108 calWeek = (th - yth) / 864e5 / 7 + 1;
23110 fillMonths.cn.push({
23118 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23120 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23123 if (this.todayHighlight &&
23124 prevMonth.getUTCFullYear() == today.getFullYear() &&
23125 prevMonth.getUTCMonth() == today.getMonth() &&
23126 prevMonth.getUTCDate() == today.getDate()) {
23127 clsName += ' today';
23130 if (currentDate && prevMonth.valueOf() === currentDate) {
23131 clsName += ' active';
23134 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23135 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23136 clsName += ' disabled';
23139 fillMonths.cn.push({
23141 cls: 'day ' + clsName,
23142 html: prevMonth.getDate()
23145 prevMonth.setDate(prevMonth.getDate()+1);
23148 var currentYear = this.date && this.date.getUTCFullYear();
23149 var currentMonth = this.date && this.date.getUTCMonth();
23151 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23153 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23154 v.removeClass('active');
23156 if(currentYear === year && k === currentMonth){
23157 v.addClass('active');
23160 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23161 v.addClass('disabled');
23167 year = parseInt(year/10, 10) * 10;
23169 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23171 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23174 for (var i = -1; i < 11; i++) {
23175 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23177 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23185 showMode: function(dir)
23188 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23191 Roo.each(this.picker().select('>div',true).elements, function(v){
23192 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23195 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23200 if(this.isInline) {
23204 this.picker().removeClass(['bottom', 'top']);
23206 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23208 * place to the top of element!
23212 this.picker().addClass('top');
23213 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23218 this.picker().addClass('bottom');
23220 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23223 parseDate : function(value)
23225 if(!value || value instanceof Date){
23228 var v = Date.parseDate(value, this.format);
23229 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23230 v = Date.parseDate(value, 'Y-m-d');
23232 if(!v && this.altFormats){
23233 if(!this.altFormatsArray){
23234 this.altFormatsArray = this.altFormats.split("|");
23236 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23237 v = Date.parseDate(value, this.altFormatsArray[i]);
23243 formatDate : function(date, fmt)
23245 return (!date || !(date instanceof Date)) ?
23246 date : date.dateFormat(fmt || this.format);
23249 onFocus : function()
23251 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23255 onBlur : function()
23257 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23259 var d = this.inputEl().getValue();
23266 showPopup : function()
23268 this.picker().show();
23272 this.fireEvent('showpopup', this, this.date);
23275 hidePopup : function()
23277 if(this.isInline) {
23280 this.picker().hide();
23281 this.viewMode = this.startViewMode;
23284 this.fireEvent('hidepopup', this, this.date);
23288 onMousedown: function(e)
23290 e.stopPropagation();
23291 e.preventDefault();
23296 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23300 setValue: function(v)
23302 if(this.fireEvent('beforeselect', this, v) !== false){
23303 var d = new Date(this.parseDate(v) ).clearTime();
23305 if(isNaN(d.getTime())){
23306 this.date = this.viewDate = '';
23307 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23311 v = this.formatDate(d);
23313 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23315 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23319 this.fireEvent('select', this, this.date);
23323 getValue: function()
23325 return this.formatDate(this.date);
23328 fireKey: function(e)
23330 if (!this.picker().isVisible()){
23331 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23337 var dateChanged = false,
23339 newDate, newViewDate;
23344 e.preventDefault();
23348 if (!this.keyboardNavigation) {
23351 dir = e.keyCode == 37 ? -1 : 1;
23354 newDate = this.moveYear(this.date, dir);
23355 newViewDate = this.moveYear(this.viewDate, dir);
23356 } else if (e.shiftKey){
23357 newDate = this.moveMonth(this.date, dir);
23358 newViewDate = this.moveMonth(this.viewDate, dir);
23360 newDate = new Date(this.date);
23361 newDate.setUTCDate(this.date.getUTCDate() + dir);
23362 newViewDate = new Date(this.viewDate);
23363 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23365 if (this.dateWithinRange(newDate)){
23366 this.date = newDate;
23367 this.viewDate = newViewDate;
23368 this.setValue(this.formatDate(this.date));
23370 e.preventDefault();
23371 dateChanged = true;
23376 if (!this.keyboardNavigation) {
23379 dir = e.keyCode == 38 ? -1 : 1;
23381 newDate = this.moveYear(this.date, dir);
23382 newViewDate = this.moveYear(this.viewDate, dir);
23383 } else if (e.shiftKey){
23384 newDate = this.moveMonth(this.date, dir);
23385 newViewDate = this.moveMonth(this.viewDate, dir);
23387 newDate = new Date(this.date);
23388 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23389 newViewDate = new Date(this.viewDate);
23390 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23392 if (this.dateWithinRange(newDate)){
23393 this.date = newDate;
23394 this.viewDate = newViewDate;
23395 this.setValue(this.formatDate(this.date));
23397 e.preventDefault();
23398 dateChanged = true;
23402 this.setValue(this.formatDate(this.date));
23404 e.preventDefault();
23407 this.setValue(this.formatDate(this.date));
23421 onClick: function(e)
23423 e.stopPropagation();
23424 e.preventDefault();
23426 var target = e.getTarget();
23428 if(target.nodeName.toLowerCase() === 'i'){
23429 target = Roo.get(target).dom.parentNode;
23432 var nodeName = target.nodeName;
23433 var className = target.className;
23434 var html = target.innerHTML;
23435 //Roo.log(nodeName);
23437 switch(nodeName.toLowerCase()) {
23439 switch(className) {
23445 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23446 switch(this.viewMode){
23448 this.viewDate = this.moveMonth(this.viewDate, dir);
23452 this.viewDate = this.moveYear(this.viewDate, dir);
23458 var date = new Date();
23459 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23461 this.setValue(this.formatDate(this.date));
23468 if (className.indexOf('disabled') < 0) {
23469 if (!this.viewDate) {
23470 this.viewDate = new Date();
23472 this.viewDate.setUTCDate(1);
23473 if (className.indexOf('month') > -1) {
23474 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23476 var year = parseInt(html, 10) || 0;
23477 this.viewDate.setUTCFullYear(year);
23481 if(this.singleMode){
23482 this.setValue(this.formatDate(this.viewDate));
23493 //Roo.log(className);
23494 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23495 var day = parseInt(html, 10) || 1;
23496 var year = (this.viewDate || new Date()).getUTCFullYear(),
23497 month = (this.viewDate || new Date()).getUTCMonth();
23499 if (className.indexOf('old') > -1) {
23506 } else if (className.indexOf('new') > -1) {
23514 //Roo.log([year,month,day]);
23515 this.date = this.UTCDate(year, month, day,0,0,0,0);
23516 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23518 //Roo.log(this.formatDate(this.date));
23519 this.setValue(this.formatDate(this.date));
23526 setStartDate: function(startDate)
23528 this.startDate = startDate || -Infinity;
23529 if (this.startDate !== -Infinity) {
23530 this.startDate = this.parseDate(this.startDate);
23533 this.updateNavArrows();
23536 setEndDate: function(endDate)
23538 this.endDate = endDate || Infinity;
23539 if (this.endDate !== Infinity) {
23540 this.endDate = this.parseDate(this.endDate);
23543 this.updateNavArrows();
23546 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23548 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23549 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23550 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23552 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23553 return parseInt(d, 10);
23556 this.updateNavArrows();
23559 updateNavArrows: function()
23561 if(this.singleMode){
23565 var d = new Date(this.viewDate),
23566 year = d.getUTCFullYear(),
23567 month = d.getUTCMonth();
23569 Roo.each(this.picker().select('.prev', true).elements, function(v){
23571 switch (this.viewMode) {
23574 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23580 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23587 Roo.each(this.picker().select('.next', true).elements, function(v){
23589 switch (this.viewMode) {
23592 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23598 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23606 moveMonth: function(date, dir)
23611 var new_date = new Date(date.valueOf()),
23612 day = new_date.getUTCDate(),
23613 month = new_date.getUTCMonth(),
23614 mag = Math.abs(dir),
23616 dir = dir > 0 ? 1 : -1;
23619 // If going back one month, make sure month is not current month
23620 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23622 return new_date.getUTCMonth() == month;
23624 // If going forward one month, make sure month is as expected
23625 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23627 return new_date.getUTCMonth() != new_month;
23629 new_month = month + dir;
23630 new_date.setUTCMonth(new_month);
23631 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23632 if (new_month < 0 || new_month > 11) {
23633 new_month = (new_month + 12) % 12;
23636 // For magnitudes >1, move one month at a time...
23637 for (var i=0; i<mag; i++) {
23638 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23639 new_date = this.moveMonth(new_date, dir);
23641 // ...then reset the day, keeping it in the new month
23642 new_month = new_date.getUTCMonth();
23643 new_date.setUTCDate(day);
23645 return new_month != new_date.getUTCMonth();
23648 // Common date-resetting loop -- if date is beyond end of month, make it
23651 new_date.setUTCDate(--day);
23652 new_date.setUTCMonth(new_month);
23657 moveYear: function(date, dir)
23659 return this.moveMonth(date, dir*12);
23662 dateWithinRange: function(date)
23664 return date >= this.startDate && date <= this.endDate;
23670 this.picker().remove();
23673 validateValue : function(value)
23675 if(this.getVisibilityEl().hasClass('hidden')){
23679 if(value.length < 1) {
23680 if(this.allowBlank){
23686 if(value.length < this.minLength){
23689 if(value.length > this.maxLength){
23693 var vt = Roo.form.VTypes;
23694 if(!vt[this.vtype](value, this)){
23698 if(typeof this.validator == "function"){
23699 var msg = this.validator(value);
23705 if(this.regex && !this.regex.test(value)){
23709 if(typeof(this.parseDate(value)) == 'undefined'){
23713 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23717 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23727 this.date = this.viewDate = '';
23729 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23734 Roo.apply(Roo.bootstrap.form.DateField, {
23745 html: '<i class="fa fa-arrow-left"/>'
23755 html: '<i class="fa fa-arrow-right"/>'
23797 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23798 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23799 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23800 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23801 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23814 navFnc: 'FullYear',
23819 navFnc: 'FullYear',
23824 Roo.apply(Roo.bootstrap.form.DateField, {
23828 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23832 cls: 'datepicker-days',
23836 cls: 'table-condensed',
23838 Roo.bootstrap.form.DateField.head,
23842 Roo.bootstrap.form.DateField.footer
23849 cls: 'datepicker-months',
23853 cls: 'table-condensed',
23855 Roo.bootstrap.form.DateField.head,
23856 Roo.bootstrap.form.DateField.content,
23857 Roo.bootstrap.form.DateField.footer
23864 cls: 'datepicker-years',
23868 cls: 'table-condensed',
23870 Roo.bootstrap.form.DateField.head,
23871 Roo.bootstrap.form.DateField.content,
23872 Roo.bootstrap.form.DateField.footer
23891 * @class Roo.bootstrap.form.TimeField
23892 * @extends Roo.bootstrap.form.Input
23893 * Bootstrap DateField class
23897 * Create a new TimeField
23898 * @param {Object} config The config object
23901 Roo.bootstrap.form.TimeField = function(config){
23902 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23906 * Fires when this field show.
23907 * @param {Roo.bootstrap.form.DateField} thisthis
23908 * @param {Mixed} date The date value
23913 * Fires when this field hide.
23914 * @param {Roo.bootstrap.form.DateField} this
23915 * @param {Mixed} date The date value
23920 * Fires when select a date.
23921 * @param {Roo.bootstrap.form.DateField} this
23922 * @param {Mixed} date The date value
23928 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23931 * @cfg {String} format
23932 * The default time format string which can be overriden for localization support. The format must be
23933 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23937 getAutoCreate : function()
23939 this.after = '<i class="fa far fa-clock"></i>';
23940 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23944 onRender: function(ct, position)
23947 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23949 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23951 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23953 this.pop = this.picker().select('>.datepicker-time',true).first();
23954 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23956 this.picker().on('mousedown', this.onMousedown, this);
23957 this.picker().on('click', this.onClick, this);
23959 this.picker().addClass('datepicker-dropdown');
23964 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23965 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23966 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23967 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23968 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23969 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23973 fireKey: function(e){
23974 if (!this.picker().isVisible()){
23975 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23981 e.preventDefault();
23989 this.onTogglePeriod();
23992 this.onIncrementMinutes();
23995 this.onDecrementMinutes();
24004 onClick: function(e) {
24005 e.stopPropagation();
24006 e.preventDefault();
24009 picker : function()
24011 return this.pickerEl;
24014 fillTime: function()
24016 var time = this.pop.select('tbody', true).first();
24018 time.dom.innerHTML = '';
24033 cls: 'hours-up fa fas fa-chevron-up'
24053 cls: 'minutes-up fa fas fa-chevron-up'
24074 cls: 'timepicker-hour',
24089 cls: 'timepicker-minute',
24104 cls: 'btn btn-primary period',
24126 cls: 'hours-down fa fas fa-chevron-down'
24146 cls: 'minutes-down fa fas fa-chevron-down'
24164 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24171 var hours = this.time.getHours();
24172 var minutes = this.time.getMinutes();
24185 hours = hours - 12;
24189 hours = '0' + hours;
24193 minutes = '0' + minutes;
24196 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24197 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24198 this.pop.select('button', true).first().dom.innerHTML = period;
24204 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24206 var cls = ['bottom'];
24208 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24215 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24219 //this.picker().setXY(20000,20000);
24220 this.picker().addClass(cls.join('-'));
24224 Roo.each(cls, function(c){
24229 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24230 //_this.picker().setTop(_this.inputEl().getHeight());
24234 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24236 //_this.picker().setTop(0 - _this.picker().getHeight());
24241 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24245 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24253 onFocus : function()
24255 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24259 onBlur : function()
24261 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24267 this.picker().show();
24272 this.fireEvent('show', this, this.date);
24277 this.picker().hide();
24280 this.fireEvent('hide', this, this.date);
24283 setTime : function()
24286 this.setValue(this.time.format(this.format));
24288 this.fireEvent('select', this, this.date);
24293 onMousedown: function(e){
24294 e.stopPropagation();
24295 e.preventDefault();
24298 onIncrementHours: function()
24300 Roo.log('onIncrementHours');
24301 this.time = this.time.add(Date.HOUR, 1);
24306 onDecrementHours: function()
24308 Roo.log('onDecrementHours');
24309 this.time = this.time.add(Date.HOUR, -1);
24313 onIncrementMinutes: function()
24315 Roo.log('onIncrementMinutes');
24316 this.time = this.time.add(Date.MINUTE, 1);
24320 onDecrementMinutes: function()
24322 Roo.log('onDecrementMinutes');
24323 this.time = this.time.add(Date.MINUTE, -1);
24327 onTogglePeriod: function()
24329 Roo.log('onTogglePeriod');
24330 this.time = this.time.add(Date.HOUR, 12);
24338 Roo.apply(Roo.bootstrap.form.TimeField, {
24342 cls: 'datepicker dropdown-menu',
24346 cls: 'datepicker-time',
24350 cls: 'table-condensed',
24379 cls: 'btn btn-info ok',
24407 * @class Roo.bootstrap.form.MonthField
24408 * @extends Roo.bootstrap.form.Input
24409 * Bootstrap MonthField class
24411 * @cfg {String} language default en
24414 * Create a new MonthField
24415 * @param {Object} config The config object
24418 Roo.bootstrap.form.MonthField = function(config){
24419 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24424 * Fires when this field show.
24425 * @param {Roo.bootstrap.form.MonthField} this
24426 * @param {Mixed} date The date value
24431 * Fires when this field hide.
24432 * @param {Roo.bootstrap.form.MonthField} this
24433 * @param {Mixed} date The date value
24438 * Fires when select a date.
24439 * @param {Roo.bootstrap.form.MonthField} this
24440 * @param {String} oldvalue The old value
24441 * @param {String} newvalue The new value
24447 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24449 onRender: function(ct, position)
24452 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24454 this.language = this.language || 'en';
24455 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24456 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24458 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24459 this.isInline = false;
24460 this.isInput = true;
24461 this.component = this.el.select('.add-on', true).first() || false;
24462 this.component = (this.component && this.component.length === 0) ? false : this.component;
24463 this.hasInput = this.component && this.inputEL().length;
24465 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24467 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24469 this.picker().on('mousedown', this.onMousedown, this);
24470 this.picker().on('click', this.onClick, this);
24472 this.picker().addClass('datepicker-dropdown');
24474 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24475 v.setStyle('width', '189px');
24482 if(this.isInline) {
24488 setValue: function(v, suppressEvent)
24490 var o = this.getValue();
24492 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24496 if(suppressEvent !== true){
24497 this.fireEvent('select', this, o, v);
24502 getValue: function()
24507 onClick: function(e)
24509 e.stopPropagation();
24510 e.preventDefault();
24512 var target = e.getTarget();
24514 if(target.nodeName.toLowerCase() === 'i'){
24515 target = Roo.get(target).dom.parentNode;
24518 var nodeName = target.nodeName;
24519 var className = target.className;
24520 var html = target.innerHTML;
24522 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24526 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24528 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24534 picker : function()
24536 return this.pickerEl;
24539 fillMonths: function()
24542 var months = this.picker().select('>.datepicker-months td', true).first();
24544 months.dom.innerHTML = '';
24550 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24553 months.createChild(month);
24562 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24563 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24566 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24567 e.removeClass('active');
24569 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24570 e.addClass('active');
24577 if(this.isInline) {
24581 this.picker().removeClass(['bottom', 'top']);
24583 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24585 * place to the top of element!
24589 this.picker().addClass('top');
24590 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24595 this.picker().addClass('bottom');
24597 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24600 onFocus : function()
24602 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24606 onBlur : function()
24608 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24610 var d = this.inputEl().getValue();
24619 this.picker().show();
24620 this.picker().select('>.datepicker-months', true).first().show();
24624 this.fireEvent('show', this, this.date);
24629 if(this.isInline) {
24632 this.picker().hide();
24633 this.fireEvent('hide', this, this.date);
24637 onMousedown: function(e)
24639 e.stopPropagation();
24640 e.preventDefault();
24645 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24649 fireKey: function(e)
24651 if (!this.picker().isVisible()){
24652 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24663 e.preventDefault();
24667 dir = e.keyCode == 37 ? -1 : 1;
24669 this.vIndex = this.vIndex + dir;
24671 if(this.vIndex < 0){
24675 if(this.vIndex > 11){
24679 if(isNaN(this.vIndex)){
24683 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24689 dir = e.keyCode == 38 ? -1 : 1;
24691 this.vIndex = this.vIndex + dir * 4;
24693 if(this.vIndex < 0){
24697 if(this.vIndex > 11){
24701 if(isNaN(this.vIndex)){
24705 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24710 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24711 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24715 e.preventDefault();
24718 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24719 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24735 this.picker().remove();
24740 Roo.apply(Roo.bootstrap.form.MonthField, {
24759 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24760 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24765 Roo.apply(Roo.bootstrap.form.MonthField, {
24769 cls: 'datepicker dropdown-menu roo-dynamic',
24773 cls: 'datepicker-months',
24777 cls: 'table-condensed',
24779 Roo.bootstrap.form.DateField.content
24799 * @class Roo.bootstrap.form.CheckBox
24800 * @extends Roo.bootstrap.form.Input
24801 * Bootstrap CheckBox class
24803 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24804 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24805 * @cfg {String} boxLabel The text that appears beside the checkbox
24806 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24807 * @cfg {Boolean} checked initnal the element
24808 * @cfg {Boolean} inline inline the element (default false)
24809 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24810 * @cfg {String} tooltip label tooltip
24813 * Create a new CheckBox
24814 * @param {Object} config The config object
24817 Roo.bootstrap.form.CheckBox = function(config){
24818 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24823 * Fires when the element is checked or unchecked.
24824 * @param {Roo.bootstrap.form.CheckBox} this This input
24825 * @param {Boolean} checked The new checked value
24830 * Fires when the element is click.
24831 * @param {Roo.bootstrap.form.CheckBox} this This input
24838 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24840 inputType: 'checkbox',
24849 // checkbox success does not make any sense really..
24854 getAutoCreate : function()
24856 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24862 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24865 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24871 type : this.inputType,
24872 value : this.inputValue,
24873 cls : 'roo-' + this.inputType, //'form-box',
24874 placeholder : this.placeholder || ''
24878 if(this.inputType != 'radio'){
24882 cls : 'roo-hidden-value',
24883 value : this.checked ? this.inputValue : this.valueOff
24888 if (this.weight) { // Validity check?
24889 cfg.cls += " " + this.inputType + "-" + this.weight;
24892 if (this.disabled) {
24893 input.disabled=true;
24897 input.checked = this.checked;
24902 input.name = this.name;
24904 if(this.inputType != 'radio'){
24905 hidden.name = this.name;
24906 input.name = '_hidden_' + this.name;
24911 input.cls += ' input-' + this.size;
24916 ['xs','sm','md','lg'].map(function(size){
24917 if (settings[size]) {
24918 cfg.cls += ' col-' + size + '-' + settings[size];
24922 var inputblock = input;
24924 if (this.before || this.after) {
24927 cls : 'input-group',
24932 inputblock.cn.push({
24934 cls : 'input-group-addon',
24939 inputblock.cn.push(input);
24941 if(this.inputType != 'radio'){
24942 inputblock.cn.push(hidden);
24946 inputblock.cn.push({
24948 cls : 'input-group-addon',
24954 var boxLabelCfg = false;
24960 //'for': id, // box label is handled by onclick - so no for...
24962 html: this.boxLabel
24965 boxLabelCfg.tooltip = this.tooltip;
24971 if (align ==='left' && this.fieldLabel.length) {
24972 // Roo.log("left and has label");
24977 cls : 'control-label',
24978 html : this.fieldLabel
24989 cfg.cn[1].cn.push(boxLabelCfg);
24992 if(this.labelWidth > 12){
24993 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24996 if(this.labelWidth < 13 && this.labelmd == 0){
24997 this.labelmd = this.labelWidth;
25000 if(this.labellg > 0){
25001 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25002 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25005 if(this.labelmd > 0){
25006 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25007 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25010 if(this.labelsm > 0){
25011 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25012 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25015 if(this.labelxs > 0){
25016 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25017 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25020 } else if ( this.fieldLabel.length) {
25021 // Roo.log(" label");
25025 tag: this.boxLabel ? 'span' : 'label',
25027 cls: 'control-label box-input-label',
25028 //cls : 'input-group-addon',
25029 html : this.fieldLabel
25036 cfg.cn.push(boxLabelCfg);
25041 // Roo.log(" no label && no align");
25042 cfg.cn = [ inputblock ] ;
25044 cfg.cn.push(boxLabelCfg);
25052 if(this.inputType != 'radio'){
25053 cfg.cn.push(hidden);
25061 * return the real input element.
25063 inputEl: function ()
25065 return this.el.select('input.roo-' + this.inputType,true).first();
25067 hiddenEl: function ()
25069 return this.el.select('input.roo-hidden-value',true).first();
25072 labelEl: function()
25074 return this.el.select('label.control-label',true).first();
25076 /* depricated... */
25080 return this.labelEl();
25083 boxLabelEl: function()
25085 return this.el.select('label.box-label',true).first();
25088 initEvents : function()
25090 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25092 this.inputEl().on('click', this.onClick, this);
25094 if (this.boxLabel) {
25095 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25098 this.startValue = this.getValue();
25101 Roo.bootstrap.form.CheckBox.register(this);
25105 onClick : function(e)
25107 if(this.fireEvent('click', this, e) !== false){
25108 this.setChecked(!this.checked);
25113 setChecked : function(state,suppressEvent)
25115 this.startValue = this.getValue();
25117 if(this.inputType == 'radio'){
25119 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25120 e.dom.checked = false;
25123 this.inputEl().dom.checked = true;
25125 this.inputEl().dom.value = this.inputValue;
25127 if(suppressEvent !== true){
25128 this.fireEvent('check', this, true);
25136 this.checked = state;
25138 this.inputEl().dom.checked = state;
25141 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25143 if(suppressEvent !== true){
25144 this.fireEvent('check', this, state);
25150 getValue : function()
25152 if(this.inputType == 'radio'){
25153 return this.getGroupValue();
25156 return this.hiddenEl().dom.value;
25160 getGroupValue : function()
25162 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25166 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25169 setValue : function(v,suppressEvent)
25171 if(this.inputType == 'radio'){
25172 this.setGroupValue(v, suppressEvent);
25176 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25181 setGroupValue : function(v, suppressEvent)
25183 this.startValue = this.getValue();
25185 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25186 e.dom.checked = false;
25188 if(e.dom.value == v){
25189 e.dom.checked = true;
25193 if(suppressEvent !== true){
25194 this.fireEvent('check', this, true);
25202 validate : function()
25204 if(this.getVisibilityEl().hasClass('hidden')){
25210 (this.inputType == 'radio' && this.validateRadio()) ||
25211 (this.inputType == 'checkbox' && this.validateCheckbox())
25217 this.markInvalid();
25221 validateRadio : function()
25223 if(this.getVisibilityEl().hasClass('hidden')){
25227 if(this.allowBlank){
25233 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25234 if(!e.dom.checked){
25246 validateCheckbox : function()
25249 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25250 //return (this.getValue() == this.inputValue) ? true : false;
25253 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25261 for(var i in group){
25262 if(group[i].el.isVisible(true)){
25270 for(var i in group){
25275 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25282 * Mark this field as valid
25284 markValid : function()
25288 this.fireEvent('valid', this);
25290 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25293 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25300 if(this.inputType == 'radio'){
25301 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25302 var fg = e.findParent('.form-group', false, true);
25303 if (Roo.bootstrap.version == 3) {
25304 fg.removeClass([_this.invalidClass, _this.validClass]);
25305 fg.addClass(_this.validClass);
25307 fg.removeClass(['is-valid', 'is-invalid']);
25308 fg.addClass('is-valid');
25316 var fg = this.el.findParent('.form-group', false, true);
25317 if (Roo.bootstrap.version == 3) {
25318 fg.removeClass([this.invalidClass, this.validClass]);
25319 fg.addClass(this.validClass);
25321 fg.removeClass(['is-valid', 'is-invalid']);
25322 fg.addClass('is-valid');
25327 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25333 for(var i in group){
25334 var fg = group[i].el.findParent('.form-group', false, true);
25335 if (Roo.bootstrap.version == 3) {
25336 fg.removeClass([this.invalidClass, this.validClass]);
25337 fg.addClass(this.validClass);
25339 fg.removeClass(['is-valid', 'is-invalid']);
25340 fg.addClass('is-valid');
25346 * Mark this field as invalid
25347 * @param {String} msg The validation message
25349 markInvalid : function(msg)
25351 if(this.allowBlank){
25357 this.fireEvent('invalid', this, msg);
25359 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25362 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25366 label.markInvalid();
25369 if(this.inputType == 'radio'){
25371 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25372 var fg = e.findParent('.form-group', false, true);
25373 if (Roo.bootstrap.version == 3) {
25374 fg.removeClass([_this.invalidClass, _this.validClass]);
25375 fg.addClass(_this.invalidClass);
25377 fg.removeClass(['is-invalid', 'is-valid']);
25378 fg.addClass('is-invalid');
25386 var fg = this.el.findParent('.form-group', false, true);
25387 if (Roo.bootstrap.version == 3) {
25388 fg.removeClass([_this.invalidClass, _this.validClass]);
25389 fg.addClass(_this.invalidClass);
25391 fg.removeClass(['is-invalid', 'is-valid']);
25392 fg.addClass('is-invalid');
25397 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25403 for(var i in group){
25404 var fg = group[i].el.findParent('.form-group', false, true);
25405 if (Roo.bootstrap.version == 3) {
25406 fg.removeClass([_this.invalidClass, _this.validClass]);
25407 fg.addClass(_this.invalidClass);
25409 fg.removeClass(['is-invalid', 'is-valid']);
25410 fg.addClass('is-invalid');
25416 clearInvalid : function()
25418 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25420 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25422 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25424 if (label && label.iconEl) {
25425 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25426 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25430 disable : function()
25432 if(this.inputType != 'radio'){
25433 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25440 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25441 _this.getActionEl().addClass(this.disabledClass);
25442 e.dom.disabled = true;
25446 this.disabled = true;
25447 this.fireEvent("disable", this);
25451 enable : function()
25453 if(this.inputType != 'radio'){
25454 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25461 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25462 _this.getActionEl().removeClass(this.disabledClass);
25463 e.dom.disabled = false;
25467 this.disabled = false;
25468 this.fireEvent("enable", this);
25472 setBoxLabel : function(v)
25477 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25483 Roo.apply(Roo.bootstrap.form.CheckBox, {
25488 * register a CheckBox Group
25489 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25491 register : function(checkbox)
25493 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25494 this.groups[checkbox.groupId] = {};
25497 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25501 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25505 * fetch a CheckBox Group based on the group ID
25506 * @param {string} the group ID
25507 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25509 get: function(groupId) {
25510 if (typeof(this.groups[groupId]) == 'undefined') {
25514 return this.groups[groupId] ;
25527 * @class Roo.bootstrap.form.Radio
25528 * @extends Roo.bootstrap.Component
25529 * Bootstrap Radio class
25530 * @cfg {String} boxLabel - the label associated
25531 * @cfg {String} value - the value of radio
25534 * Create a new Radio
25535 * @param {Object} config The config object
25537 Roo.bootstrap.form.Radio = function(config){
25538 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25542 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25548 getAutoCreate : function()
25552 cls : 'form-group radio',
25557 html : this.boxLabel
25565 initEvents : function()
25567 this.parent().register(this);
25569 this.el.on('click', this.onClick, this);
25573 onClick : function(e)
25575 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25576 this.setChecked(true);
25580 setChecked : function(state, suppressEvent)
25582 this.parent().setValue(this.value, suppressEvent);
25586 setBoxLabel : function(v)
25591 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25606 * @class Roo.bootstrap.form.SecurePass
25607 * @extends Roo.bootstrap.form.Input
25608 * Bootstrap SecurePass class
25612 * Create a new SecurePass
25613 * @param {Object} config The config object
25616 Roo.bootstrap.form.SecurePass = function (config) {
25617 // these go here, so the translation tool can replace them..
25619 PwdEmpty: "Please type a password, and then retype it to confirm.",
25620 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25621 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25622 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25623 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25624 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25625 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25626 TooWeak: "Your password is Too Weak."
25628 this.meterLabel = "Password strength:";
25629 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25630 this.meterClass = [
25631 "roo-password-meter-tooweak",
25632 "roo-password-meter-weak",
25633 "roo-password-meter-medium",
25634 "roo-password-meter-strong",
25635 "roo-password-meter-grey"
25640 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25643 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25645 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25647 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25648 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25649 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25650 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25651 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25652 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25653 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25663 * @cfg {String/Object} Label for the strength meter (defaults to
25664 * 'Password strength:')
25669 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25670 * ['Weak', 'Medium', 'Strong'])
25673 pwdStrengths: false,
25686 initEvents: function ()
25688 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25690 if (this.el.is('input[type=password]') && Roo.isSafari) {
25691 this.el.on('keydown', this.SafariOnKeyDown, this);
25694 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25697 onRender: function (ct, position)
25699 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25700 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25701 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25703 this.trigger.createChild({
25708 cls: 'roo-password-meter-grey col-xs-12',
25711 //width: this.meterWidth + 'px'
25715 cls: 'roo-password-meter-text'
25721 if (this.hideTrigger) {
25722 this.trigger.setDisplayed(false);
25724 this.setSize(this.width || '', this.height || '');
25727 onDestroy: function ()
25729 if (this.trigger) {
25730 this.trigger.removeAllListeners();
25731 this.trigger.remove();
25734 this.wrap.remove();
25736 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25739 checkStrength: function ()
25741 var pwd = this.inputEl().getValue();
25742 if (pwd == this._lastPwd) {
25747 if (this.ClientSideStrongPassword(pwd)) {
25749 } else if (this.ClientSideMediumPassword(pwd)) {
25751 } else if (this.ClientSideWeakPassword(pwd)) {
25757 Roo.log('strength1: ' + strength);
25759 //var pm = this.trigger.child('div/div/div').dom;
25760 var pm = this.trigger.child('div/div');
25761 pm.removeClass(this.meterClass);
25762 pm.addClass(this.meterClass[strength]);
25765 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25767 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25769 this._lastPwd = pwd;
25773 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25775 this._lastPwd = '';
25777 var pm = this.trigger.child('div/div');
25778 pm.removeClass(this.meterClass);
25779 pm.addClass('roo-password-meter-grey');
25782 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25785 this.inputEl().dom.type='password';
25788 validateValue: function (value)
25790 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25793 if (value.length == 0) {
25794 if (this.allowBlank) {
25795 this.clearInvalid();
25799 this.markInvalid(this.errors.PwdEmpty);
25800 this.errorMsg = this.errors.PwdEmpty;
25808 if (!value.match(/[\x21-\x7e]+/)) {
25809 this.markInvalid(this.errors.PwdBadChar);
25810 this.errorMsg = this.errors.PwdBadChar;
25813 if (value.length < 6) {
25814 this.markInvalid(this.errors.PwdShort);
25815 this.errorMsg = this.errors.PwdShort;
25818 if (value.length > 16) {
25819 this.markInvalid(this.errors.PwdLong);
25820 this.errorMsg = this.errors.PwdLong;
25824 if (this.ClientSideStrongPassword(value)) {
25826 } else if (this.ClientSideMediumPassword(value)) {
25828 } else if (this.ClientSideWeakPassword(value)) {
25835 if (strength < 2) {
25836 //this.markInvalid(this.errors.TooWeak);
25837 this.errorMsg = this.errors.TooWeak;
25842 console.log('strength2: ' + strength);
25844 //var pm = this.trigger.child('div/div/div').dom;
25846 var pm = this.trigger.child('div/div');
25847 pm.removeClass(this.meterClass);
25848 pm.addClass(this.meterClass[strength]);
25850 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25852 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25854 this.errorMsg = '';
25858 CharacterSetChecks: function (type)
25861 this.fResult = false;
25864 isctype: function (character, type)
25867 case this.kCapitalLetter:
25868 if (character >= 'A' && character <= 'Z') {
25873 case this.kSmallLetter:
25874 if (character >= 'a' && character <= 'z') {
25880 if (character >= '0' && character <= '9') {
25885 case this.kPunctuation:
25886 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25897 IsLongEnough: function (pwd, size)
25899 return !(pwd == null || isNaN(size) || pwd.length < size);
25902 SpansEnoughCharacterSets: function (word, nb)
25904 if (!this.IsLongEnough(word, nb))
25909 var characterSetChecks = new Array(
25910 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25911 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25914 for (var index = 0; index < word.length; ++index) {
25915 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25916 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25917 characterSetChecks[nCharSet].fResult = true;
25924 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25925 if (characterSetChecks[nCharSet].fResult) {
25930 if (nCharSets < nb) {
25936 ClientSideStrongPassword: function (pwd)
25938 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25941 ClientSideMediumPassword: function (pwd)
25943 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25946 ClientSideWeakPassword: function (pwd)
25948 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25951 })//<script type="text/javascript">
25954 * Based Ext JS Library 1.1.1
25955 * Copyright(c) 2006-2007, Ext JS, LLC.
25961 * @class Roo.HtmlEditorCore
25962 * @extends Roo.Component
25963 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25965 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25968 Roo.HtmlEditorCore = function(config){
25971 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25976 * @event initialize
25977 * Fires when the editor is fully initialized (including the iframe)
25978 * @param {Roo.HtmlEditorCore} this
25983 * Fires when the editor is first receives the focus. Any insertion must wait
25984 * until after this event.
25985 * @param {Roo.HtmlEditorCore} this
25989 * @event beforesync
25990 * Fires before the textarea is updated with content from the editor iframe. Return false
25991 * to cancel the sync.
25992 * @param {Roo.HtmlEditorCore} this
25993 * @param {String} html
25997 * @event beforepush
25998 * Fires before the iframe editor is updated with content from the textarea. Return false
25999 * to cancel the push.
26000 * @param {Roo.HtmlEditorCore} this
26001 * @param {String} html
26006 * Fires when the textarea is updated with content from the editor iframe.
26007 * @param {Roo.HtmlEditorCore} this
26008 * @param {String} html
26013 * Fires when the iframe editor is updated with content from the textarea.
26014 * @param {Roo.HtmlEditorCore} this
26015 * @param {String} html
26020 * @event editorevent
26021 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26022 * @param {Roo.HtmlEditorCore} this
26028 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26030 // defaults : white / black...
26031 this.applyBlacklists();
26038 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26042 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26048 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26053 * @cfg {Number} height (in pixels)
26057 * @cfg {Number} width (in pixels)
26062 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26065 stylesheets: false,
26068 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26070 allowComments: false,
26074 // private properties
26075 validationEvent : false,
26077 initialized : false,
26079 sourceEditMode : false,
26080 onFocus : Roo.emptyFn,
26082 hideMode:'offsets',
26086 // blacklist + whitelisted elements..
26093 * Protected method that will not generally be called directly. It
26094 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26095 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26097 getDocMarkup : function(){
26101 // inherit styels from page...??
26102 if (this.stylesheets === false) {
26104 Roo.get(document.head).select('style').each(function(node) {
26105 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26108 Roo.get(document.head).select('link').each(function(node) {
26109 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26112 } else if (!this.stylesheets.length) {
26114 st = '<style type="text/css">' +
26115 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26118 for (var i in this.stylesheets) {
26119 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26124 st += '<style type="text/css">' +
26125 'IMG { cursor: pointer } ' +
26128 var cls = 'roo-htmleditor-body';
26130 if(this.bodyCls.length){
26131 cls += ' ' + this.bodyCls;
26134 return '<html><head>' + st +
26135 //<style type="text/css">' +
26136 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26138 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26142 onRender : function(ct, position)
26145 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26146 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26149 this.el.dom.style.border = '0 none';
26150 this.el.dom.setAttribute('tabIndex', -1);
26151 this.el.addClass('x-hidden hide');
26155 if(Roo.isIE){ // fix IE 1px bogus margin
26156 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26160 this.frameId = Roo.id();
26164 var iframe = this.owner.wrap.createChild({
26166 cls: 'form-control', // bootstrap..
26168 name: this.frameId,
26169 frameBorder : 'no',
26170 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26175 this.iframe = iframe.dom;
26177 this.assignDocWin();
26179 this.doc.designMode = 'on';
26182 this.doc.write(this.getDocMarkup());
26186 var task = { // must defer to wait for browser to be ready
26188 //console.log("run task?" + this.doc.readyState);
26189 this.assignDocWin();
26190 if(this.doc.body || this.doc.readyState == 'complete'){
26192 this.doc.designMode="on";
26196 Roo.TaskMgr.stop(task);
26197 this.initEditor.defer(10, this);
26204 Roo.TaskMgr.start(task);
26209 onResize : function(w, h)
26211 Roo.log('resize: ' +w + ',' + h );
26212 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26216 if(typeof w == 'number'){
26218 this.iframe.style.width = w + 'px';
26220 if(typeof h == 'number'){
26222 this.iframe.style.height = h + 'px';
26224 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26231 * Toggles the editor between standard and source edit mode.
26232 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26234 toggleSourceEdit : function(sourceEditMode){
26236 this.sourceEditMode = sourceEditMode === true;
26238 if(this.sourceEditMode){
26240 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26243 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26244 //this.iframe.className = '';
26247 //this.setSize(this.owner.wrap.getSize());
26248 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26255 * Protected method that will not generally be called directly. If you need/want
26256 * custom HTML cleanup, this is the method you should override.
26257 * @param {String} html The HTML to be cleaned
26258 * return {String} The cleaned HTML
26260 cleanHtml : function(html){
26261 html = String(html);
26262 if(html.length > 5){
26263 if(Roo.isSafari){ // strip safari nonsense
26264 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26267 if(html == ' '){
26274 * HTML Editor -> Textarea
26275 * Protected method that will not generally be called directly. Syncs the contents
26276 * of the editor iframe with the textarea.
26278 syncValue : function(){
26279 if(this.initialized){
26280 var bd = (this.doc.body || this.doc.documentElement);
26281 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26282 var html = bd.innerHTML;
26284 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26285 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26287 html = '<div style="'+m[0]+'">' + html + '</div>';
26290 html = this.cleanHtml(html);
26291 // fix up the special chars.. normaly like back quotes in word...
26292 // however we do not want to do this with chinese..
26293 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26295 var cc = match.charCodeAt();
26297 // Get the character value, handling surrogate pairs
26298 if (match.length == 2) {
26299 // It's a surrogate pair, calculate the Unicode code point
26300 var high = match.charCodeAt(0) - 0xD800;
26301 var low = match.charCodeAt(1) - 0xDC00;
26302 cc = (high * 0x400) + low + 0x10000;
26304 (cc >= 0x4E00 && cc < 0xA000 ) ||
26305 (cc >= 0x3400 && cc < 0x4E00 ) ||
26306 (cc >= 0xf900 && cc < 0xfb00 )
26311 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26312 return "&#" + cc + ";";
26319 if(this.owner.fireEvent('beforesync', this, html) !== false){
26320 this.el.dom.value = html;
26321 this.owner.fireEvent('sync', this, html);
26327 * Protected method that will not generally be called directly. Pushes the value of the textarea
26328 * into the iframe editor.
26330 pushValue : function(){
26331 if(this.initialized){
26332 var v = this.el.dom.value.trim();
26334 // if(v.length < 1){
26338 if(this.owner.fireEvent('beforepush', this, v) !== false){
26339 var d = (this.doc.body || this.doc.documentElement);
26341 this.cleanUpPaste();
26342 this.el.dom.value = d.innerHTML;
26343 this.owner.fireEvent('push', this, v);
26349 deferFocus : function(){
26350 this.focus.defer(10, this);
26354 focus : function(){
26355 if(this.win && !this.sourceEditMode){
26362 assignDocWin: function()
26364 var iframe = this.iframe;
26367 this.doc = iframe.contentWindow.document;
26368 this.win = iframe.contentWindow;
26370 // if (!Roo.get(this.frameId)) {
26373 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26374 // this.win = Roo.get(this.frameId).dom.contentWindow;
26376 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26380 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26381 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26386 initEditor : function(){
26387 //console.log("INIT EDITOR");
26388 this.assignDocWin();
26392 this.doc.designMode="on";
26394 this.doc.write(this.getDocMarkup());
26397 var dbody = (this.doc.body || this.doc.documentElement);
26398 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26399 // this copies styles from the containing element into thsi one..
26400 // not sure why we need all of this..
26401 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26403 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26404 //ss['background-attachment'] = 'fixed'; // w3c
26405 dbody.bgProperties = 'fixed'; // ie
26406 //Roo.DomHelper.applyStyles(dbody, ss);
26407 Roo.EventManager.on(this.doc, {
26408 //'mousedown': this.onEditorEvent,
26409 'mouseup': this.onEditorEvent,
26410 'dblclick': this.onEditorEvent,
26411 'click': this.onEditorEvent,
26412 'keyup': this.onEditorEvent,
26417 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26419 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26420 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26422 this.initialized = true;
26424 this.owner.fireEvent('initialize', this);
26429 onDestroy : function(){
26435 //for (var i =0; i < this.toolbars.length;i++) {
26436 // // fixme - ask toolbars for heights?
26437 // this.toolbars[i].onDestroy();
26440 //this.wrap.dom.innerHTML = '';
26441 //this.wrap.remove();
26446 onFirstFocus : function(){
26448 this.assignDocWin();
26451 this.activated = true;
26454 if(Roo.isGecko){ // prevent silly gecko errors
26456 var s = this.win.getSelection();
26457 if(!s.focusNode || s.focusNode.nodeType != 3){
26458 var r = s.getRangeAt(0);
26459 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26464 this.execCmd('useCSS', true);
26465 this.execCmd('styleWithCSS', false);
26468 this.owner.fireEvent('activate', this);
26472 adjustFont: function(btn){
26473 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26474 //if(Roo.isSafari){ // safari
26477 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26478 if(Roo.isSafari){ // safari
26479 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26480 v = (v < 10) ? 10 : v;
26481 v = (v > 48) ? 48 : v;
26482 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26487 v = Math.max(1, v+adjust);
26489 this.execCmd('FontSize', v );
26492 onEditorEvent : function(e)
26494 this.owner.fireEvent('editorevent', this, e);
26495 // this.updateToolbar();
26496 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26499 insertTag : function(tg)
26501 // could be a bit smarter... -> wrap the current selected tRoo..
26502 if (tg.toLowerCase() == 'span' ||
26503 tg.toLowerCase() == 'code' ||
26504 tg.toLowerCase() == 'sup' ||
26505 tg.toLowerCase() == 'sub'
26508 range = this.createRange(this.getSelection());
26509 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26510 wrappingNode.appendChild(range.extractContents());
26511 range.insertNode(wrappingNode);
26518 this.execCmd("formatblock", tg);
26522 insertText : function(txt)
26526 var range = this.createRange();
26527 range.deleteContents();
26528 //alert(Sender.getAttribute('label'));
26530 range.insertNode(this.doc.createTextNode(txt));
26536 * Executes a Midas editor command on the editor document and performs necessary focus and
26537 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26538 * @param {String} cmd The Midas command
26539 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26541 relayCmd : function(cmd, value){
26543 this.execCmd(cmd, value);
26544 this.owner.fireEvent('editorevent', this);
26545 //this.updateToolbar();
26546 this.owner.deferFocus();
26550 * Executes a Midas editor command directly on the editor document.
26551 * For visual commands, you should use {@link #relayCmd} instead.
26552 * <b>This should only be called after the editor is initialized.</b>
26553 * @param {String} cmd The Midas command
26554 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26556 execCmd : function(cmd, value){
26557 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26564 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26566 * @param {String} text | dom node..
26568 insertAtCursor : function(text)
26571 if(!this.activated){
26577 var r = this.doc.selection.createRange();
26588 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26592 // from jquery ui (MIT licenced)
26594 var win = this.win;
26596 if (win.getSelection && win.getSelection().getRangeAt) {
26597 range = win.getSelection().getRangeAt(0);
26598 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26599 range.insertNode(node);
26600 } else if (win.document.selection && win.document.selection.createRange) {
26601 // no firefox support
26602 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26603 win.document.selection.createRange().pasteHTML(txt);
26605 // no firefox support
26606 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26607 this.execCmd('InsertHTML', txt);
26616 mozKeyPress : function(e){
26618 var c = e.getCharCode(), cmd;
26621 c = String.fromCharCode(c).toLowerCase();
26635 this.cleanUpPaste.defer(100, this);
26643 e.preventDefault();
26651 fixKeys : function(){ // load time branching for fastest keydown performance
26653 return function(e){
26654 var k = e.getKey(), r;
26657 r = this.doc.selection.createRange();
26660 r.pasteHTML('    ');
26667 r = this.doc.selection.createRange();
26669 var target = r.parentElement();
26670 if(!target || target.tagName.toLowerCase() != 'li'){
26672 r.pasteHTML('<br />');
26678 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26679 this.cleanUpPaste.defer(100, this);
26685 }else if(Roo.isOpera){
26686 return function(e){
26687 var k = e.getKey();
26691 this.execCmd('InsertHTML','    ');
26694 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26695 this.cleanUpPaste.defer(100, this);
26700 }else if(Roo.isSafari){
26701 return function(e){
26702 var k = e.getKey();
26706 this.execCmd('InsertText','\t');
26710 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26711 this.cleanUpPaste.defer(100, this);
26719 getAllAncestors: function()
26721 var p = this.getSelectedNode();
26724 a.push(p); // push blank onto stack..
26725 p = this.getParentElement();
26729 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26733 a.push(this.doc.body);
26737 lastSelNode : false,
26740 getSelection : function()
26742 this.assignDocWin();
26743 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26746 getSelectedNode: function()
26748 // this may only work on Gecko!!!
26750 // should we cache this!!!!
26755 var range = this.createRange(this.getSelection()).cloneRange();
26758 var parent = range.parentElement();
26760 var testRange = range.duplicate();
26761 testRange.moveToElementText(parent);
26762 if (testRange.inRange(range)) {
26765 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26768 parent = parent.parentElement;
26773 // is ancestor a text element.
26774 var ac = range.commonAncestorContainer;
26775 if (ac.nodeType == 3) {
26776 ac = ac.parentNode;
26779 var ar = ac.childNodes;
26782 var other_nodes = [];
26783 var has_other_nodes = false;
26784 for (var i=0;i<ar.length;i++) {
26785 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26788 // fullly contained node.
26790 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26795 // probably selected..
26796 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26797 other_nodes.push(ar[i]);
26801 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26806 has_other_nodes = true;
26808 if (!nodes.length && other_nodes.length) {
26809 nodes= other_nodes;
26811 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26817 createRange: function(sel)
26819 // this has strange effects when using with
26820 // top toolbar - not sure if it's a great idea.
26821 //this.editor.contentWindow.focus();
26822 if (typeof sel != "undefined") {
26824 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26826 return this.doc.createRange();
26829 return this.doc.createRange();
26832 getParentElement: function()
26835 this.assignDocWin();
26836 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26838 var range = this.createRange(sel);
26841 var p = range.commonAncestorContainer;
26842 while (p.nodeType == 3) { // text node
26853 * Range intersection.. the hard stuff...
26857 * [ -- selected range --- ]
26861 * if end is before start or hits it. fail.
26862 * if start is after end or hits it fail.
26864 * if either hits (but other is outside. - then it's not
26870 // @see http://www.thismuchiknow.co.uk/?p=64.
26871 rangeIntersectsNode : function(range, node)
26873 var nodeRange = node.ownerDocument.createRange();
26875 nodeRange.selectNode(node);
26877 nodeRange.selectNodeContents(node);
26880 var rangeStartRange = range.cloneRange();
26881 rangeStartRange.collapse(true);
26883 var rangeEndRange = range.cloneRange();
26884 rangeEndRange.collapse(false);
26886 var nodeStartRange = nodeRange.cloneRange();
26887 nodeStartRange.collapse(true);
26889 var nodeEndRange = nodeRange.cloneRange();
26890 nodeEndRange.collapse(false);
26892 return rangeStartRange.compareBoundaryPoints(
26893 Range.START_TO_START, nodeEndRange) == -1 &&
26894 rangeEndRange.compareBoundaryPoints(
26895 Range.START_TO_START, nodeStartRange) == 1;
26899 rangeCompareNode : function(range, node)
26901 var nodeRange = node.ownerDocument.createRange();
26903 nodeRange.selectNode(node);
26905 nodeRange.selectNodeContents(node);
26909 range.collapse(true);
26911 nodeRange.collapse(true);
26913 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26914 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26916 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26918 var nodeIsBefore = ss == 1;
26919 var nodeIsAfter = ee == -1;
26921 if (nodeIsBefore && nodeIsAfter) {
26924 if (!nodeIsBefore && nodeIsAfter) {
26925 return 1; //right trailed.
26928 if (nodeIsBefore && !nodeIsAfter) {
26929 return 2; // left trailed.
26935 // private? - in a new class?
26936 cleanUpPaste : function()
26938 // cleans up the whole document..
26939 Roo.log('cleanuppaste');
26941 this.cleanUpChildren(this.doc.body);
26942 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26943 if (clean != this.doc.body.innerHTML) {
26944 this.doc.body.innerHTML = clean;
26949 cleanWordChars : function(input) {// change the chars to hex code
26950 var he = Roo.HtmlEditorCore;
26952 var output = input;
26953 Roo.each(he.swapCodes, function(sw) {
26954 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26956 output = output.replace(swapper, sw[1]);
26963 cleanUpChildren : function (n)
26965 if (!n.childNodes.length) {
26968 for (var i = n.childNodes.length-1; i > -1 ; i--) {
26969 this.cleanUpChild(n.childNodes[i]);
26976 cleanUpChild : function (node)
26979 //console.log(node);
26980 if (node.nodeName == "#text") {
26981 // clean up silly Windows -- stuff?
26984 if (node.nodeName == "#comment") {
26985 if (!this.allowComments) {
26986 node.parentNode.removeChild(node);
26988 // clean up silly Windows -- stuff?
26991 var lcname = node.tagName.toLowerCase();
26992 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26993 // whitelist of tags..
26995 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26997 node.parentNode.removeChild(node);
27002 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27004 // spans with no attributes - just remove them..
27005 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27006 remove_keep_children = true;
27009 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27010 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27012 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27013 // remove_keep_children = true;
27016 if (remove_keep_children) {
27017 this.cleanUpChildren(node);
27018 // inserts everything just before this node...
27019 while (node.childNodes.length) {
27020 var cn = node.childNodes[0];
27021 node.removeChild(cn);
27022 node.parentNode.insertBefore(cn, node);
27024 node.parentNode.removeChild(node);
27028 if (!node.attributes || !node.attributes.length) {
27033 this.cleanUpChildren(node);
27037 function cleanAttr(n,v)
27040 if (v.match(/^\./) || v.match(/^\//)) {
27043 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27046 if (v.match(/^#/)) {
27049 if (v.match(/^\{/)) { // allow template editing.
27052 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27053 node.removeAttribute(n);
27057 var cwhite = this.cwhite;
27058 var cblack = this.cblack;
27060 function cleanStyle(n,v)
27062 if (v.match(/expression/)) { //XSS?? should we even bother..
27063 node.removeAttribute(n);
27067 var parts = v.split(/;/);
27070 Roo.each(parts, function(p) {
27071 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27075 var l = p.split(':').shift().replace(/\s+/g,'');
27076 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27078 if ( cwhite.length && cblack.indexOf(l) > -1) {
27079 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27080 //node.removeAttribute(n);
27084 // only allow 'c whitelisted system attributes'
27085 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27086 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27087 //node.removeAttribute(n);
27097 if (clean.length) {
27098 node.setAttribute(n, clean.join(';'));
27100 node.removeAttribute(n);
27106 for (var i = node.attributes.length-1; i > -1 ; i--) {
27107 var a = node.attributes[i];
27110 if (a.name.toLowerCase().substr(0,2)=='on') {
27111 node.removeAttribute(a.name);
27114 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27115 node.removeAttribute(a.name);
27118 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27119 cleanAttr(a.name,a.value); // fixme..
27122 if (a.name == 'style') {
27123 cleanStyle(a.name,a.value);
27126 /// clean up MS crap..
27127 // tecnically this should be a list of valid class'es..
27130 if (a.name == 'class') {
27131 if (a.value.match(/^Mso/)) {
27132 node.removeAttribute('class');
27135 if (a.value.match(/^body$/)) {
27136 node.removeAttribute('class');
27147 this.cleanUpChildren(node);
27153 * Clean up MS wordisms...
27155 cleanWord : function(node)
27158 this.cleanWord(this.doc.body);
27163 node.nodeName == 'SPAN' &&
27164 !node.hasAttributes() &&
27165 node.childNodes.length == 1 &&
27166 node.firstChild.nodeName == "#text"
27168 var textNode = node.firstChild;
27169 node.removeChild(textNode);
27170 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27171 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27173 node.parentNode.insertBefore(textNode, node);
27174 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27175 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27177 node.parentNode.removeChild(node);
27180 if (node.nodeName == "#text") {
27181 // clean up silly Windows -- stuff?
27184 if (node.nodeName == "#comment") {
27185 node.parentNode.removeChild(node);
27186 // clean up silly Windows -- stuff?
27190 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27191 node.parentNode.removeChild(node);
27194 //Roo.log(node.tagName);
27195 // remove - but keep children..
27196 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27197 //Roo.log('-- removed');
27198 while (node.childNodes.length) {
27199 var cn = node.childNodes[0];
27200 node.removeChild(cn);
27201 node.parentNode.insertBefore(cn, node);
27202 // move node to parent - and clean it..
27203 this.cleanWord(cn);
27205 node.parentNode.removeChild(node);
27206 /// no need to iterate chidlren = it's got none..
27207 //this.iterateChildren(node, this.cleanWord);
27211 if (node.className.length) {
27213 var cn = node.className.split(/\W+/);
27215 Roo.each(cn, function(cls) {
27216 if (cls.match(/Mso[a-zA-Z]+/)) {
27221 node.className = cna.length ? cna.join(' ') : '';
27223 node.removeAttribute("class");
27227 if (node.hasAttribute("lang")) {
27228 node.removeAttribute("lang");
27231 if (node.hasAttribute("style")) {
27233 var styles = node.getAttribute("style").split(";");
27235 Roo.each(styles, function(s) {
27236 if (!s.match(/:/)) {
27239 var kv = s.split(":");
27240 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27243 // what ever is left... we allow.
27246 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27247 if (!nstyle.length) {
27248 node.removeAttribute('style');
27251 this.iterateChildren(node, this.cleanWord);
27257 * iterateChildren of a Node, calling fn each time, using this as the scole..
27258 * @param {DomNode} node node to iterate children of.
27259 * @param {Function} fn method of this class to call on each item.
27261 iterateChildren : function(node, fn)
27263 if (!node.childNodes.length) {
27266 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27267 fn.call(this, node.childNodes[i])
27273 * cleanTableWidths.
27275 * Quite often pasting from word etc.. results in tables with column and widths.
27276 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27279 cleanTableWidths : function(node)
27284 this.cleanTableWidths(this.doc.body);
27289 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27292 Roo.log(node.tagName);
27293 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27294 this.iterateChildren(node, this.cleanTableWidths);
27297 if (node.hasAttribute('width')) {
27298 node.removeAttribute('width');
27302 if (node.hasAttribute("style")) {
27305 var styles = node.getAttribute("style").split(";");
27307 Roo.each(styles, function(s) {
27308 if (!s.match(/:/)) {
27311 var kv = s.split(":");
27312 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27315 // what ever is left... we allow.
27318 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27319 if (!nstyle.length) {
27320 node.removeAttribute('style');
27324 this.iterateChildren(node, this.cleanTableWidths);
27332 domToHTML : function(currentElement, depth, nopadtext) {
27334 depth = depth || 0;
27335 nopadtext = nopadtext || false;
27337 if (!currentElement) {
27338 return this.domToHTML(this.doc.body);
27341 //Roo.log(currentElement);
27343 var allText = false;
27344 var nodeName = currentElement.nodeName;
27345 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27347 if (nodeName == '#text') {
27349 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27354 if (nodeName != 'BODY') {
27357 // Prints the node tagName, such as <A>, <IMG>, etc
27360 for(i = 0; i < currentElement.attributes.length;i++) {
27362 var aname = currentElement.attributes.item(i).name;
27363 if (!currentElement.attributes.item(i).value.length) {
27366 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27369 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27378 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27381 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27386 // Traverse the tree
27388 var currentElementChild = currentElement.childNodes.item(i);
27389 var allText = true;
27390 var innerHTML = '';
27392 while (currentElementChild) {
27393 // Formatting code (indent the tree so it looks nice on the screen)
27394 var nopad = nopadtext;
27395 if (lastnode == 'SPAN') {
27399 if (currentElementChild.nodeName == '#text') {
27400 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27401 toadd = nopadtext ? toadd : toadd.trim();
27402 if (!nopad && toadd.length > 80) {
27403 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27405 innerHTML += toadd;
27408 currentElementChild = currentElement.childNodes.item(i);
27414 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27416 // Recursively traverse the tree structure of the child node
27417 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27418 lastnode = currentElementChild.nodeName;
27420 currentElementChild=currentElement.childNodes.item(i);
27426 // The remaining code is mostly for formatting the tree
27427 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27432 ret+= "</"+tagName+">";
27438 applyBlacklists : function()
27440 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27441 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27445 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27446 if (b.indexOf(tag) > -1) {
27449 this.white.push(tag);
27453 Roo.each(w, function(tag) {
27454 if (b.indexOf(tag) > -1) {
27457 if (this.white.indexOf(tag) > -1) {
27460 this.white.push(tag);
27465 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27466 if (w.indexOf(tag) > -1) {
27469 this.black.push(tag);
27473 Roo.each(b, function(tag) {
27474 if (w.indexOf(tag) > -1) {
27477 if (this.black.indexOf(tag) > -1) {
27480 this.black.push(tag);
27485 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27486 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27490 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27491 if (b.indexOf(tag) > -1) {
27494 this.cwhite.push(tag);
27498 Roo.each(w, function(tag) {
27499 if (b.indexOf(tag) > -1) {
27502 if (this.cwhite.indexOf(tag) > -1) {
27505 this.cwhite.push(tag);
27510 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27511 if (w.indexOf(tag) > -1) {
27514 this.cblack.push(tag);
27518 Roo.each(b, function(tag) {
27519 if (w.indexOf(tag) > -1) {
27522 if (this.cblack.indexOf(tag) > -1) {
27525 this.cblack.push(tag);
27530 setStylesheets : function(stylesheets)
27532 if(typeof(stylesheets) == 'string'){
27533 Roo.get(this.iframe.contentDocument.head).createChild({
27535 rel : 'stylesheet',
27544 Roo.each(stylesheets, function(s) {
27549 Roo.get(_this.iframe.contentDocument.head).createChild({
27551 rel : 'stylesheet',
27560 removeStylesheets : function()
27564 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27569 setStyle : function(style)
27571 Roo.get(this.iframe.contentDocument.head).createChild({
27580 // hide stuff that is not compatible
27594 * @event specialkey
27598 * @cfg {String} fieldClass @hide
27601 * @cfg {String} focusClass @hide
27604 * @cfg {String} autoCreate @hide
27607 * @cfg {String} inputType @hide
27610 * @cfg {String} invalidClass @hide
27613 * @cfg {String} invalidText @hide
27616 * @cfg {String} msgFx @hide
27619 * @cfg {String} validateOnBlur @hide
27623 Roo.HtmlEditorCore.white = [
27624 'area', 'br', 'img', 'input', 'hr', 'wbr',
27626 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27627 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27628 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27629 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27630 'table', 'ul', 'xmp',
27632 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27635 'dir', 'menu', 'ol', 'ul', 'dl',
27641 Roo.HtmlEditorCore.black = [
27642 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27644 'base', 'basefont', 'bgsound', 'blink', 'body',
27645 'frame', 'frameset', 'head', 'html', 'ilayer',
27646 'iframe', 'layer', 'link', 'meta', 'object',
27647 'script', 'style' ,'title', 'xml' // clean later..
27649 Roo.HtmlEditorCore.clean = [
27650 'script', 'style', 'title', 'xml'
27652 Roo.HtmlEditorCore.remove = [
27657 Roo.HtmlEditorCore.ablack = [
27661 Roo.HtmlEditorCore.aclean = [
27662 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27666 Roo.HtmlEditorCore.pwhite= [
27667 'http', 'https', 'mailto'
27670 // white listed style attributes.
27671 Roo.HtmlEditorCore.cwhite= [
27672 // 'text-align', /// default is to allow most things..
27678 // black listed style attributes.
27679 Roo.HtmlEditorCore.cblack= [
27680 // 'font-size' -- this can be set by the project
27684 Roo.HtmlEditorCore.swapCodes =[
27685 [ 8211, "–" ],
27686 [ 8212, "—" ],
27703 * @class Roo.bootstrap.form.HtmlEditor
27704 * @extends Roo.bootstrap.form.TextArea
27705 * Bootstrap HtmlEditor class
27708 * Create a new HtmlEditor
27709 * @param {Object} config The config object
27712 Roo.bootstrap.form.HtmlEditor = function(config){
27713 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27714 if (!this.toolbars) {
27715 this.toolbars = [];
27718 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27721 * @event initialize
27722 * Fires when the editor is fully initialized (including the iframe)
27723 * @param {HtmlEditor} this
27728 * Fires when the editor is first receives the focus. Any insertion must wait
27729 * until after this event.
27730 * @param {HtmlEditor} this
27734 * @event beforesync
27735 * Fires before the textarea is updated with content from the editor iframe. Return false
27736 * to cancel the sync.
27737 * @param {HtmlEditor} this
27738 * @param {String} html
27742 * @event beforepush
27743 * Fires before the iframe editor is updated with content from the textarea. Return false
27744 * to cancel the push.
27745 * @param {HtmlEditor} this
27746 * @param {String} html
27751 * Fires when the textarea is updated with content from the editor iframe.
27752 * @param {HtmlEditor} this
27753 * @param {String} html
27758 * Fires when the iframe editor is updated with content from the textarea.
27759 * @param {HtmlEditor} this
27760 * @param {String} html
27764 * @event editmodechange
27765 * Fires when the editor switches edit modes
27766 * @param {HtmlEditor} this
27767 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27769 editmodechange: true,
27771 * @event editorevent
27772 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27773 * @param {HtmlEditor} this
27777 * @event firstfocus
27778 * Fires when on first focus - needed by toolbars..
27779 * @param {HtmlEditor} this
27784 * Auto save the htmlEditor value as a file into Events
27785 * @param {HtmlEditor} this
27789 * @event savedpreview
27790 * preview the saved version of htmlEditor
27791 * @param {HtmlEditor} this
27798 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27802 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27807 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27812 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27817 * @cfg {Number} height (in pixels)
27821 * @cfg {Number} width (in pixels)
27826 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27829 stylesheets: false,
27834 // private properties
27835 validationEvent : false,
27837 initialized : false,
27840 onFocus : Roo.emptyFn,
27842 hideMode:'offsets',
27844 tbContainer : false,
27848 toolbarContainer :function() {
27849 return this.wrap.select('.x-html-editor-tb',true).first();
27853 * Protected method that will not generally be called directly. It
27854 * is called when the editor creates its toolbar. Override this method if you need to
27855 * add custom toolbar buttons.
27856 * @param {HtmlEditor} editor
27858 createToolbar : function(){
27859 Roo.log('renewing');
27860 Roo.log("create toolbars");
27862 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27863 this.toolbars[0].render(this.toolbarContainer());
27867 // if (!editor.toolbars || !editor.toolbars.length) {
27868 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27871 // for (var i =0 ; i < editor.toolbars.length;i++) {
27872 // editor.toolbars[i] = Roo.factory(
27873 // typeof(editor.toolbars[i]) == 'string' ?
27874 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27875 // Roo.bootstrap.form.HtmlEditor);
27876 // editor.toolbars[i].init(editor);
27882 onRender : function(ct, position)
27884 // Roo.log("Call onRender: " + this.xtype);
27886 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27888 this.wrap = this.inputEl().wrap({
27889 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27892 this.editorcore.onRender(ct, position);
27894 if (this.resizable) {
27895 this.resizeEl = new Roo.Resizable(this.wrap, {
27899 minHeight : this.height,
27900 height: this.height,
27901 handles : this.resizable,
27904 resize : function(r, w, h) {
27905 _t.onResize(w,h); // -something
27911 this.createToolbar(this);
27914 if(!this.width && this.resizable){
27915 this.setSize(this.wrap.getSize());
27917 if (this.resizeEl) {
27918 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27919 // should trigger onReize..
27925 onResize : function(w, h)
27927 Roo.log('resize: ' +w + ',' + h );
27928 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27932 if(this.inputEl() ){
27933 if(typeof w == 'number'){
27934 var aw = w - this.wrap.getFrameWidth('lr');
27935 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27938 if(typeof h == 'number'){
27939 var tbh = -11; // fixme it needs to tool bar size!
27940 for (var i =0; i < this.toolbars.length;i++) {
27941 // fixme - ask toolbars for heights?
27942 tbh += this.toolbars[i].el.getHeight();
27943 //if (this.toolbars[i].footer) {
27944 // tbh += this.toolbars[i].footer.el.getHeight();
27952 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27953 ah -= 5; // knock a few pixes off for look..
27954 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27958 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27959 this.editorcore.onResize(ew,eh);
27964 * Toggles the editor between standard and source edit mode.
27965 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27967 toggleSourceEdit : function(sourceEditMode)
27969 this.editorcore.toggleSourceEdit(sourceEditMode);
27971 if(this.editorcore.sourceEditMode){
27972 Roo.log('editor - showing textarea');
27975 // Roo.log(this.syncValue());
27977 this.inputEl().removeClass(['hide', 'x-hidden']);
27978 this.inputEl().dom.removeAttribute('tabIndex');
27979 this.inputEl().focus();
27981 Roo.log('editor - hiding textarea');
27983 // Roo.log(this.pushValue());
27986 this.inputEl().addClass(['hide', 'x-hidden']);
27987 this.inputEl().dom.setAttribute('tabIndex', -1);
27988 //this.deferFocus();
27991 if(this.resizable){
27992 this.setSize(this.wrap.getSize());
27995 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27998 // private (for BoxComponent)
27999 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28001 // private (for BoxComponent)
28002 getResizeEl : function(){
28006 // private (for BoxComponent)
28007 getPositionEl : function(){
28012 initEvents : function(){
28013 this.originalValue = this.getValue();
28017 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28020 // markInvalid : Roo.emptyFn,
28022 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28025 // clearInvalid : Roo.emptyFn,
28027 setValue : function(v){
28028 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28029 this.editorcore.pushValue();
28034 deferFocus : function(){
28035 this.focus.defer(10, this);
28039 focus : function(){
28040 this.editorcore.focus();
28046 onDestroy : function(){
28052 for (var i =0; i < this.toolbars.length;i++) {
28053 // fixme - ask toolbars for heights?
28054 this.toolbars[i].onDestroy();
28057 this.wrap.dom.innerHTML = '';
28058 this.wrap.remove();
28063 onFirstFocus : function(){
28064 //Roo.log("onFirstFocus");
28065 this.editorcore.onFirstFocus();
28066 for (var i =0; i < this.toolbars.length;i++) {
28067 this.toolbars[i].onFirstFocus();
28073 syncValue : function()
28075 this.editorcore.syncValue();
28078 pushValue : function()
28080 this.editorcore.pushValue();
28084 // hide stuff that is not compatible
28098 * @event specialkey
28102 * @cfg {String} fieldClass @hide
28105 * @cfg {String} focusClass @hide
28108 * @cfg {String} autoCreate @hide
28111 * @cfg {String} inputType @hide
28115 * @cfg {String} invalidText @hide
28118 * @cfg {String} msgFx @hide
28121 * @cfg {String} validateOnBlur @hide
28130 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28132 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28133 * @parent Roo.bootstrap.form.HtmlEditor
28134 * @extends Roo.bootstrap.nav.Simplebar
28140 new Roo.bootstrap.form.HtmlEditor({
28143 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28144 disable : { fonts: 1 , format: 1, ..., ... , ...],
28150 * @cfg {Object} disable List of elements to disable..
28151 * @cfg {Array} btns List of additional buttons.
28155 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28158 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28161 Roo.apply(this, config);
28163 // default disabled, based on 'good practice'..
28164 this.disable = this.disable || {};
28165 Roo.applyIf(this.disable, {
28168 specialElements : true
28170 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28172 this.editor = config.editor;
28173 this.editorcore = config.editor.editorcore;
28175 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28177 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28178 // dont call parent... till later.
28180 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28185 editorcore : false,
28190 "h1","h2","h3","h4","h5","h6",
28192 "abbr", "acronym", "address", "cite", "samp", "var",
28196 onRender : function(ct, position)
28198 // Roo.log("Call onRender: " + this.xtype);
28200 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28202 this.el.dom.style.marginBottom = '0';
28204 var editorcore = this.editorcore;
28205 var editor= this.editor;
28208 var btn = function(id,cmd , toggle, handler, html){
28210 var event = toggle ? 'toggle' : 'click';
28215 xns: Roo.bootstrap,
28219 enableToggle:toggle !== false,
28221 pressed : toggle ? false : null,
28224 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28225 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28231 // var cb_box = function...
28236 xns: Roo.bootstrap,
28241 xns: Roo.bootstrap,
28245 Roo.each(this.formats, function(f) {
28246 style.menu.items.push({
28248 xns: Roo.bootstrap,
28249 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28254 editorcore.insertTag(this.tagname);
28261 children.push(style);
28263 btn('bold',false,true);
28264 btn('italic',false,true);
28265 btn('align-left', 'justifyleft',true);
28266 btn('align-center', 'justifycenter',true);
28267 btn('align-right' , 'justifyright',true);
28268 btn('link', false, false, function(btn) {
28269 //Roo.log("create link?");
28270 var url = prompt(this.createLinkText, this.defaultLinkValue);
28271 if(url && url != 'http:/'+'/'){
28272 this.editorcore.relayCmd('createlink', url);
28275 btn('list','insertunorderedlist',true);
28276 btn('pencil', false,true, function(btn){
28278 this.toggleSourceEdit(btn.pressed);
28281 if (this.editor.btns.length > 0) {
28282 for (var i = 0; i<this.editor.btns.length; i++) {
28283 children.push(this.editor.btns[i]);
28291 xns: Roo.bootstrap,
28296 xns: Roo.bootstrap,
28301 cog.menu.items.push({
28303 xns: Roo.bootstrap,
28304 html : Clean styles,
28309 editorcore.insertTag(this.tagname);
28318 this.xtype = 'NavSimplebar';
28320 for(var i=0;i< children.length;i++) {
28322 this.buttons.add(this.addxtypeChild(children[i]));
28326 editor.on('editorevent', this.updateToolbar, this);
28328 onBtnClick : function(id)
28330 this.editorcore.relayCmd(id);
28331 this.editorcore.focus();
28335 * Protected method that will not generally be called directly. It triggers
28336 * a toolbar update by reading the markup state of the current selection in the editor.
28338 updateToolbar: function(){
28340 if(!this.editorcore.activated){
28341 this.editor.onFirstFocus(); // is this neeed?
28345 var btns = this.buttons;
28346 var doc = this.editorcore.doc;
28347 btns.get('bold').setActive(doc.queryCommandState('bold'));
28348 btns.get('italic').setActive(doc.queryCommandState('italic'));
28349 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28351 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28352 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28353 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28355 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28356 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28359 var ans = this.editorcore.getAllAncestors();
28360 if (this.formatCombo) {
28363 var store = this.formatCombo.store;
28364 this.formatCombo.setValue("");
28365 for (var i =0; i < ans.length;i++) {
28366 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28368 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28376 // hides menus... - so this cant be on a menu...
28377 Roo.bootstrap.MenuMgr.hideAll();
28379 Roo.bootstrap.menu.Manager.hideAll();
28380 //this.editorsyncValue();
28382 onFirstFocus: function() {
28383 this.buttons.each(function(item){
28387 toggleSourceEdit : function(sourceEditMode){
28390 if(sourceEditMode){
28391 Roo.log("disabling buttons");
28392 this.buttons.each( function(item){
28393 if(item.cmd != 'pencil'){
28399 Roo.log("enabling buttons");
28400 if(this.editorcore.initialized){
28401 this.buttons.each( function(item){
28407 Roo.log("calling toggole on editor");
28408 // tell the editor that it's been pressed..
28409 this.editor.toggleSourceEdit(sourceEditMode);
28423 * @class Roo.bootstrap.form.Markdown
28424 * @extends Roo.bootstrap.form.TextArea
28425 * Bootstrap Showdown editable area
28426 * @cfg {string} content
28429 * Create a new Showdown
28432 Roo.bootstrap.form.Markdown = function(config){
28433 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28437 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28441 initEvents : function()
28444 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28445 this.markdownEl = this.el.createChild({
28446 cls : 'roo-markdown-area'
28448 this.inputEl().addClass('d-none');
28449 if (this.getValue() == '') {
28450 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28453 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28455 this.markdownEl.on('click', this.toggleTextEdit, this);
28456 this.on('blur', this.toggleTextEdit, this);
28457 this.on('specialkey', this.resizeTextArea, this);
28460 toggleTextEdit : function()
28462 var sh = this.markdownEl.getHeight();
28463 this.inputEl().addClass('d-none');
28464 this.markdownEl.addClass('d-none');
28465 if (!this.editing) {
28467 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28468 this.inputEl().removeClass('d-none');
28469 this.inputEl().focus();
28470 this.editing = true;
28473 // show showdown...
28474 this.updateMarkdown();
28475 this.markdownEl.removeClass('d-none');
28476 this.editing = false;
28479 updateMarkdown : function()
28481 if (this.getValue() == '') {
28482 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28486 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28489 resizeTextArea: function () {
28492 Roo.log([sh, this.getValue().split("\n").length * 30]);
28493 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28495 setValue : function(val)
28497 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28498 if (!this.editing) {
28499 this.updateMarkdown();
28505 if (!this.editing) {
28506 this.toggleTextEdit();
28514 * Ext JS Library 1.1.1
28515 * Copyright(c) 2006-2007, Ext JS, LLC.
28517 * Originally Released Under LGPL - original licence link has changed is not relivant.
28520 * <script type="text/javascript">
28524 * @class Roo.bootstrap.PagingToolbar
28525 * @extends Roo.bootstrap.nav.Simplebar
28526 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28528 * Create a new PagingToolbar
28529 * @param {Object} config The config object
28530 * @param {Roo.data.Store} store
28532 Roo.bootstrap.PagingToolbar = function(config)
28534 // old args format still supported... - xtype is prefered..
28535 // created from xtype...
28537 this.ds = config.dataSource;
28539 if (config.store && !this.ds) {
28540 this.store= Roo.factory(config.store, Roo.data);
28541 this.ds = this.store;
28542 this.ds.xmodule = this.xmodule || false;
28545 this.toolbarItems = [];
28546 if (config.items) {
28547 this.toolbarItems = config.items;
28550 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28555 this.bind(this.ds);
28558 if (Roo.bootstrap.version == 4) {
28559 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28561 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28566 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28568 * @cfg {Roo.bootstrap.Button} buttons[]
28569 * Buttons for the toolbar
28572 * @cfg {Roo.data.Store} store
28573 * The underlying data store providing the paged data
28576 * @cfg {String/HTMLElement/Element} container
28577 * container The id or element that will contain the toolbar
28580 * @cfg {Boolean} displayInfo
28581 * True to display the displayMsg (defaults to false)
28584 * @cfg {Number} pageSize
28585 * The number of records to display per page (defaults to 20)
28589 * @cfg {String} displayMsg
28590 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28592 displayMsg : 'Displaying {0} - {1} of {2}',
28594 * @cfg {String} emptyMsg
28595 * The message to display when no records are found (defaults to "No data to display")
28597 emptyMsg : 'No data to display',
28599 * Customizable piece of the default paging text (defaults to "Page")
28602 beforePageText : "Page",
28604 * Customizable piece of the default paging text (defaults to "of %0")
28607 afterPageText : "of {0}",
28609 * Customizable piece of the default paging text (defaults to "First Page")
28612 firstText : "First Page",
28614 * Customizable piece of the default paging text (defaults to "Previous Page")
28617 prevText : "Previous Page",
28619 * Customizable piece of the default paging text (defaults to "Next Page")
28622 nextText : "Next Page",
28624 * Customizable piece of the default paging text (defaults to "Last Page")
28627 lastText : "Last Page",
28629 * Customizable piece of the default paging text (defaults to "Refresh")
28632 refreshText : "Refresh",
28636 onRender : function(ct, position)
28638 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28639 this.navgroup.parentId = this.id;
28640 this.navgroup.onRender(this.el, null);
28641 // add the buttons to the navgroup
28643 if(this.displayInfo){
28644 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28645 this.displayEl = this.el.select('.x-paging-info', true).first();
28646 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28647 // this.displayEl = navel.el.select('span',true).first();
28653 Roo.each(_this.buttons, function(e){ // this might need to use render????
28654 Roo.factory(e).render(_this.el);
28658 Roo.each(_this.toolbarItems, function(e) {
28659 _this.navgroup.addItem(e);
28663 this.first = this.navgroup.addItem({
28664 tooltip: this.firstText,
28665 cls: "prev btn-outline-secondary",
28666 html : ' <i class="fa fa-step-backward"></i>',
28668 preventDefault: true,
28669 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28672 this.prev = this.navgroup.addItem({
28673 tooltip: this.prevText,
28674 cls: "prev btn-outline-secondary",
28675 html : ' <i class="fa fa-backward"></i>',
28677 preventDefault: true,
28678 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28680 //this.addSeparator();
28683 var field = this.navgroup.addItem( {
28685 cls : 'x-paging-position btn-outline-secondary',
28687 html : this.beforePageText +
28688 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28689 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28692 this.field = field.el.select('input', true).first();
28693 this.field.on("keydown", this.onPagingKeydown, this);
28694 this.field.on("focus", function(){this.dom.select();});
28697 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28698 //this.field.setHeight(18);
28699 //this.addSeparator();
28700 this.next = this.navgroup.addItem({
28701 tooltip: this.nextText,
28702 cls: "next btn-outline-secondary",
28703 html : ' <i class="fa fa-forward"></i>',
28705 preventDefault: true,
28706 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28708 this.last = this.navgroup.addItem({
28709 tooltip: this.lastText,
28710 html : ' <i class="fa fa-step-forward"></i>',
28711 cls: "next btn-outline-secondary",
28713 preventDefault: true,
28714 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28716 //this.addSeparator();
28717 this.loading = this.navgroup.addItem({
28718 tooltip: this.refreshText,
28719 cls: "btn-outline-secondary",
28720 html : ' <i class="fa fa-refresh"></i>',
28721 preventDefault: true,
28722 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28728 updateInfo : function(){
28729 if(this.displayEl){
28730 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28731 var msg = count == 0 ?
28735 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28737 this.displayEl.update(msg);
28742 onLoad : function(ds, r, o)
28744 this.cursor = o.params && o.params.start ? o.params.start : 0;
28746 var d = this.getPageData(),
28751 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28752 this.field.dom.value = ap;
28753 this.first.setDisabled(ap == 1);
28754 this.prev.setDisabled(ap == 1);
28755 this.next.setDisabled(ap == ps);
28756 this.last.setDisabled(ap == ps);
28757 this.loading.enable();
28762 getPageData : function(){
28763 var total = this.ds.getTotalCount();
28766 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28767 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28772 onLoadError : function(){
28773 this.loading.enable();
28777 onPagingKeydown : function(e){
28778 var k = e.getKey();
28779 var d = this.getPageData();
28781 var v = this.field.dom.value, pageNum;
28782 if(!v || isNaN(pageNum = parseInt(v, 10))){
28783 this.field.dom.value = d.activePage;
28786 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28787 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28790 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))
28792 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28793 this.field.dom.value = pageNum;
28794 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28797 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28799 var v = this.field.dom.value, pageNum;
28800 var increment = (e.shiftKey) ? 10 : 1;
28801 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28804 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28805 this.field.dom.value = d.activePage;
28808 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28810 this.field.dom.value = parseInt(v, 10) + increment;
28811 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28812 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28819 beforeLoad : function(){
28821 this.loading.disable();
28826 onClick : function(which){
28835 ds.load({params:{start: 0, limit: this.pageSize}});
28838 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28841 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28844 var total = ds.getTotalCount();
28845 var extra = total % this.pageSize;
28846 var lastStart = extra ? (total - extra) : total-this.pageSize;
28847 ds.load({params:{start: lastStart, limit: this.pageSize}});
28850 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28856 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28857 * @param {Roo.data.Store} store The data store to unbind
28859 unbind : function(ds){
28860 ds.un("beforeload", this.beforeLoad, this);
28861 ds.un("load", this.onLoad, this);
28862 ds.un("loadexception", this.onLoadError, this);
28863 ds.un("remove", this.updateInfo, this);
28864 ds.un("add", this.updateInfo, this);
28865 this.ds = undefined;
28869 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28870 * @param {Roo.data.Store} store The data store to bind
28872 bind : function(ds){
28873 ds.on("beforeload", this.beforeLoad, this);
28874 ds.on("load", this.onLoad, this);
28875 ds.on("loadexception", this.onLoadError, this);
28876 ds.on("remove", this.updateInfo, this);
28877 ds.on("add", this.updateInfo, this);
28888 * @class Roo.bootstrap.MessageBar
28889 * @extends Roo.bootstrap.Component
28890 * Bootstrap MessageBar class
28891 * @cfg {String} html contents of the MessageBar
28892 * @cfg {String} weight (info | success | warning | danger) default info
28893 * @cfg {String} beforeClass insert the bar before the given class
28894 * @cfg {Boolean} closable (true | false) default false
28895 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28898 * Create a new Element
28899 * @param {Object} config The config object
28902 Roo.bootstrap.MessageBar = function(config){
28903 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28906 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28912 beforeClass: 'bootstrap-sticky-wrap',
28914 getAutoCreate : function(){
28918 cls: 'alert alert-dismissable alert-' + this.weight,
28923 html: this.html || ''
28929 cfg.cls += ' alert-messages-fixed';
28943 onRender : function(ct, position)
28945 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28948 var cfg = Roo.apply({}, this.getAutoCreate());
28952 cfg.cls += ' ' + this.cls;
28955 cfg.style = this.style;
28957 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28959 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28962 this.el.select('>button.close').on('click', this.hide, this);
28968 if (!this.rendered) {
28974 this.fireEvent('show', this);
28980 if (!this.rendered) {
28986 this.fireEvent('hide', this);
28989 update : function()
28991 // var e = this.el.dom.firstChild;
28993 // if(this.closable){
28994 // e = e.nextSibling;
28997 // e.data = this.html || '';
28999 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29015 * @class Roo.bootstrap.Graph
29016 * @extends Roo.bootstrap.Component
29017 * Bootstrap Graph class
29021 @cfg {String} graphtype bar | vbar | pie
29022 @cfg {number} g_x coodinator | centre x (pie)
29023 @cfg {number} g_y coodinator | centre y (pie)
29024 @cfg {number} g_r radius (pie)
29025 @cfg {number} g_height height of the chart (respected by all elements in the set)
29026 @cfg {number} g_width width of the chart (respected by all elements in the set)
29027 @cfg {Object} title The title of the chart
29030 -opts (object) options for the chart
29032 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29033 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29035 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.
29036 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29038 o stretch (boolean)
29040 -opts (object) options for the pie
29043 o startAngle (number)
29044 o endAngle (number)
29048 * Create a new Input
29049 * @param {Object} config The config object
29052 Roo.bootstrap.Graph = function(config){
29053 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29059 * The img click event for the img.
29060 * @param {Roo.EventObject} e
29066 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29077 //g_colors: this.colors,
29084 getAutoCreate : function(){
29095 onRender : function(ct,position){
29098 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29100 if (typeof(Raphael) == 'undefined') {
29101 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29105 this.raphael = Raphael(this.el.dom);
29107 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29108 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29109 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29110 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29112 r.text(160, 10, "Single Series Chart").attr(txtattr);
29113 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29114 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29115 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29117 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29118 r.barchart(330, 10, 300, 220, data1);
29119 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29120 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29123 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29124 // r.barchart(30, 30, 560, 250, xdata, {
29125 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29126 // axis : "0 0 1 1",
29127 // axisxlabels : xdata
29128 // //yvalues : cols,
29131 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29133 // this.load(null,xdata,{
29134 // axis : "0 0 1 1",
29135 // axisxlabels : xdata
29140 load : function(graphtype,xdata,opts)
29142 this.raphael.clear();
29144 graphtype = this.graphtype;
29149 var r = this.raphael,
29150 fin = function () {
29151 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29153 fout = function () {
29154 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29156 pfin = function() {
29157 this.sector.stop();
29158 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29161 this.label[0].stop();
29162 this.label[0].attr({ r: 7.5 });
29163 this.label[1].attr({ "font-weight": 800 });
29166 pfout = function() {
29167 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29170 this.label[0].animate({ r: 5 }, 500, "bounce");
29171 this.label[1].attr({ "font-weight": 400 });
29177 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29180 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29183 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29184 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29186 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29193 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29198 setTitle: function(o)
29203 initEvents: function() {
29206 this.el.on('click', this.onClick, this);
29210 onClick : function(e)
29212 Roo.log('img onclick');
29213 this.fireEvent('click', this, e);
29225 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29228 * @class Roo.bootstrap.dash.NumberBox
29229 * @extends Roo.bootstrap.Component
29230 * Bootstrap NumberBox class
29231 * @cfg {String} headline Box headline
29232 * @cfg {String} content Box content
29233 * @cfg {String} icon Box icon
29234 * @cfg {String} footer Footer text
29235 * @cfg {String} fhref Footer href
29238 * Create a new NumberBox
29239 * @param {Object} config The config object
29243 Roo.bootstrap.dash.NumberBox = function(config){
29244 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29248 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29257 getAutoCreate : function(){
29261 cls : 'small-box ',
29269 cls : 'roo-headline',
29270 html : this.headline
29274 cls : 'roo-content',
29275 html : this.content
29289 cls : 'ion ' + this.icon
29298 cls : 'small-box-footer',
29299 href : this.fhref || '#',
29303 cfg.cn.push(footer);
29310 onRender : function(ct,position){
29311 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29318 setHeadline: function (value)
29320 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29323 setFooter: function (value, href)
29325 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29328 this.el.select('a.small-box-footer',true).first().attr('href', href);
29333 setContent: function (value)
29335 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29338 initEvents: function()
29352 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29355 * @class Roo.bootstrap.dash.TabBox
29356 * @extends Roo.bootstrap.Component
29357 * @children Roo.bootstrap.dash.TabPane
29358 * Bootstrap TabBox class
29359 * @cfg {String} title Title of the TabBox
29360 * @cfg {String} icon Icon of the TabBox
29361 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29362 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29365 * Create a new TabBox
29366 * @param {Object} config The config object
29370 Roo.bootstrap.dash.TabBox = function(config){
29371 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29376 * When a pane is added
29377 * @param {Roo.bootstrap.dash.TabPane} pane
29381 * @event activatepane
29382 * When a pane is activated
29383 * @param {Roo.bootstrap.dash.TabPane} pane
29385 "activatepane" : true
29393 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29398 tabScrollable : false,
29400 getChildContainer : function()
29402 return this.el.select('.tab-content', true).first();
29405 getAutoCreate : function(){
29409 cls: 'pull-left header',
29417 cls: 'fa ' + this.icon
29423 cls: 'nav nav-tabs pull-right',
29429 if(this.tabScrollable){
29436 cls: 'nav nav-tabs pull-right',
29447 cls: 'nav-tabs-custom',
29452 cls: 'tab-content no-padding',
29460 initEvents : function()
29462 //Roo.log('add add pane handler');
29463 this.on('addpane', this.onAddPane, this);
29466 * Updates the box title
29467 * @param {String} html to set the title to.
29469 setTitle : function(value)
29471 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29473 onAddPane : function(pane)
29475 this.panes.push(pane);
29476 //Roo.log('addpane');
29478 // tabs are rendere left to right..
29479 if(!this.showtabs){
29483 var ctr = this.el.select('.nav-tabs', true).first();
29486 var existing = ctr.select('.nav-tab',true);
29487 var qty = existing.getCount();;
29490 var tab = ctr.createChild({
29492 cls : 'nav-tab' + (qty ? '' : ' active'),
29500 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29503 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29505 pane.el.addClass('active');
29510 onTabClick : function(ev,un,ob,pane)
29512 //Roo.log('tab - prev default');
29513 ev.preventDefault();
29516 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29517 pane.tab.addClass('active');
29518 //Roo.log(pane.title);
29519 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29520 // technically we should have a deactivate event.. but maybe add later.
29521 // and it should not de-activate the selected tab...
29522 this.fireEvent('activatepane', pane);
29523 pane.el.addClass('active');
29524 pane.fireEvent('activate');
29529 getActivePane : function()
29532 Roo.each(this.panes, function(p) {
29533 if(p.el.hasClass('active')){
29554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29556 * @class Roo.bootstrap.TabPane
29557 * @extends Roo.bootstrap.Component
29558 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29559 * Bootstrap TabPane class
29560 * @cfg {Boolean} active (false | true) Default false
29561 * @cfg {String} title title of panel
29565 * Create a new TabPane
29566 * @param {Object} config The config object
29569 Roo.bootstrap.dash.TabPane = function(config){
29570 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29576 * When a pane is activated
29577 * @param {Roo.bootstrap.dash.TabPane} pane
29584 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29589 // the tabBox that this is attached to.
29592 getAutoCreate : function()
29600 cfg.cls += ' active';
29605 initEvents : function()
29607 //Roo.log('trigger add pane handler');
29608 this.parent().fireEvent('addpane', this)
29612 * Updates the tab title
29613 * @param {String} html to set the title to.
29615 setTitle: function(str)
29621 this.tab.select('a', true).first().dom.innerHTML = str;
29640 * @class Roo.bootstrap.Tooltip
29641 * Bootstrap Tooltip class
29642 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29643 * to determine which dom element triggers the tooltip.
29645 * It needs to add support for additional attributes like tooltip-position
29648 * Create a new Toolti
29649 * @param {Object} config The config object
29652 Roo.bootstrap.Tooltip = function(config){
29653 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29655 this.alignment = Roo.bootstrap.Tooltip.alignment;
29657 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29658 this.alignment = config.alignment;
29663 Roo.apply(Roo.bootstrap.Tooltip, {
29665 * @function init initialize tooltip monitoring.
29669 currentTip : false,
29670 currentRegion : false,
29676 Roo.get(document).on('mouseover', this.enter ,this);
29677 Roo.get(document).on('mouseout', this.leave, this);
29680 this.currentTip = new Roo.bootstrap.Tooltip();
29683 enter : function(ev)
29685 var dom = ev.getTarget();
29687 //Roo.log(['enter',dom]);
29688 var el = Roo.fly(dom);
29689 if (this.currentEl) {
29691 //Roo.log(this.currentEl);
29692 //Roo.log(this.currentEl.contains(dom));
29693 if (this.currentEl == el) {
29696 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29702 if (this.currentTip.el) {
29703 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29707 if(!el || el.dom == document){
29713 if (!el.attr('tooltip')) {
29714 pel = el.findParent("[tooltip]");
29716 bindEl = Roo.get(pel);
29722 // you can not look for children, as if el is the body.. then everythign is the child..
29723 if (!pel && !el.attr('tooltip')) { //
29724 if (!el.select("[tooltip]").elements.length) {
29727 // is the mouse over this child...?
29728 bindEl = el.select("[tooltip]").first();
29729 var xy = ev.getXY();
29730 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29731 //Roo.log("not in region.");
29734 //Roo.log("child element over..");
29737 this.currentEl = el;
29738 this.currentTip.bind(bindEl);
29739 this.currentRegion = Roo.lib.Region.getRegion(dom);
29740 this.currentTip.enter();
29743 leave : function(ev)
29745 var dom = ev.getTarget();
29746 //Roo.log(['leave',dom]);
29747 if (!this.currentEl) {
29752 if (dom != this.currentEl.dom) {
29755 var xy = ev.getXY();
29756 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29759 // only activate leave if mouse cursor is outside... bounding box..
29764 if (this.currentTip) {
29765 this.currentTip.leave();
29767 //Roo.log('clear currentEl');
29768 this.currentEl = false;
29773 'left' : ['r-l', [-2,0], 'right'],
29774 'right' : ['l-r', [2,0], 'left'],
29775 'bottom' : ['t-b', [0,2], 'top'],
29776 'top' : [ 'b-t', [0,-2], 'bottom']
29782 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29787 delay : null, // can be { show : 300 , hide: 500}
29791 hoverState : null, //???
29793 placement : 'bottom',
29797 getAutoCreate : function(){
29804 cls : 'tooltip-arrow arrow'
29807 cls : 'tooltip-inner'
29814 bind : function(el)
29819 initEvents : function()
29821 this.arrowEl = this.el.select('.arrow', true).first();
29822 this.innerEl = this.el.select('.tooltip-inner', true).first();
29825 enter : function () {
29827 if (this.timeout != null) {
29828 clearTimeout(this.timeout);
29831 this.hoverState = 'in';
29832 //Roo.log("enter - show");
29833 if (!this.delay || !this.delay.show) {
29838 this.timeout = setTimeout(function () {
29839 if (_t.hoverState == 'in') {
29842 }, this.delay.show);
29846 clearTimeout(this.timeout);
29848 this.hoverState = 'out';
29849 if (!this.delay || !this.delay.hide) {
29855 this.timeout = setTimeout(function () {
29856 //Roo.log("leave - timeout");
29858 if (_t.hoverState == 'out') {
29860 Roo.bootstrap.Tooltip.currentEl = false;
29865 show : function (msg)
29868 this.render(document.body);
29871 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29873 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29875 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29877 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29878 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29880 var placement = typeof this.placement == 'function' ?
29881 this.placement.call(this, this.el, on_el) :
29884 var autoToken = /\s?auto?\s?/i;
29885 var autoPlace = autoToken.test(placement);
29887 placement = placement.replace(autoToken, '') || 'top';
29891 //this.el.setXY([0,0]);
29893 //this.el.dom.style.display='block';
29895 //this.el.appendTo(on_el);
29897 var p = this.getPosition();
29898 var box = this.el.getBox();
29904 var align = this.alignment[placement];
29906 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29908 if(placement == 'top' || placement == 'bottom'){
29910 placement = 'right';
29913 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29914 placement = 'left';
29917 var scroll = Roo.select('body', true).first().getScroll();
29919 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29923 align = this.alignment[placement];
29925 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29929 var elems = document.getElementsByTagName('div');
29930 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29931 for (var i = 0; i < elems.length; i++) {
29932 var zindex = Number.parseInt(
29933 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29936 if (zindex > highest) {
29943 this.el.dom.style.zIndex = highest;
29945 this.el.alignTo(this.bindEl, align[0],align[1]);
29946 //var arrow = this.el.select('.arrow',true).first();
29947 //arrow.set(align[2],
29949 this.el.addClass(placement);
29950 this.el.addClass("bs-tooltip-"+ placement);
29952 this.el.addClass('in fade show');
29954 this.hoverState = null;
29956 if (this.el.hasClass('fade')) {
29971 //this.el.setXY([0,0]);
29972 this.el.removeClass(['show', 'in']);
29988 * @class Roo.bootstrap.LocationPicker
29989 * @extends Roo.bootstrap.Component
29990 * Bootstrap LocationPicker class
29991 * @cfg {Number} latitude Position when init default 0
29992 * @cfg {Number} longitude Position when init default 0
29993 * @cfg {Number} zoom default 15
29994 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29995 * @cfg {Boolean} mapTypeControl default false
29996 * @cfg {Boolean} disableDoubleClickZoom default false
29997 * @cfg {Boolean} scrollwheel default true
29998 * @cfg {Boolean} streetViewControl default false
29999 * @cfg {Number} radius default 0
30000 * @cfg {String} locationName
30001 * @cfg {Boolean} draggable default true
30002 * @cfg {Boolean} enableAutocomplete default false
30003 * @cfg {Boolean} enableReverseGeocode default true
30004 * @cfg {String} markerTitle
30007 * Create a new LocationPicker
30008 * @param {Object} config The config object
30012 Roo.bootstrap.LocationPicker = function(config){
30014 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30019 * Fires when the picker initialized.
30020 * @param {Roo.bootstrap.LocationPicker} this
30021 * @param {Google Location} location
30025 * @event positionchanged
30026 * Fires when the picker position changed.
30027 * @param {Roo.bootstrap.LocationPicker} this
30028 * @param {Google Location} location
30030 positionchanged : true,
30033 * Fires when the map resize.
30034 * @param {Roo.bootstrap.LocationPicker} this
30039 * Fires when the map show.
30040 * @param {Roo.bootstrap.LocationPicker} this
30045 * Fires when the map hide.
30046 * @param {Roo.bootstrap.LocationPicker} this
30051 * Fires when click the map.
30052 * @param {Roo.bootstrap.LocationPicker} this
30053 * @param {Map event} e
30057 * @event mapRightClick
30058 * Fires when right click the map.
30059 * @param {Roo.bootstrap.LocationPicker} this
30060 * @param {Map event} e
30062 mapRightClick : true,
30064 * @event markerClick
30065 * Fires when click the marker.
30066 * @param {Roo.bootstrap.LocationPicker} this
30067 * @param {Map event} e
30069 markerClick : true,
30071 * @event markerRightClick
30072 * Fires when right click the marker.
30073 * @param {Roo.bootstrap.LocationPicker} this
30074 * @param {Map event} e
30076 markerRightClick : true,
30078 * @event OverlayViewDraw
30079 * Fires when OverlayView Draw
30080 * @param {Roo.bootstrap.LocationPicker} this
30082 OverlayViewDraw : true,
30084 * @event OverlayViewOnAdd
30085 * Fires when OverlayView Draw
30086 * @param {Roo.bootstrap.LocationPicker} this
30088 OverlayViewOnAdd : true,
30090 * @event OverlayViewOnRemove
30091 * Fires when OverlayView Draw
30092 * @param {Roo.bootstrap.LocationPicker} this
30094 OverlayViewOnRemove : true,
30096 * @event OverlayViewShow
30097 * Fires when OverlayView Draw
30098 * @param {Roo.bootstrap.LocationPicker} this
30099 * @param {Pixel} cpx
30101 OverlayViewShow : true,
30103 * @event OverlayViewHide
30104 * Fires when OverlayView Draw
30105 * @param {Roo.bootstrap.LocationPicker} this
30107 OverlayViewHide : true,
30109 * @event loadexception
30110 * Fires when load google lib failed.
30111 * @param {Roo.bootstrap.LocationPicker} this
30113 loadexception : true
30118 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30120 gMapContext: false,
30126 mapTypeControl: false,
30127 disableDoubleClickZoom: false,
30129 streetViewControl: false,
30133 enableAutocomplete: false,
30134 enableReverseGeocode: true,
30137 getAutoCreate: function()
30142 cls: 'roo-location-picker'
30148 initEvents: function(ct, position)
30150 if(!this.el.getWidth() || this.isApplied()){
30154 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30159 initial: function()
30161 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30162 this.fireEvent('loadexception', this);
30166 if(!this.mapTypeId){
30167 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30170 this.gMapContext = this.GMapContext();
30172 this.initOverlayView();
30174 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30178 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30179 _this.setPosition(_this.gMapContext.marker.position);
30182 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30183 _this.fireEvent('mapClick', this, event);
30187 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30188 _this.fireEvent('mapRightClick', this, event);
30192 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30193 _this.fireEvent('markerClick', this, event);
30197 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30198 _this.fireEvent('markerRightClick', this, event);
30202 this.setPosition(this.gMapContext.location);
30204 this.fireEvent('initial', this, this.gMapContext.location);
30207 initOverlayView: function()
30211 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30215 _this.fireEvent('OverlayViewDraw', _this);
30220 _this.fireEvent('OverlayViewOnAdd', _this);
30223 onRemove: function()
30225 _this.fireEvent('OverlayViewOnRemove', _this);
30228 show: function(cpx)
30230 _this.fireEvent('OverlayViewShow', _this, cpx);
30235 _this.fireEvent('OverlayViewHide', _this);
30241 fromLatLngToContainerPixel: function(event)
30243 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30246 isApplied: function()
30248 return this.getGmapContext() == false ? false : true;
30251 getGmapContext: function()
30253 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30256 GMapContext: function()
30258 var position = new google.maps.LatLng(this.latitude, this.longitude);
30260 var _map = new google.maps.Map(this.el.dom, {
30263 mapTypeId: this.mapTypeId,
30264 mapTypeControl: this.mapTypeControl,
30265 disableDoubleClickZoom: this.disableDoubleClickZoom,
30266 scrollwheel: this.scrollwheel,
30267 streetViewControl: this.streetViewControl,
30268 locationName: this.locationName,
30269 draggable: this.draggable,
30270 enableAutocomplete: this.enableAutocomplete,
30271 enableReverseGeocode: this.enableReverseGeocode
30274 var _marker = new google.maps.Marker({
30275 position: position,
30277 title: this.markerTitle,
30278 draggable: this.draggable
30285 location: position,
30286 radius: this.radius,
30287 locationName: this.locationName,
30288 addressComponents: {
30289 formatted_address: null,
30290 addressLine1: null,
30291 addressLine2: null,
30293 streetNumber: null,
30297 stateOrProvince: null
30300 domContainer: this.el.dom,
30301 geodecoder: new google.maps.Geocoder()
30305 drawCircle: function(center, radius, options)
30307 if (this.gMapContext.circle != null) {
30308 this.gMapContext.circle.setMap(null);
30312 options = Roo.apply({}, options, {
30313 strokeColor: "#0000FF",
30314 strokeOpacity: .35,
30316 fillColor: "#0000FF",
30320 options.map = this.gMapContext.map;
30321 options.radius = radius;
30322 options.center = center;
30323 this.gMapContext.circle = new google.maps.Circle(options);
30324 return this.gMapContext.circle;
30330 setPosition: function(location)
30332 this.gMapContext.location = location;
30333 this.gMapContext.marker.setPosition(location);
30334 this.gMapContext.map.panTo(location);
30335 this.drawCircle(location, this.gMapContext.radius, {});
30339 if (this.gMapContext.settings.enableReverseGeocode) {
30340 this.gMapContext.geodecoder.geocode({
30341 latLng: this.gMapContext.location
30342 }, function(results, status) {
30344 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30345 _this.gMapContext.locationName = results[0].formatted_address;
30346 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30348 _this.fireEvent('positionchanged', this, location);
30355 this.fireEvent('positionchanged', this, location);
30360 google.maps.event.trigger(this.gMapContext.map, "resize");
30362 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30364 this.fireEvent('resize', this);
30367 setPositionByLatLng: function(latitude, longitude)
30369 this.setPosition(new google.maps.LatLng(latitude, longitude));
30372 getCurrentPosition: function()
30375 latitude: this.gMapContext.location.lat(),
30376 longitude: this.gMapContext.location.lng()
30380 getAddressName: function()
30382 return this.gMapContext.locationName;
30385 getAddressComponents: function()
30387 return this.gMapContext.addressComponents;
30390 address_component_from_google_geocode: function(address_components)
30394 for (var i = 0; i < address_components.length; i++) {
30395 var component = address_components[i];
30396 if (component.types.indexOf("postal_code") >= 0) {
30397 result.postalCode = component.short_name;
30398 } else if (component.types.indexOf("street_number") >= 0) {
30399 result.streetNumber = component.short_name;
30400 } else if (component.types.indexOf("route") >= 0) {
30401 result.streetName = component.short_name;
30402 } else if (component.types.indexOf("neighborhood") >= 0) {
30403 result.city = component.short_name;
30404 } else if (component.types.indexOf("locality") >= 0) {
30405 result.city = component.short_name;
30406 } else if (component.types.indexOf("sublocality") >= 0) {
30407 result.district = component.short_name;
30408 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30409 result.stateOrProvince = component.short_name;
30410 } else if (component.types.indexOf("country") >= 0) {
30411 result.country = component.short_name;
30415 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30416 result.addressLine2 = "";
30420 setZoomLevel: function(zoom)
30422 this.gMapContext.map.setZoom(zoom);
30435 this.fireEvent('show', this);
30446 this.fireEvent('hide', this);
30451 Roo.apply(Roo.bootstrap.LocationPicker, {
30453 OverlayView : function(map, options)
30455 options = options || {};
30462 * @class Roo.bootstrap.Alert
30463 * @extends Roo.bootstrap.Component
30464 * Bootstrap Alert class - shows an alert area box
30466 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30467 Enter a valid email address
30470 * @cfg {String} title The title of alert
30471 * @cfg {String} html The content of alert
30472 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30473 * @cfg {String} fa font-awesomeicon
30474 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30475 * @cfg {Boolean} close true to show a x closer
30479 * Create a new alert
30480 * @param {Object} config The config object
30484 Roo.bootstrap.Alert = function(config){
30485 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30489 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30495 faicon: false, // BC
30499 getAutoCreate : function()
30511 style : this.close ? '' : 'display:none'
30515 cls : 'roo-alert-icon'
30520 cls : 'roo-alert-title',
30525 cls : 'roo-alert-text',
30532 cfg.cn[0].cls += ' fa ' + this.faicon;
30535 cfg.cn[0].cls += ' fa ' + this.fa;
30539 cfg.cls += ' alert-' + this.weight;
30545 initEvents: function()
30547 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30548 this.titleEl = this.el.select('.roo-alert-title',true).first();
30549 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30550 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30551 if (this.seconds > 0) {
30552 this.hide.defer(this.seconds, this);
30556 * Set the Title Message HTML
30557 * @param {String} html
30559 setTitle : function(str)
30561 this.titleEl.dom.innerHTML = str;
30565 * Set the Body Message HTML
30566 * @param {String} html
30568 setHtml : function(str)
30570 this.htmlEl.dom.innerHTML = str;
30573 * Set the Weight of the alert
30574 * @param {String} (success|info|warning|danger) weight
30577 setWeight : function(weight)
30580 this.el.removeClass('alert-' + this.weight);
30583 this.weight = weight;
30585 this.el.addClass('alert-' + this.weight);
30588 * Set the Icon of the alert
30589 * @param {String} see fontawsome names (name without the 'fa-' bit)
30591 setIcon : function(icon)
30594 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30597 this.faicon = icon;
30599 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30624 * @class Roo.bootstrap.UploadCropbox
30625 * @extends Roo.bootstrap.Component
30626 * Bootstrap UploadCropbox class
30627 * @cfg {String} emptyText show when image has been loaded
30628 * @cfg {String} rotateNotify show when image too small to rotate
30629 * @cfg {Number} errorTimeout default 3000
30630 * @cfg {Number} minWidth default 300
30631 * @cfg {Number} minHeight default 300
30632 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30633 * @cfg {Boolean} isDocument (true|false) default false
30634 * @cfg {String} url action url
30635 * @cfg {String} paramName default 'imageUpload'
30636 * @cfg {String} method default POST
30637 * @cfg {Boolean} loadMask (true|false) default true
30638 * @cfg {Boolean} loadingText default 'Loading...'
30641 * Create a new UploadCropbox
30642 * @param {Object} config The config object
30645 Roo.bootstrap.UploadCropbox = function(config){
30646 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30650 * @event beforeselectfile
30651 * Fire before select file
30652 * @param {Roo.bootstrap.UploadCropbox} this
30654 "beforeselectfile" : true,
30657 * Fire after initEvent
30658 * @param {Roo.bootstrap.UploadCropbox} this
30663 * Fire after initEvent
30664 * @param {Roo.bootstrap.UploadCropbox} this
30665 * @param {String} data
30670 * Fire when preparing the file data
30671 * @param {Roo.bootstrap.UploadCropbox} this
30672 * @param {Object} file
30677 * Fire when get exception
30678 * @param {Roo.bootstrap.UploadCropbox} this
30679 * @param {XMLHttpRequest} xhr
30681 "exception" : true,
30683 * @event beforeloadcanvas
30684 * Fire before load the canvas
30685 * @param {Roo.bootstrap.UploadCropbox} this
30686 * @param {String} src
30688 "beforeloadcanvas" : true,
30691 * Fire when trash image
30692 * @param {Roo.bootstrap.UploadCropbox} this
30697 * Fire when download the image
30698 * @param {Roo.bootstrap.UploadCropbox} this
30702 * @event footerbuttonclick
30703 * Fire when footerbuttonclick
30704 * @param {Roo.bootstrap.UploadCropbox} this
30705 * @param {String} type
30707 "footerbuttonclick" : true,
30711 * @param {Roo.bootstrap.UploadCropbox} this
30716 * Fire when rotate the image
30717 * @param {Roo.bootstrap.UploadCropbox} this
30718 * @param {String} pos
30723 * Fire when inspect the file
30724 * @param {Roo.bootstrap.UploadCropbox} this
30725 * @param {Object} file
30730 * Fire when xhr upload the file
30731 * @param {Roo.bootstrap.UploadCropbox} this
30732 * @param {Object} data
30737 * Fire when arrange the file data
30738 * @param {Roo.bootstrap.UploadCropbox} this
30739 * @param {Object} formData
30744 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30747 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30749 emptyText : 'Click to upload image',
30750 rotateNotify : 'Image is too small to rotate',
30751 errorTimeout : 3000,
30765 cropType : 'image/jpeg',
30767 canvasLoaded : false,
30768 isDocument : false,
30770 paramName : 'imageUpload',
30772 loadingText : 'Loading...',
30775 getAutoCreate : function()
30779 cls : 'roo-upload-cropbox',
30783 cls : 'roo-upload-cropbox-selector',
30788 cls : 'roo-upload-cropbox-body',
30789 style : 'cursor:pointer',
30793 cls : 'roo-upload-cropbox-preview'
30797 cls : 'roo-upload-cropbox-thumb'
30801 cls : 'roo-upload-cropbox-empty-notify',
30802 html : this.emptyText
30806 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30807 html : this.rotateNotify
30813 cls : 'roo-upload-cropbox-footer',
30816 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30826 onRender : function(ct, position)
30828 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30830 if (this.buttons.length) {
30832 Roo.each(this.buttons, function(bb) {
30834 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30836 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30842 this.maskEl = this.el;
30846 initEvents : function()
30848 this.urlAPI = (window.createObjectURL && window) ||
30849 (window.URL && URL.revokeObjectURL && URL) ||
30850 (window.webkitURL && webkitURL);
30852 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30853 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30855 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30856 this.selectorEl.hide();
30858 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30859 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30861 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30862 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30863 this.thumbEl.hide();
30865 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30866 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30868 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30869 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30870 this.errorEl.hide();
30872 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30873 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30874 this.footerEl.hide();
30876 this.setThumbBoxSize();
30882 this.fireEvent('initial', this);
30889 window.addEventListener("resize", function() { _this.resize(); } );
30891 this.bodyEl.on('click', this.beforeSelectFile, this);
30894 this.bodyEl.on('touchstart', this.onTouchStart, this);
30895 this.bodyEl.on('touchmove', this.onTouchMove, this);
30896 this.bodyEl.on('touchend', this.onTouchEnd, this);
30900 this.bodyEl.on('mousedown', this.onMouseDown, this);
30901 this.bodyEl.on('mousemove', this.onMouseMove, this);
30902 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30903 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30904 Roo.get(document).on('mouseup', this.onMouseUp, this);
30907 this.selectorEl.on('change', this.onFileSelected, this);
30913 this.baseScale = 1;
30915 this.baseRotate = 1;
30916 this.dragable = false;
30917 this.pinching = false;
30920 this.cropData = false;
30921 this.notifyEl.dom.innerHTML = this.emptyText;
30923 this.selectorEl.dom.value = '';
30927 resize : function()
30929 if(this.fireEvent('resize', this) != false){
30930 this.setThumbBoxPosition();
30931 this.setCanvasPosition();
30935 onFooterButtonClick : function(e, el, o, type)
30938 case 'rotate-left' :
30939 this.onRotateLeft(e);
30941 case 'rotate-right' :
30942 this.onRotateRight(e);
30945 this.beforeSelectFile(e);
30960 this.fireEvent('footerbuttonclick', this, type);
30963 beforeSelectFile : function(e)
30965 e.preventDefault();
30967 if(this.fireEvent('beforeselectfile', this) != false){
30968 this.selectorEl.dom.click();
30972 onFileSelected : function(e)
30974 e.preventDefault();
30976 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30980 var file = this.selectorEl.dom.files[0];
30982 if(this.fireEvent('inspect', this, file) != false){
30983 this.prepare(file);
30988 trash : function(e)
30990 this.fireEvent('trash', this);
30993 download : function(e)
30995 this.fireEvent('download', this);
30998 loadCanvas : function(src)
31000 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31004 this.imageEl = document.createElement('img');
31008 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31010 this.imageEl.src = src;
31014 onLoadCanvas : function()
31016 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31017 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31019 this.bodyEl.un('click', this.beforeSelectFile, this);
31021 this.notifyEl.hide();
31022 this.thumbEl.show();
31023 this.footerEl.show();
31025 this.baseRotateLevel();
31027 if(this.isDocument){
31028 this.setThumbBoxSize();
31031 this.setThumbBoxPosition();
31033 this.baseScaleLevel();
31039 this.canvasLoaded = true;
31042 this.maskEl.unmask();
31047 setCanvasPosition : function()
31049 if(!this.canvasEl){
31053 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31054 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31056 this.previewEl.setLeft(pw);
31057 this.previewEl.setTop(ph);
31061 onMouseDown : function(e)
31065 this.dragable = true;
31066 this.pinching = false;
31068 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31069 this.dragable = false;
31073 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31074 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31078 onMouseMove : function(e)
31082 if(!this.canvasLoaded){
31086 if (!this.dragable){
31090 var minX = Math.ceil(this.thumbEl.getLeft(true));
31091 var minY = Math.ceil(this.thumbEl.getTop(true));
31093 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31094 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31096 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31097 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31099 x = x - this.mouseX;
31100 y = y - this.mouseY;
31102 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31103 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31105 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31106 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31108 this.previewEl.setLeft(bgX);
31109 this.previewEl.setTop(bgY);
31111 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31112 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31115 onMouseUp : function(e)
31119 this.dragable = false;
31122 onMouseWheel : function(e)
31126 this.startScale = this.scale;
31128 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31130 if(!this.zoomable()){
31131 this.scale = this.startScale;
31140 zoomable : function()
31142 var minScale = this.thumbEl.getWidth() / this.minWidth;
31144 if(this.minWidth < this.minHeight){
31145 minScale = this.thumbEl.getHeight() / this.minHeight;
31148 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31149 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31153 (this.rotate == 0 || this.rotate == 180) &&
31155 width > this.imageEl.OriginWidth ||
31156 height > this.imageEl.OriginHeight ||
31157 (width < this.minWidth && height < this.minHeight)
31165 (this.rotate == 90 || this.rotate == 270) &&
31167 width > this.imageEl.OriginWidth ||
31168 height > this.imageEl.OriginHeight ||
31169 (width < this.minHeight && height < this.minWidth)
31176 !this.isDocument &&
31177 (this.rotate == 0 || this.rotate == 180) &&
31179 width < this.minWidth ||
31180 width > this.imageEl.OriginWidth ||
31181 height < this.minHeight ||
31182 height > this.imageEl.OriginHeight
31189 !this.isDocument &&
31190 (this.rotate == 90 || this.rotate == 270) &&
31192 width < this.minHeight ||
31193 width > this.imageEl.OriginWidth ||
31194 height < this.minWidth ||
31195 height > this.imageEl.OriginHeight
31205 onRotateLeft : function(e)
31207 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31209 var minScale = this.thumbEl.getWidth() / this.minWidth;
31211 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31212 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31214 this.startScale = this.scale;
31216 while (this.getScaleLevel() < minScale){
31218 this.scale = this.scale + 1;
31220 if(!this.zoomable()){
31225 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31226 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31231 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31238 this.scale = this.startScale;
31240 this.onRotateFail();
31245 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31247 if(this.isDocument){
31248 this.setThumbBoxSize();
31249 this.setThumbBoxPosition();
31250 this.setCanvasPosition();
31255 this.fireEvent('rotate', this, 'left');
31259 onRotateRight : function(e)
31261 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31263 var minScale = this.thumbEl.getWidth() / this.minWidth;
31265 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31266 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31268 this.startScale = this.scale;
31270 while (this.getScaleLevel() < minScale){
31272 this.scale = this.scale + 1;
31274 if(!this.zoomable()){
31279 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31280 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31285 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31292 this.scale = this.startScale;
31294 this.onRotateFail();
31299 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31301 if(this.isDocument){
31302 this.setThumbBoxSize();
31303 this.setThumbBoxPosition();
31304 this.setCanvasPosition();
31309 this.fireEvent('rotate', this, 'right');
31312 onRotateFail : function()
31314 this.errorEl.show(true);
31318 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31323 this.previewEl.dom.innerHTML = '';
31325 var canvasEl = document.createElement("canvas");
31327 var contextEl = canvasEl.getContext("2d");
31329 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31330 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31331 var center = this.imageEl.OriginWidth / 2;
31333 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31334 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31335 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31336 center = this.imageEl.OriginHeight / 2;
31339 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31341 contextEl.translate(center, center);
31342 contextEl.rotate(this.rotate * Math.PI / 180);
31344 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31346 this.canvasEl = document.createElement("canvas");
31348 this.contextEl = this.canvasEl.getContext("2d");
31350 switch (this.rotate) {
31353 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31354 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31356 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31361 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31362 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31364 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31365 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);
31369 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31374 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31375 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31377 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31378 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);
31382 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);
31387 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31388 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31390 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31391 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31395 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);
31402 this.previewEl.appendChild(this.canvasEl);
31404 this.setCanvasPosition();
31409 if(!this.canvasLoaded){
31413 var imageCanvas = document.createElement("canvas");
31415 var imageContext = imageCanvas.getContext("2d");
31417 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31418 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31420 var center = imageCanvas.width / 2;
31422 imageContext.translate(center, center);
31424 imageContext.rotate(this.rotate * Math.PI / 180);
31426 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31428 var canvas = document.createElement("canvas");
31430 var context = canvas.getContext("2d");
31432 canvas.width = this.minWidth;
31433 canvas.height = this.minHeight;
31435 switch (this.rotate) {
31438 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31439 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31441 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31442 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31444 var targetWidth = this.minWidth - 2 * x;
31445 var targetHeight = this.minHeight - 2 * y;
31449 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31450 scale = targetWidth / width;
31453 if(x > 0 && y == 0){
31454 scale = targetHeight / height;
31457 if(x > 0 && y > 0){
31458 scale = targetWidth / width;
31460 if(width < height){
31461 scale = targetHeight / height;
31465 context.scale(scale, scale);
31467 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31468 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31470 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31471 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31473 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31478 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31479 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31481 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31482 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31484 var targetWidth = this.minWidth - 2 * x;
31485 var targetHeight = this.minHeight - 2 * y;
31489 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31490 scale = targetWidth / width;
31493 if(x > 0 && y == 0){
31494 scale = targetHeight / height;
31497 if(x > 0 && y > 0){
31498 scale = targetWidth / width;
31500 if(width < height){
31501 scale = targetHeight / height;
31505 context.scale(scale, scale);
31507 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31508 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31510 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31511 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31513 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31515 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31520 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31521 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31523 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31524 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31526 var targetWidth = this.minWidth - 2 * x;
31527 var targetHeight = this.minHeight - 2 * y;
31531 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31532 scale = targetWidth / width;
31535 if(x > 0 && y == 0){
31536 scale = targetHeight / height;
31539 if(x > 0 && y > 0){
31540 scale = targetWidth / width;
31542 if(width < height){
31543 scale = targetHeight / height;
31547 context.scale(scale, scale);
31549 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31550 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31552 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31553 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31555 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31556 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31558 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31563 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31564 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31566 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31567 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31569 var targetWidth = this.minWidth - 2 * x;
31570 var targetHeight = this.minHeight - 2 * y;
31574 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31575 scale = targetWidth / width;
31578 if(x > 0 && y == 0){
31579 scale = targetHeight / height;
31582 if(x > 0 && y > 0){
31583 scale = targetWidth / width;
31585 if(width < height){
31586 scale = targetHeight / height;
31590 context.scale(scale, scale);
31592 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31593 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31595 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31596 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31598 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31600 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31607 this.cropData = canvas.toDataURL(this.cropType);
31609 if(this.fireEvent('crop', this, this.cropData) !== false){
31610 this.process(this.file, this.cropData);
31617 setThumbBoxSize : function()
31621 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31622 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31623 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31625 this.minWidth = width;
31626 this.minHeight = height;
31628 if(this.rotate == 90 || this.rotate == 270){
31629 this.minWidth = height;
31630 this.minHeight = width;
31635 width = Math.ceil(this.minWidth * height / this.minHeight);
31637 if(this.minWidth > this.minHeight){
31639 height = Math.ceil(this.minHeight * width / this.minWidth);
31642 this.thumbEl.setStyle({
31643 width : width + 'px',
31644 height : height + 'px'
31651 setThumbBoxPosition : function()
31653 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31654 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31656 this.thumbEl.setLeft(x);
31657 this.thumbEl.setTop(y);
31661 baseRotateLevel : function()
31663 this.baseRotate = 1;
31666 typeof(this.exif) != 'undefined' &&
31667 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31668 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31670 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31673 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31677 baseScaleLevel : function()
31681 if(this.isDocument){
31683 if(this.baseRotate == 6 || this.baseRotate == 8){
31685 height = this.thumbEl.getHeight();
31686 this.baseScale = height / this.imageEl.OriginWidth;
31688 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31689 width = this.thumbEl.getWidth();
31690 this.baseScale = width / this.imageEl.OriginHeight;
31696 height = this.thumbEl.getHeight();
31697 this.baseScale = height / this.imageEl.OriginHeight;
31699 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31700 width = this.thumbEl.getWidth();
31701 this.baseScale = width / this.imageEl.OriginWidth;
31707 if(this.baseRotate == 6 || this.baseRotate == 8){
31709 width = this.thumbEl.getHeight();
31710 this.baseScale = width / this.imageEl.OriginHeight;
31712 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31713 height = this.thumbEl.getWidth();
31714 this.baseScale = height / this.imageEl.OriginHeight;
31717 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31718 height = this.thumbEl.getWidth();
31719 this.baseScale = height / this.imageEl.OriginHeight;
31721 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31722 width = this.thumbEl.getHeight();
31723 this.baseScale = width / this.imageEl.OriginWidth;
31730 width = this.thumbEl.getWidth();
31731 this.baseScale = width / this.imageEl.OriginWidth;
31733 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31734 height = this.thumbEl.getHeight();
31735 this.baseScale = height / this.imageEl.OriginHeight;
31738 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31740 height = this.thumbEl.getHeight();
31741 this.baseScale = height / this.imageEl.OriginHeight;
31743 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31744 width = this.thumbEl.getWidth();
31745 this.baseScale = width / this.imageEl.OriginWidth;
31753 getScaleLevel : function()
31755 return this.baseScale * Math.pow(1.1, this.scale);
31758 onTouchStart : function(e)
31760 if(!this.canvasLoaded){
31761 this.beforeSelectFile(e);
31765 var touches = e.browserEvent.touches;
31771 if(touches.length == 1){
31772 this.onMouseDown(e);
31776 if(touches.length != 2){
31782 for(var i = 0, finger; finger = touches[i]; i++){
31783 coords.push(finger.pageX, finger.pageY);
31786 var x = Math.pow(coords[0] - coords[2], 2);
31787 var y = Math.pow(coords[1] - coords[3], 2);
31789 this.startDistance = Math.sqrt(x + y);
31791 this.startScale = this.scale;
31793 this.pinching = true;
31794 this.dragable = false;
31798 onTouchMove : function(e)
31800 if(!this.pinching && !this.dragable){
31804 var touches = e.browserEvent.touches;
31811 this.onMouseMove(e);
31817 for(var i = 0, finger; finger = touches[i]; i++){
31818 coords.push(finger.pageX, finger.pageY);
31821 var x = Math.pow(coords[0] - coords[2], 2);
31822 var y = Math.pow(coords[1] - coords[3], 2);
31824 this.endDistance = Math.sqrt(x + y);
31826 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31828 if(!this.zoomable()){
31829 this.scale = this.startScale;
31837 onTouchEnd : function(e)
31839 this.pinching = false;
31840 this.dragable = false;
31844 process : function(file, crop)
31847 this.maskEl.mask(this.loadingText);
31850 this.xhr = new XMLHttpRequest();
31852 file.xhr = this.xhr;
31854 this.xhr.open(this.method, this.url, true);
31857 "Accept": "application/json",
31858 "Cache-Control": "no-cache",
31859 "X-Requested-With": "XMLHttpRequest"
31862 for (var headerName in headers) {
31863 var headerValue = headers[headerName];
31865 this.xhr.setRequestHeader(headerName, headerValue);
31871 this.xhr.onload = function()
31873 _this.xhrOnLoad(_this.xhr);
31876 this.xhr.onerror = function()
31878 _this.xhrOnError(_this.xhr);
31881 var formData = new FormData();
31883 formData.append('returnHTML', 'NO');
31886 formData.append('crop', crop);
31889 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31890 formData.append(this.paramName, file, file.name);
31893 if(typeof(file.filename) != 'undefined'){
31894 formData.append('filename', file.filename);
31897 if(typeof(file.mimetype) != 'undefined'){
31898 formData.append('mimetype', file.mimetype);
31901 if(this.fireEvent('arrange', this, formData) != false){
31902 this.xhr.send(formData);
31906 xhrOnLoad : function(xhr)
31909 this.maskEl.unmask();
31912 if (xhr.readyState !== 4) {
31913 this.fireEvent('exception', this, xhr);
31917 var response = Roo.decode(xhr.responseText);
31919 if(!response.success){
31920 this.fireEvent('exception', this, xhr);
31924 var response = Roo.decode(xhr.responseText);
31926 this.fireEvent('upload', this, response);
31930 xhrOnError : function()
31933 this.maskEl.unmask();
31936 Roo.log('xhr on error');
31938 var response = Roo.decode(xhr.responseText);
31944 prepare : function(file)
31947 this.maskEl.mask(this.loadingText);
31953 if(typeof(file) === 'string'){
31954 this.loadCanvas(file);
31958 if(!file || !this.urlAPI){
31963 this.cropType = file.type;
31967 if(this.fireEvent('prepare', this, this.file) != false){
31969 var reader = new FileReader();
31971 reader.onload = function (e) {
31972 if (e.target.error) {
31973 Roo.log(e.target.error);
31977 var buffer = e.target.result,
31978 dataView = new DataView(buffer),
31980 maxOffset = dataView.byteLength - 4,
31984 if (dataView.getUint16(0) === 0xffd8) {
31985 while (offset < maxOffset) {
31986 markerBytes = dataView.getUint16(offset);
31988 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31989 markerLength = dataView.getUint16(offset + 2) + 2;
31990 if (offset + markerLength > dataView.byteLength) {
31991 Roo.log('Invalid meta data: Invalid segment size.');
31995 if(markerBytes == 0xffe1){
31996 _this.parseExifData(
32003 offset += markerLength;
32013 var url = _this.urlAPI.createObjectURL(_this.file);
32015 _this.loadCanvas(url);
32020 reader.readAsArrayBuffer(this.file);
32026 parseExifData : function(dataView, offset, length)
32028 var tiffOffset = offset + 10,
32032 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32033 // No Exif data, might be XMP data instead
32037 // Check for the ASCII code for "Exif" (0x45786966):
32038 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32039 // No Exif data, might be XMP data instead
32042 if (tiffOffset + 8 > dataView.byteLength) {
32043 Roo.log('Invalid Exif data: Invalid segment size.');
32046 // Check for the two null bytes:
32047 if (dataView.getUint16(offset + 8) !== 0x0000) {
32048 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32051 // Check the byte alignment:
32052 switch (dataView.getUint16(tiffOffset)) {
32054 littleEndian = true;
32057 littleEndian = false;
32060 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32063 // Check for the TIFF tag marker (0x002A):
32064 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32065 Roo.log('Invalid Exif data: Missing TIFF marker.');
32068 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32069 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32071 this.parseExifTags(
32074 tiffOffset + dirOffset,
32079 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32084 if (dirOffset + 6 > dataView.byteLength) {
32085 Roo.log('Invalid Exif data: Invalid directory offset.');
32088 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32089 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32090 if (dirEndOffset + 4 > dataView.byteLength) {
32091 Roo.log('Invalid Exif data: Invalid directory size.');
32094 for (i = 0; i < tagsNumber; i += 1) {
32098 dirOffset + 2 + 12 * i, // tag offset
32102 // Return the offset to the next directory:
32103 return dataView.getUint32(dirEndOffset, littleEndian);
32106 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32108 var tag = dataView.getUint16(offset, littleEndian);
32110 this.exif[tag] = this.getExifValue(
32114 dataView.getUint16(offset + 2, littleEndian), // tag type
32115 dataView.getUint32(offset + 4, littleEndian), // tag length
32120 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32122 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32131 Roo.log('Invalid Exif data: Invalid tag type.');
32135 tagSize = tagType.size * length;
32136 // Determine if the value is contained in the dataOffset bytes,
32137 // or if the value at the dataOffset is a pointer to the actual data:
32138 dataOffset = tagSize > 4 ?
32139 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32140 if (dataOffset + tagSize > dataView.byteLength) {
32141 Roo.log('Invalid Exif data: Invalid data offset.');
32144 if (length === 1) {
32145 return tagType.getValue(dataView, dataOffset, littleEndian);
32148 for (i = 0; i < length; i += 1) {
32149 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32152 if (tagType.ascii) {
32154 // Concatenate the chars:
32155 for (i = 0; i < values.length; i += 1) {
32157 // Ignore the terminating NULL byte(s):
32158 if (c === '\u0000') {
32170 Roo.apply(Roo.bootstrap.UploadCropbox, {
32172 'Orientation': 0x0112
32176 1: 0, //'top-left',
32178 3: 180, //'bottom-right',
32179 // 4: 'bottom-left',
32181 6: 90, //'right-top',
32182 // 7: 'right-bottom',
32183 8: 270 //'left-bottom'
32187 // byte, 8-bit unsigned int:
32189 getValue: function (dataView, dataOffset) {
32190 return dataView.getUint8(dataOffset);
32194 // ascii, 8-bit byte:
32196 getValue: function (dataView, dataOffset) {
32197 return String.fromCharCode(dataView.getUint8(dataOffset));
32202 // short, 16 bit int:
32204 getValue: function (dataView, dataOffset, littleEndian) {
32205 return dataView.getUint16(dataOffset, littleEndian);
32209 // long, 32 bit int:
32211 getValue: function (dataView, dataOffset, littleEndian) {
32212 return dataView.getUint32(dataOffset, littleEndian);
32216 // rational = two long values, first is numerator, second is denominator:
32218 getValue: function (dataView, dataOffset, littleEndian) {
32219 return dataView.getUint32(dataOffset, littleEndian) /
32220 dataView.getUint32(dataOffset + 4, littleEndian);
32224 // slong, 32 bit signed int:
32226 getValue: function (dataView, dataOffset, littleEndian) {
32227 return dataView.getInt32(dataOffset, littleEndian);
32231 // srational, two slongs, first is numerator, second is denominator:
32233 getValue: function (dataView, dataOffset, littleEndian) {
32234 return dataView.getInt32(dataOffset, littleEndian) /
32235 dataView.getInt32(dataOffset + 4, littleEndian);
32245 cls : 'btn-group roo-upload-cropbox-rotate-left',
32246 action : 'rotate-left',
32250 cls : 'btn btn-default',
32251 html : '<i class="fa fa-undo"></i>'
32257 cls : 'btn-group roo-upload-cropbox-picture',
32258 action : 'picture',
32262 cls : 'btn btn-default',
32263 html : '<i class="fa fa-picture-o"></i>'
32269 cls : 'btn-group roo-upload-cropbox-rotate-right',
32270 action : 'rotate-right',
32274 cls : 'btn btn-default',
32275 html : '<i class="fa fa-repeat"></i>'
32283 cls : 'btn-group roo-upload-cropbox-rotate-left',
32284 action : 'rotate-left',
32288 cls : 'btn btn-default',
32289 html : '<i class="fa fa-undo"></i>'
32295 cls : 'btn-group roo-upload-cropbox-download',
32296 action : 'download',
32300 cls : 'btn btn-default',
32301 html : '<i class="fa fa-download"></i>'
32307 cls : 'btn-group roo-upload-cropbox-crop',
32312 cls : 'btn btn-default',
32313 html : '<i class="fa fa-crop"></i>'
32319 cls : 'btn-group roo-upload-cropbox-trash',
32324 cls : 'btn btn-default',
32325 html : '<i class="fa fa-trash"></i>'
32331 cls : 'btn-group roo-upload-cropbox-rotate-right',
32332 action : 'rotate-right',
32336 cls : 'btn btn-default',
32337 html : '<i class="fa fa-repeat"></i>'
32345 cls : 'btn-group roo-upload-cropbox-rotate-left',
32346 action : 'rotate-left',
32350 cls : 'btn btn-default',
32351 html : '<i class="fa fa-undo"></i>'
32357 cls : 'btn-group roo-upload-cropbox-rotate-right',
32358 action : 'rotate-right',
32362 cls : 'btn btn-default',
32363 html : '<i class="fa fa-repeat"></i>'
32376 * @class Roo.bootstrap.DocumentManager
32377 * @extends Roo.bootstrap.Component
32378 * Bootstrap DocumentManager class
32379 * @cfg {String} paramName default 'imageUpload'
32380 * @cfg {String} toolTipName default 'filename'
32381 * @cfg {String} method default POST
32382 * @cfg {String} url action url
32383 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32384 * @cfg {Boolean} multiple multiple upload default true
32385 * @cfg {Number} thumbSize default 300
32386 * @cfg {String} fieldLabel
32387 * @cfg {Number} labelWidth default 4
32388 * @cfg {String} labelAlign (left|top) default left
32389 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32390 * @cfg {Number} labellg set the width of label (1-12)
32391 * @cfg {Number} labelmd set the width of label (1-12)
32392 * @cfg {Number} labelsm set the width of label (1-12)
32393 * @cfg {Number} labelxs set the width of label (1-12)
32396 * Create a new DocumentManager
32397 * @param {Object} config The config object
32400 Roo.bootstrap.DocumentManager = function(config){
32401 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32404 this.delegates = [];
32409 * Fire when initial the DocumentManager
32410 * @param {Roo.bootstrap.DocumentManager} this
32415 * inspect selected file
32416 * @param {Roo.bootstrap.DocumentManager} this
32417 * @param {File} file
32422 * Fire when xhr load exception
32423 * @param {Roo.bootstrap.DocumentManager} this
32424 * @param {XMLHttpRequest} xhr
32426 "exception" : true,
32428 * @event afterupload
32429 * Fire when xhr load exception
32430 * @param {Roo.bootstrap.DocumentManager} this
32431 * @param {XMLHttpRequest} xhr
32433 "afterupload" : true,
32436 * prepare the form data
32437 * @param {Roo.bootstrap.DocumentManager} this
32438 * @param {Object} formData
32443 * Fire when remove the file
32444 * @param {Roo.bootstrap.DocumentManager} this
32445 * @param {Object} file
32450 * Fire after refresh the file
32451 * @param {Roo.bootstrap.DocumentManager} this
32456 * Fire after click the image
32457 * @param {Roo.bootstrap.DocumentManager} this
32458 * @param {Object} file
32463 * Fire when upload a image and editable set to true
32464 * @param {Roo.bootstrap.DocumentManager} this
32465 * @param {Object} file
32469 * @event beforeselectfile
32470 * Fire before select file
32471 * @param {Roo.bootstrap.DocumentManager} this
32473 "beforeselectfile" : true,
32476 * Fire before process file
32477 * @param {Roo.bootstrap.DocumentManager} this
32478 * @param {Object} file
32482 * @event previewrendered
32483 * Fire when preview rendered
32484 * @param {Roo.bootstrap.DocumentManager} this
32485 * @param {Object} file
32487 "previewrendered" : true,
32490 "previewResize" : true
32495 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32504 paramName : 'imageUpload',
32505 toolTipName : 'filename',
32508 labelAlign : 'left',
32518 getAutoCreate : function()
32520 var managerWidget = {
32522 cls : 'roo-document-manager',
32526 cls : 'roo-document-manager-selector',
32531 cls : 'roo-document-manager-uploader',
32535 cls : 'roo-document-manager-upload-btn',
32536 html : '<i class="fa fa-plus"></i>'
32547 cls : 'column col-md-12',
32552 if(this.fieldLabel.length){
32557 cls : 'column col-md-12',
32558 html : this.fieldLabel
32562 cls : 'column col-md-12',
32567 if(this.labelAlign == 'left'){
32572 html : this.fieldLabel
32581 if(this.labelWidth > 12){
32582 content[0].style = "width: " + this.labelWidth + 'px';
32585 if(this.labelWidth < 13 && this.labelmd == 0){
32586 this.labelmd = this.labelWidth;
32589 if(this.labellg > 0){
32590 content[0].cls += ' col-lg-' + this.labellg;
32591 content[1].cls += ' col-lg-' + (12 - this.labellg);
32594 if(this.labelmd > 0){
32595 content[0].cls += ' col-md-' + this.labelmd;
32596 content[1].cls += ' col-md-' + (12 - this.labelmd);
32599 if(this.labelsm > 0){
32600 content[0].cls += ' col-sm-' + this.labelsm;
32601 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32604 if(this.labelxs > 0){
32605 content[0].cls += ' col-xs-' + this.labelxs;
32606 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32614 cls : 'row clearfix',
32622 initEvents : function()
32624 this.managerEl = this.el.select('.roo-document-manager', true).first();
32625 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32627 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32628 this.selectorEl.hide();
32631 this.selectorEl.attr('multiple', 'multiple');
32634 this.selectorEl.on('change', this.onFileSelected, this);
32636 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32637 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32639 this.uploader.on('click', this.onUploaderClick, this);
32641 this.renderProgressDialog();
32645 window.addEventListener("resize", function() { _this.refresh(); } );
32647 this.fireEvent('initial', this);
32650 renderProgressDialog : function()
32654 this.progressDialog = new Roo.bootstrap.Modal({
32655 cls : 'roo-document-manager-progress-dialog',
32656 allow_close : false,
32667 btnclick : function() {
32668 _this.uploadCancel();
32674 this.progressDialog.render(Roo.get(document.body));
32676 this.progress = new Roo.bootstrap.Progress({
32677 cls : 'roo-document-manager-progress',
32682 this.progress.render(this.progressDialog.getChildContainer());
32684 this.progressBar = new Roo.bootstrap.ProgressBar({
32685 cls : 'roo-document-manager-progress-bar',
32688 aria_valuemax : 12,
32692 this.progressBar.render(this.progress.getChildContainer());
32695 onUploaderClick : function(e)
32697 e.preventDefault();
32699 if(this.fireEvent('beforeselectfile', this) != false){
32700 this.selectorEl.dom.click();
32705 onFileSelected : function(e)
32707 e.preventDefault();
32709 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32713 Roo.each(this.selectorEl.dom.files, function(file){
32714 if(this.fireEvent('inspect', this, file) != false){
32715 this.files.push(file);
32725 this.selectorEl.dom.value = '';
32727 if(!this.files || !this.files.length){
32731 if(this.boxes > 0 && this.files.length > this.boxes){
32732 this.files = this.files.slice(0, this.boxes);
32735 this.uploader.show();
32737 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32738 this.uploader.hide();
32747 Roo.each(this.files, function(file){
32749 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32750 var f = this.renderPreview(file);
32755 if(file.type.indexOf('image') != -1){
32756 this.delegates.push(
32758 _this.process(file);
32759 }).createDelegate(this)
32767 _this.process(file);
32768 }).createDelegate(this)
32773 this.files = files;
32775 this.delegates = this.delegates.concat(docs);
32777 if(!this.delegates.length){
32782 this.progressBar.aria_valuemax = this.delegates.length;
32789 arrange : function()
32791 if(!this.delegates.length){
32792 this.progressDialog.hide();
32797 var delegate = this.delegates.shift();
32799 this.progressDialog.show();
32801 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32803 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32808 refresh : function()
32810 this.uploader.show();
32812 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32813 this.uploader.hide();
32816 Roo.isTouch ? this.closable(false) : this.closable(true);
32818 this.fireEvent('refresh', this);
32821 onRemove : function(e, el, o)
32823 e.preventDefault();
32825 this.fireEvent('remove', this, o);
32829 remove : function(o)
32833 Roo.each(this.files, function(file){
32834 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32843 this.files = files;
32850 Roo.each(this.files, function(file){
32855 file.target.remove();
32864 onClick : function(e, el, o)
32866 e.preventDefault();
32868 this.fireEvent('click', this, o);
32872 closable : function(closable)
32874 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32876 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32888 xhrOnLoad : function(xhr)
32890 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32894 if (xhr.readyState !== 4) {
32896 this.fireEvent('exception', this, xhr);
32900 var response = Roo.decode(xhr.responseText);
32902 if(!response.success){
32904 this.fireEvent('exception', this, xhr);
32908 var file = this.renderPreview(response.data);
32910 this.files.push(file);
32914 this.fireEvent('afterupload', this, xhr);
32918 xhrOnError : function(xhr)
32920 Roo.log('xhr on error');
32922 var response = Roo.decode(xhr.responseText);
32929 process : function(file)
32931 if(this.fireEvent('process', this, file) !== false){
32932 if(this.editable && file.type.indexOf('image') != -1){
32933 this.fireEvent('edit', this, file);
32937 this.uploadStart(file, false);
32944 uploadStart : function(file, crop)
32946 this.xhr = new XMLHttpRequest();
32948 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32953 file.xhr = this.xhr;
32955 this.managerEl.createChild({
32957 cls : 'roo-document-manager-loading',
32961 tooltip : file.name,
32962 cls : 'roo-document-manager-thumb',
32963 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32969 this.xhr.open(this.method, this.url, true);
32972 "Accept": "application/json",
32973 "Cache-Control": "no-cache",
32974 "X-Requested-With": "XMLHttpRequest"
32977 for (var headerName in headers) {
32978 var headerValue = headers[headerName];
32980 this.xhr.setRequestHeader(headerName, headerValue);
32986 this.xhr.onload = function()
32988 _this.xhrOnLoad(_this.xhr);
32991 this.xhr.onerror = function()
32993 _this.xhrOnError(_this.xhr);
32996 var formData = new FormData();
32998 formData.append('returnHTML', 'NO');
33001 formData.append('crop', crop);
33004 formData.append(this.paramName, file, file.name);
33011 if(this.fireEvent('prepare', this, formData, options) != false){
33013 if(options.manually){
33017 this.xhr.send(formData);
33021 this.uploadCancel();
33024 uploadCancel : function()
33030 this.delegates = [];
33032 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33039 renderPreview : function(file)
33041 if(typeof(file.target) != 'undefined' && file.target){
33045 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33047 var previewEl = this.managerEl.createChild({
33049 cls : 'roo-document-manager-preview',
33053 tooltip : file[this.toolTipName],
33054 cls : 'roo-document-manager-thumb',
33055 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33060 html : '<i class="fa fa-times-circle"></i>'
33065 var close = previewEl.select('button.close', true).first();
33067 close.on('click', this.onRemove, this, file);
33069 file.target = previewEl;
33071 var image = previewEl.select('img', true).first();
33075 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33077 image.on('click', this.onClick, this, file);
33079 this.fireEvent('previewrendered', this, file);
33085 onPreviewLoad : function(file, image)
33087 if(typeof(file.target) == 'undefined' || !file.target){
33091 var width = image.dom.naturalWidth || image.dom.width;
33092 var height = image.dom.naturalHeight || image.dom.height;
33094 if(!this.previewResize) {
33098 if(width > height){
33099 file.target.addClass('wide');
33103 file.target.addClass('tall');
33108 uploadFromSource : function(file, crop)
33110 this.xhr = new XMLHttpRequest();
33112 this.managerEl.createChild({
33114 cls : 'roo-document-manager-loading',
33118 tooltip : file.name,
33119 cls : 'roo-document-manager-thumb',
33120 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33126 this.xhr.open(this.method, this.url, true);
33129 "Accept": "application/json",
33130 "Cache-Control": "no-cache",
33131 "X-Requested-With": "XMLHttpRequest"
33134 for (var headerName in headers) {
33135 var headerValue = headers[headerName];
33137 this.xhr.setRequestHeader(headerName, headerValue);
33143 this.xhr.onload = function()
33145 _this.xhrOnLoad(_this.xhr);
33148 this.xhr.onerror = function()
33150 _this.xhrOnError(_this.xhr);
33153 var formData = new FormData();
33155 formData.append('returnHTML', 'NO');
33157 formData.append('crop', crop);
33159 if(typeof(file.filename) != 'undefined'){
33160 formData.append('filename', file.filename);
33163 if(typeof(file.mimetype) != 'undefined'){
33164 formData.append('mimetype', file.mimetype);
33169 if(this.fireEvent('prepare', this, formData) != false){
33170 this.xhr.send(formData);
33180 * @class Roo.bootstrap.DocumentViewer
33181 * @extends Roo.bootstrap.Component
33182 * Bootstrap DocumentViewer class
33183 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33184 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33187 * Create a new DocumentViewer
33188 * @param {Object} config The config object
33191 Roo.bootstrap.DocumentViewer = function(config){
33192 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33197 * Fire after initEvent
33198 * @param {Roo.bootstrap.DocumentViewer} this
33204 * @param {Roo.bootstrap.DocumentViewer} this
33209 * Fire after download button
33210 * @param {Roo.bootstrap.DocumentViewer} this
33215 * Fire after trash button
33216 * @param {Roo.bootstrap.DocumentViewer} this
33223 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33225 showDownload : true,
33229 getAutoCreate : function()
33233 cls : 'roo-document-viewer',
33237 cls : 'roo-document-viewer-body',
33241 cls : 'roo-document-viewer-thumb',
33245 cls : 'roo-document-viewer-image'
33253 cls : 'roo-document-viewer-footer',
33256 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33260 cls : 'btn-group roo-document-viewer-download',
33264 cls : 'btn btn-default',
33265 html : '<i class="fa fa-download"></i>'
33271 cls : 'btn-group roo-document-viewer-trash',
33275 cls : 'btn btn-default',
33276 html : '<i class="fa fa-trash"></i>'
33289 initEvents : function()
33291 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33292 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33294 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33295 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33297 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33298 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33300 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33301 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33303 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33304 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33306 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33307 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33309 this.bodyEl.on('click', this.onClick, this);
33310 this.downloadBtn.on('click', this.onDownload, this);
33311 this.trashBtn.on('click', this.onTrash, this);
33313 this.downloadBtn.hide();
33314 this.trashBtn.hide();
33316 if(this.showDownload){
33317 this.downloadBtn.show();
33320 if(this.showTrash){
33321 this.trashBtn.show();
33324 if(!this.showDownload && !this.showTrash) {
33325 this.footerEl.hide();
33330 initial : function()
33332 this.fireEvent('initial', this);
33336 onClick : function(e)
33338 e.preventDefault();
33340 this.fireEvent('click', this);
33343 onDownload : function(e)
33345 e.preventDefault();
33347 this.fireEvent('download', this);
33350 onTrash : function(e)
33352 e.preventDefault();
33354 this.fireEvent('trash', this);
33366 * @class Roo.bootstrap.form.FieldLabel
33367 * @extends Roo.bootstrap.Component
33368 * Bootstrap FieldLabel class
33369 * @cfg {String} html contents of the element
33370 * @cfg {String} tag tag of the element default label
33371 * @cfg {String} cls class of the element
33372 * @cfg {String} target label target
33373 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33374 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33375 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33376 * @cfg {String} iconTooltip default "This field is required"
33377 * @cfg {String} indicatorpos (left|right) default left
33380 * Create a new FieldLabel
33381 * @param {Object} config The config object
33384 Roo.bootstrap.form.FieldLabel = function(config){
33385 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33390 * Fires after the field has been marked as invalid.
33391 * @param {Roo.form.FieldLabel} this
33392 * @param {String} msg The validation message
33397 * Fires after the field has been validated with no errors.
33398 * @param {Roo.form.FieldLabel} this
33404 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33411 invalidClass : 'has-warning',
33412 validClass : 'has-success',
33413 iconTooltip : 'This field is required',
33414 indicatorpos : 'left',
33416 getAutoCreate : function(){
33419 if (!this.allowBlank) {
33425 cls : 'roo-bootstrap-field-label ' + this.cls,
33430 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33431 tooltip : this.iconTooltip
33440 if(this.indicatorpos == 'right'){
33443 cls : 'roo-bootstrap-field-label ' + this.cls,
33452 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33453 tooltip : this.iconTooltip
33462 initEvents: function()
33464 Roo.bootstrap.Element.superclass.initEvents.call(this);
33466 this.indicator = this.indicatorEl();
33468 if(this.indicator){
33469 this.indicator.removeClass('visible');
33470 this.indicator.addClass('invisible');
33473 Roo.bootstrap.form.FieldLabel.register(this);
33476 indicatorEl : function()
33478 var indicator = this.el.select('i.roo-required-indicator',true).first();
33489 * Mark this field as valid
33491 markValid : function()
33493 if(this.indicator){
33494 this.indicator.removeClass('visible');
33495 this.indicator.addClass('invisible');
33497 if (Roo.bootstrap.version == 3) {
33498 this.el.removeClass(this.invalidClass);
33499 this.el.addClass(this.validClass);
33501 this.el.removeClass('is-invalid');
33502 this.el.addClass('is-valid');
33506 this.fireEvent('valid', this);
33510 * Mark this field as invalid
33511 * @param {String} msg The validation message
33513 markInvalid : function(msg)
33515 if(this.indicator){
33516 this.indicator.removeClass('invisible');
33517 this.indicator.addClass('visible');
33519 if (Roo.bootstrap.version == 3) {
33520 this.el.removeClass(this.validClass);
33521 this.el.addClass(this.invalidClass);
33523 this.el.removeClass('is-valid');
33524 this.el.addClass('is-invalid');
33528 this.fireEvent('invalid', this, msg);
33534 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33539 * register a FieldLabel Group
33540 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33542 register : function(label)
33544 if(this.groups.hasOwnProperty(label.target)){
33548 this.groups[label.target] = label;
33552 * fetch a FieldLabel Group based on the target
33553 * @param {string} target
33554 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33556 get: function(target) {
33557 if (typeof(this.groups[target]) == 'undefined') {
33561 return this.groups[target] ;
33570 * page DateSplitField.
33576 * @class Roo.bootstrap.form.DateSplitField
33577 * @extends Roo.bootstrap.Component
33578 * Bootstrap DateSplitField class
33579 * @cfg {string} fieldLabel - the label associated
33580 * @cfg {Number} labelWidth set the width of label (0-12)
33581 * @cfg {String} labelAlign (top|left)
33582 * @cfg {Boolean} dayAllowBlank (true|false) default false
33583 * @cfg {Boolean} monthAllowBlank (true|false) default false
33584 * @cfg {Boolean} yearAllowBlank (true|false) default false
33585 * @cfg {string} dayPlaceholder
33586 * @cfg {string} monthPlaceholder
33587 * @cfg {string} yearPlaceholder
33588 * @cfg {string} dayFormat default 'd'
33589 * @cfg {string} monthFormat default 'm'
33590 * @cfg {string} yearFormat default 'Y'
33591 * @cfg {Number} labellg set the width of label (1-12)
33592 * @cfg {Number} labelmd set the width of label (1-12)
33593 * @cfg {Number} labelsm set the width of label (1-12)
33594 * @cfg {Number} labelxs set the width of label (1-12)
33598 * Create a new DateSplitField
33599 * @param {Object} config The config object
33602 Roo.bootstrap.form.DateSplitField = function(config){
33603 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33609 * getting the data of years
33610 * @param {Roo.bootstrap.form.DateSplitField} this
33611 * @param {Object} years
33616 * getting the data of days
33617 * @param {Roo.bootstrap.form.DateSplitField} this
33618 * @param {Object} days
33623 * Fires after the field has been marked as invalid.
33624 * @param {Roo.form.Field} this
33625 * @param {String} msg The validation message
33630 * Fires after the field has been validated with no errors.
33631 * @param {Roo.form.Field} this
33637 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33640 labelAlign : 'top',
33642 dayAllowBlank : false,
33643 monthAllowBlank : false,
33644 yearAllowBlank : false,
33645 dayPlaceholder : '',
33646 monthPlaceholder : '',
33647 yearPlaceholder : '',
33651 isFormField : true,
33657 getAutoCreate : function()
33661 cls : 'row roo-date-split-field-group',
33666 cls : 'form-hidden-field roo-date-split-field-group-value',
33672 var labelCls = 'col-md-12';
33673 var contentCls = 'col-md-4';
33675 if(this.fieldLabel){
33679 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33683 html : this.fieldLabel
33688 if(this.labelAlign == 'left'){
33690 if(this.labelWidth > 12){
33691 label.style = "width: " + this.labelWidth + 'px';
33694 if(this.labelWidth < 13 && this.labelmd == 0){
33695 this.labelmd = this.labelWidth;
33698 if(this.labellg > 0){
33699 labelCls = ' col-lg-' + this.labellg;
33700 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33703 if(this.labelmd > 0){
33704 labelCls = ' col-md-' + this.labelmd;
33705 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33708 if(this.labelsm > 0){
33709 labelCls = ' col-sm-' + this.labelsm;
33710 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33713 if(this.labelxs > 0){
33714 labelCls = ' col-xs-' + this.labelxs;
33715 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33719 label.cls += ' ' + labelCls;
33721 cfg.cn.push(label);
33724 Roo.each(['day', 'month', 'year'], function(t){
33727 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33734 inputEl: function ()
33736 return this.el.select('.roo-date-split-field-group-value', true).first();
33739 onRender : function(ct, position)
33743 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33745 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33747 this.dayField = new Roo.bootstrap.form.ComboBox({
33748 allowBlank : this.dayAllowBlank,
33749 alwaysQuery : true,
33750 displayField : 'value',
33753 forceSelection : true,
33755 placeholder : this.dayPlaceholder,
33756 selectOnFocus : true,
33757 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33758 triggerAction : 'all',
33760 valueField : 'value',
33761 store : new Roo.data.SimpleStore({
33762 data : (function() {
33764 _this.fireEvent('days', _this, days);
33767 fields : [ 'value' ]
33770 select : function (_self, record, index)
33772 _this.setValue(_this.getValue());
33777 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33779 this.monthField = new Roo.bootstrap.form.MonthField({
33780 after : '<i class=\"fa fa-calendar\"></i>',
33781 allowBlank : this.monthAllowBlank,
33782 placeholder : this.monthPlaceholder,
33785 render : function (_self)
33787 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33788 e.preventDefault();
33792 select : function (_self, oldvalue, newvalue)
33794 _this.setValue(_this.getValue());
33799 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33801 this.yearField = new Roo.bootstrap.form.ComboBox({
33802 allowBlank : this.yearAllowBlank,
33803 alwaysQuery : true,
33804 displayField : 'value',
33807 forceSelection : true,
33809 placeholder : this.yearPlaceholder,
33810 selectOnFocus : true,
33811 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33812 triggerAction : 'all',
33814 valueField : 'value',
33815 store : new Roo.data.SimpleStore({
33816 data : (function() {
33818 _this.fireEvent('years', _this, years);
33821 fields : [ 'value' ]
33824 select : function (_self, record, index)
33826 _this.setValue(_this.getValue());
33831 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33834 setValue : function(v, format)
33836 this.inputEl.dom.value = v;
33838 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33840 var d = Date.parseDate(v, f);
33847 this.setDay(d.format(this.dayFormat));
33848 this.setMonth(d.format(this.monthFormat));
33849 this.setYear(d.format(this.yearFormat));
33856 setDay : function(v)
33858 this.dayField.setValue(v);
33859 this.inputEl.dom.value = this.getValue();
33864 setMonth : function(v)
33866 this.monthField.setValue(v, true);
33867 this.inputEl.dom.value = this.getValue();
33872 setYear : function(v)
33874 this.yearField.setValue(v);
33875 this.inputEl.dom.value = this.getValue();
33880 getDay : function()
33882 return this.dayField.getValue();
33885 getMonth : function()
33887 return this.monthField.getValue();
33890 getYear : function()
33892 return this.yearField.getValue();
33895 getValue : function()
33897 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33899 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33909 this.inputEl.dom.value = '';
33914 validate : function()
33916 var d = this.dayField.validate();
33917 var m = this.monthField.validate();
33918 var y = this.yearField.validate();
33923 (!this.dayAllowBlank && !d) ||
33924 (!this.monthAllowBlank && !m) ||
33925 (!this.yearAllowBlank && !y)
33930 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33939 this.markInvalid();
33944 markValid : function()
33947 var label = this.el.select('label', true).first();
33948 var icon = this.el.select('i.fa-star', true).first();
33954 this.fireEvent('valid', this);
33958 * Mark this field as invalid
33959 * @param {String} msg The validation message
33961 markInvalid : function(msg)
33964 var label = this.el.select('label', true).first();
33965 var icon = this.el.select('i.fa-star', true).first();
33967 if(label && !icon){
33968 this.el.select('.roo-date-split-field-label', true).createChild({
33970 cls : 'text-danger fa fa-lg fa-star',
33971 tooltip : 'This field is required',
33972 style : 'margin-right:5px;'
33976 this.fireEvent('invalid', this, msg);
33979 clearInvalid : function()
33981 var label = this.el.select('label', true).first();
33982 var icon = this.el.select('i.fa-star', true).first();
33988 this.fireEvent('valid', this);
33991 getName: function()
34001 * @class Roo.bootstrap.LayoutMasonry
34002 * @extends Roo.bootstrap.Component
34003 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34004 * Bootstrap Layout Masonry class
34007 * http://masonry.desandro.com
34009 * The idea is to render all the bricks based on vertical width...
34011 * The original code extends 'outlayer' - we might need to use that....
34014 * Create a new Element
34015 * @param {Object} config The config object
34018 Roo.bootstrap.LayoutMasonry = function(config){
34020 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34024 Roo.bootstrap.LayoutMasonry.register(this);
34030 * Fire after layout the items
34031 * @param {Roo.bootstrap.LayoutMasonry} this
34032 * @param {Roo.EventObject} e
34039 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34042 * @cfg {Boolean} isLayoutInstant = no animation?
34044 isLayoutInstant : false, // needed?
34047 * @cfg {Number} boxWidth width of the columns
34052 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34057 * @cfg {Number} padWidth padding below box..
34062 * @cfg {Number} gutter gutter width..
34067 * @cfg {Number} maxCols maximum number of columns
34073 * @cfg {Boolean} isAutoInitial defalut true
34075 isAutoInitial : true,
34080 * @cfg {Boolean} isHorizontal defalut false
34082 isHorizontal : false,
34084 currentSize : null,
34090 bricks: null, //CompositeElement
34094 _isLayoutInited : false,
34096 // isAlternative : false, // only use for vertical layout...
34099 * @cfg {Number} alternativePadWidth padding below box..
34101 alternativePadWidth : 50,
34103 selectedBrick : [],
34105 getAutoCreate : function(){
34107 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34111 cls: 'blog-masonary-wrapper ' + this.cls,
34113 cls : 'mas-boxes masonary'
34120 getChildContainer: function( )
34122 if (this.boxesEl) {
34123 return this.boxesEl;
34126 this.boxesEl = this.el.select('.mas-boxes').first();
34128 return this.boxesEl;
34132 initEvents : function()
34136 if(this.isAutoInitial){
34137 Roo.log('hook children rendered');
34138 this.on('childrenrendered', function() {
34139 Roo.log('children rendered');
34145 initial : function()
34147 this.selectedBrick = [];
34149 this.currentSize = this.el.getBox(true);
34151 Roo.EventManager.onWindowResize(this.resize, this);
34153 if(!this.isAutoInitial){
34161 //this.layout.defer(500,this);
34165 resize : function()
34167 var cs = this.el.getBox(true);
34170 this.currentSize.width == cs.width &&
34171 this.currentSize.x == cs.x &&
34172 this.currentSize.height == cs.height &&
34173 this.currentSize.y == cs.y
34175 Roo.log("no change in with or X or Y");
34179 this.currentSize = cs;
34185 layout : function()
34187 this._resetLayout();
34189 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34191 this.layoutItems( isInstant );
34193 this._isLayoutInited = true;
34195 this.fireEvent('layout', this);
34199 _resetLayout : function()
34201 if(this.isHorizontal){
34202 this.horizontalMeasureColumns();
34206 this.verticalMeasureColumns();
34210 verticalMeasureColumns : function()
34212 this.getContainerWidth();
34214 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34215 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34219 var boxWidth = this.boxWidth + this.padWidth;
34221 if(this.containerWidth < this.boxWidth){
34222 boxWidth = this.containerWidth
34225 var containerWidth = this.containerWidth;
34227 var cols = Math.floor(containerWidth / boxWidth);
34229 this.cols = Math.max( cols, 1 );
34231 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34233 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34235 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34237 this.colWidth = boxWidth + avail - this.padWidth;
34239 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34240 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34243 horizontalMeasureColumns : function()
34245 this.getContainerWidth();
34247 var boxWidth = this.boxWidth;
34249 if(this.containerWidth < boxWidth){
34250 boxWidth = this.containerWidth;
34253 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34255 this.el.setHeight(boxWidth);
34259 getContainerWidth : function()
34261 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34264 layoutItems : function( isInstant )
34266 Roo.log(this.bricks);
34268 var items = Roo.apply([], this.bricks);
34270 if(this.isHorizontal){
34271 this._horizontalLayoutItems( items , isInstant );
34275 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34276 // this._verticalAlternativeLayoutItems( items , isInstant );
34280 this._verticalLayoutItems( items , isInstant );
34284 _verticalLayoutItems : function ( items , isInstant)
34286 if ( !items || !items.length ) {
34291 ['xs', 'xs', 'xs', 'tall'],
34292 ['xs', 'xs', 'tall'],
34293 ['xs', 'xs', 'sm'],
34294 ['xs', 'xs', 'xs'],
34300 ['sm', 'xs', 'xs'],
34304 ['tall', 'xs', 'xs', 'xs'],
34305 ['tall', 'xs', 'xs'],
34317 Roo.each(items, function(item, k){
34319 switch (item.size) {
34320 // these layouts take up a full box,
34331 boxes.push([item]);
34354 var filterPattern = function(box, length)
34362 var pattern = box.slice(0, length);
34366 Roo.each(pattern, function(i){
34367 format.push(i.size);
34370 Roo.each(standard, function(s){
34372 if(String(s) != String(format)){
34381 if(!match && length == 1){
34386 filterPattern(box, length - 1);
34390 queue.push(pattern);
34392 box = box.slice(length, box.length);
34394 filterPattern(box, 4);
34400 Roo.each(boxes, function(box, k){
34406 if(box.length == 1){
34411 filterPattern(box, 4);
34415 this._processVerticalLayoutQueue( queue, isInstant );
34419 // _verticalAlternativeLayoutItems : function( items , isInstant )
34421 // if ( !items || !items.length ) {
34425 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34429 _horizontalLayoutItems : function ( items , isInstant)
34431 if ( !items || !items.length || items.length < 3) {
34437 var eItems = items.slice(0, 3);
34439 items = items.slice(3, items.length);
34442 ['xs', 'xs', 'xs', 'wide'],
34443 ['xs', 'xs', 'wide'],
34444 ['xs', 'xs', 'sm'],
34445 ['xs', 'xs', 'xs'],
34451 ['sm', 'xs', 'xs'],
34455 ['wide', 'xs', 'xs', 'xs'],
34456 ['wide', 'xs', 'xs'],
34469 Roo.each(items, function(item, k){
34471 switch (item.size) {
34482 boxes.push([item]);
34506 var filterPattern = function(box, length)
34514 var pattern = box.slice(0, length);
34518 Roo.each(pattern, function(i){
34519 format.push(i.size);
34522 Roo.each(standard, function(s){
34524 if(String(s) != String(format)){
34533 if(!match && length == 1){
34538 filterPattern(box, length - 1);
34542 queue.push(pattern);
34544 box = box.slice(length, box.length);
34546 filterPattern(box, 4);
34552 Roo.each(boxes, function(box, k){
34558 if(box.length == 1){
34563 filterPattern(box, 4);
34570 var pos = this.el.getBox(true);
34574 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34576 var hit_end = false;
34578 Roo.each(queue, function(box){
34582 Roo.each(box, function(b){
34584 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34594 Roo.each(box, function(b){
34596 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34599 mx = Math.max(mx, b.x);
34603 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34607 Roo.each(box, function(b){
34609 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34623 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34626 /** Sets position of item in DOM
34627 * @param {Element} item
34628 * @param {Number} x - horizontal position
34629 * @param {Number} y - vertical position
34630 * @param {Boolean} isInstant - disables transitions
34632 _processVerticalLayoutQueue : function( queue, isInstant )
34634 var pos = this.el.getBox(true);
34639 for (var i = 0; i < this.cols; i++){
34643 Roo.each(queue, function(box, k){
34645 var col = k % this.cols;
34647 Roo.each(box, function(b,kk){
34649 b.el.position('absolute');
34651 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34652 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34654 if(b.size == 'md-left' || b.size == 'md-right'){
34655 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34656 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34659 b.el.setWidth(width);
34660 b.el.setHeight(height);
34662 b.el.select('iframe',true).setSize(width,height);
34666 for (var i = 0; i < this.cols; i++){
34668 if(maxY[i] < maxY[col]){
34673 col = Math.min(col, i);
34677 x = pos.x + col * (this.colWidth + this.padWidth);
34681 var positions = [];
34683 switch (box.length){
34685 positions = this.getVerticalOneBoxColPositions(x, y, box);
34688 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34691 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34694 positions = this.getVerticalFourBoxColPositions(x, y, box);
34700 Roo.each(box, function(b,kk){
34702 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34704 var sz = b.el.getSize();
34706 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34714 for (var i = 0; i < this.cols; i++){
34715 mY = Math.max(mY, maxY[i]);
34718 this.el.setHeight(mY - pos.y);
34722 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34724 // var pos = this.el.getBox(true);
34727 // var maxX = pos.right;
34729 // var maxHeight = 0;
34731 // Roo.each(items, function(item, k){
34735 // item.el.position('absolute');
34737 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34739 // item.el.setWidth(width);
34741 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34743 // item.el.setHeight(height);
34746 // item.el.setXY([x, y], isInstant ? false : true);
34748 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34751 // y = y + height + this.alternativePadWidth;
34753 // maxHeight = maxHeight + height + this.alternativePadWidth;
34757 // this.el.setHeight(maxHeight);
34761 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34763 var pos = this.el.getBox(true);
34768 var maxX = pos.right;
34770 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34772 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34774 Roo.each(queue, function(box, k){
34776 Roo.each(box, function(b, kk){
34778 b.el.position('absolute');
34780 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34781 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34783 if(b.size == 'md-left' || b.size == 'md-right'){
34784 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34785 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34788 b.el.setWidth(width);
34789 b.el.setHeight(height);
34797 var positions = [];
34799 switch (box.length){
34801 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34804 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34807 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34810 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34816 Roo.each(box, function(b,kk){
34818 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34820 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34828 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34830 Roo.each(eItems, function(b,k){
34832 b.size = (k == 0) ? 'sm' : 'xs';
34833 b.x = (k == 0) ? 2 : 1;
34834 b.y = (k == 0) ? 2 : 1;
34836 b.el.position('absolute');
34838 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34840 b.el.setWidth(width);
34842 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34844 b.el.setHeight(height);
34848 var positions = [];
34851 x : maxX - this.unitWidth * 2 - this.gutter,
34856 x : maxX - this.unitWidth,
34857 y : minY + (this.unitWidth + this.gutter) * 2
34861 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34865 Roo.each(eItems, function(b,k){
34867 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34873 getVerticalOneBoxColPositions : function(x, y, box)
34877 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34879 if(box[0].size == 'md-left'){
34883 if(box[0].size == 'md-right'){
34888 x : x + (this.unitWidth + this.gutter) * rand,
34895 getVerticalTwoBoxColPositions : function(x, y, box)
34899 if(box[0].size == 'xs'){
34903 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34907 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34921 x : x + (this.unitWidth + this.gutter) * 2,
34922 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34929 getVerticalThreeBoxColPositions : function(x, y, box)
34933 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34941 x : x + (this.unitWidth + this.gutter) * 1,
34946 x : x + (this.unitWidth + this.gutter) * 2,
34954 if(box[0].size == 'xs' && box[1].size == 'xs'){
34963 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34967 x : x + (this.unitWidth + this.gutter) * 1,
34981 x : x + (this.unitWidth + this.gutter) * 2,
34986 x : x + (this.unitWidth + this.gutter) * 2,
34987 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34994 getVerticalFourBoxColPositions : function(x, y, box)
34998 if(box[0].size == 'xs'){
35007 y : y + (this.unitHeight + this.gutter) * 1
35012 y : y + (this.unitHeight + this.gutter) * 2
35016 x : x + (this.unitWidth + this.gutter) * 1,
35030 x : x + (this.unitWidth + this.gutter) * 2,
35035 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35036 y : y + (this.unitHeight + this.gutter) * 1
35040 x : x + (this.unitWidth + this.gutter) * 2,
35041 y : y + (this.unitWidth + this.gutter) * 2
35048 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35052 if(box[0].size == 'md-left'){
35054 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35061 if(box[0].size == 'md-right'){
35063 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35064 y : minY + (this.unitWidth + this.gutter) * 1
35070 var rand = Math.floor(Math.random() * (4 - box[0].y));
35073 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35074 y : minY + (this.unitWidth + this.gutter) * rand
35081 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35085 if(box[0].size == 'xs'){
35088 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35093 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35094 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35102 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35107 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35108 y : minY + (this.unitWidth + this.gutter) * 2
35115 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35119 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35122 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35127 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35128 y : minY + (this.unitWidth + this.gutter) * 1
35132 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35133 y : minY + (this.unitWidth + this.gutter) * 2
35140 if(box[0].size == 'xs' && box[1].size == 'xs'){
35143 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35148 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35153 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35154 y : minY + (this.unitWidth + this.gutter) * 1
35162 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35167 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35168 y : minY + (this.unitWidth + this.gutter) * 2
35172 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35173 y : minY + (this.unitWidth + this.gutter) * 2
35180 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35184 if(box[0].size == 'xs'){
35187 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35192 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35197 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),
35202 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35203 y : minY + (this.unitWidth + this.gutter) * 1
35211 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35216 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35217 y : minY + (this.unitWidth + this.gutter) * 2
35221 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35222 y : minY + (this.unitWidth + this.gutter) * 2
35226 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),
35227 y : minY + (this.unitWidth + this.gutter) * 2
35235 * remove a Masonry Brick
35236 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35238 removeBrick : function(brick_id)
35244 for (var i = 0; i<this.bricks.length; i++) {
35245 if (this.bricks[i].id == brick_id) {
35246 this.bricks.splice(i,1);
35247 this.el.dom.removeChild(Roo.get(brick_id).dom);
35254 * adds a Masonry Brick
35255 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35257 addBrick : function(cfg)
35259 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35260 //this.register(cn);
35261 cn.parentId = this.id;
35262 cn.render(this.el);
35267 * register a Masonry Brick
35268 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35271 register : function(brick)
35273 this.bricks.push(brick);
35274 brick.masonryId = this.id;
35278 * clear all the Masonry Brick
35280 clearAll : function()
35283 //this.getChildContainer().dom.innerHTML = "";
35284 this.el.dom.innerHTML = '';
35287 getSelected : function()
35289 if (!this.selectedBrick) {
35293 return this.selectedBrick;
35297 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35301 * register a Masonry Layout
35302 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35305 register : function(layout)
35307 this.groups[layout.id] = layout;
35310 * fetch a Masonry Layout based on the masonry layout ID
35311 * @param {string} the masonry layout to add
35312 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35315 get: function(layout_id) {
35316 if (typeof(this.groups[layout_id]) == 'undefined') {
35319 return this.groups[layout_id] ;
35331 * http://masonry.desandro.com
35333 * The idea is to render all the bricks based on vertical width...
35335 * The original code extends 'outlayer' - we might need to use that....
35341 * @class Roo.bootstrap.LayoutMasonryAuto
35342 * @extends Roo.bootstrap.Component
35343 * Bootstrap Layout Masonry class
35346 * Create a new Element
35347 * @param {Object} config The config object
35350 Roo.bootstrap.LayoutMasonryAuto = function(config){
35351 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35354 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35357 * @cfg {Boolean} isFitWidth - resize the width..
35359 isFitWidth : false, // options..
35361 * @cfg {Boolean} isOriginLeft = left align?
35363 isOriginLeft : true,
35365 * @cfg {Boolean} isOriginTop = top align?
35367 isOriginTop : false,
35369 * @cfg {Boolean} isLayoutInstant = no animation?
35371 isLayoutInstant : false, // needed?
35373 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35375 isResizingContainer : true,
35377 * @cfg {Number} columnWidth width of the columns
35383 * @cfg {Number} maxCols maximum number of columns
35388 * @cfg {Number} padHeight padding below box..
35394 * @cfg {Boolean} isAutoInitial defalut true
35397 isAutoInitial : true,
35403 initialColumnWidth : 0,
35404 currentSize : null,
35406 colYs : null, // array.
35413 bricks: null, //CompositeElement
35414 cols : 0, // array?
35415 // element : null, // wrapped now this.el
35416 _isLayoutInited : null,
35419 getAutoCreate : function(){
35423 cls: 'blog-masonary-wrapper ' + this.cls,
35425 cls : 'mas-boxes masonary'
35432 getChildContainer: function( )
35434 if (this.boxesEl) {
35435 return this.boxesEl;
35438 this.boxesEl = this.el.select('.mas-boxes').first();
35440 return this.boxesEl;
35444 initEvents : function()
35448 if(this.isAutoInitial){
35449 Roo.log('hook children rendered');
35450 this.on('childrenrendered', function() {
35451 Roo.log('children rendered');
35458 initial : function()
35460 this.reloadItems();
35462 this.currentSize = this.el.getBox(true);
35464 /// was window resize... - let's see if this works..
35465 Roo.EventManager.onWindowResize(this.resize, this);
35467 if(!this.isAutoInitial){
35472 this.layout.defer(500,this);
35475 reloadItems: function()
35477 this.bricks = this.el.select('.masonry-brick', true);
35479 this.bricks.each(function(b) {
35480 //Roo.log(b.getSize());
35481 if (!b.attr('originalwidth')) {
35482 b.attr('originalwidth', b.getSize().width);
35487 Roo.log(this.bricks.elements.length);
35490 resize : function()
35493 var cs = this.el.getBox(true);
35495 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35496 Roo.log("no change in with or X");
35499 this.currentSize = cs;
35503 layout : function()
35506 this._resetLayout();
35507 //this._manageStamps();
35509 // don't animate first layout
35510 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35511 this.layoutItems( isInstant );
35513 // flag for initalized
35514 this._isLayoutInited = true;
35517 layoutItems : function( isInstant )
35519 //var items = this._getItemsForLayout( this.items );
35520 // original code supports filtering layout items.. we just ignore it..
35522 this._layoutItems( this.bricks , isInstant );
35524 this._postLayout();
35526 _layoutItems : function ( items , isInstant)
35528 //this.fireEvent( 'layout', this, items );
35531 if ( !items || !items.elements.length ) {
35532 // no items, emit event with empty array
35537 items.each(function(item) {
35538 Roo.log("layout item");
35540 // get x/y object from method
35541 var position = this._getItemLayoutPosition( item );
35543 position.item = item;
35544 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35545 queue.push( position );
35548 this._processLayoutQueue( queue );
35550 /** Sets position of item in DOM
35551 * @param {Element} item
35552 * @param {Number} x - horizontal position
35553 * @param {Number} y - vertical position
35554 * @param {Boolean} isInstant - disables transitions
35556 _processLayoutQueue : function( queue )
35558 for ( var i=0, len = queue.length; i < len; i++ ) {
35559 var obj = queue[i];
35560 obj.item.position('absolute');
35561 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35567 * Any logic you want to do after each layout,
35568 * i.e. size the container
35570 _postLayout : function()
35572 this.resizeContainer();
35575 resizeContainer : function()
35577 if ( !this.isResizingContainer ) {
35580 var size = this._getContainerSize();
35582 this.el.setSize(size.width,size.height);
35583 this.boxesEl.setSize(size.width,size.height);
35589 _resetLayout : function()
35591 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35592 this.colWidth = this.el.getWidth();
35593 //this.gutter = this.el.getWidth();
35595 this.measureColumns();
35601 this.colYs.push( 0 );
35607 measureColumns : function()
35609 this.getContainerWidth();
35610 // if columnWidth is 0, default to outerWidth of first item
35611 if ( !this.columnWidth ) {
35612 var firstItem = this.bricks.first();
35613 Roo.log(firstItem);
35614 this.columnWidth = this.containerWidth;
35615 if (firstItem && firstItem.attr('originalwidth') ) {
35616 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35618 // columnWidth fall back to item of first element
35619 Roo.log("set column width?");
35620 this.initialColumnWidth = this.columnWidth ;
35622 // if first elem has no width, default to size of container
35627 if (this.initialColumnWidth) {
35628 this.columnWidth = this.initialColumnWidth;
35633 // column width is fixed at the top - however if container width get's smaller we should
35636 // this bit calcs how man columns..
35638 var columnWidth = this.columnWidth += this.gutter;
35640 // calculate columns
35641 var containerWidth = this.containerWidth + this.gutter;
35643 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35644 // fix rounding errors, typically with gutters
35645 var excess = columnWidth - containerWidth % columnWidth;
35648 // if overshoot is less than a pixel, round up, otherwise floor it
35649 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35650 cols = Math[ mathMethod ]( cols );
35651 this.cols = Math.max( cols, 1 );
35652 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35654 // padding positioning..
35655 var totalColWidth = this.cols * this.columnWidth;
35656 var padavail = this.containerWidth - totalColWidth;
35657 // so for 2 columns - we need 3 'pads'
35659 var padNeeded = (1+this.cols) * this.padWidth;
35661 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35663 this.columnWidth += padExtra
35664 //this.padWidth = Math.floor(padavail / ( this.cols));
35666 // adjust colum width so that padding is fixed??
35668 // we have 3 columns ... total = width * 3
35669 // we have X left over... that should be used by
35671 //if (this.expandC) {
35679 getContainerWidth : function()
35681 /* // container is parent if fit width
35682 var container = this.isFitWidth ? this.element.parentNode : this.element;
35683 // check that this.size and size are there
35684 // IE8 triggers resize on body size change, so they might not be
35686 var size = getSize( container ); //FIXME
35687 this.containerWidth = size && size.innerWidth; //FIXME
35690 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35694 _getItemLayoutPosition : function( item ) // what is item?
35696 // we resize the item to our columnWidth..
35698 item.setWidth(this.columnWidth);
35699 item.autoBoxAdjust = false;
35701 var sz = item.getSize();
35703 // how many columns does this brick span
35704 var remainder = this.containerWidth % this.columnWidth;
35706 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35707 // round if off by 1 pixel, otherwise use ceil
35708 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35709 colSpan = Math.min( colSpan, this.cols );
35711 // normally this should be '1' as we dont' currently allow multi width columns..
35713 var colGroup = this._getColGroup( colSpan );
35714 // get the minimum Y value from the columns
35715 var minimumY = Math.min.apply( Math, colGroup );
35716 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35718 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35720 // position the brick
35722 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35723 y: this.currentSize.y + minimumY + this.padHeight
35727 // apply setHeight to necessary columns
35728 var setHeight = minimumY + sz.height + this.padHeight;
35729 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35731 var setSpan = this.cols + 1 - colGroup.length;
35732 for ( var i = 0; i < setSpan; i++ ) {
35733 this.colYs[ shortColIndex + i ] = setHeight ;
35740 * @param {Number} colSpan - number of columns the element spans
35741 * @returns {Array} colGroup
35743 _getColGroup : function( colSpan )
35745 if ( colSpan < 2 ) {
35746 // if brick spans only one column, use all the column Ys
35751 // how many different places could this brick fit horizontally
35752 var groupCount = this.cols + 1 - colSpan;
35753 // for each group potential horizontal position
35754 for ( var i = 0; i < groupCount; i++ ) {
35755 // make an array of colY values for that one group
35756 var groupColYs = this.colYs.slice( i, i + colSpan );
35757 // and get the max value of the array
35758 colGroup[i] = Math.max.apply( Math, groupColYs );
35763 _manageStamp : function( stamp )
35765 var stampSize = stamp.getSize();
35766 var offset = stamp.getBox();
35767 // get the columns that this stamp affects
35768 var firstX = this.isOriginLeft ? offset.x : offset.right;
35769 var lastX = firstX + stampSize.width;
35770 var firstCol = Math.floor( firstX / this.columnWidth );
35771 firstCol = Math.max( 0, firstCol );
35773 var lastCol = Math.floor( lastX / this.columnWidth );
35774 // lastCol should not go over if multiple of columnWidth #425
35775 lastCol -= lastX % this.columnWidth ? 0 : 1;
35776 lastCol = Math.min( this.cols - 1, lastCol );
35778 // set colYs to bottom of the stamp
35779 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35782 for ( var i = firstCol; i <= lastCol; i++ ) {
35783 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35788 _getContainerSize : function()
35790 this.maxY = Math.max.apply( Math, this.colYs );
35795 if ( this.isFitWidth ) {
35796 size.width = this._getContainerFitWidth();
35802 _getContainerFitWidth : function()
35804 var unusedCols = 0;
35805 // count unused columns
35808 if ( this.colYs[i] !== 0 ) {
35813 // fit container to columns that have been used
35814 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35817 needsResizeLayout : function()
35819 var previousWidth = this.containerWidth;
35820 this.getContainerWidth();
35821 return previousWidth !== this.containerWidth;
35836 * @class Roo.bootstrap.MasonryBrick
35837 * @extends Roo.bootstrap.Component
35838 * Bootstrap MasonryBrick class
35841 * Create a new MasonryBrick
35842 * @param {Object} config The config object
35845 Roo.bootstrap.MasonryBrick = function(config){
35847 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35849 Roo.bootstrap.MasonryBrick.register(this);
35855 * When a MasonryBrick is clcik
35856 * @param {Roo.bootstrap.MasonryBrick} this
35857 * @param {Roo.EventObject} e
35863 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35866 * @cfg {String} title
35870 * @cfg {String} html
35874 * @cfg {String} bgimage
35878 * @cfg {String} videourl
35882 * @cfg {String} cls
35886 * @cfg {String} href
35890 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35895 * @cfg {String} placetitle (center|bottom)
35900 * @cfg {Boolean} isFitContainer defalut true
35902 isFitContainer : true,
35905 * @cfg {Boolean} preventDefault defalut false
35907 preventDefault : false,
35910 * @cfg {Boolean} inverse defalut false
35912 maskInverse : false,
35914 getAutoCreate : function()
35916 if(!this.isFitContainer){
35917 return this.getSplitAutoCreate();
35920 var cls = 'masonry-brick masonry-brick-full';
35922 if(this.href.length){
35923 cls += ' masonry-brick-link';
35926 if(this.bgimage.length){
35927 cls += ' masonry-brick-image';
35930 if(this.maskInverse){
35931 cls += ' mask-inverse';
35934 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35935 cls += ' enable-mask';
35939 cls += ' masonry-' + this.size + '-brick';
35942 if(this.placetitle.length){
35944 switch (this.placetitle) {
35946 cls += ' masonry-center-title';
35949 cls += ' masonry-bottom-title';
35956 if(!this.html.length && !this.bgimage.length){
35957 cls += ' masonry-center-title';
35960 if(!this.html.length && this.bgimage.length){
35961 cls += ' masonry-bottom-title';
35966 cls += ' ' + this.cls;
35970 tag: (this.href.length) ? 'a' : 'div',
35975 cls: 'masonry-brick-mask'
35979 cls: 'masonry-brick-paragraph',
35985 if(this.href.length){
35986 cfg.href = this.href;
35989 var cn = cfg.cn[1].cn;
35991 if(this.title.length){
35994 cls: 'masonry-brick-title',
35999 if(this.html.length){
36002 cls: 'masonry-brick-text',
36007 if (!this.title.length && !this.html.length) {
36008 cfg.cn[1].cls += ' hide';
36011 if(this.bgimage.length){
36014 cls: 'masonry-brick-image-view',
36019 if(this.videourl.length){
36020 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36021 // youtube support only?
36024 cls: 'masonry-brick-image-view',
36027 allowfullscreen : true
36035 getSplitAutoCreate : function()
36037 var cls = 'masonry-brick masonry-brick-split';
36039 if(this.href.length){
36040 cls += ' masonry-brick-link';
36043 if(this.bgimage.length){
36044 cls += ' masonry-brick-image';
36048 cls += ' masonry-' + this.size + '-brick';
36051 switch (this.placetitle) {
36053 cls += ' masonry-center-title';
36056 cls += ' masonry-bottom-title';
36059 if(!this.bgimage.length){
36060 cls += ' masonry-center-title';
36063 if(this.bgimage.length){
36064 cls += ' masonry-bottom-title';
36070 cls += ' ' + this.cls;
36074 tag: (this.href.length) ? 'a' : 'div',
36079 cls: 'masonry-brick-split-head',
36083 cls: 'masonry-brick-paragraph',
36090 cls: 'masonry-brick-split-body',
36096 if(this.href.length){
36097 cfg.href = this.href;
36100 if(this.title.length){
36101 cfg.cn[0].cn[0].cn.push({
36103 cls: 'masonry-brick-title',
36108 if(this.html.length){
36109 cfg.cn[1].cn.push({
36111 cls: 'masonry-brick-text',
36116 if(this.bgimage.length){
36117 cfg.cn[0].cn.push({
36119 cls: 'masonry-brick-image-view',
36124 if(this.videourl.length){
36125 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36126 // youtube support only?
36127 cfg.cn[0].cn.cn.push({
36129 cls: 'masonry-brick-image-view',
36132 allowfullscreen : true
36139 initEvents: function()
36141 switch (this.size) {
36174 this.el.on('touchstart', this.onTouchStart, this);
36175 this.el.on('touchmove', this.onTouchMove, this);
36176 this.el.on('touchend', this.onTouchEnd, this);
36177 this.el.on('contextmenu', this.onContextMenu, this);
36179 this.el.on('mouseenter' ,this.enter, this);
36180 this.el.on('mouseleave', this.leave, this);
36181 this.el.on('click', this.onClick, this);
36184 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36185 this.parent().bricks.push(this);
36190 onClick: function(e, el)
36192 var time = this.endTimer - this.startTimer;
36193 // Roo.log(e.preventDefault());
36196 e.preventDefault();
36201 if(!this.preventDefault){
36205 e.preventDefault();
36207 if (this.activeClass != '') {
36208 this.selectBrick();
36211 this.fireEvent('click', this, e);
36214 enter: function(e, el)
36216 e.preventDefault();
36218 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36222 if(this.bgimage.length && this.html.length){
36223 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36227 leave: function(e, el)
36229 e.preventDefault();
36231 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36235 if(this.bgimage.length && this.html.length){
36236 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36240 onTouchStart: function(e, el)
36242 // e.preventDefault();
36244 this.touchmoved = false;
36246 if(!this.isFitContainer){
36250 if(!this.bgimage.length || !this.html.length){
36254 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36256 this.timer = new Date().getTime();
36260 onTouchMove: function(e, el)
36262 this.touchmoved = true;
36265 onContextMenu : function(e,el)
36267 e.preventDefault();
36268 e.stopPropagation();
36272 onTouchEnd: function(e, el)
36274 // e.preventDefault();
36276 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36283 if(!this.bgimage.length || !this.html.length){
36285 if(this.href.length){
36286 window.location.href = this.href;
36292 if(!this.isFitContainer){
36296 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36298 window.location.href = this.href;
36301 //selection on single brick only
36302 selectBrick : function() {
36304 if (!this.parentId) {
36308 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36309 var index = m.selectedBrick.indexOf(this.id);
36312 m.selectedBrick.splice(index,1);
36313 this.el.removeClass(this.activeClass);
36317 for(var i = 0; i < m.selectedBrick.length; i++) {
36318 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36319 b.el.removeClass(b.activeClass);
36322 m.selectedBrick = [];
36324 m.selectedBrick.push(this.id);
36325 this.el.addClass(this.activeClass);
36329 isSelected : function(){
36330 return this.el.hasClass(this.activeClass);
36335 Roo.apply(Roo.bootstrap.MasonryBrick, {
36338 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36340 * register a Masonry Brick
36341 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36344 register : function(brick)
36346 //this.groups[brick.id] = brick;
36347 this.groups.add(brick.id, brick);
36350 * fetch a masonry brick based on the masonry brick ID
36351 * @param {string} the masonry brick to add
36352 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36355 get: function(brick_id)
36357 // if (typeof(this.groups[brick_id]) == 'undefined') {
36360 // return this.groups[brick_id] ;
36362 if(this.groups.key(brick_id)) {
36363 return this.groups.key(brick_id);
36381 * @class Roo.bootstrap.Brick
36382 * @extends Roo.bootstrap.Component
36383 * Bootstrap Brick class
36386 * Create a new Brick
36387 * @param {Object} config The config object
36390 Roo.bootstrap.Brick = function(config){
36391 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36397 * When a Brick is click
36398 * @param {Roo.bootstrap.Brick} this
36399 * @param {Roo.EventObject} e
36405 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36408 * @cfg {String} title
36412 * @cfg {String} html
36416 * @cfg {String} bgimage
36420 * @cfg {String} cls
36424 * @cfg {String} href
36428 * @cfg {String} video
36432 * @cfg {Boolean} square
36436 getAutoCreate : function()
36438 var cls = 'roo-brick';
36440 if(this.href.length){
36441 cls += ' roo-brick-link';
36444 if(this.bgimage.length){
36445 cls += ' roo-brick-image';
36448 if(!this.html.length && !this.bgimage.length){
36449 cls += ' roo-brick-center-title';
36452 if(!this.html.length && this.bgimage.length){
36453 cls += ' roo-brick-bottom-title';
36457 cls += ' ' + this.cls;
36461 tag: (this.href.length) ? 'a' : 'div',
36466 cls: 'roo-brick-paragraph',
36472 if(this.href.length){
36473 cfg.href = this.href;
36476 var cn = cfg.cn[0].cn;
36478 if(this.title.length){
36481 cls: 'roo-brick-title',
36486 if(this.html.length){
36489 cls: 'roo-brick-text',
36496 if(this.bgimage.length){
36499 cls: 'roo-brick-image-view',
36507 initEvents: function()
36509 if(this.title.length || this.html.length){
36510 this.el.on('mouseenter' ,this.enter, this);
36511 this.el.on('mouseleave', this.leave, this);
36514 Roo.EventManager.onWindowResize(this.resize, this);
36516 if(this.bgimage.length){
36517 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36518 this.imageEl.on('load', this.onImageLoad, this);
36525 onImageLoad : function()
36530 resize : function()
36532 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36534 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36536 if(this.bgimage.length){
36537 var image = this.el.select('.roo-brick-image-view', true).first();
36539 image.setWidth(paragraph.getWidth());
36542 image.setHeight(paragraph.getWidth());
36545 this.el.setHeight(image.getHeight());
36546 paragraph.setHeight(image.getHeight());
36552 enter: function(e, el)
36554 e.preventDefault();
36556 if(this.bgimage.length){
36557 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36558 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36562 leave: function(e, el)
36564 e.preventDefault();
36566 if(this.bgimage.length){
36567 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36568 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36583 * @class Roo.bootstrap.form.NumberField
36584 * @extends Roo.bootstrap.form.Input
36585 * Bootstrap NumberField class
36591 * Create a new NumberField
36592 * @param {Object} config The config object
36595 Roo.bootstrap.form.NumberField = function(config){
36596 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36599 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36602 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36604 allowDecimals : true,
36606 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36608 decimalSeparator : ".",
36610 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36612 decimalPrecision : 2,
36614 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36616 allowNegative : true,
36619 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36623 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36625 minValue : Number.NEGATIVE_INFINITY,
36627 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36629 maxValue : Number.MAX_VALUE,
36631 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36633 minText : "The minimum value for this field is {0}",
36635 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36637 maxText : "The maximum value for this field is {0}",
36639 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36640 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36642 nanText : "{0} is not a valid number",
36644 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36646 thousandsDelimiter : false,
36648 * @cfg {String} valueAlign alignment of value
36650 valueAlign : "left",
36652 getAutoCreate : function()
36654 var hiddenInput = {
36658 cls: 'hidden-number-input'
36662 hiddenInput.name = this.name;
36667 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36669 this.name = hiddenInput.name;
36671 if(cfg.cn.length > 0) {
36672 cfg.cn.push(hiddenInput);
36679 initEvents : function()
36681 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36683 var allowed = "0123456789";
36685 if(this.allowDecimals){
36686 allowed += this.decimalSeparator;
36689 if(this.allowNegative){
36693 if(this.thousandsDelimiter) {
36697 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36699 var keyPress = function(e){
36701 var k = e.getKey();
36703 var c = e.getCharCode();
36706 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36707 allowed.indexOf(String.fromCharCode(c)) === -1
36713 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36717 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36722 this.el.on("keypress", keyPress, this);
36725 validateValue : function(value)
36728 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36732 var num = this.parseValue(value);
36735 this.markInvalid(String.format(this.nanText, value));
36739 if(num < this.minValue){
36740 this.markInvalid(String.format(this.minText, this.minValue));
36744 if(num > this.maxValue){
36745 this.markInvalid(String.format(this.maxText, this.maxValue));
36752 getValue : function()
36754 var v = this.hiddenEl().getValue();
36756 return this.fixPrecision(this.parseValue(v));
36759 parseValue : function(value)
36761 if(this.thousandsDelimiter) {
36763 r = new RegExp(",", "g");
36764 value = value.replace(r, "");
36767 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36768 return isNaN(value) ? '' : value;
36771 fixPrecision : function(value)
36773 if(this.thousandsDelimiter) {
36775 r = new RegExp(",", "g");
36776 value = value.replace(r, "");
36779 var nan = isNaN(value);
36781 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36782 return nan ? '' : value;
36784 return parseFloat(value).toFixed(this.decimalPrecision);
36787 setValue : function(v)
36789 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36795 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36797 this.inputEl().dom.value = (v == '') ? '' :
36798 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36800 if(!this.allowZero && v === '0') {
36801 this.hiddenEl().dom.value = '';
36802 this.inputEl().dom.value = '';
36809 decimalPrecisionFcn : function(v)
36811 return Math.floor(v);
36814 beforeBlur : function()
36816 var v = this.parseValue(this.getRawValue());
36818 if(v || v === 0 || v === ''){
36823 hiddenEl : function()
36825 return this.el.select('input.hidden-number-input',true).first();
36837 * @class Roo.bootstrap.DocumentSlider
36838 * @extends Roo.bootstrap.Component
36839 * Bootstrap DocumentSlider class
36842 * Create a new DocumentViewer
36843 * @param {Object} config The config object
36846 Roo.bootstrap.DocumentSlider = function(config){
36847 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36854 * Fire after initEvent
36855 * @param {Roo.bootstrap.DocumentSlider} this
36860 * Fire after update
36861 * @param {Roo.bootstrap.DocumentSlider} this
36867 * @param {Roo.bootstrap.DocumentSlider} this
36873 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36879 getAutoCreate : function()
36883 cls : 'roo-document-slider',
36887 cls : 'roo-document-slider-header',
36891 cls : 'roo-document-slider-header-title'
36897 cls : 'roo-document-slider-body',
36901 cls : 'roo-document-slider-prev',
36905 cls : 'fa fa-chevron-left'
36911 cls : 'roo-document-slider-thumb',
36915 cls : 'roo-document-slider-image'
36921 cls : 'roo-document-slider-next',
36925 cls : 'fa fa-chevron-right'
36937 initEvents : function()
36939 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36940 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36942 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36943 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36945 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36946 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36948 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36949 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36951 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36952 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36954 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36955 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36957 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36958 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36960 this.thumbEl.on('click', this.onClick, this);
36962 this.prevIndicator.on('click', this.prev, this);
36964 this.nextIndicator.on('click', this.next, this);
36968 initial : function()
36970 if(this.files.length){
36971 this.indicator = 1;
36975 this.fireEvent('initial', this);
36978 update : function()
36980 this.imageEl.attr('src', this.files[this.indicator - 1]);
36982 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36984 this.prevIndicator.show();
36986 if(this.indicator == 1){
36987 this.prevIndicator.hide();
36990 this.nextIndicator.show();
36992 if(this.indicator == this.files.length){
36993 this.nextIndicator.hide();
36996 this.thumbEl.scrollTo('top');
36998 this.fireEvent('update', this);
37001 onClick : function(e)
37003 e.preventDefault();
37005 this.fireEvent('click', this);
37010 e.preventDefault();
37012 this.indicator = Math.max(1, this.indicator - 1);
37019 e.preventDefault();
37021 this.indicator = Math.min(this.files.length, this.indicator + 1);
37035 * @class Roo.bootstrap.form.RadioSet
37036 * @extends Roo.bootstrap.form.Input
37037 * @children Roo.bootstrap.form.Radio
37038 * Bootstrap RadioSet class
37039 * @cfg {String} indicatorpos (left|right) default left
37040 * @cfg {Boolean} inline (true|false) inline the element (default true)
37041 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37043 * Create a new RadioSet
37044 * @param {Object} config The config object
37047 Roo.bootstrap.form.RadioSet = function(config){
37049 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37053 Roo.bootstrap.form.RadioSet.register(this);
37058 * Fires when the element is checked or unchecked.
37059 * @param {Roo.bootstrap.form.RadioSet} this This radio
37060 * @param {Roo.bootstrap.form.Radio} item The checked item
37065 * Fires when the element is click.
37066 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37067 * @param {Roo.bootstrap.form.Radio} item The checked item
37068 * @param {Roo.EventObject} e The event object
37075 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37083 indicatorpos : 'left',
37085 getAutoCreate : function()
37089 cls : 'roo-radio-set-label',
37093 html : this.fieldLabel
37097 if (Roo.bootstrap.version == 3) {
37100 if(this.indicatorpos == 'left'){
37103 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37104 tooltip : 'This field is required'
37109 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37110 tooltip : 'This field is required'
37116 cls : 'roo-radio-set-items'
37119 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37121 if (align === 'left' && this.fieldLabel.length) {
37124 cls : "roo-radio-set-right",
37130 if(this.labelWidth > 12){
37131 label.style = "width: " + this.labelWidth + 'px';
37134 if(this.labelWidth < 13 && this.labelmd == 0){
37135 this.labelmd = this.labelWidth;
37138 if(this.labellg > 0){
37139 label.cls += ' col-lg-' + this.labellg;
37140 items.cls += ' col-lg-' + (12 - this.labellg);
37143 if(this.labelmd > 0){
37144 label.cls += ' col-md-' + this.labelmd;
37145 items.cls += ' col-md-' + (12 - this.labelmd);
37148 if(this.labelsm > 0){
37149 label.cls += ' col-sm-' + this.labelsm;
37150 items.cls += ' col-sm-' + (12 - this.labelsm);
37153 if(this.labelxs > 0){
37154 label.cls += ' col-xs-' + this.labelxs;
37155 items.cls += ' col-xs-' + (12 - this.labelxs);
37161 cls : 'roo-radio-set',
37165 cls : 'roo-radio-set-input',
37168 value : this.value ? this.value : ''
37175 if(this.weight.length){
37176 cfg.cls += ' roo-radio-' + this.weight;
37180 cfg.cls += ' roo-radio-set-inline';
37184 ['xs','sm','md','lg'].map(function(size){
37185 if (settings[size]) {
37186 cfg.cls += ' col-' + size + '-' + settings[size];
37194 initEvents : function()
37196 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37197 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37199 if(!this.fieldLabel.length){
37200 this.labelEl.hide();
37203 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37204 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37206 this.indicator = this.indicatorEl();
37208 if(this.indicator){
37209 this.indicator.addClass('invisible');
37212 this.originalValue = this.getValue();
37216 inputEl: function ()
37218 return this.el.select('.roo-radio-set-input', true).first();
37221 getChildContainer : function()
37223 return this.itemsEl;
37226 register : function(item)
37228 this.radioes.push(item);
37232 validate : function()
37234 if(this.getVisibilityEl().hasClass('hidden')){
37240 Roo.each(this.radioes, function(i){
37249 if(this.allowBlank) {
37253 if(this.disabled || valid){
37258 this.markInvalid();
37263 markValid : function()
37265 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37266 this.indicatorEl().removeClass('visible');
37267 this.indicatorEl().addClass('invisible');
37271 if (Roo.bootstrap.version == 3) {
37272 this.el.removeClass([this.invalidClass, this.validClass]);
37273 this.el.addClass(this.validClass);
37275 this.el.removeClass(['is-invalid','is-valid']);
37276 this.el.addClass(['is-valid']);
37278 this.fireEvent('valid', this);
37281 markInvalid : function(msg)
37283 if(this.allowBlank || this.disabled){
37287 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37288 this.indicatorEl().removeClass('invisible');
37289 this.indicatorEl().addClass('visible');
37291 if (Roo.bootstrap.version == 3) {
37292 this.el.removeClass([this.invalidClass, this.validClass]);
37293 this.el.addClass(this.invalidClass);
37295 this.el.removeClass(['is-invalid','is-valid']);
37296 this.el.addClass(['is-invalid']);
37299 this.fireEvent('invalid', this, msg);
37303 setValue : function(v, suppressEvent)
37305 if(this.value === v){
37312 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37315 Roo.each(this.radioes, function(i){
37317 i.el.removeClass('checked');
37320 Roo.each(this.radioes, function(i){
37322 if(i.value === v || i.value.toString() === v.toString()){
37324 i.el.addClass('checked');
37326 if(suppressEvent !== true){
37327 this.fireEvent('check', this, i);
37338 clearInvalid : function(){
37340 if(!this.el || this.preventMark){
37344 this.el.removeClass([this.invalidClass]);
37346 this.fireEvent('valid', this);
37351 Roo.apply(Roo.bootstrap.form.RadioSet, {
37355 register : function(set)
37357 this.groups[set.name] = set;
37360 get: function(name)
37362 if (typeof(this.groups[name]) == 'undefined') {
37366 return this.groups[name] ;
37372 * Ext JS Library 1.1.1
37373 * Copyright(c) 2006-2007, Ext JS, LLC.
37375 * Originally Released Under LGPL - original licence link has changed is not relivant.
37378 * <script type="text/javascript">
37383 * @class Roo.bootstrap.SplitBar
37384 * @extends Roo.util.Observable
37385 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37389 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37390 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37391 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37392 split.minSize = 100;
37393 split.maxSize = 600;
37394 split.animate = true;
37395 split.on('moved', splitterMoved);
37398 * Create a new SplitBar
37399 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37400 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37401 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37402 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37403 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37404 position of the SplitBar).
37406 Roo.bootstrap.SplitBar = function(cfg){
37411 // dragElement : elm
37412 // resizingElement: el,
37414 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37415 // placement : Roo.bootstrap.SplitBar.LEFT ,
37416 // existingProxy ???
37419 this.el = Roo.get(cfg.dragElement, true);
37420 this.el.dom.unselectable = "on";
37422 this.resizingEl = Roo.get(cfg.resizingElement, true);
37426 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37427 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37430 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37433 * The minimum size of the resizing element. (Defaults to 0)
37439 * The maximum size of the resizing element. (Defaults to 2000)
37442 this.maxSize = 2000;
37445 * Whether to animate the transition to the new size
37448 this.animate = false;
37451 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37454 this.useShim = false;
37459 if(!cfg.existingProxy){
37461 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37463 this.proxy = Roo.get(cfg.existingProxy).dom;
37466 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37469 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37472 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37475 this.dragSpecs = {};
37478 * @private The adapter to use to positon and resize elements
37480 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37481 this.adapter.init(this);
37483 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37485 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37486 this.el.addClass("roo-splitbar-h");
37489 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37490 this.el.addClass("roo-splitbar-v");
37496 * Fires when the splitter is moved (alias for {@link #event-moved})
37497 * @param {Roo.bootstrap.SplitBar} this
37498 * @param {Number} newSize the new width or height
37503 * Fires when the splitter is moved
37504 * @param {Roo.bootstrap.SplitBar} this
37505 * @param {Number} newSize the new width or height
37509 * @event beforeresize
37510 * Fires before the splitter is dragged
37511 * @param {Roo.bootstrap.SplitBar} this
37513 "beforeresize" : true,
37515 "beforeapply" : true
37518 Roo.util.Observable.call(this);
37521 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37522 onStartProxyDrag : function(x, y){
37523 this.fireEvent("beforeresize", this);
37525 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37527 o.enableDisplayMode("block");
37528 // all splitbars share the same overlay
37529 Roo.bootstrap.SplitBar.prototype.overlay = o;
37531 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37532 this.overlay.show();
37533 Roo.get(this.proxy).setDisplayed("block");
37534 var size = this.adapter.getElementSize(this);
37535 this.activeMinSize = this.getMinimumSize();;
37536 this.activeMaxSize = this.getMaximumSize();;
37537 var c1 = size - this.activeMinSize;
37538 var c2 = Math.max(this.activeMaxSize - size, 0);
37539 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37540 this.dd.resetConstraints();
37541 this.dd.setXConstraint(
37542 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37543 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37545 this.dd.setYConstraint(0, 0);
37547 this.dd.resetConstraints();
37548 this.dd.setXConstraint(0, 0);
37549 this.dd.setYConstraint(
37550 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37551 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37554 this.dragSpecs.startSize = size;
37555 this.dragSpecs.startPoint = [x, y];
37556 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37560 * @private Called after the drag operation by the DDProxy
37562 onEndProxyDrag : function(e){
37563 Roo.get(this.proxy).setDisplayed(false);
37564 var endPoint = Roo.lib.Event.getXY(e);
37566 this.overlay.hide();
37569 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37570 newSize = this.dragSpecs.startSize +
37571 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37572 endPoint[0] - this.dragSpecs.startPoint[0] :
37573 this.dragSpecs.startPoint[0] - endPoint[0]
37576 newSize = this.dragSpecs.startSize +
37577 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37578 endPoint[1] - this.dragSpecs.startPoint[1] :
37579 this.dragSpecs.startPoint[1] - endPoint[1]
37582 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37583 if(newSize != this.dragSpecs.startSize){
37584 if(this.fireEvent('beforeapply', this, newSize) !== false){
37585 this.adapter.setElementSize(this, newSize);
37586 this.fireEvent("moved", this, newSize);
37587 this.fireEvent("resize", this, newSize);
37593 * Get the adapter this SplitBar uses
37594 * @return The adapter object
37596 getAdapter : function(){
37597 return this.adapter;
37601 * Set the adapter this SplitBar uses
37602 * @param {Object} adapter A SplitBar adapter object
37604 setAdapter : function(adapter){
37605 this.adapter = adapter;
37606 this.adapter.init(this);
37610 * Gets the minimum size for the resizing element
37611 * @return {Number} The minimum size
37613 getMinimumSize : function(){
37614 return this.minSize;
37618 * Sets the minimum size for the resizing element
37619 * @param {Number} minSize The minimum size
37621 setMinimumSize : function(minSize){
37622 this.minSize = minSize;
37626 * Gets the maximum size for the resizing element
37627 * @return {Number} The maximum size
37629 getMaximumSize : function(){
37630 return this.maxSize;
37634 * Sets the maximum size for the resizing element
37635 * @param {Number} maxSize The maximum size
37637 setMaximumSize : function(maxSize){
37638 this.maxSize = maxSize;
37642 * Sets the initialize size for the resizing element
37643 * @param {Number} size The initial size
37645 setCurrentSize : function(size){
37646 var oldAnimate = this.animate;
37647 this.animate = false;
37648 this.adapter.setElementSize(this, size);
37649 this.animate = oldAnimate;
37653 * Destroy this splitbar.
37654 * @param {Boolean} removeEl True to remove the element
37656 destroy : function(removeEl){
37658 this.shim.remove();
37661 this.proxy.parentNode.removeChild(this.proxy);
37669 * @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.
37671 Roo.bootstrap.SplitBar.createProxy = function(dir){
37672 var proxy = new Roo.Element(document.createElement("div"));
37673 proxy.unselectable();
37674 var cls = 'roo-splitbar-proxy';
37675 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37676 document.body.appendChild(proxy.dom);
37681 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37682 * Default Adapter. It assumes the splitter and resizing element are not positioned
37683 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37685 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37688 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37689 // do nothing for now
37690 init : function(s){
37694 * Called before drag operations to get the current size of the resizing element.
37695 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37697 getElementSize : function(s){
37698 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37699 return s.resizingEl.getWidth();
37701 return s.resizingEl.getHeight();
37706 * Called after drag operations to set the size of the resizing element.
37707 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37708 * @param {Number} newSize The new size to set
37709 * @param {Function} onComplete A function to be invoked when resizing is complete
37711 setElementSize : function(s, newSize, onComplete){
37712 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37714 s.resizingEl.setWidth(newSize);
37716 onComplete(s, newSize);
37719 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37724 s.resizingEl.setHeight(newSize);
37726 onComplete(s, newSize);
37729 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37736 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37737 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37738 * Adapter that moves the splitter element to align with the resized sizing element.
37739 * Used with an absolute positioned SplitBar.
37740 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37741 * document.body, make sure you assign an id to the body element.
37743 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37744 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37745 this.container = Roo.get(container);
37748 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37749 init : function(s){
37750 this.basic.init(s);
37753 getElementSize : function(s){
37754 return this.basic.getElementSize(s);
37757 setElementSize : function(s, newSize, onComplete){
37758 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37761 moveSplitter : function(s){
37762 var yes = Roo.bootstrap.SplitBar;
37763 switch(s.placement){
37765 s.el.setX(s.resizingEl.getRight());
37768 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37771 s.el.setY(s.resizingEl.getBottom());
37774 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37781 * Orientation constant - Create a vertical SplitBar
37785 Roo.bootstrap.SplitBar.VERTICAL = 1;
37788 * Orientation constant - Create a horizontal SplitBar
37792 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37795 * Placement constant - The resizing element is to the left of the splitter element
37799 Roo.bootstrap.SplitBar.LEFT = 1;
37802 * Placement constant - The resizing element is to the right of the splitter element
37806 Roo.bootstrap.SplitBar.RIGHT = 2;
37809 * Placement constant - The resizing element is positioned above the splitter element
37813 Roo.bootstrap.SplitBar.TOP = 3;
37816 * Placement constant - The resizing element is positioned under splitter element
37820 Roo.bootstrap.SplitBar.BOTTOM = 4;
37823 * Ext JS Library 1.1.1
37824 * Copyright(c) 2006-2007, Ext JS, LLC.
37826 * Originally Released Under LGPL - original licence link has changed is not relivant.
37829 * <script type="text/javascript">
37833 * @class Roo.bootstrap.layout.Manager
37834 * @extends Roo.bootstrap.Component
37836 * Base class for layout managers.
37838 Roo.bootstrap.layout.Manager = function(config)
37840 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37846 /** false to disable window resize monitoring @type Boolean */
37847 this.monitorWindowResize = true;
37852 * Fires when a layout is performed.
37853 * @param {Roo.LayoutManager} this
37857 * @event regionresized
37858 * Fires when the user resizes a region.
37859 * @param {Roo.LayoutRegion} region The resized region
37860 * @param {Number} newSize The new size (width for east/west, height for north/south)
37862 "regionresized" : true,
37864 * @event regioncollapsed
37865 * Fires when a region is collapsed.
37866 * @param {Roo.LayoutRegion} region The collapsed region
37868 "regioncollapsed" : true,
37870 * @event regionexpanded
37871 * Fires when a region is expanded.
37872 * @param {Roo.LayoutRegion} region The expanded region
37874 "regionexpanded" : true
37876 this.updating = false;
37879 this.el = Roo.get(config.el);
37885 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37890 monitorWindowResize : true,
37896 onRender : function(ct, position)
37899 this.el = Roo.get(ct);
37902 //this.fireEvent('render',this);
37906 initEvents: function()
37910 // ie scrollbar fix
37911 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37912 document.body.scroll = "no";
37913 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37914 this.el.position('relative');
37916 this.id = this.el.id;
37917 this.el.addClass("roo-layout-container");
37918 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37919 if(this.el.dom != document.body ) {
37920 this.el.on('resize', this.layout,this);
37921 this.el.on('show', this.layout,this);
37927 * Returns true if this layout is currently being updated
37928 * @return {Boolean}
37930 isUpdating : function(){
37931 return this.updating;
37935 * Suspend the LayoutManager from doing auto-layouts while
37936 * making multiple add or remove calls
37938 beginUpdate : function(){
37939 this.updating = true;
37943 * Restore auto-layouts and optionally disable the manager from performing a layout
37944 * @param {Boolean} noLayout true to disable a layout update
37946 endUpdate : function(noLayout){
37947 this.updating = false;
37953 layout: function(){
37957 onRegionResized : function(region, newSize){
37958 this.fireEvent("regionresized", region, newSize);
37962 onRegionCollapsed : function(region){
37963 this.fireEvent("regioncollapsed", region);
37966 onRegionExpanded : function(region){
37967 this.fireEvent("regionexpanded", region);
37971 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37972 * performs box-model adjustments.
37973 * @return {Object} The size as an object {width: (the width), height: (the height)}
37975 getViewSize : function()
37978 if(this.el.dom != document.body){
37979 size = this.el.getSize();
37981 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37983 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37984 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37989 * Returns the Element this layout is bound to.
37990 * @return {Roo.Element}
37992 getEl : function(){
37997 * Returns the specified region.
37998 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37999 * @return {Roo.LayoutRegion}
38001 getRegion : function(target){
38002 return this.regions[target.toLowerCase()];
38005 onWindowResize : function(){
38006 if(this.monitorWindowResize){
38013 * Ext JS Library 1.1.1
38014 * Copyright(c) 2006-2007, Ext JS, LLC.
38016 * Originally Released Under LGPL - original licence link has changed is not relivant.
38019 * <script type="text/javascript">
38022 * @class Roo.bootstrap.layout.Border
38023 * @extends Roo.bootstrap.layout.Manager
38025 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38026 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38027 * please see: examples/bootstrap/nested.html<br><br>
38029 <b>The container the layout is rendered into can be either the body element or any other element.
38030 If it is not the body element, the container needs to either be an absolute positioned element,
38031 or you will need to add "position:relative" to the css of the container. You will also need to specify
38032 the container size if it is not the body element.</b>
38035 * Create a new Border
38036 * @param {Object} config Configuration options
38038 Roo.bootstrap.layout.Border = function(config){
38039 config = config || {};
38040 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38044 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38045 if(config[region]){
38046 config[region].region = region;
38047 this.addRegion(config[region]);
38053 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38055 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38058 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38061 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38064 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38067 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38070 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38076 parent : false, // this might point to a 'nest' or a ???
38079 * Creates and adds a new region if it doesn't already exist.
38080 * @param {String} target The target region key (north, south, east, west or center).
38081 * @param {Object} config The regions config object
38082 * @return {BorderLayoutRegion} The new region
38084 addRegion : function(config)
38086 if(!this.regions[config.region]){
38087 var r = this.factory(config);
38088 this.bindRegion(r);
38090 return this.regions[config.region];
38094 bindRegion : function(r){
38095 this.regions[r.config.region] = r;
38097 r.on("visibilitychange", this.layout, this);
38098 r.on("paneladded", this.layout, this);
38099 r.on("panelremoved", this.layout, this);
38100 r.on("invalidated", this.layout, this);
38101 r.on("resized", this.onRegionResized, this);
38102 r.on("collapsed", this.onRegionCollapsed, this);
38103 r.on("expanded", this.onRegionExpanded, this);
38107 * Performs a layout update.
38109 layout : function()
38111 if(this.updating) {
38115 // render all the rebions if they have not been done alreayd?
38116 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38117 if(this.regions[region] && !this.regions[region].bodyEl){
38118 this.regions[region].onRender(this.el)
38122 var size = this.getViewSize();
38123 var w = size.width;
38124 var h = size.height;
38129 //var x = 0, y = 0;
38131 var rs = this.regions;
38132 var north = rs["north"];
38133 var south = rs["south"];
38134 var west = rs["west"];
38135 var east = rs["east"];
38136 var center = rs["center"];
38137 //if(this.hideOnLayout){ // not supported anymore
38138 //c.el.setStyle("display", "none");
38140 if(north && north.isVisible()){
38141 var b = north.getBox();
38142 var m = north.getMargins();
38143 b.width = w - (m.left+m.right);
38146 centerY = b.height + b.y + m.bottom;
38147 centerH -= centerY;
38148 north.updateBox(this.safeBox(b));
38150 if(south && south.isVisible()){
38151 var b = south.getBox();
38152 var m = south.getMargins();
38153 b.width = w - (m.left+m.right);
38155 var totalHeight = (b.height + m.top + m.bottom);
38156 b.y = h - totalHeight + m.top;
38157 centerH -= totalHeight;
38158 south.updateBox(this.safeBox(b));
38160 if(west && west.isVisible()){
38161 var b = west.getBox();
38162 var m = west.getMargins();
38163 b.height = centerH - (m.top+m.bottom);
38165 b.y = centerY + m.top;
38166 var totalWidth = (b.width + m.left + m.right);
38167 centerX += totalWidth;
38168 centerW -= totalWidth;
38169 west.updateBox(this.safeBox(b));
38171 if(east && east.isVisible()){
38172 var b = east.getBox();
38173 var m = east.getMargins();
38174 b.height = centerH - (m.top+m.bottom);
38175 var totalWidth = (b.width + m.left + m.right);
38176 b.x = w - totalWidth + m.left;
38177 b.y = centerY + m.top;
38178 centerW -= totalWidth;
38179 east.updateBox(this.safeBox(b));
38182 var m = center.getMargins();
38184 x: centerX + m.left,
38185 y: centerY + m.top,
38186 width: centerW - (m.left+m.right),
38187 height: centerH - (m.top+m.bottom)
38189 //if(this.hideOnLayout){
38190 //center.el.setStyle("display", "block");
38192 center.updateBox(this.safeBox(centerBox));
38195 this.fireEvent("layout", this);
38199 safeBox : function(box){
38200 box.width = Math.max(0, box.width);
38201 box.height = Math.max(0, box.height);
38206 * Adds a ContentPanel (or subclass) to this layout.
38207 * @param {String} target The target region key (north, south, east, west or center).
38208 * @param {Roo.ContentPanel} panel The panel to add
38209 * @return {Roo.ContentPanel} The added panel
38211 add : function(target, panel){
38213 target = target.toLowerCase();
38214 return this.regions[target].add(panel);
38218 * Remove a ContentPanel (or subclass) to this layout.
38219 * @param {String} target The target region key (north, south, east, west or center).
38220 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38221 * @return {Roo.ContentPanel} The removed panel
38223 remove : function(target, panel){
38224 target = target.toLowerCase();
38225 return this.regions[target].remove(panel);
38229 * Searches all regions for a panel with the specified id
38230 * @param {String} panelId
38231 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38233 findPanel : function(panelId){
38234 var rs = this.regions;
38235 for(var target in rs){
38236 if(typeof rs[target] != "function"){
38237 var p = rs[target].getPanel(panelId);
38247 * Searches all regions for a panel with the specified id and activates (shows) it.
38248 * @param {String/ContentPanel} panelId The panels id or the panel itself
38249 * @return {Roo.ContentPanel} The shown panel or null
38251 showPanel : function(panelId) {
38252 var rs = this.regions;
38253 for(var target in rs){
38254 var r = rs[target];
38255 if(typeof r != "function"){
38256 if(r.hasPanel(panelId)){
38257 return r.showPanel(panelId);
38265 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38266 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38269 restoreState : function(provider){
38271 provider = Roo.state.Manager;
38273 var sm = new Roo.LayoutStateManager();
38274 sm.init(this, provider);
38280 * Adds a xtype elements to the layout.
38284 xtype : 'ContentPanel',
38291 xtype : 'NestedLayoutPanel',
38297 items : [ ... list of content panels or nested layout panels.. ]
38301 * @param {Object} cfg Xtype definition of item to add.
38303 addxtype : function(cfg)
38305 // basically accepts a pannel...
38306 // can accept a layout region..!?!?
38307 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38310 // theory? children can only be panels??
38312 //if (!cfg.xtype.match(/Panel$/)) {
38317 if (typeof(cfg.region) == 'undefined') {
38318 Roo.log("Failed to add Panel, region was not set");
38322 var region = cfg.region;
38328 xitems = cfg.items;
38333 if ( region == 'center') {
38334 Roo.log("Center: " + cfg.title);
38340 case 'Content': // ContentPanel (el, cfg)
38341 case 'Scroll': // ContentPanel (el, cfg)
38343 cfg.autoCreate = cfg.autoCreate || true;
38344 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38346 // var el = this.el.createChild();
38347 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38350 this.add(region, ret);
38354 case 'TreePanel': // our new panel!
38355 cfg.el = this.el.createChild();
38356 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38357 this.add(region, ret);
38362 // create a new Layout (which is a Border Layout...
38364 var clayout = cfg.layout;
38365 clayout.el = this.el.createChild();
38366 clayout.items = clayout.items || [];
38370 // replace this exitems with the clayout ones..
38371 xitems = clayout.items;
38373 // force background off if it's in center...
38374 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38375 cfg.background = false;
38377 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38380 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38381 //console.log('adding nested layout panel ' + cfg.toSource());
38382 this.add(region, ret);
38383 nb = {}; /// find first...
38388 // needs grid and region
38390 //var el = this.getRegion(region).el.createChild();
38392 *var el = this.el.createChild();
38393 // create the grid first...
38394 cfg.grid.container = el;
38395 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38398 if (region == 'center' && this.active ) {
38399 cfg.background = false;
38402 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38404 this.add(region, ret);
38406 if (cfg.background) {
38407 // render grid on panel activation (if panel background)
38408 ret.on('activate', function(gp) {
38409 if (!gp.grid.rendered) {
38410 // gp.grid.render(el);
38414 // cfg.grid.render(el);
38420 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38421 // it was the old xcomponent building that caused this before.
38422 // espeically if border is the top element in the tree.
38432 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38434 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38435 this.add(region, ret);
38439 throw "Can not add '" + cfg.xtype + "' to Border";
38445 this.beginUpdate();
38449 Roo.each(xitems, function(i) {
38450 region = nb && i.region ? i.region : false;
38452 var add = ret.addxtype(i);
38455 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38456 if (!i.background) {
38457 abn[region] = nb[region] ;
38464 // make the last non-background panel active..
38465 //if (nb) { Roo.log(abn); }
38468 for(var r in abn) {
38469 region = this.getRegion(r);
38471 // tried using nb[r], but it does not work..
38473 region.showPanel(abn[r]);
38484 factory : function(cfg)
38487 var validRegions = Roo.bootstrap.layout.Border.regions;
38489 var target = cfg.region;
38492 var r = Roo.bootstrap.layout;
38496 return new r.North(cfg);
38498 return new r.South(cfg);
38500 return new r.East(cfg);
38502 return new r.West(cfg);
38504 return new r.Center(cfg);
38506 throw 'Layout region "'+target+'" not supported.';
38513 * Ext JS Library 1.1.1
38514 * Copyright(c) 2006-2007, Ext JS, LLC.
38516 * Originally Released Under LGPL - original licence link has changed is not relivant.
38519 * <script type="text/javascript">
38523 * @class Roo.bootstrap.layout.Basic
38524 * @extends Roo.util.Observable
38525 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38526 * and does not have a titlebar, tabs or any other features. All it does is size and position
38527 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38528 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38529 * @cfg {string} region the region that it inhabits..
38530 * @cfg {bool} skipConfig skip config?
38534 Roo.bootstrap.layout.Basic = function(config){
38536 this.mgr = config.mgr;
38538 this.position = config.region;
38540 var skipConfig = config.skipConfig;
38544 * @scope Roo.BasicLayoutRegion
38548 * @event beforeremove
38549 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38550 * @param {Roo.LayoutRegion} this
38551 * @param {Roo.ContentPanel} panel The panel
38552 * @param {Object} e The cancel event object
38554 "beforeremove" : true,
38556 * @event invalidated
38557 * Fires when the layout for this region is changed.
38558 * @param {Roo.LayoutRegion} this
38560 "invalidated" : true,
38562 * @event visibilitychange
38563 * Fires when this region is shown or hidden
38564 * @param {Roo.LayoutRegion} this
38565 * @param {Boolean} visibility true or false
38567 "visibilitychange" : true,
38569 * @event paneladded
38570 * Fires when a panel is added.
38571 * @param {Roo.LayoutRegion} this
38572 * @param {Roo.ContentPanel} panel The panel
38574 "paneladded" : true,
38576 * @event panelremoved
38577 * Fires when a panel is removed.
38578 * @param {Roo.LayoutRegion} this
38579 * @param {Roo.ContentPanel} panel The panel
38581 "panelremoved" : true,
38583 * @event beforecollapse
38584 * Fires when this region before collapse.
38585 * @param {Roo.LayoutRegion} this
38587 "beforecollapse" : true,
38590 * Fires when this region is collapsed.
38591 * @param {Roo.LayoutRegion} this
38593 "collapsed" : true,
38596 * Fires when this region is expanded.
38597 * @param {Roo.LayoutRegion} this
38602 * Fires when this region is slid into view.
38603 * @param {Roo.LayoutRegion} this
38605 "slideshow" : true,
38608 * Fires when this region slides out of view.
38609 * @param {Roo.LayoutRegion} this
38611 "slidehide" : true,
38613 * @event panelactivated
38614 * Fires when a panel is activated.
38615 * @param {Roo.LayoutRegion} this
38616 * @param {Roo.ContentPanel} panel The activated panel
38618 "panelactivated" : true,
38621 * Fires when the user resizes this region.
38622 * @param {Roo.LayoutRegion} this
38623 * @param {Number} newSize The new size (width for east/west, height for north/south)
38627 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38628 this.panels = new Roo.util.MixedCollection();
38629 this.panels.getKey = this.getPanelId.createDelegate(this);
38631 this.activePanel = null;
38632 // ensure listeners are added...
38634 if (config.listeners || config.events) {
38635 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38636 listeners : config.listeners || {},
38637 events : config.events || {}
38641 if(skipConfig !== true){
38642 this.applyConfig(config);
38646 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38648 getPanelId : function(p){
38652 applyConfig : function(config){
38653 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38654 this.config = config;
38659 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38660 * the width, for horizontal (north, south) the height.
38661 * @param {Number} newSize The new width or height
38663 resizeTo : function(newSize){
38664 var el = this.el ? this.el :
38665 (this.activePanel ? this.activePanel.getEl() : null);
38667 switch(this.position){
38670 el.setWidth(newSize);
38671 this.fireEvent("resized", this, newSize);
38675 el.setHeight(newSize);
38676 this.fireEvent("resized", this, newSize);
38682 getBox : function(){
38683 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38686 getMargins : function(){
38687 return this.margins;
38690 updateBox : function(box){
38692 var el = this.activePanel.getEl();
38693 el.dom.style.left = box.x + "px";
38694 el.dom.style.top = box.y + "px";
38695 this.activePanel.setSize(box.width, box.height);
38699 * Returns the container element for this region.
38700 * @return {Roo.Element}
38702 getEl : function(){
38703 return this.activePanel;
38707 * Returns true if this region is currently visible.
38708 * @return {Boolean}
38710 isVisible : function(){
38711 return this.activePanel ? true : false;
38714 setActivePanel : function(panel){
38715 panel = this.getPanel(panel);
38716 if(this.activePanel && this.activePanel != panel){
38717 this.activePanel.setActiveState(false);
38718 this.activePanel.getEl().setLeftTop(-10000,-10000);
38720 this.activePanel = panel;
38721 panel.setActiveState(true);
38723 panel.setSize(this.box.width, this.box.height);
38725 this.fireEvent("panelactivated", this, panel);
38726 this.fireEvent("invalidated");
38730 * Show the specified panel.
38731 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38732 * @return {Roo.ContentPanel} The shown panel or null
38734 showPanel : function(panel){
38735 panel = this.getPanel(panel);
38737 this.setActivePanel(panel);
38743 * Get the active panel for this region.
38744 * @return {Roo.ContentPanel} The active panel or null
38746 getActivePanel : function(){
38747 return this.activePanel;
38751 * Add the passed ContentPanel(s)
38752 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38753 * @return {Roo.ContentPanel} The panel added (if only one was added)
38755 add : function(panel){
38756 if(arguments.length > 1){
38757 for(var i = 0, len = arguments.length; i < len; i++) {
38758 this.add(arguments[i]);
38762 if(this.hasPanel(panel)){
38763 this.showPanel(panel);
38766 var el = panel.getEl();
38767 if(el.dom.parentNode != this.mgr.el.dom){
38768 this.mgr.el.dom.appendChild(el.dom);
38770 if(panel.setRegion){
38771 panel.setRegion(this);
38773 this.panels.add(panel);
38774 el.setStyle("position", "absolute");
38775 if(!panel.background){
38776 this.setActivePanel(panel);
38777 if(this.config.initialSize && this.panels.getCount()==1){
38778 this.resizeTo(this.config.initialSize);
38781 this.fireEvent("paneladded", this, panel);
38786 * Returns true if the panel is in this region.
38787 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38788 * @return {Boolean}
38790 hasPanel : function(panel){
38791 if(typeof panel == "object"){ // must be panel obj
38792 panel = panel.getId();
38794 return this.getPanel(panel) ? true : false;
38798 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38799 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38800 * @param {Boolean} preservePanel Overrides the config preservePanel option
38801 * @return {Roo.ContentPanel} The panel that was removed
38803 remove : function(panel, preservePanel){
38804 panel = this.getPanel(panel);
38809 this.fireEvent("beforeremove", this, panel, e);
38810 if(e.cancel === true){
38813 var panelId = panel.getId();
38814 this.panels.removeKey(panelId);
38819 * Returns the panel specified or null if it's not in this region.
38820 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38821 * @return {Roo.ContentPanel}
38823 getPanel : function(id){
38824 if(typeof id == "object"){ // must be panel obj
38827 return this.panels.get(id);
38831 * Returns this regions position (north/south/east/west/center).
38834 getPosition: function(){
38835 return this.position;
38839 * Ext JS Library 1.1.1
38840 * Copyright(c) 2006-2007, Ext JS, LLC.
38842 * Originally Released Under LGPL - original licence link has changed is not relivant.
38845 * <script type="text/javascript">
38849 * @class Roo.bootstrap.layout.Region
38850 * @extends Roo.bootstrap.layout.Basic
38851 * This class represents a region in a layout manager.
38853 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38854 * @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})
38855 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38856 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38857 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38858 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38859 * @cfg {String} title The title for the region (overrides panel titles)
38860 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38861 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38862 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38863 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38864 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38865 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38866 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38867 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38868 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38869 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38871 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38872 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38873 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38874 * @cfg {Number} width For East/West panels
38875 * @cfg {Number} height For North/South panels
38876 * @cfg {Boolean} split To show the splitter
38877 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38879 * @cfg {string} cls Extra CSS classes to add to region
38881 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38882 * @cfg {string} region the region that it inhabits..
38885 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38886 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38888 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38889 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38890 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38892 Roo.bootstrap.layout.Region = function(config)
38894 this.applyConfig(config);
38896 var mgr = config.mgr;
38897 var pos = config.region;
38898 config.skipConfig = true;
38899 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38902 this.onRender(mgr.el);
38905 this.visible = true;
38906 this.collapsed = false;
38907 this.unrendered_panels = [];
38910 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38912 position: '', // set by wrapper (eg. north/south etc..)
38913 unrendered_panels : null, // unrendered panels.
38915 tabPosition : false,
38917 mgr: false, // points to 'Border'
38920 createBody : function(){
38921 /** This region's body element
38922 * @type Roo.Element */
38923 this.bodyEl = this.el.createChild({
38925 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38929 onRender: function(ctr, pos)
38931 var dh = Roo.DomHelper;
38932 /** This region's container element
38933 * @type Roo.Element */
38934 this.el = dh.append(ctr.dom, {
38936 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38938 /** This region's title element
38939 * @type Roo.Element */
38941 this.titleEl = dh.append(this.el.dom, {
38943 unselectable: "on",
38944 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38946 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38947 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38951 this.titleEl.enableDisplayMode();
38952 /** This region's title text element
38953 * @type HTMLElement */
38954 this.titleTextEl = this.titleEl.dom.firstChild;
38955 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38957 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38958 this.closeBtn.enableDisplayMode();
38959 this.closeBtn.on("click", this.closeClicked, this);
38960 this.closeBtn.hide();
38962 this.createBody(this.config);
38963 if(this.config.hideWhenEmpty){
38965 this.on("paneladded", this.validateVisibility, this);
38966 this.on("panelremoved", this.validateVisibility, this);
38968 if(this.autoScroll){
38969 this.bodyEl.setStyle("overflow", "auto");
38971 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38973 //if(c.titlebar !== false){
38974 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38975 this.titleEl.hide();
38977 this.titleEl.show();
38978 if(this.config.title){
38979 this.titleTextEl.innerHTML = this.config.title;
38983 if(this.config.collapsed){
38984 this.collapse(true);
38986 if(this.config.hidden){
38990 if (this.unrendered_panels && this.unrendered_panels.length) {
38991 for (var i =0;i< this.unrendered_panels.length; i++) {
38992 this.add(this.unrendered_panels[i]);
38994 this.unrendered_panels = null;
39000 applyConfig : function(c)
39003 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39004 var dh = Roo.DomHelper;
39005 if(c.titlebar !== false){
39006 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39007 this.collapseBtn.on("click", this.collapse, this);
39008 this.collapseBtn.enableDisplayMode();
39010 if(c.showPin === true || this.showPin){
39011 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39012 this.stickBtn.enableDisplayMode();
39013 this.stickBtn.on("click", this.expand, this);
39014 this.stickBtn.hide();
39019 /** This region's collapsed element
39020 * @type Roo.Element */
39023 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39024 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39027 if(c.floatable !== false){
39028 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39029 this.collapsedEl.on("click", this.collapseClick, this);
39032 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39033 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39034 id: "message", unselectable: "on", style:{"float":"left"}});
39035 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39037 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39038 this.expandBtn.on("click", this.expand, this);
39042 if(this.collapseBtn){
39043 this.collapseBtn.setVisible(c.collapsible == true);
39046 this.cmargins = c.cmargins || this.cmargins ||
39047 (this.position == "west" || this.position == "east" ?
39048 {top: 0, left: 2, right:2, bottom: 0} :
39049 {top: 2, left: 0, right:0, bottom: 2});
39051 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39054 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39056 this.autoScroll = c.autoScroll || false;
39061 this.duration = c.duration || .30;
39062 this.slideDuration = c.slideDuration || .45;
39067 * Returns true if this region is currently visible.
39068 * @return {Boolean}
39070 isVisible : function(){
39071 return this.visible;
39075 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39076 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39078 //setCollapsedTitle : function(title){
39079 // title = title || " ";
39080 // if(this.collapsedTitleTextEl){
39081 // this.collapsedTitleTextEl.innerHTML = title;
39085 getBox : function(){
39087 // if(!this.collapsed){
39088 b = this.el.getBox(false, true);
39090 // b = this.collapsedEl.getBox(false, true);
39095 getMargins : function(){
39096 return this.margins;
39097 //return this.collapsed ? this.cmargins : this.margins;
39100 highlight : function(){
39101 this.el.addClass("x-layout-panel-dragover");
39104 unhighlight : function(){
39105 this.el.removeClass("x-layout-panel-dragover");
39108 updateBox : function(box)
39110 if (!this.bodyEl) {
39111 return; // not rendered yet..
39115 if(!this.collapsed){
39116 this.el.dom.style.left = box.x + "px";
39117 this.el.dom.style.top = box.y + "px";
39118 this.updateBody(box.width, box.height);
39120 this.collapsedEl.dom.style.left = box.x + "px";
39121 this.collapsedEl.dom.style.top = box.y + "px";
39122 this.collapsedEl.setSize(box.width, box.height);
39125 this.tabs.autoSizeTabs();
39129 updateBody : function(w, h)
39132 this.el.setWidth(w);
39133 w -= this.el.getBorderWidth("rl");
39134 if(this.config.adjustments){
39135 w += this.config.adjustments[0];
39138 if(h !== null && h > 0){
39139 this.el.setHeight(h);
39140 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39141 h -= this.el.getBorderWidth("tb");
39142 if(this.config.adjustments){
39143 h += this.config.adjustments[1];
39145 this.bodyEl.setHeight(h);
39147 h = this.tabs.syncHeight(h);
39150 if(this.panelSize){
39151 w = w !== null ? w : this.panelSize.width;
39152 h = h !== null ? h : this.panelSize.height;
39154 if(this.activePanel){
39155 var el = this.activePanel.getEl();
39156 w = w !== null ? w : el.getWidth();
39157 h = h !== null ? h : el.getHeight();
39158 this.panelSize = {width: w, height: h};
39159 this.activePanel.setSize(w, h);
39161 if(Roo.isIE && this.tabs){
39162 this.tabs.el.repaint();
39167 * Returns the container element for this region.
39168 * @return {Roo.Element}
39170 getEl : function(){
39175 * Hides this region.
39178 //if(!this.collapsed){
39179 this.el.dom.style.left = "-2000px";
39182 // this.collapsedEl.dom.style.left = "-2000px";
39183 // this.collapsedEl.hide();
39185 this.visible = false;
39186 this.fireEvent("visibilitychange", this, false);
39190 * Shows this region if it was previously hidden.
39193 //if(!this.collapsed){
39196 // this.collapsedEl.show();
39198 this.visible = true;
39199 this.fireEvent("visibilitychange", this, true);
39202 closeClicked : function(){
39203 if(this.activePanel){
39204 this.remove(this.activePanel);
39208 collapseClick : function(e){
39210 e.stopPropagation();
39213 e.stopPropagation();
39219 * Collapses this region.
39220 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39223 collapse : function(skipAnim, skipCheck = false){
39224 if(this.collapsed) {
39228 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39230 this.collapsed = true;
39232 this.split.el.hide();
39234 if(this.config.animate && skipAnim !== true){
39235 this.fireEvent("invalidated", this);
39236 this.animateCollapse();
39238 this.el.setLocation(-20000,-20000);
39240 this.collapsedEl.show();
39241 this.fireEvent("collapsed", this);
39242 this.fireEvent("invalidated", this);
39248 animateCollapse : function(){
39253 * Expands this region if it was previously collapsed.
39254 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39255 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39258 expand : function(e, skipAnim){
39260 e.stopPropagation();
39262 if(!this.collapsed || this.el.hasActiveFx()) {
39266 this.afterSlideIn();
39269 this.collapsed = false;
39270 if(this.config.animate && skipAnim !== true){
39271 this.animateExpand();
39275 this.split.el.show();
39277 this.collapsedEl.setLocation(-2000,-2000);
39278 this.collapsedEl.hide();
39279 this.fireEvent("invalidated", this);
39280 this.fireEvent("expanded", this);
39284 animateExpand : function(){
39288 initTabs : function()
39290 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39292 var ts = new Roo.bootstrap.panel.Tabs({
39293 el: this.bodyEl.dom,
39295 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39296 disableTooltips: this.config.disableTabTips,
39297 toolbar : this.config.toolbar
39300 if(this.config.hideTabs){
39301 ts.stripWrap.setDisplayed(false);
39304 ts.resizeTabs = this.config.resizeTabs === true;
39305 ts.minTabWidth = this.config.minTabWidth || 40;
39306 ts.maxTabWidth = this.config.maxTabWidth || 250;
39307 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39308 ts.monitorResize = false;
39309 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39310 ts.bodyEl.addClass('roo-layout-tabs-body');
39311 this.panels.each(this.initPanelAsTab, this);
39314 initPanelAsTab : function(panel){
39315 var ti = this.tabs.addTab(
39319 this.config.closeOnTab && panel.isClosable(),
39322 if(panel.tabTip !== undefined){
39323 ti.setTooltip(panel.tabTip);
39325 ti.on("activate", function(){
39326 this.setActivePanel(panel);
39329 if(this.config.closeOnTab){
39330 ti.on("beforeclose", function(t, e){
39332 this.remove(panel);
39336 panel.tabItem = ti;
39341 updatePanelTitle : function(panel, title)
39343 if(this.activePanel == panel){
39344 this.updateTitle(title);
39347 var ti = this.tabs.getTab(panel.getEl().id);
39349 if(panel.tabTip !== undefined){
39350 ti.setTooltip(panel.tabTip);
39355 updateTitle : function(title){
39356 if(this.titleTextEl && !this.config.title){
39357 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39361 setActivePanel : function(panel)
39363 panel = this.getPanel(panel);
39364 if(this.activePanel && this.activePanel != panel){
39365 if(this.activePanel.setActiveState(false) === false){
39369 this.activePanel = panel;
39370 panel.setActiveState(true);
39371 if(this.panelSize){
39372 panel.setSize(this.panelSize.width, this.panelSize.height);
39375 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39377 this.updateTitle(panel.getTitle());
39379 this.fireEvent("invalidated", this);
39381 this.fireEvent("panelactivated", this, panel);
39385 * Shows the specified panel.
39386 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39387 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39389 showPanel : function(panel)
39391 panel = this.getPanel(panel);
39394 var tab = this.tabs.getTab(panel.getEl().id);
39395 if(tab.isHidden()){
39396 this.tabs.unhideTab(tab.id);
39400 this.setActivePanel(panel);
39407 * Get the active panel for this region.
39408 * @return {Roo.ContentPanel} The active panel or null
39410 getActivePanel : function(){
39411 return this.activePanel;
39414 validateVisibility : function(){
39415 if(this.panels.getCount() < 1){
39416 this.updateTitle(" ");
39417 this.closeBtn.hide();
39420 if(!this.isVisible()){
39427 * Adds the passed ContentPanel(s) to this region.
39428 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39429 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39431 add : function(panel)
39433 if(arguments.length > 1){
39434 for(var i = 0, len = arguments.length; i < len; i++) {
39435 this.add(arguments[i]);
39440 // if we have not been rendered yet, then we can not really do much of this..
39441 if (!this.bodyEl) {
39442 this.unrendered_panels.push(panel);
39449 if(this.hasPanel(panel)){
39450 this.showPanel(panel);
39453 panel.setRegion(this);
39454 this.panels.add(panel);
39455 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39456 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39457 // and hide them... ???
39458 this.bodyEl.dom.appendChild(panel.getEl().dom);
39459 if(panel.background !== true){
39460 this.setActivePanel(panel);
39462 this.fireEvent("paneladded", this, panel);
39469 this.initPanelAsTab(panel);
39473 if(panel.background !== true){
39474 this.tabs.activate(panel.getEl().id);
39476 this.fireEvent("paneladded", this, panel);
39481 * Hides the tab for the specified panel.
39482 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39484 hidePanel : function(panel){
39485 if(this.tabs && (panel = this.getPanel(panel))){
39486 this.tabs.hideTab(panel.getEl().id);
39491 * Unhides the tab for a previously hidden panel.
39492 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39494 unhidePanel : function(panel){
39495 if(this.tabs && (panel = this.getPanel(panel))){
39496 this.tabs.unhideTab(panel.getEl().id);
39500 clearPanels : function(){
39501 while(this.panels.getCount() > 0){
39502 this.remove(this.panels.first());
39507 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39508 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39509 * @param {Boolean} preservePanel Overrides the config preservePanel option
39510 * @return {Roo.ContentPanel} The panel that was removed
39512 remove : function(panel, preservePanel)
39514 panel = this.getPanel(panel);
39519 this.fireEvent("beforeremove", this, panel, e);
39520 if(e.cancel === true){
39523 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39524 var panelId = panel.getId();
39525 this.panels.removeKey(panelId);
39527 document.body.appendChild(panel.getEl().dom);
39530 this.tabs.removeTab(panel.getEl().id);
39531 }else if (!preservePanel){
39532 this.bodyEl.dom.removeChild(panel.getEl().dom);
39534 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39535 var p = this.panels.first();
39536 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39537 tempEl.appendChild(p.getEl().dom);
39538 this.bodyEl.update("");
39539 this.bodyEl.dom.appendChild(p.getEl().dom);
39541 this.updateTitle(p.getTitle());
39543 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39544 this.setActivePanel(p);
39546 panel.setRegion(null);
39547 if(this.activePanel == panel){
39548 this.activePanel = null;
39550 if(this.config.autoDestroy !== false && preservePanel !== true){
39551 try{panel.destroy();}catch(e){}
39553 this.fireEvent("panelremoved", this, panel);
39558 * Returns the TabPanel component used by this region
39559 * @return {Roo.TabPanel}
39561 getTabs : function(){
39565 createTool : function(parentEl, className){
39566 var btn = Roo.DomHelper.append(parentEl, {
39568 cls: "x-layout-tools-button",
39571 cls: "roo-layout-tools-button-inner " + className,
39575 btn.addClassOnOver("roo-layout-tools-button-over");
39580 * Ext JS Library 1.1.1
39581 * Copyright(c) 2006-2007, Ext JS, LLC.
39583 * Originally Released Under LGPL - original licence link has changed is not relivant.
39586 * <script type="text/javascript">
39592 * @class Roo.SplitLayoutRegion
39593 * @extends Roo.LayoutRegion
39594 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39596 Roo.bootstrap.layout.Split = function(config){
39597 this.cursor = config.cursor;
39598 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39601 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39603 splitTip : "Drag to resize.",
39604 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39605 useSplitTips : false,
39607 applyConfig : function(config){
39608 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39611 onRender : function(ctr,pos) {
39613 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39614 if(!this.config.split){
39619 var splitEl = Roo.DomHelper.append(ctr.dom, {
39621 id: this.el.id + "-split",
39622 cls: "roo-layout-split roo-layout-split-"+this.position,
39625 /** The SplitBar for this region
39626 * @type Roo.SplitBar */
39627 // does not exist yet...
39628 Roo.log([this.position, this.orientation]);
39630 this.split = new Roo.bootstrap.SplitBar({
39631 dragElement : splitEl,
39632 resizingElement: this.el,
39633 orientation : this.orientation
39636 this.split.on("moved", this.onSplitMove, this);
39637 this.split.useShim = this.config.useShim === true;
39638 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39639 if(this.useSplitTips){
39640 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39642 //if(config.collapsible){
39643 // this.split.el.on("dblclick", this.collapse, this);
39646 if(typeof this.config.minSize != "undefined"){
39647 this.split.minSize = this.config.minSize;
39649 if(typeof this.config.maxSize != "undefined"){
39650 this.split.maxSize = this.config.maxSize;
39652 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39653 this.hideSplitter();
39658 getHMaxSize : function(){
39659 var cmax = this.config.maxSize || 10000;
39660 var center = this.mgr.getRegion("center");
39661 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39664 getVMaxSize : function(){
39665 var cmax = this.config.maxSize || 10000;
39666 var center = this.mgr.getRegion("center");
39667 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39670 onSplitMove : function(split, newSize){
39671 this.fireEvent("resized", this, newSize);
39675 * Returns the {@link Roo.SplitBar} for this region.
39676 * @return {Roo.SplitBar}
39678 getSplitBar : function(){
39683 this.hideSplitter();
39684 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39687 hideSplitter : function(){
39689 this.split.el.setLocation(-2000,-2000);
39690 this.split.el.hide();
39696 this.split.el.show();
39698 Roo.bootstrap.layout.Split.superclass.show.call(this);
39701 beforeSlide: function(){
39702 if(Roo.isGecko){// firefox overflow auto bug workaround
39703 this.bodyEl.clip();
39705 this.tabs.bodyEl.clip();
39707 if(this.activePanel){
39708 this.activePanel.getEl().clip();
39710 if(this.activePanel.beforeSlide){
39711 this.activePanel.beforeSlide();
39717 afterSlide : function(){
39718 if(Roo.isGecko){// firefox overflow auto bug workaround
39719 this.bodyEl.unclip();
39721 this.tabs.bodyEl.unclip();
39723 if(this.activePanel){
39724 this.activePanel.getEl().unclip();
39725 if(this.activePanel.afterSlide){
39726 this.activePanel.afterSlide();
39732 initAutoHide : function(){
39733 if(this.autoHide !== false){
39734 if(!this.autoHideHd){
39735 var st = new Roo.util.DelayedTask(this.slideIn, this);
39736 this.autoHideHd = {
39737 "mouseout": function(e){
39738 if(!e.within(this.el, true)){
39742 "mouseover" : function(e){
39748 this.el.on(this.autoHideHd);
39752 clearAutoHide : function(){
39753 if(this.autoHide !== false){
39754 this.el.un("mouseout", this.autoHideHd.mouseout);
39755 this.el.un("mouseover", this.autoHideHd.mouseover);
39759 clearMonitor : function(){
39760 Roo.get(document).un("click", this.slideInIf, this);
39763 // these names are backwards but not changed for compat
39764 slideOut : function(){
39765 if(this.isSlid || this.el.hasActiveFx()){
39768 this.isSlid = true;
39769 if(this.collapseBtn){
39770 this.collapseBtn.hide();
39772 this.closeBtnState = this.closeBtn.getStyle('display');
39773 this.closeBtn.hide();
39775 this.stickBtn.show();
39778 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39779 this.beforeSlide();
39780 this.el.setStyle("z-index", 10001);
39781 this.el.slideIn(this.getSlideAnchor(), {
39782 callback: function(){
39784 this.initAutoHide();
39785 Roo.get(document).on("click", this.slideInIf, this);
39786 this.fireEvent("slideshow", this);
39793 afterSlideIn : function(){
39794 this.clearAutoHide();
39795 this.isSlid = false;
39796 this.clearMonitor();
39797 this.el.setStyle("z-index", "");
39798 if(this.collapseBtn){
39799 this.collapseBtn.show();
39801 this.closeBtn.setStyle('display', this.closeBtnState);
39803 this.stickBtn.hide();
39805 this.fireEvent("slidehide", this);
39808 slideIn : function(cb){
39809 if(!this.isSlid || this.el.hasActiveFx()){
39813 this.isSlid = false;
39814 this.beforeSlide();
39815 this.el.slideOut(this.getSlideAnchor(), {
39816 callback: function(){
39817 this.el.setLeftTop(-10000, -10000);
39819 this.afterSlideIn();
39827 slideInIf : function(e){
39828 if(!e.within(this.el)){
39833 animateCollapse : function(){
39834 this.beforeSlide();
39835 this.el.setStyle("z-index", 20000);
39836 var anchor = this.getSlideAnchor();
39837 this.el.slideOut(anchor, {
39838 callback : function(){
39839 this.el.setStyle("z-index", "");
39840 this.collapsedEl.slideIn(anchor, {duration:.3});
39842 this.el.setLocation(-10000,-10000);
39844 this.fireEvent("collapsed", this);
39851 animateExpand : function(){
39852 this.beforeSlide();
39853 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39854 this.el.setStyle("z-index", 20000);
39855 this.collapsedEl.hide({
39858 this.el.slideIn(this.getSlideAnchor(), {
39859 callback : function(){
39860 this.el.setStyle("z-index", "");
39863 this.split.el.show();
39865 this.fireEvent("invalidated", this);
39866 this.fireEvent("expanded", this);
39894 getAnchor : function(){
39895 return this.anchors[this.position];
39898 getCollapseAnchor : function(){
39899 return this.canchors[this.position];
39902 getSlideAnchor : function(){
39903 return this.sanchors[this.position];
39906 getAlignAdj : function(){
39907 var cm = this.cmargins;
39908 switch(this.position){
39924 getExpandAdj : function(){
39925 var c = this.collapsedEl, cm = this.cmargins;
39926 switch(this.position){
39928 return [-(cm.right+c.getWidth()+cm.left), 0];
39931 return [cm.right+c.getWidth()+cm.left, 0];
39934 return [0, -(cm.top+cm.bottom+c.getHeight())];
39937 return [0, cm.top+cm.bottom+c.getHeight()];
39943 * Ext JS Library 1.1.1
39944 * Copyright(c) 2006-2007, Ext JS, LLC.
39946 * Originally Released Under LGPL - original licence link has changed is not relivant.
39949 * <script type="text/javascript">
39952 * These classes are private internal classes
39954 Roo.bootstrap.layout.Center = function(config){
39955 config.region = "center";
39956 Roo.bootstrap.layout.Region.call(this, config);
39957 this.visible = true;
39958 this.minWidth = config.minWidth || 20;
39959 this.minHeight = config.minHeight || 20;
39962 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39964 // center panel can't be hidden
39968 // center panel can't be hidden
39971 getMinWidth: function(){
39972 return this.minWidth;
39975 getMinHeight: function(){
39976 return this.minHeight;
39990 Roo.bootstrap.layout.North = function(config)
39992 config.region = 'north';
39993 config.cursor = 'n-resize';
39995 Roo.bootstrap.layout.Split.call(this, config);
39999 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40000 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40001 this.split.el.addClass("roo-layout-split-v");
40003 //var size = config.initialSize || config.height;
40004 //if(this.el && typeof size != "undefined"){
40005 // this.el.setHeight(size);
40008 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40010 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40013 onRender : function(ctr, pos)
40015 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40016 var size = this.config.initialSize || this.config.height;
40017 if(this.el && typeof size != "undefined"){
40018 this.el.setHeight(size);
40023 getBox : function(){
40024 if(this.collapsed){
40025 return this.collapsedEl.getBox();
40027 var box = this.el.getBox();
40029 box.height += this.split.el.getHeight();
40034 updateBox : function(box){
40035 if(this.split && !this.collapsed){
40036 box.height -= this.split.el.getHeight();
40037 this.split.el.setLeft(box.x);
40038 this.split.el.setTop(box.y+box.height);
40039 this.split.el.setWidth(box.width);
40041 if(this.collapsed){
40042 this.updateBody(box.width, null);
40044 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40052 Roo.bootstrap.layout.South = function(config){
40053 config.region = 'south';
40054 config.cursor = 's-resize';
40055 Roo.bootstrap.layout.Split.call(this, config);
40057 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40058 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40059 this.split.el.addClass("roo-layout-split-v");
40064 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40065 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40067 onRender : function(ctr, pos)
40069 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40070 var size = this.config.initialSize || this.config.height;
40071 if(this.el && typeof size != "undefined"){
40072 this.el.setHeight(size);
40077 getBox : function(){
40078 if(this.collapsed){
40079 return this.collapsedEl.getBox();
40081 var box = this.el.getBox();
40083 var sh = this.split.el.getHeight();
40090 updateBox : function(box){
40091 if(this.split && !this.collapsed){
40092 var sh = this.split.el.getHeight();
40095 this.split.el.setLeft(box.x);
40096 this.split.el.setTop(box.y-sh);
40097 this.split.el.setWidth(box.width);
40099 if(this.collapsed){
40100 this.updateBody(box.width, null);
40102 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40106 Roo.bootstrap.layout.East = function(config){
40107 config.region = "east";
40108 config.cursor = "e-resize";
40109 Roo.bootstrap.layout.Split.call(this, config);
40111 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40112 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40113 this.split.el.addClass("roo-layout-split-h");
40117 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40118 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40120 onRender : function(ctr, pos)
40122 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40123 var size = this.config.initialSize || this.config.width;
40124 if(this.el && typeof size != "undefined"){
40125 this.el.setWidth(size);
40130 getBox : function(){
40131 if(this.collapsed){
40132 return this.collapsedEl.getBox();
40134 var box = this.el.getBox();
40136 var sw = this.split.el.getWidth();
40143 updateBox : function(box){
40144 if(this.split && !this.collapsed){
40145 var sw = this.split.el.getWidth();
40147 this.split.el.setLeft(box.x);
40148 this.split.el.setTop(box.y);
40149 this.split.el.setHeight(box.height);
40152 if(this.collapsed){
40153 this.updateBody(null, box.height);
40155 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40159 Roo.bootstrap.layout.West = function(config){
40160 config.region = "west";
40161 config.cursor = "w-resize";
40163 Roo.bootstrap.layout.Split.call(this, config);
40165 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40166 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40167 this.split.el.addClass("roo-layout-split-h");
40171 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40172 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40174 onRender: function(ctr, pos)
40176 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40177 var size = this.config.initialSize || this.config.width;
40178 if(typeof size != "undefined"){
40179 this.el.setWidth(size);
40183 getBox : function(){
40184 if(this.collapsed){
40185 return this.collapsedEl.getBox();
40187 var box = this.el.getBox();
40188 if (box.width == 0) {
40189 box.width = this.config.width; // kludge?
40192 box.width += this.split.el.getWidth();
40197 updateBox : function(box){
40198 if(this.split && !this.collapsed){
40199 var sw = this.split.el.getWidth();
40201 this.split.el.setLeft(box.x+box.width);
40202 this.split.el.setTop(box.y);
40203 this.split.el.setHeight(box.height);
40205 if(this.collapsed){
40206 this.updateBody(null, box.height);
40208 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40212 * Ext JS Library 1.1.1
40213 * Copyright(c) 2006-2007, Ext JS, LLC.
40215 * Originally Released Under LGPL - original licence link has changed is not relivant.
40218 * <script type="text/javascript">
40221 * @class Roo.bootstrap.paenl.Content
40222 * @extends Roo.util.Observable
40224 * @children Roo.bootstrap.Component
40225 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40226 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40227 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40228 * @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
40229 * @cfg {Boolean} closable True if the panel can be closed/removed
40230 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40231 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40232 * @cfg {Toolbar} toolbar A toolbar for this panel
40233 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40234 * @cfg {String} title The title for this panel
40235 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40236 * @cfg {String} url Calls {@link #setUrl} with this value
40237 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40238 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40239 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40240 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40241 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40242 * @cfg {Boolean} badges render the badges
40243 * @cfg {String} cls extra classes to use
40244 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40247 * Create a new ContentPanel.
40248 * @param {String/Object} config A string to set only the title or a config object
40251 Roo.bootstrap.panel.Content = function( config){
40253 this.tpl = config.tpl || false;
40255 var el = config.el;
40256 var content = config.content;
40258 if(config.autoCreate){ // xtype is available if this is called from factory
40261 this.el = Roo.get(el);
40262 if(!this.el && config && config.autoCreate){
40263 if(typeof config.autoCreate == "object"){
40264 if(!config.autoCreate.id){
40265 config.autoCreate.id = config.id||el;
40267 this.el = Roo.DomHelper.append(document.body,
40268 config.autoCreate, true);
40272 cls: (config.cls || '') +
40273 (config.background ? ' bg-' + config.background : '') +
40274 " roo-layout-inactive-content",
40277 if (config.iframe) {
40281 style : 'border: 0px',
40282 src : 'about:blank'
40288 elcfg.html = config.html;
40292 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40293 if (config.iframe) {
40294 this.iframeEl = this.el.select('iframe',true).first();
40299 this.closable = false;
40300 this.loaded = false;
40301 this.active = false;
40304 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40306 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40308 this.wrapEl = this.el; //this.el.wrap();
40310 if (config.toolbar.items) {
40311 ti = config.toolbar.items ;
40312 delete config.toolbar.items ;
40316 this.toolbar.render(this.wrapEl, 'before');
40317 for(var i =0;i < ti.length;i++) {
40318 // Roo.log(['add child', items[i]]);
40319 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40321 this.toolbar.items = nitems;
40322 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40323 delete config.toolbar;
40327 // xtype created footer. - not sure if will work as we normally have to render first..
40328 if (this.footer && !this.footer.el && this.footer.xtype) {
40329 if (!this.wrapEl) {
40330 this.wrapEl = this.el.wrap();
40333 this.footer.container = this.wrapEl.createChild();
40335 this.footer = Roo.factory(this.footer, Roo);
40340 if(typeof config == "string"){
40341 this.title = config;
40343 Roo.apply(this, config);
40347 this.resizeEl = Roo.get(this.resizeEl, true);
40349 this.resizeEl = this.el;
40351 // handle view.xtype
40359 * Fires when this panel is activated.
40360 * @param {Roo.ContentPanel} this
40364 * @event deactivate
40365 * Fires when this panel is activated.
40366 * @param {Roo.ContentPanel} this
40368 "deactivate" : true,
40372 * Fires when this panel is resized if fitToFrame is true.
40373 * @param {Roo.ContentPanel} this
40374 * @param {Number} width The width after any component adjustments
40375 * @param {Number} height The height after any component adjustments
40381 * Fires when this tab is created
40382 * @param {Roo.ContentPanel} this
40388 * Fires when this content is scrolled
40389 * @param {Roo.ContentPanel} this
40390 * @param {Event} scrollEvent
40401 if(this.autoScroll && !this.iframe){
40402 this.resizeEl.setStyle("overflow", "auto");
40403 this.resizeEl.on('scroll', this.onScroll, this);
40405 // fix randome scrolling
40406 //this.el.on('scroll', function() {
40407 // Roo.log('fix random scolling');
40408 // this.scrollTo('top',0);
40411 content = content || this.content;
40413 this.setContent(content);
40415 if(config && config.url){
40416 this.setUrl(this.url, this.params, this.loadOnce);
40421 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40423 if (this.view && typeof(this.view.xtype) != 'undefined') {
40424 this.view.el = this.el.appendChild(document.createElement("div"));
40425 this.view = Roo.factory(this.view);
40426 this.view.render && this.view.render(false, '');
40430 this.fireEvent('render', this);
40433 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40443 /* Resize Element - use this to work out scroll etc. */
40446 setRegion : function(region){
40447 this.region = region;
40448 this.setActiveClass(region && !this.background);
40452 setActiveClass: function(state)
40455 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40456 this.el.setStyle('position','relative');
40458 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40459 this.el.setStyle('position', 'absolute');
40464 * Returns the toolbar for this Panel if one was configured.
40465 * @return {Roo.Toolbar}
40467 getToolbar : function(){
40468 return this.toolbar;
40471 setActiveState : function(active)
40473 this.active = active;
40474 this.setActiveClass(active);
40476 if(this.fireEvent("deactivate", this) === false){
40481 this.fireEvent("activate", this);
40485 * Updates this panel's element (not for iframe)
40486 * @param {String} content The new content
40487 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40489 setContent : function(content, loadScripts){
40494 this.el.update(content, loadScripts);
40497 ignoreResize : function(w, h){
40498 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40501 this.lastSize = {width: w, height: h};
40506 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40507 * @return {Roo.UpdateManager} The UpdateManager
40509 getUpdateManager : function(){
40513 return this.el.getUpdateManager();
40516 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40517 * Does not work with IFRAME contents
40518 * @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:
40521 url: "your-url.php",
40522 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40523 callback: yourFunction,
40524 scope: yourObject, //(optional scope)
40527 text: "Loading...",
40533 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40534 * 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.
40535 * @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}
40536 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40537 * @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.
40538 * @return {Roo.ContentPanel} this
40546 var um = this.el.getUpdateManager();
40547 um.update.apply(um, arguments);
40553 * 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.
40554 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40555 * @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)
40556 * @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)
40557 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40559 setUrl : function(url, params, loadOnce){
40561 this.iframeEl.dom.src = url;
40565 if(this.refreshDelegate){
40566 this.removeListener("activate", this.refreshDelegate);
40568 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40569 this.on("activate", this.refreshDelegate);
40570 return this.el.getUpdateManager();
40573 _handleRefresh : function(url, params, loadOnce){
40574 if(!loadOnce || !this.loaded){
40575 var updater = this.el.getUpdateManager();
40576 updater.update(url, params, this._setLoaded.createDelegate(this));
40580 _setLoaded : function(){
40581 this.loaded = true;
40585 * Returns this panel's id
40588 getId : function(){
40593 * Returns this panel's element - used by regiosn to add.
40594 * @return {Roo.Element}
40596 getEl : function(){
40597 return this.wrapEl || this.el;
40602 adjustForComponents : function(width, height)
40604 //Roo.log('adjustForComponents ');
40605 if(this.resizeEl != this.el){
40606 width -= this.el.getFrameWidth('lr');
40607 height -= this.el.getFrameWidth('tb');
40610 var te = this.toolbar.getEl();
40611 te.setWidth(width);
40612 height -= te.getHeight();
40615 var te = this.footer.getEl();
40616 te.setWidth(width);
40617 height -= te.getHeight();
40621 if(this.adjustments){
40622 width += this.adjustments[0];
40623 height += this.adjustments[1];
40625 return {"width": width, "height": height};
40628 setSize : function(width, height){
40629 if(this.fitToFrame && !this.ignoreResize(width, height)){
40630 if(this.fitContainer && this.resizeEl != this.el){
40631 this.el.setSize(width, height);
40633 var size = this.adjustForComponents(width, height);
40635 this.iframeEl.setSize(width,height);
40638 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40639 this.fireEvent('resize', this, size.width, size.height);
40646 * Returns this panel's title
40649 getTitle : function(){
40651 if (typeof(this.title) != 'object') {
40656 for (var k in this.title) {
40657 if (!this.title.hasOwnProperty(k)) {
40661 if (k.indexOf('-') >= 0) {
40662 var s = k.split('-');
40663 for (var i = 0; i<s.length; i++) {
40664 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40667 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40674 * Set this panel's title
40675 * @param {String} title
40677 setTitle : function(title){
40678 this.title = title;
40680 this.region.updatePanelTitle(this, title);
40685 * Returns true is this panel was configured to be closable
40686 * @return {Boolean}
40688 isClosable : function(){
40689 return this.closable;
40692 beforeSlide : function(){
40694 this.resizeEl.clip();
40697 afterSlide : function(){
40699 this.resizeEl.unclip();
40703 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40704 * Will fail silently if the {@link #setUrl} method has not been called.
40705 * This does not activate the panel, just updates its content.
40707 refresh : function(){
40708 if(this.refreshDelegate){
40709 this.loaded = false;
40710 this.refreshDelegate();
40715 * Destroys this panel
40717 destroy : function(){
40718 this.el.removeAllListeners();
40719 var tempEl = document.createElement("span");
40720 tempEl.appendChild(this.el.dom);
40721 tempEl.innerHTML = "";
40727 * form - if the content panel contains a form - this is a reference to it.
40728 * @type {Roo.form.Form}
40732 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40733 * This contains a reference to it.
40739 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40749 * @param {Object} cfg Xtype definition of item to add.
40753 getChildContainer: function () {
40754 return this.getEl();
40758 onScroll : function(e)
40760 this.fireEvent('scroll', this, e);
40765 var ret = new Roo.factory(cfg);
40770 if (cfg.xtype.match(/^Form$/)) {
40773 //if (this.footer) {
40774 // el = this.footer.container.insertSibling(false, 'before');
40776 el = this.el.createChild();
40779 this.form = new Roo.form.Form(cfg);
40782 if ( this.form.allItems.length) {
40783 this.form.render(el.dom);
40787 // should only have one of theses..
40788 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40789 // views.. should not be just added - used named prop 'view''
40791 cfg.el = this.el.appendChild(document.createElement("div"));
40794 var ret = new Roo.factory(cfg);
40796 ret.render && ret.render(false, ''); // render blank..
40806 * @class Roo.bootstrap.panel.Grid
40807 * @extends Roo.bootstrap.panel.Content
40809 * Create a new GridPanel.
40810 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40811 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40812 * @param {Object} config A the config object
40818 Roo.bootstrap.panel.Grid = function(config)
40822 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40823 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40825 config.el = this.wrapper;
40826 //this.el = this.wrapper;
40828 if (config.container) {
40829 // ctor'ed from a Border/panel.grid
40832 this.wrapper.setStyle("overflow", "hidden");
40833 this.wrapper.addClass('roo-grid-container');
40838 if(config.toolbar){
40839 var tool_el = this.wrapper.createChild();
40840 this.toolbar = Roo.factory(config.toolbar);
40842 if (config.toolbar.items) {
40843 ti = config.toolbar.items ;
40844 delete config.toolbar.items ;
40848 this.toolbar.render(tool_el);
40849 for(var i =0;i < ti.length;i++) {
40850 // Roo.log(['add child', items[i]]);
40851 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40853 this.toolbar.items = nitems;
40855 delete config.toolbar;
40858 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40859 config.grid.scrollBody = true;;
40860 config.grid.monitorWindowResize = false; // turn off autosizing
40861 config.grid.autoHeight = false;
40862 config.grid.autoWidth = false;
40864 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40866 if (config.background) {
40867 // render grid on panel activation (if panel background)
40868 this.on('activate', function(gp) {
40869 if (!gp.grid.rendered) {
40870 gp.grid.render(this.wrapper);
40871 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40876 this.grid.render(this.wrapper);
40877 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40880 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40881 // ??? needed ??? config.el = this.wrapper;
40886 // xtype created footer. - not sure if will work as we normally have to render first..
40887 if (this.footer && !this.footer.el && this.footer.xtype) {
40889 var ctr = this.grid.getView().getFooterPanel(true);
40890 this.footer.dataSource = this.grid.dataSource;
40891 this.footer = Roo.factory(this.footer, Roo);
40892 this.footer.render(ctr);
40902 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40903 getId : function(){
40904 return this.grid.id;
40908 * Returns the grid for this panel
40909 * @return {Roo.bootstrap.Table}
40911 getGrid : function(){
40915 setSize : function(width, height){
40916 if(!this.ignoreResize(width, height)){
40917 var grid = this.grid;
40918 var size = this.adjustForComponents(width, height);
40919 // tfoot is not a footer?
40922 var gridel = grid.getGridEl();
40923 gridel.setSize(size.width, size.height);
40925 var tbd = grid.getGridEl().select('tbody', true).first();
40926 var thd = grid.getGridEl().select('thead',true).first();
40927 var tbf= grid.getGridEl().select('tfoot', true).first();
40930 size.height -= tbf.getHeight();
40933 size.height -= thd.getHeight();
40936 tbd.setSize(size.width, size.height );
40937 // this is for the account management tab -seems to work there.
40938 var thd = grid.getGridEl().select('thead',true).first();
40940 // tbd.setSize(size.width, size.height - thd.getHeight());
40949 beforeSlide : function(){
40950 this.grid.getView().scroller.clip();
40953 afterSlide : function(){
40954 this.grid.getView().scroller.unclip();
40957 destroy : function(){
40958 this.grid.destroy();
40960 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40965 * @class Roo.bootstrap.panel.Nest
40966 * @extends Roo.bootstrap.panel.Content
40968 * Create a new Panel, that can contain a layout.Border.
40971 * @param {String/Object} config A string to set only the title or a config object
40973 Roo.bootstrap.panel.Nest = function(config)
40975 // construct with only one argument..
40976 /* FIXME - implement nicer consturctors
40977 if (layout.layout) {
40979 layout = config.layout;
40980 delete config.layout;
40982 if (layout.xtype && !layout.getEl) {
40983 // then layout needs constructing..
40984 layout = Roo.factory(layout, Roo);
40988 config.el = config.layout.getEl();
40990 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40992 config.layout.monitorWindowResize = false; // turn off autosizing
40993 this.layout = config.layout;
40994 this.layout.getEl().addClass("roo-layout-nested-layout");
40995 this.layout.parent = this;
41002 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41004 * @cfg {Roo.BorderLayout} layout The layout for this panel
41008 setSize : function(width, height){
41009 if(!this.ignoreResize(width, height)){
41010 var size = this.adjustForComponents(width, height);
41011 var el = this.layout.getEl();
41012 if (size.height < 1) {
41013 el.setWidth(size.width);
41015 el.setSize(size.width, size.height);
41017 var touch = el.dom.offsetWidth;
41018 this.layout.layout();
41019 // ie requires a double layout on the first pass
41020 if(Roo.isIE && !this.initialized){
41021 this.initialized = true;
41022 this.layout.layout();
41027 // activate all subpanels if not currently active..
41029 setActiveState : function(active){
41030 this.active = active;
41031 this.setActiveClass(active);
41034 this.fireEvent("deactivate", this);
41038 this.fireEvent("activate", this);
41039 // not sure if this should happen before or after..
41040 if (!this.layout) {
41041 return; // should not happen..
41044 for (var r in this.layout.regions) {
41045 reg = this.layout.getRegion(r);
41046 if (reg.getActivePanel()) {
41047 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41048 reg.setActivePanel(reg.getActivePanel());
41051 if (!reg.panels.length) {
41054 reg.showPanel(reg.getPanel(0));
41063 * Returns the nested BorderLayout for this panel
41064 * @return {Roo.BorderLayout}
41066 getLayout : function(){
41067 return this.layout;
41071 * Adds a xtype elements to the layout of the nested panel
41075 xtype : 'ContentPanel',
41082 xtype : 'NestedLayoutPanel',
41088 items : [ ... list of content panels or nested layout panels.. ]
41092 * @param {Object} cfg Xtype definition of item to add.
41094 addxtype : function(cfg) {
41095 return this.layout.addxtype(cfg);
41100 * Ext JS Library 1.1.1
41101 * Copyright(c) 2006-2007, Ext JS, LLC.
41103 * Originally Released Under LGPL - original licence link has changed is not relivant.
41106 * <script type="text/javascript">
41109 * @class Roo.TabPanel
41110 * @extends Roo.util.Observable
41111 * A lightweight tab container.
41115 // basic tabs 1, built from existing content
41116 var tabs = new Roo.TabPanel("tabs1");
41117 tabs.addTab("script", "View Script");
41118 tabs.addTab("markup", "View Markup");
41119 tabs.activate("script");
41121 // more advanced tabs, built from javascript
41122 var jtabs = new Roo.TabPanel("jtabs");
41123 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41125 // set up the UpdateManager
41126 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41127 var updater = tab2.getUpdateManager();
41128 updater.setDefaultUrl("ajax1.htm");
41129 tab2.on('activate', updater.refresh, updater, true);
41131 // Use setUrl for Ajax loading
41132 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41133 tab3.setUrl("ajax2.htm", null, true);
41136 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41139 jtabs.activate("jtabs-1");
41142 * Create a new TabPanel.
41143 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41144 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41146 Roo.bootstrap.panel.Tabs = function(config){
41148 * The container element for this TabPanel.
41149 * @type Roo.Element
41151 this.el = Roo.get(config.el);
41154 if(typeof config == "boolean"){
41155 this.tabPosition = config ? "bottom" : "top";
41157 Roo.apply(this, config);
41161 if(this.tabPosition == "bottom"){
41162 // if tabs are at the bottom = create the body first.
41163 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41164 this.el.addClass("roo-tabs-bottom");
41166 // next create the tabs holders
41168 if (this.tabPosition == "west"){
41170 var reg = this.region; // fake it..
41172 if (!reg.mgr.parent) {
41175 reg = reg.mgr.parent.region;
41177 Roo.log("got nest?");
41179 if (reg.mgr.getRegion('west')) {
41180 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41181 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41182 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41183 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41184 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41192 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41193 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41194 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41195 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41200 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41203 // finally - if tabs are at the top, then create the body last..
41204 if(this.tabPosition != "bottom"){
41205 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41206 * @type Roo.Element
41208 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41209 this.el.addClass("roo-tabs-top");
41213 this.bodyEl.setStyle("position", "relative");
41215 this.active = null;
41216 this.activateDelegate = this.activate.createDelegate(this);
41221 * Fires when the active tab changes
41222 * @param {Roo.TabPanel} this
41223 * @param {Roo.TabPanelItem} activePanel The new active tab
41227 * @event beforetabchange
41228 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41229 * @param {Roo.TabPanel} this
41230 * @param {Object} e Set cancel to true on this object to cancel the tab change
41231 * @param {Roo.TabPanelItem} tab The tab being changed to
41233 "beforetabchange" : true
41236 Roo.EventManager.onWindowResize(this.onResize, this);
41237 this.cpad = this.el.getPadding("lr");
41238 this.hiddenCount = 0;
41241 // toolbar on the tabbar support...
41242 if (this.toolbar) {
41243 alert("no toolbar support yet");
41244 this.toolbar = false;
41246 var tcfg = this.toolbar;
41247 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41248 this.toolbar = new Roo.Toolbar(tcfg);
41249 if (Roo.isSafari) {
41250 var tbl = tcfg.container.child('table', true);
41251 tbl.setAttribute('width', '100%');
41259 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41262 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41264 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41266 tabPosition : "top",
41268 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41270 currentTabWidth : 0,
41272 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41276 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41280 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41282 preferredTabWidth : 175,
41284 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41286 resizeTabs : false,
41288 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41290 monitorResize : true,
41292 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41294 toolbar : false, // set by caller..
41296 region : false, /// set by caller
41298 disableTooltips : true, // not used yet...
41301 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41302 * @param {String} id The id of the div to use <b>or create</b>
41303 * @param {String} text The text for the tab
41304 * @param {String} content (optional) Content to put in the TabPanelItem body
41305 * @param {Boolean} closable (optional) True to create a close icon on the tab
41306 * @return {Roo.TabPanelItem} The created TabPanelItem
41308 addTab : function(id, text, content, closable, tpl)
41310 var item = new Roo.bootstrap.panel.TabItem({
41314 closable : closable,
41317 this.addTabItem(item);
41319 item.setContent(content);
41325 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41326 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41327 * @return {Roo.TabPanelItem}
41329 getTab : function(id){
41330 return this.items[id];
41334 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41335 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41337 hideTab : function(id){
41338 var t = this.items[id];
41341 this.hiddenCount++;
41342 this.autoSizeTabs();
41347 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41348 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41350 unhideTab : function(id){
41351 var t = this.items[id];
41353 t.setHidden(false);
41354 this.hiddenCount--;
41355 this.autoSizeTabs();
41360 * Adds an existing {@link Roo.TabPanelItem}.
41361 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41363 addTabItem : function(item)
41365 this.items[item.id] = item;
41366 this.items.push(item);
41367 this.autoSizeTabs();
41368 // if(this.resizeTabs){
41369 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41370 // this.autoSizeTabs();
41372 // item.autoSize();
41377 * Removes a {@link Roo.TabPanelItem}.
41378 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41380 removeTab : function(id){
41381 var items = this.items;
41382 var tab = items[id];
41383 if(!tab) { return; }
41384 var index = items.indexOf(tab);
41385 if(this.active == tab && items.length > 1){
41386 var newTab = this.getNextAvailable(index);
41391 this.stripEl.dom.removeChild(tab.pnode.dom);
41392 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41393 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41395 items.splice(index, 1);
41396 delete this.items[tab.id];
41397 tab.fireEvent("close", tab);
41398 tab.purgeListeners();
41399 this.autoSizeTabs();
41402 getNextAvailable : function(start){
41403 var items = this.items;
41405 // look for a next tab that will slide over to
41406 // replace the one being removed
41407 while(index < items.length){
41408 var item = items[++index];
41409 if(item && !item.isHidden()){
41413 // if one isn't found select the previous tab (on the left)
41416 var item = items[--index];
41417 if(item && !item.isHidden()){
41425 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41426 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41428 disableTab : function(id){
41429 var tab = this.items[id];
41430 if(tab && this.active != tab){
41436 * Enables a {@link Roo.TabPanelItem} that is disabled.
41437 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41439 enableTab : function(id){
41440 var tab = this.items[id];
41445 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41446 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41447 * @return {Roo.TabPanelItem} The TabPanelItem.
41449 activate : function(id)
41451 //Roo.log('activite:' + id);
41453 var tab = this.items[id];
41457 if(tab == this.active || tab.disabled){
41461 this.fireEvent("beforetabchange", this, e, tab);
41462 if(e.cancel !== true && !tab.disabled){
41464 this.active.hide();
41466 this.active = this.items[id];
41467 this.active.show();
41468 this.fireEvent("tabchange", this, this.active);
41474 * Gets the active {@link Roo.TabPanelItem}.
41475 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41477 getActiveTab : function(){
41478 return this.active;
41482 * Updates the tab body element to fit the height of the container element
41483 * for overflow scrolling
41484 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41486 syncHeight : function(targetHeight){
41487 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41488 var bm = this.bodyEl.getMargins();
41489 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41490 this.bodyEl.setHeight(newHeight);
41494 onResize : function(){
41495 if(this.monitorResize){
41496 this.autoSizeTabs();
41501 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41503 beginUpdate : function(){
41504 this.updating = true;
41508 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41510 endUpdate : function(){
41511 this.updating = false;
41512 this.autoSizeTabs();
41516 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41518 autoSizeTabs : function()
41520 var count = this.items.length;
41521 var vcount = count - this.hiddenCount;
41524 this.stripEl.hide();
41526 this.stripEl.show();
41529 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41534 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41535 var availWidth = Math.floor(w / vcount);
41536 var b = this.stripBody;
41537 if(b.getWidth() > w){
41538 var tabs = this.items;
41539 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41540 if(availWidth < this.minTabWidth){
41541 /*if(!this.sleft){ // incomplete scrolling code
41542 this.createScrollButtons();
41545 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41548 if(this.currentTabWidth < this.preferredTabWidth){
41549 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41555 * Returns the number of tabs in this TabPanel.
41558 getCount : function(){
41559 return this.items.length;
41563 * Resizes all the tabs to the passed width
41564 * @param {Number} The new width
41566 setTabWidth : function(width){
41567 this.currentTabWidth = width;
41568 for(var i = 0, len = this.items.length; i < len; i++) {
41569 if(!this.items[i].isHidden()) {
41570 this.items[i].setWidth(width);
41576 * Destroys this TabPanel
41577 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41579 destroy : function(removeEl){
41580 Roo.EventManager.removeResizeListener(this.onResize, this);
41581 for(var i = 0, len = this.items.length; i < len; i++){
41582 this.items[i].purgeListeners();
41584 if(removeEl === true){
41585 this.el.update("");
41590 createStrip : function(container)
41592 var strip = document.createElement("nav");
41593 strip.className = Roo.bootstrap.version == 4 ?
41594 "navbar-light bg-light" :
41595 "navbar navbar-default"; //"x-tabs-wrap";
41596 container.appendChild(strip);
41600 createStripList : function(strip)
41602 // div wrapper for retard IE
41603 // returns the "tr" element.
41604 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41605 //'<div class="x-tabs-strip-wrap">'+
41606 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41607 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41608 return strip.firstChild; //.firstChild.firstChild.firstChild;
41610 createBody : function(container)
41612 var body = document.createElement("div");
41613 Roo.id(body, "tab-body");
41614 //Roo.fly(body).addClass("x-tabs-body");
41615 Roo.fly(body).addClass("tab-content");
41616 container.appendChild(body);
41619 createItemBody :function(bodyEl, id){
41620 var body = Roo.getDom(id);
41622 body = document.createElement("div");
41625 //Roo.fly(body).addClass("x-tabs-item-body");
41626 Roo.fly(body).addClass("tab-pane");
41627 bodyEl.insertBefore(body, bodyEl.firstChild);
41631 createStripElements : function(stripEl, text, closable, tpl)
41633 var td = document.createElement("li"); // was td..
41634 td.className = 'nav-item';
41636 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41639 stripEl.appendChild(td);
41641 td.className = "x-tabs-closable";
41642 if(!this.closeTpl){
41643 this.closeTpl = new Roo.Template(
41644 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41645 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41646 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41649 var el = this.closeTpl.overwrite(td, {"text": text});
41650 var close = el.getElementsByTagName("div")[0];
41651 var inner = el.getElementsByTagName("em")[0];
41652 return {"el": el, "close": close, "inner": inner};
41655 // not sure what this is..
41656 // if(!this.tabTpl){
41657 //this.tabTpl = new Roo.Template(
41658 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41659 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41661 // this.tabTpl = new Roo.Template(
41662 // '<a href="#">' +
41663 // '<span unselectable="on"' +
41664 // (this.disableTooltips ? '' : ' title="{text}"') +
41665 // ' >{text}</span></a>'
41671 var template = tpl || this.tabTpl || false;
41674 template = new Roo.Template(
41675 Roo.bootstrap.version == 4 ?
41677 '<a class="nav-link" href="#" unselectable="on"' +
41678 (this.disableTooltips ? '' : ' title="{text}"') +
41681 '<a class="nav-link" href="#">' +
41682 '<span unselectable="on"' +
41683 (this.disableTooltips ? '' : ' title="{text}"') +
41684 ' >{text}</span></a>'
41689 switch (typeof(template)) {
41693 template = new Roo.Template(template);
41699 var el = template.overwrite(td, {"text": text});
41701 var inner = el.getElementsByTagName("span")[0];
41703 return {"el": el, "inner": inner};
41711 * @class Roo.TabPanelItem
41712 * @extends Roo.util.Observable
41713 * Represents an individual item (tab plus body) in a TabPanel.
41714 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41715 * @param {String} id The id of this TabPanelItem
41716 * @param {String} text The text for the tab of this TabPanelItem
41717 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41719 Roo.bootstrap.panel.TabItem = function(config){
41721 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41722 * @type Roo.TabPanel
41724 this.tabPanel = config.panel;
41726 * The id for this TabPanelItem
41729 this.id = config.id;
41731 this.disabled = false;
41733 this.text = config.text;
41735 this.loaded = false;
41736 this.closable = config.closable;
41739 * The body element for this TabPanelItem.
41740 * @type Roo.Element
41742 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41743 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41744 this.bodyEl.setStyle("display", "block");
41745 this.bodyEl.setStyle("zoom", "1");
41746 //this.hideAction();
41748 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41750 this.el = Roo.get(els.el);
41751 this.inner = Roo.get(els.inner, true);
41752 this.textEl = Roo.bootstrap.version == 4 ?
41753 this.el : Roo.get(this.el.dom.firstChild, true);
41755 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41756 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41759 // this.el.on("mousedown", this.onTabMouseDown, this);
41760 this.el.on("click", this.onTabClick, this);
41762 if(config.closable){
41763 var c = Roo.get(els.close, true);
41764 c.dom.title = this.closeText;
41765 c.addClassOnOver("close-over");
41766 c.on("click", this.closeClick, this);
41772 * Fires when this tab becomes the active tab.
41773 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41774 * @param {Roo.TabPanelItem} this
41778 * @event beforeclose
41779 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41780 * @param {Roo.TabPanelItem} this
41781 * @param {Object} e Set cancel to true on this object to cancel the close.
41783 "beforeclose": true,
41786 * Fires when this tab is closed.
41787 * @param {Roo.TabPanelItem} this
41791 * @event deactivate
41792 * Fires when this tab is no longer the active tab.
41793 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41794 * @param {Roo.TabPanelItem} this
41796 "deactivate" : true
41798 this.hidden = false;
41800 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41803 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41805 purgeListeners : function(){
41806 Roo.util.Observable.prototype.purgeListeners.call(this);
41807 this.el.removeAllListeners();
41810 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41813 this.status_node.addClass("active");
41816 this.tabPanel.stripWrap.repaint();
41818 this.fireEvent("activate", this.tabPanel, this);
41822 * Returns true if this tab is the active tab.
41823 * @return {Boolean}
41825 isActive : function(){
41826 return this.tabPanel.getActiveTab() == this;
41830 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41833 this.status_node.removeClass("active");
41835 this.fireEvent("deactivate", this.tabPanel, this);
41838 hideAction : function(){
41839 this.bodyEl.hide();
41840 this.bodyEl.setStyle("position", "absolute");
41841 this.bodyEl.setLeft("-20000px");
41842 this.bodyEl.setTop("-20000px");
41845 showAction : function(){
41846 this.bodyEl.setStyle("position", "relative");
41847 this.bodyEl.setTop("");
41848 this.bodyEl.setLeft("");
41849 this.bodyEl.show();
41853 * Set the tooltip for the tab.
41854 * @param {String} tooltip The tab's tooltip
41856 setTooltip : function(text){
41857 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41858 this.textEl.dom.qtip = text;
41859 this.textEl.dom.removeAttribute('title');
41861 this.textEl.dom.title = text;
41865 onTabClick : function(e){
41866 e.preventDefault();
41867 this.tabPanel.activate(this.id);
41870 onTabMouseDown : function(e){
41871 e.preventDefault();
41872 this.tabPanel.activate(this.id);
41875 getWidth : function(){
41876 return this.inner.getWidth();
41879 setWidth : function(width){
41880 var iwidth = width - this.linode.getPadding("lr");
41881 this.inner.setWidth(iwidth);
41882 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41883 this.linode.setWidth(width);
41887 * Show or hide the tab
41888 * @param {Boolean} hidden True to hide or false to show.
41890 setHidden : function(hidden){
41891 this.hidden = hidden;
41892 this.linode.setStyle("display", hidden ? "none" : "");
41896 * Returns true if this tab is "hidden"
41897 * @return {Boolean}
41899 isHidden : function(){
41900 return this.hidden;
41904 * Returns the text for this tab
41907 getText : function(){
41911 autoSize : function(){
41912 //this.el.beginMeasure();
41913 this.textEl.setWidth(1);
41915 * #2804 [new] Tabs in Roojs
41916 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41918 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41919 //this.el.endMeasure();
41923 * Sets the text for the tab (Note: this also sets the tooltip text)
41924 * @param {String} text The tab's text and tooltip
41926 setText : function(text){
41928 this.textEl.update(text);
41929 this.setTooltip(text);
41930 //if(!this.tabPanel.resizeTabs){
41931 // this.autoSize();
41935 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41937 activate : function(){
41938 this.tabPanel.activate(this.id);
41942 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41944 disable : function(){
41945 if(this.tabPanel.active != this){
41946 this.disabled = true;
41947 this.status_node.addClass("disabled");
41952 * Enables this TabPanelItem if it was previously disabled.
41954 enable : function(){
41955 this.disabled = false;
41956 this.status_node.removeClass("disabled");
41960 * Sets the content for this TabPanelItem.
41961 * @param {String} content The content
41962 * @param {Boolean} loadScripts true to look for and load scripts
41964 setContent : function(content, loadScripts){
41965 this.bodyEl.update(content, loadScripts);
41969 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41970 * @return {Roo.UpdateManager} The UpdateManager
41972 getUpdateManager : function(){
41973 return this.bodyEl.getUpdateManager();
41977 * Set a URL to be used to load the content for this TabPanelItem.
41978 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41979 * @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)
41980 * @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)
41981 * @return {Roo.UpdateManager} The UpdateManager
41983 setUrl : function(url, params, loadOnce){
41984 if(this.refreshDelegate){
41985 this.un('activate', this.refreshDelegate);
41987 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41988 this.on("activate", this.refreshDelegate);
41989 return this.bodyEl.getUpdateManager();
41993 _handleRefresh : function(url, params, loadOnce){
41994 if(!loadOnce || !this.loaded){
41995 var updater = this.bodyEl.getUpdateManager();
41996 updater.update(url, params, this._setLoaded.createDelegate(this));
42001 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42002 * Will fail silently if the setUrl method has not been called.
42003 * This does not activate the panel, just updates its content.
42005 refresh : function(){
42006 if(this.refreshDelegate){
42007 this.loaded = false;
42008 this.refreshDelegate();
42013 _setLoaded : function(){
42014 this.loaded = true;
42018 closeClick : function(e){
42021 this.fireEvent("beforeclose", this, o);
42022 if(o.cancel !== true){
42023 this.tabPanel.removeTab(this.id);
42027 * The text displayed in the tooltip for the close icon.
42030 closeText : "Close this tab"
42033 * This script refer to:
42034 * Title: International Telephone Input
42035 * Author: Jack O'Connor
42036 * Code version: v12.1.12
42037 * Availability: https://github.com/jackocnr/intl-tel-input.git
42040 Roo.bootstrap.form.PhoneInputData = function() {
42043 "Afghanistan (افغانستان)",
42048 "Albania (Shqipëri)",
42053 "Algeria (الجزائر)",
42078 "Antigua and Barbuda",
42088 "Armenia (Հայաստան)",
42104 "Austria (Österreich)",
42109 "Azerbaijan (Azərbaycan)",
42119 "Bahrain (البحرين)",
42124 "Bangladesh (বাংলাদেশ)",
42134 "Belarus (Беларусь)",
42139 "Belgium (België)",
42169 "Bosnia and Herzegovina (Босна и Херцеговина)",
42184 "British Indian Ocean Territory",
42189 "British Virgin Islands",
42199 "Bulgaria (България)",
42209 "Burundi (Uburundi)",
42214 "Cambodia (កម្ពុជា)",
42219 "Cameroon (Cameroun)",
42228 ["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"]
42231 "Cape Verde (Kabu Verdi)",
42236 "Caribbean Netherlands",
42247 "Central African Republic (République centrafricaine)",
42267 "Christmas Island",
42273 "Cocos (Keeling) Islands",
42284 "Comoros (جزر القمر)",
42289 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42294 "Congo (Republic) (Congo-Brazzaville)",
42314 "Croatia (Hrvatska)",
42335 "Czech Republic (Česká republika)",
42340 "Denmark (Danmark)",
42355 "Dominican Republic (República Dominicana)",
42359 ["809", "829", "849"]
42377 "Equatorial Guinea (Guinea Ecuatorial)",
42397 "Falkland Islands (Islas Malvinas)",
42402 "Faroe Islands (Føroyar)",
42423 "French Guiana (Guyane française)",
42428 "French Polynesia (Polynésie française)",
42443 "Georgia (საქართველო)",
42448 "Germany (Deutschland)",
42468 "Greenland (Kalaallit Nunaat)",
42505 "Guinea-Bissau (Guiné Bissau)",
42530 "Hungary (Magyarország)",
42535 "Iceland (Ísland)",
42555 "Iraq (العراق)",
42571 "Israel (ישראל)",
42598 "Jordan (الأردن)",
42603 "Kazakhstan (Казахстан)",
42624 "Kuwait (الكويت)",
42629 "Kyrgyzstan (Кыргызстан)",
42639 "Latvia (Latvija)",
42644 "Lebanon (لبنان)",
42659 "Libya (ليبيا)",
42669 "Lithuania (Lietuva)",
42684 "Macedonia (FYROM) (Македонија)",
42689 "Madagascar (Madagasikara)",
42719 "Marshall Islands",
42729 "Mauritania (موريتانيا)",
42734 "Mauritius (Moris)",
42755 "Moldova (Republica Moldova)",
42765 "Mongolia (Монгол)",
42770 "Montenegro (Crna Gora)",
42780 "Morocco (المغرب)",
42786 "Mozambique (Moçambique)",
42791 "Myanmar (Burma) (မြန်မာ)",
42796 "Namibia (Namibië)",
42811 "Netherlands (Nederland)",
42816 "New Caledonia (Nouvelle-Calédonie)",
42851 "North Korea (조선 민주주의 인민 공화국)",
42856 "Northern Mariana Islands",
42872 "Pakistan (پاکستان)",
42882 "Palestine (فلسطين)",
42892 "Papua New Guinea",
42934 "Réunion (La Réunion)",
42940 "Romania (România)",
42956 "Saint Barthélemy",
42967 "Saint Kitts and Nevis",
42977 "Saint Martin (Saint-Martin (partie française))",
42983 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42988 "Saint Vincent and the Grenadines",
43003 "São Tomé and Príncipe (São Tomé e Príncipe)",
43008 "Saudi Arabia (المملكة العربية السعودية)",
43013 "Senegal (Sénégal)",
43043 "Slovakia (Slovensko)",
43048 "Slovenia (Slovenija)",
43058 "Somalia (Soomaaliya)",
43068 "South Korea (대한민국)",
43073 "South Sudan (جنوب السودان)",
43083 "Sri Lanka (ශ්රී ලංකාව)",
43088 "Sudan (السودان)",
43098 "Svalbard and Jan Mayen",
43109 "Sweden (Sverige)",
43114 "Switzerland (Schweiz)",
43119 "Syria (سوريا)",
43164 "Trinidad and Tobago",
43169 "Tunisia (تونس)",
43174 "Turkey (Türkiye)",
43184 "Turks and Caicos Islands",
43194 "U.S. Virgin Islands",
43204 "Ukraine (Україна)",
43209 "United Arab Emirates (الإمارات العربية المتحدة)",
43231 "Uzbekistan (Oʻzbekiston)",
43241 "Vatican City (Città del Vaticano)",
43252 "Vietnam (Việt Nam)",
43257 "Wallis and Futuna (Wallis-et-Futuna)",
43262 "Western Sahara (الصحراء الغربية)",
43268 "Yemen (اليمن)",
43292 * This script refer to:
43293 * Title: International Telephone Input
43294 * Author: Jack O'Connor
43295 * Code version: v12.1.12
43296 * Availability: https://github.com/jackocnr/intl-tel-input.git
43300 * @class Roo.bootstrap.form.PhoneInput
43301 * @extends Roo.bootstrap.form.TriggerField
43302 * An input with International dial-code selection
43304 * @cfg {String} defaultDialCode default '+852'
43305 * @cfg {Array} preferedCountries default []
43308 * Create a new PhoneInput.
43309 * @param {Object} config Configuration options
43312 Roo.bootstrap.form.PhoneInput = function(config) {
43313 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43316 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43318 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43320 listWidth: undefined,
43322 selectedClass: 'active',
43324 invalidClass : "has-warning",
43326 validClass: 'has-success',
43328 allowed: '0123456789',
43333 * @cfg {String} defaultDialCode The default dial code when initializing the input
43335 defaultDialCode: '+852',
43338 * @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
43340 preferedCountries: false,
43342 getAutoCreate : function()
43344 var data = Roo.bootstrap.form.PhoneInputData();
43345 var align = this.labelAlign || this.parentLabelAlign();
43348 this.allCountries = [];
43349 this.dialCodeMapping = [];
43351 for (var i = 0; i < data.length; i++) {
43353 this.allCountries[i] = {
43357 priority: c[3] || 0,
43358 areaCodes: c[4] || null
43360 this.dialCodeMapping[c[2]] = {
43363 priority: c[3] || 0,
43364 areaCodes: c[4] || null
43376 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43377 maxlength: this.max_length,
43378 cls : 'form-control tel-input',
43379 autocomplete: 'new-password'
43382 var hiddenInput = {
43385 cls: 'hidden-tel-input'
43389 hiddenInput.name = this.name;
43392 if (this.disabled) {
43393 input.disabled = true;
43396 var flag_container = {
43413 cls: this.hasFeedback ? 'has-feedback' : '',
43419 cls: 'dial-code-holder',
43426 cls: 'roo-select2-container input-group',
43433 if (this.fieldLabel.length) {
43436 tooltip: 'This field is required'
43442 cls: 'control-label',
43448 html: this.fieldLabel
43451 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43457 if(this.indicatorpos == 'right') {
43458 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43465 if(align == 'left') {
43473 if(this.labelWidth > 12){
43474 label.style = "width: " + this.labelWidth + 'px';
43476 if(this.labelWidth < 13 && this.labelmd == 0){
43477 this.labelmd = this.labelWidth;
43479 if(this.labellg > 0){
43480 label.cls += ' col-lg-' + this.labellg;
43481 input.cls += ' col-lg-' + (12 - this.labellg);
43483 if(this.labelmd > 0){
43484 label.cls += ' col-md-' + this.labelmd;
43485 container.cls += ' col-md-' + (12 - this.labelmd);
43487 if(this.labelsm > 0){
43488 label.cls += ' col-sm-' + this.labelsm;
43489 container.cls += ' col-sm-' + (12 - this.labelsm);
43491 if(this.labelxs > 0){
43492 label.cls += ' col-xs-' + this.labelxs;
43493 container.cls += ' col-xs-' + (12 - this.labelxs);
43503 var settings = this;
43505 ['xs','sm','md','lg'].map(function(size){
43506 if (settings[size]) {
43507 cfg.cls += ' col-' + size + '-' + settings[size];
43511 this.store = new Roo.data.Store({
43512 proxy : new Roo.data.MemoryProxy({}),
43513 reader : new Roo.data.JsonReader({
43524 'name' : 'dialCode',
43528 'name' : 'priority',
43532 'name' : 'areaCodes',
43539 if(!this.preferedCountries) {
43540 this.preferedCountries = [
43547 var p = this.preferedCountries.reverse();
43550 for (var i = 0; i < p.length; i++) {
43551 for (var j = 0; j < this.allCountries.length; j++) {
43552 if(this.allCountries[j].iso2 == p[i]) {
43553 var t = this.allCountries[j];
43554 this.allCountries.splice(j,1);
43555 this.allCountries.unshift(t);
43561 this.store.proxy.data = {
43563 data: this.allCountries
43569 initEvents : function()
43572 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43574 this.indicator = this.indicatorEl();
43575 this.flag = this.flagEl();
43576 this.dialCodeHolder = this.dialCodeHolderEl();
43578 this.trigger = this.el.select('div.flag-box',true).first();
43579 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43584 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43585 _this.list.setWidth(lw);
43588 this.list.on('mouseover', this.onViewOver, this);
43589 this.list.on('mousemove', this.onViewMove, this);
43590 this.inputEl().on("keyup", this.onKeyUp, this);
43591 this.inputEl().on("keypress", this.onKeyPress, this);
43593 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43595 this.view = new Roo.View(this.list, this.tpl, {
43596 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43599 this.view.on('click', this.onViewClick, this);
43600 this.setValue(this.defaultDialCode);
43603 onTriggerClick : function(e)
43605 Roo.log('trigger click');
43610 if(this.isExpanded()){
43612 this.hasFocus = false;
43614 this.store.load({});
43615 this.hasFocus = true;
43620 isExpanded : function()
43622 return this.list.isVisible();
43625 collapse : function()
43627 if(!this.isExpanded()){
43631 Roo.get(document).un('mousedown', this.collapseIf, this);
43632 Roo.get(document).un('mousewheel', this.collapseIf, this);
43633 this.fireEvent('collapse', this);
43637 expand : function()
43641 if(this.isExpanded() || !this.hasFocus){
43645 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43646 this.list.setWidth(lw);
43649 this.restrictHeight();
43651 Roo.get(document).on('mousedown', this.collapseIf, this);
43652 Roo.get(document).on('mousewheel', this.collapseIf, this);
43654 this.fireEvent('expand', this);
43657 restrictHeight : function()
43659 this.list.alignTo(this.inputEl(), this.listAlign);
43660 this.list.alignTo(this.inputEl(), this.listAlign);
43663 onViewOver : function(e, t)
43665 if(this.inKeyMode){
43668 var item = this.view.findItemFromChild(t);
43671 var index = this.view.indexOf(item);
43672 this.select(index, false);
43677 onViewClick : function(view, doFocus, el, e)
43679 var index = this.view.getSelectedIndexes()[0];
43681 var r = this.store.getAt(index);
43684 this.onSelect(r, index);
43686 if(doFocus !== false && !this.blockFocus){
43687 this.inputEl().focus();
43691 onViewMove : function(e, t)
43693 this.inKeyMode = false;
43696 select : function(index, scrollIntoView)
43698 this.selectedIndex = index;
43699 this.view.select(index);
43700 if(scrollIntoView !== false){
43701 var el = this.view.getNode(index);
43703 this.list.scrollChildIntoView(el, false);
43708 createList : function()
43710 this.list = Roo.get(document.body).createChild({
43712 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43713 style: 'display:none'
43716 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43719 collapseIf : function(e)
43721 var in_combo = e.within(this.el);
43722 var in_list = e.within(this.list);
43723 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43725 if (in_combo || in_list || is_list) {
43731 onSelect : function(record, index)
43733 if(this.fireEvent('beforeselect', this, record, index) !== false){
43735 this.setFlagClass(record.data.iso2);
43736 this.setDialCode(record.data.dialCode);
43737 this.hasFocus = false;
43739 this.fireEvent('select', this, record, index);
43743 flagEl : function()
43745 var flag = this.el.select('div.flag',true).first();
43752 dialCodeHolderEl : function()
43754 var d = this.el.select('input.dial-code-holder',true).first();
43761 setDialCode : function(v)
43763 this.dialCodeHolder.dom.value = '+'+v;
43766 setFlagClass : function(n)
43768 this.flag.dom.className = 'flag '+n;
43771 getValue : function()
43773 var v = this.inputEl().getValue();
43774 if(this.dialCodeHolder) {
43775 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43780 setValue : function(v)
43782 var d = this.getDialCode(v);
43784 //invalid dial code
43785 if(v.length == 0 || !d || d.length == 0) {
43787 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43788 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43794 this.setFlagClass(this.dialCodeMapping[d].iso2);
43795 this.setDialCode(d);
43796 this.inputEl().dom.value = v.replace('+'+d,'');
43797 this.hiddenEl().dom.value = this.getValue();
43802 getDialCode : function(v)
43806 if (v.length == 0) {
43807 return this.dialCodeHolder.dom.value;
43811 if (v.charAt(0) != "+") {
43814 var numericChars = "";
43815 for (var i = 1; i < v.length; i++) {
43816 var c = v.charAt(i);
43819 if (this.dialCodeMapping[numericChars]) {
43820 dialCode = v.substr(1, i);
43822 if (numericChars.length == 4) {
43832 this.setValue(this.defaultDialCode);
43836 hiddenEl : function()
43838 return this.el.select('input.hidden-tel-input',true).first();
43841 // after setting val
43842 onKeyUp : function(e){
43843 this.setValue(this.getValue());
43846 onKeyPress : function(e){
43847 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43854 * @class Roo.bootstrap.form.MoneyField
43855 * @extends Roo.bootstrap.form.ComboBox
43856 * Bootstrap MoneyField class
43859 * Create a new MoneyField.
43860 * @param {Object} config Configuration options
43863 Roo.bootstrap.form.MoneyField = function(config) {
43865 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43869 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43872 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43874 allowDecimals : true,
43876 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43878 decimalSeparator : ".",
43880 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43882 decimalPrecision : 0,
43884 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43886 allowNegative : true,
43888 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43892 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43894 minValue : Number.NEGATIVE_INFINITY,
43896 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43898 maxValue : Number.MAX_VALUE,
43900 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43902 minText : "The minimum value for this field is {0}",
43904 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43906 maxText : "The maximum value for this field is {0}",
43908 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43909 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43911 nanText : "{0} is not a valid number",
43913 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43917 * @cfg {String} defaults currency of the MoneyField
43918 * value should be in lkey
43920 defaultCurrency : false,
43922 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43924 thousandsDelimiter : false,
43926 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43935 * @cfg {Roo.data.Store} store Store to lookup currency??
43939 getAutoCreate : function()
43941 var align = this.labelAlign || this.parentLabelAlign();
43953 cls : 'form-control roo-money-amount-input',
43954 autocomplete: 'new-password'
43957 var hiddenInput = {
43961 cls: 'hidden-number-input'
43964 if(this.max_length) {
43965 input.maxlength = this.max_length;
43969 hiddenInput.name = this.name;
43972 if (this.disabled) {
43973 input.disabled = true;
43976 var clg = 12 - this.inputlg;
43977 var cmd = 12 - this.inputmd;
43978 var csm = 12 - this.inputsm;
43979 var cxs = 12 - this.inputxs;
43983 cls : 'row roo-money-field',
43987 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43991 cls: 'roo-select2-container input-group',
43995 cls : 'form-control roo-money-currency-input',
43996 autocomplete: 'new-password',
43998 name : this.currencyName
44002 cls : 'input-group-addon',
44016 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44020 cls: this.hasFeedback ? 'has-feedback' : '',
44031 if (this.fieldLabel.length) {
44034 tooltip: 'This field is required'
44040 cls: 'control-label',
44046 html: this.fieldLabel
44049 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44055 if(this.indicatorpos == 'right') {
44056 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44063 if(align == 'left') {
44071 if(this.labelWidth > 12){
44072 label.style = "width: " + this.labelWidth + 'px';
44074 if(this.labelWidth < 13 && this.labelmd == 0){
44075 this.labelmd = this.labelWidth;
44077 if(this.labellg > 0){
44078 label.cls += ' col-lg-' + this.labellg;
44079 input.cls += ' col-lg-' + (12 - this.labellg);
44081 if(this.labelmd > 0){
44082 label.cls += ' col-md-' + this.labelmd;
44083 container.cls += ' col-md-' + (12 - this.labelmd);
44085 if(this.labelsm > 0){
44086 label.cls += ' col-sm-' + this.labelsm;
44087 container.cls += ' col-sm-' + (12 - this.labelsm);
44089 if(this.labelxs > 0){
44090 label.cls += ' col-xs-' + this.labelxs;
44091 container.cls += ' col-xs-' + (12 - this.labelxs);
44102 var settings = this;
44104 ['xs','sm','md','lg'].map(function(size){
44105 if (settings[size]) {
44106 cfg.cls += ' col-' + size + '-' + settings[size];
44113 initEvents : function()
44115 this.indicator = this.indicatorEl();
44117 this.initCurrencyEvent();
44119 this.initNumberEvent();
44122 initCurrencyEvent : function()
44125 throw "can not find store for combo";
44128 this.store = Roo.factory(this.store, Roo.data);
44129 this.store.parent = this;
44133 this.triggerEl = this.el.select('.input-group-addon', true).first();
44135 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44140 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44141 _this.list.setWidth(lw);
44144 this.list.on('mouseover', this.onViewOver, this);
44145 this.list.on('mousemove', this.onViewMove, this);
44146 this.list.on('scroll', this.onViewScroll, this);
44149 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44152 this.view = new Roo.View(this.list, this.tpl, {
44153 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44156 this.view.on('click', this.onViewClick, this);
44158 this.store.on('beforeload', this.onBeforeLoad, this);
44159 this.store.on('load', this.onLoad, this);
44160 this.store.on('loadexception', this.onLoadException, this);
44162 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44163 "up" : function(e){
44164 this.inKeyMode = true;
44168 "down" : function(e){
44169 if(!this.isExpanded()){
44170 this.onTriggerClick();
44172 this.inKeyMode = true;
44177 "enter" : function(e){
44180 if(this.fireEvent("specialkey", this, e)){
44181 this.onViewClick(false);
44187 "esc" : function(e){
44191 "tab" : function(e){
44194 if(this.fireEvent("specialkey", this, e)){
44195 this.onViewClick(false);
44203 doRelay : function(foo, bar, hname){
44204 if(hname == 'down' || this.scope.isExpanded()){
44205 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44213 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44217 initNumberEvent : function(e)
44219 this.inputEl().on("keydown" , this.fireKey, this);
44220 this.inputEl().on("focus", this.onFocus, this);
44221 this.inputEl().on("blur", this.onBlur, this);
44223 this.inputEl().relayEvent('keyup', this);
44225 if(this.indicator){
44226 this.indicator.addClass('invisible');
44229 this.originalValue = this.getValue();
44231 if(this.validationEvent == 'keyup'){
44232 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44233 this.inputEl().on('keyup', this.filterValidation, this);
44235 else if(this.validationEvent !== false){
44236 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44239 if(this.selectOnFocus){
44240 this.on("focus", this.preFocus, this);
44243 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44244 this.inputEl().on("keypress", this.filterKeys, this);
44246 this.inputEl().relayEvent('keypress', this);
44249 var allowed = "0123456789";
44251 if(this.allowDecimals){
44252 allowed += this.decimalSeparator;
44255 if(this.allowNegative){
44259 if(this.thousandsDelimiter) {
44263 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44265 var keyPress = function(e){
44267 var k = e.getKey();
44269 var c = e.getCharCode();
44272 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44273 allowed.indexOf(String.fromCharCode(c)) === -1
44279 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44283 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44288 this.inputEl().on("keypress", keyPress, this);
44292 onTriggerClick : function(e)
44299 this.loadNext = false;
44301 if(this.isExpanded()){
44306 this.hasFocus = true;
44308 if(this.triggerAction == 'all') {
44309 this.doQuery(this.allQuery, true);
44313 this.doQuery(this.getRawValue());
44316 getCurrency : function()
44318 var v = this.currencyEl().getValue();
44323 restrictHeight : function()
44325 this.list.alignTo(this.currencyEl(), this.listAlign);
44326 this.list.alignTo(this.currencyEl(), this.listAlign);
44329 onViewClick : function(view, doFocus, el, e)
44331 var index = this.view.getSelectedIndexes()[0];
44333 var r = this.store.getAt(index);
44336 this.onSelect(r, index);
44340 onSelect : function(record, index){
44342 if(this.fireEvent('beforeselect', this, record, index) !== false){
44344 this.setFromCurrencyData(index > -1 ? record.data : false);
44348 this.fireEvent('select', this, record, index);
44352 setFromCurrencyData : function(o)
44356 this.lastCurrency = o;
44358 if (this.currencyField) {
44359 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44361 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44364 this.lastSelectionText = currency;
44366 //setting default currency
44367 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44368 this.setCurrency(this.defaultCurrency);
44372 this.setCurrency(currency);
44375 setFromData : function(o)
44379 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44381 this.setFromCurrencyData(c);
44386 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44388 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44391 this.setValue(value);
44395 setCurrency : function(v)
44397 this.currencyValue = v;
44400 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44405 setValue : function(v)
44407 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44413 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44415 this.inputEl().dom.value = (v == '') ? '' :
44416 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44418 if(!this.allowZero && v === '0') {
44419 this.hiddenEl().dom.value = '';
44420 this.inputEl().dom.value = '';
44427 getRawValue : function()
44429 var v = this.inputEl().getValue();
44434 getValue : function()
44436 return this.fixPrecision(this.parseValue(this.getRawValue()));
44439 parseValue : function(value)
44441 if(this.thousandsDelimiter) {
44443 r = new RegExp(",", "g");
44444 value = value.replace(r, "");
44447 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44448 return isNaN(value) ? '' : value;
44452 fixPrecision : function(value)
44454 if(this.thousandsDelimiter) {
44456 r = new RegExp(",", "g");
44457 value = value.replace(r, "");
44460 var nan = isNaN(value);
44462 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44463 return nan ? '' : value;
44465 return parseFloat(value).toFixed(this.decimalPrecision);
44468 decimalPrecisionFcn : function(v)
44470 return Math.floor(v);
44473 validateValue : function(value)
44475 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44479 var num = this.parseValue(value);
44482 this.markInvalid(String.format(this.nanText, value));
44486 if(num < this.minValue){
44487 this.markInvalid(String.format(this.minText, this.minValue));
44491 if(num > this.maxValue){
44492 this.markInvalid(String.format(this.maxText, this.maxValue));
44499 validate : function()
44501 if(this.disabled || this.allowBlank){
44506 var currency = this.getCurrency();
44508 if(this.validateValue(this.getRawValue()) && currency.length){
44513 this.markInvalid();
44517 getName: function()
44522 beforeBlur : function()
44528 var v = this.parseValue(this.getRawValue());
44535 onBlur : function()
44539 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44540 //this.el.removeClass(this.focusClass);
44543 this.hasFocus = false;
44545 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44549 var v = this.getValue();
44551 if(String(v) !== String(this.startValue)){
44552 this.fireEvent('change', this, v, this.startValue);
44555 this.fireEvent("blur", this);
44558 inputEl : function()
44560 return this.el.select('.roo-money-amount-input', true).first();
44563 currencyEl : function()
44565 return this.el.select('.roo-money-currency-input', true).first();
44568 hiddenEl : function()
44570 return this.el.select('input.hidden-number-input',true).first();
44574 * @class Roo.bootstrap.BezierSignature
44575 * @extends Roo.bootstrap.Component
44576 * Bootstrap BezierSignature class
44577 * This script refer to:
44578 * Title: Signature Pad
44580 * Availability: https://github.com/szimek/signature_pad
44583 * Create a new BezierSignature
44584 * @param {Object} config The config object
44587 Roo.bootstrap.BezierSignature = function(config){
44588 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44594 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44601 mouse_btn_down: true,
44604 * @cfg {int} canvas height
44606 canvas_height: '200px',
44609 * @cfg {float|function} Radius of a single dot.
44614 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44619 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44624 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44629 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44634 * @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.
44636 bg_color: 'rgba(0, 0, 0, 0)',
44639 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44641 dot_color: 'black',
44644 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44646 velocity_filter_weight: 0.7,
44649 * @cfg {function} Callback when stroke begin.
44654 * @cfg {function} Callback when stroke end.
44658 getAutoCreate : function()
44660 var cls = 'roo-signature column';
44663 cls += ' ' + this.cls;
44673 for(var i = 0; i < col_sizes.length; i++) {
44674 if(this[col_sizes[i]]) {
44675 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44685 cls: 'roo-signature-body',
44689 cls: 'roo-signature-body-canvas',
44690 height: this.canvas_height,
44691 width: this.canvas_width
44698 style: 'display: none'
44706 initEvents: function()
44708 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44710 var canvas = this.canvasEl();
44712 // mouse && touch event swapping...
44713 canvas.dom.style.touchAction = 'none';
44714 canvas.dom.style.msTouchAction = 'none';
44716 this.mouse_btn_down = false;
44717 canvas.on('mousedown', this._handleMouseDown, this);
44718 canvas.on('mousemove', this._handleMouseMove, this);
44719 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44721 if (window.PointerEvent) {
44722 canvas.on('pointerdown', this._handleMouseDown, this);
44723 canvas.on('pointermove', this._handleMouseMove, this);
44724 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44727 if ('ontouchstart' in window) {
44728 canvas.on('touchstart', this._handleTouchStart, this);
44729 canvas.on('touchmove', this._handleTouchMove, this);
44730 canvas.on('touchend', this._handleTouchEnd, this);
44733 Roo.EventManager.onWindowResize(this.resize, this, true);
44735 // file input event
44736 this.fileEl().on('change', this.uploadImage, this);
44743 resize: function(){
44745 var canvas = this.canvasEl().dom;
44746 var ctx = this.canvasElCtx();
44747 var img_data = false;
44749 if(canvas.width > 0) {
44750 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44752 // setting canvas width will clean img data
44755 var style = window.getComputedStyle ?
44756 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44758 var padding_left = parseInt(style.paddingLeft) || 0;
44759 var padding_right = parseInt(style.paddingRight) || 0;
44761 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44764 ctx.putImageData(img_data, 0, 0);
44768 _handleMouseDown: function(e)
44770 if (e.browserEvent.which === 1) {
44771 this.mouse_btn_down = true;
44772 this.strokeBegin(e);
44776 _handleMouseMove: function (e)
44778 if (this.mouse_btn_down) {
44779 this.strokeMoveUpdate(e);
44783 _handleMouseUp: function (e)
44785 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44786 this.mouse_btn_down = false;
44791 _handleTouchStart: function (e) {
44793 e.preventDefault();
44794 if (e.browserEvent.targetTouches.length === 1) {
44795 // var touch = e.browserEvent.changedTouches[0];
44796 // this.strokeBegin(touch);
44798 this.strokeBegin(e); // assume e catching the correct xy...
44802 _handleTouchMove: function (e) {
44803 e.preventDefault();
44804 // var touch = event.targetTouches[0];
44805 // _this._strokeMoveUpdate(touch);
44806 this.strokeMoveUpdate(e);
44809 _handleTouchEnd: function (e) {
44810 var wasCanvasTouched = e.target === this.canvasEl().dom;
44811 if (wasCanvasTouched) {
44812 e.preventDefault();
44813 // var touch = event.changedTouches[0];
44814 // _this._strokeEnd(touch);
44819 reset: function () {
44820 this._lastPoints = [];
44821 this._lastVelocity = 0;
44822 this._lastWidth = (this.min_width + this.max_width) / 2;
44823 this.canvasElCtx().fillStyle = this.dot_color;
44826 strokeMoveUpdate: function(e)
44828 this.strokeUpdate(e);
44830 if (this.throttle) {
44831 this.throttleStroke(this.strokeUpdate, this.throttle);
44834 this.strokeUpdate(e);
44838 strokeBegin: function(e)
44840 var newPointGroup = {
44841 color: this.dot_color,
44845 if (typeof this.onBegin === 'function') {
44849 this.curve_data.push(newPointGroup);
44851 this.strokeUpdate(e);
44854 strokeUpdate: function(e)
44856 var rect = this.canvasEl().dom.getBoundingClientRect();
44857 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44858 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44859 var lastPoints = lastPointGroup.points;
44860 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44861 var isLastPointTooClose = lastPoint
44862 ? point.distanceTo(lastPoint) <= this.min_distance
44864 var color = lastPointGroup.color;
44865 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44866 var curve = this.addPoint(point);
44868 this.drawDot({color: color, point: point});
44871 this.drawCurve({color: color, curve: curve});
44881 strokeEnd: function(e)
44883 this.strokeUpdate(e);
44884 if (typeof this.onEnd === 'function') {
44889 addPoint: function (point) {
44890 var _lastPoints = this._lastPoints;
44891 _lastPoints.push(point);
44892 if (_lastPoints.length > 2) {
44893 if (_lastPoints.length === 3) {
44894 _lastPoints.unshift(_lastPoints[0]);
44896 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44897 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44898 _lastPoints.shift();
44904 calculateCurveWidths: function (startPoint, endPoint) {
44905 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44906 (1 - this.velocity_filter_weight) * this._lastVelocity;
44908 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44911 start: this._lastWidth
44914 this._lastVelocity = velocity;
44915 this._lastWidth = newWidth;
44919 drawDot: function (_a) {
44920 var color = _a.color, point = _a.point;
44921 var ctx = this.canvasElCtx();
44922 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44924 this.drawCurveSegment(point.x, point.y, width);
44926 ctx.fillStyle = color;
44930 drawCurve: function (_a) {
44931 var color = _a.color, curve = _a.curve;
44932 var ctx = this.canvasElCtx();
44933 var widthDelta = curve.endWidth - curve.startWidth;
44934 var drawSteps = Math.floor(curve.length()) * 2;
44936 ctx.fillStyle = color;
44937 for (var i = 0; i < drawSteps; i += 1) {
44938 var t = i / drawSteps;
44944 var x = uuu * curve.startPoint.x;
44945 x += 3 * uu * t * curve.control1.x;
44946 x += 3 * u * tt * curve.control2.x;
44947 x += ttt * curve.endPoint.x;
44948 var y = uuu * curve.startPoint.y;
44949 y += 3 * uu * t * curve.control1.y;
44950 y += 3 * u * tt * curve.control2.y;
44951 y += ttt * curve.endPoint.y;
44952 var width = curve.startWidth + ttt * widthDelta;
44953 this.drawCurveSegment(x, y, width);
44959 drawCurveSegment: function (x, y, width) {
44960 var ctx = this.canvasElCtx();
44962 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44963 this.is_empty = false;
44968 var ctx = this.canvasElCtx();
44969 var canvas = this.canvasEl().dom;
44970 ctx.fillStyle = this.bg_color;
44971 ctx.clearRect(0, 0, canvas.width, canvas.height);
44972 ctx.fillRect(0, 0, canvas.width, canvas.height);
44973 this.curve_data = [];
44975 this.is_empty = true;
44980 return this.el.select('input',true).first();
44983 canvasEl: function()
44985 return this.el.select('canvas',true).first();
44988 canvasElCtx: function()
44990 return this.el.select('canvas',true).first().dom.getContext('2d');
44993 getImage: function(type)
44995 if(this.is_empty) {
45000 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45003 drawFromImage: function(img_src)
45005 var img = new Image();
45007 img.onload = function(){
45008 this.canvasElCtx().drawImage(img, 0, 0);
45013 this.is_empty = false;
45016 selectImage: function()
45018 this.fileEl().dom.click();
45021 uploadImage: function(e)
45023 var reader = new FileReader();
45025 reader.onload = function(e){
45026 var img = new Image();
45027 img.onload = function(){
45029 this.canvasElCtx().drawImage(img, 0, 0);
45031 img.src = e.target.result;
45034 reader.readAsDataURL(e.target.files[0]);
45037 // Bezier Point Constructor
45038 Point: (function () {
45039 function Point(x, y, time) {
45042 this.time = time || Date.now();
45044 Point.prototype.distanceTo = function (start) {
45045 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45047 Point.prototype.equals = function (other) {
45048 return this.x === other.x && this.y === other.y && this.time === other.time;
45050 Point.prototype.velocityFrom = function (start) {
45051 return this.time !== start.time
45052 ? this.distanceTo(start) / (this.time - start.time)
45059 // Bezier Constructor
45060 Bezier: (function () {
45061 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45062 this.startPoint = startPoint;
45063 this.control2 = control2;
45064 this.control1 = control1;
45065 this.endPoint = endPoint;
45066 this.startWidth = startWidth;
45067 this.endWidth = endWidth;
45069 Bezier.fromPoints = function (points, widths, scope) {
45070 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45071 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45072 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45074 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45075 var dx1 = s1.x - s2.x;
45076 var dy1 = s1.y - s2.y;
45077 var dx2 = s2.x - s3.x;
45078 var dy2 = s2.y - s3.y;
45079 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45080 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45081 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45082 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45083 var dxm = m1.x - m2.x;
45084 var dym = m1.y - m2.y;
45085 var k = l2 / (l1 + l2);
45086 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45087 var tx = s2.x - cm.x;
45088 var ty = s2.y - cm.y;
45090 c1: new scope.Point(m1.x + tx, m1.y + ty),
45091 c2: new scope.Point(m2.x + tx, m2.y + ty)
45094 Bezier.prototype.length = function () {
45099 for (var i = 0; i <= steps; i += 1) {
45101 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45102 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45104 var xdiff = cx - px;
45105 var ydiff = cy - py;
45106 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45113 Bezier.prototype.point = function (t, start, c1, c2, end) {
45114 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45115 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45116 + (3.0 * c2 * (1.0 - t) * t * t)
45117 + (end * t * t * t);
45122 throttleStroke: function(fn, wait) {
45123 if (wait === void 0) { wait = 250; }
45125 var timeout = null;
45129 var later = function () {
45130 previous = Date.now();
45132 result = fn.apply(storedContext, storedArgs);
45134 storedContext = null;
45138 return function wrapper() {
45140 for (var _i = 0; _i < arguments.length; _i++) {
45141 args[_i] = arguments[_i];
45143 var now = Date.now();
45144 var remaining = wait - (now - previous);
45145 storedContext = this;
45147 if (remaining <= 0 || remaining > wait) {
45149 clearTimeout(timeout);
45153 result = fn.apply(storedContext, storedArgs);
45155 storedContext = null;
45159 else if (!timeout) {
45160 timeout = window.setTimeout(later, remaining);
45170 // old names for form elements
45171 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45172 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45173 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45174 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45175 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45176 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45177 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45178 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45179 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45180 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45181 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45182 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45183 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45184 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45185 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45186 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45187 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45188 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45189 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45190 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45191 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45192 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45193 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45194 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45195 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45196 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45198 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45199 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45201 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45202 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45204 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45205 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45206 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45207 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator