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.href === '' || this.preventDefault){
1352 this.setActive(true);
1353 var pi = this.parent().items;
1354 for (var i = 0;i < pi.length;i++) {
1355 if (this == pi[i]) {
1358 if (pi[i].el.hasClass('roo-button')) {
1359 pi[i].setActive(false);
1362 this.fireEvent('click', this, e);
1366 if (this.pressed === true || this.pressed === false) {
1367 this.toggleActive(e);
1371 this.fireEvent('click', this, e);
1373 onDblClick: function(e)
1375 if (this.disabled) {
1378 if(this.preventDefault){
1381 this.fireEvent('dblclick', this, e);
1384 * Enables this button
1388 this.disabled = false;
1389 this.el.removeClass('disabled');
1390 this.el.dom.removeAttribute("disabled");
1394 * Disable this button
1396 disable : function()
1398 this.disabled = true;
1399 this.el.addClass('disabled');
1400 this.el.attr("disabled", "disabled")
1403 * sets the active state on/off,
1404 * @param {Boolean} state (optional) Force a particular state
1406 setActive : function(v) {
1408 this.el[v ? 'addClass' : 'removeClass']('active');
1412 * toggles the current active state
1414 toggleActive : function(e)
1416 this.setActive(!this.pressed); // this modifies pressed...
1417 this.fireEvent('toggle', this, e, this.pressed);
1420 * get the current active state
1421 * @return {boolean} true if it's active
1423 isActive : function()
1425 return this.el.hasClass('active');
1428 * set the text of the first selected button
1430 setText : function(str)
1432 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1435 * get the text of the first selected button
1437 getText : function()
1439 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1442 setWeight : function(str)
1444 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447 var outline = this.outline ? 'outline-' : '';
1448 if (str == 'default') {
1449 this.el.addClass('btn-default btn-outline-secondary');
1452 this.el.addClass('btn-' + outline + str);
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459 Roo.bootstrap.Button.weights = [
1479 * @class Roo.bootstrap.Column
1480 * @extends Roo.bootstrap.Component
1481 * @children Roo.bootstrap.Component
1482 * Bootstrap Column class
1483 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1493 * @cfg {Boolean} hidden (true|false) hide the element
1494 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495 * @cfg {String} fa (ban|check|...) font awesome icon
1496 * @cfg {Number} fasize (1|2|....) font awsome size
1498 * @cfg {String} icon (info-sign|check|...) glyphicon name
1500 * @cfg {String} html content of column.
1503 * Create a new Column
1504 * @param {Object} config The config object
1507 Roo.bootstrap.Column = function(config){
1508 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1529 getAutoCreate : function(){
1530 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538 var sizes = ['xs','sm','md','lg'];
1539 sizes.map(function(size ,ix){
1540 //Roo.log( size + ':' + settings[size]);
1542 if (settings[size+'off'] !== false) {
1543 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1546 if (settings[size] === false) {
1550 if (!settings[size]) { // 0 = hidden
1551 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553 for (var i = ix; i > -1; i--) {
1554 cfg.cls += ' d-' + sizes[i] + '-none';
1560 cfg.cls += ' col-' + size + '-' + settings[size] + (
1561 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1567 cfg.cls += ' hidden';
1570 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571 cfg.cls +=' alert alert-' + this.alert;
1575 if (this.html.length) {
1576 cfg.html = this.html;
1580 if (this.fasize > 1) {
1581 fasize = ' fa-' + this.fasize + 'x';
1583 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1588 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1607 * @class Roo.bootstrap.Container
1608 * @extends Roo.bootstrap.Component
1609 * @children Roo.bootstrap.Component
1611 * Bootstrap Container class
1612 * @cfg {Boolean} jumbotron is it a jumbotron element
1613 * @cfg {String} html content of element
1614 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1616 * @cfg {String} header content of header (for panel)
1617 * @cfg {String} footer content of footer (for panel)
1618 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619 * @cfg {String} tag (header|aside|section) type of HTML tag.
1620 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621 * @cfg {String} fa font awesome icon
1622 * @cfg {String} icon (info-sign|check|...) glyphicon name
1623 * @cfg {Boolean} hidden (true|false) hide the element
1624 * @cfg {Boolean} expandable (true|false) default false
1625 * @cfg {Boolean} expanded (true|false) default true
1626 * @cfg {String} rheader contet on the right of header
1627 * @cfg {Boolean} clickable (true|false) default false
1631 * Create a new Container
1632 * @param {Object} config The config object
1635 Roo.bootstrap.Container = function(config){
1636 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1642 * After the panel has been expand
1644 * @param {Roo.bootstrap.Container} this
1649 * After the panel has been collapsed
1651 * @param {Roo.bootstrap.Container} this
1656 * When a element is chick
1657 * @param {Roo.bootstrap.Container} this
1658 * @param {Roo.EventObject} e
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1682 getChildContainer : function() {
1688 if (this.panel.length) {
1689 return this.el.select('.panel-body',true).first();
1696 getAutoCreate : function(){
1699 tag : this.tag || 'div',
1703 if (this.jumbotron) {
1704 cfg.cls = 'jumbotron';
1709 // - this is applied by the parent..
1711 // cfg.cls = this.cls + '';
1714 if (this.sticky.length) {
1716 var bd = Roo.get(document.body);
1717 if (!bd.hasClass('bootstrap-sticky')) {
1718 bd.addClass('bootstrap-sticky');
1719 Roo.select('html',true).setStyle('height', '100%');
1722 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726 if (this.well.length) {
1727 switch (this.well) {
1730 cfg.cls +=' well well-' +this.well;
1739 cfg.cls += ' hidden';
1743 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744 cfg.cls +=' alert alert-' + this.alert;
1749 if (this.panel.length) {
1750 cfg.cls += ' panel panel-' + this.panel;
1752 if (this.header.length) {
1756 if(this.expandable){
1758 cfg.cls = cfg.cls + ' expandable';
1762 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1770 cls : 'panel-title',
1771 html : (this.expandable ? ' ' : '') + this.header
1775 cls: 'panel-header-right',
1781 cls : 'panel-heading',
1782 style : this.expandable ? 'cursor: pointer' : '',
1790 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1795 if (this.footer.length) {
1797 cls : 'panel-footer',
1806 body.html = this.html || cfg.html;
1807 // prefix with the icons..
1809 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1812 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1817 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818 cfg.cls = 'container';
1824 initEvents: function()
1826 if(this.expandable){
1827 var headerEl = this.headerEl();
1830 headerEl.on('click', this.onToggleClick, this);
1835 this.el.on('click', this.onClick, this);
1840 onToggleClick : function()
1842 var headerEl = this.headerEl();
1858 if(this.fireEvent('expand', this)) {
1860 this.expanded = true;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864 this.el.select('.panel-body',true).first().removeClass('hide');
1866 var toggleEl = this.toggleEl();
1872 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1877 collapse : function()
1879 if(this.fireEvent('collapse', this)) {
1881 this.expanded = false;
1883 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884 this.el.select('.panel-body',true).first().addClass('hide');
1886 var toggleEl = this.toggleEl();
1892 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896 toggleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902 return this.el.select('.panel-heading .fa',true).first();
1905 headerEl : function()
1907 if(!this.el || !this.panel.length || !this.header.length){
1911 return this.el.select('.panel-heading',true).first()
1916 if(!this.el || !this.panel.length){
1920 return this.el.select('.panel-body',true).first()
1923 titleEl : function()
1925 if(!this.el || !this.panel.length || !this.header.length){
1929 return this.el.select('.panel-title',true).first();
1932 setTitle : function(v)
1934 var titleEl = this.titleEl();
1940 titleEl.dom.innerHTML = v;
1943 getTitle : function()
1946 var titleEl = this.titleEl();
1952 return titleEl.dom.innerHTML;
1955 setRightTitle : function(v)
1957 var t = this.el.select('.panel-header-right',true).first();
1963 t.dom.innerHTML = v;
1966 onClick : function(e)
1970 this.fireEvent('click', this, e);
1975 * @class Roo.bootstrap.Card
1976 * @extends Roo.bootstrap.Component
1977 * @children Roo.bootstrap.Component
1979 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1982 * possible... may not be implemented..
1983 * @cfg {String} header_image src url of image.
1984 * @cfg {String|Object} header
1985 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1988 * @cfg {String} title
1989 * @cfg {String} subtitle
1990 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991 * @cfg {String} footer
1993 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995 * @cfg {String} margin (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003 * @cfg {String} padding (0|1|2|3|4|5)
2004 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006 * @cfg {String} padding_left (0|1|2|3|4|5)
2007 * @cfg {String} padding_right (0|1|2|3|4|5)
2008 * @cfg {String} padding_x (0|1|2|3|4|5)
2009 * @cfg {String} padding_y (0|1|2|3|4|5)
2011 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @config {Boolean} dragable if this card can be dragged.
2018 * @config {String} drag_group group for drag
2019 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2020 * @config {String} drop_group group for drag
2022 * @config {Boolean} collapsable can the body be collapsed.
2023 * @config {Boolean} collapsed is the body collapsed when rendered...
2024 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025 * @config {Boolean} rotated is the body rotated when rendered...
2028 * Create a new Container
2029 * @param {Object} config The config object
2032 Roo.bootstrap.Card = function(config){
2033 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2039 * When a element a card is dropped
2040 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044 * @param {String} position 'above' or 'below'
2045 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2051 * When a element a card is rotate
2052 * @param {Roo.bootstrap.Card} this
2053 * @param {Roo.Element} n the node being dropped?
2054 * @param {Boolean} rotate status
2059 * When a card element is dragged over ready to drop (return false to block dropable)
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Object} data from dragdrop
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2074 margin: '', /// may be better in component?
2104 collapsable : false,
2113 childContainer : false,
2114 dropEl : false, /// the dom placeholde element that indicates drop location.
2115 containerEl: false, // body container
2116 bodyEl: false, // card-body
2117 headerContainerEl : false, //
2119 header_imageEl : false,
2122 layoutCls : function()
2126 Roo.log(this.margin_bottom.length);
2127 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2133 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2138 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144 // more generic support?
2152 // Roo.log("Call onRender: " + this.xtype);
2153 /* We are looking at something like this.
2155 <img src="..." class="card-img-top" alt="...">
2156 <div class="card-body">
2157 <h5 class="card-title">Card title</h5>
2158 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160 >> this bit is really the body...
2161 <div> << we will ad dthis in hopefully it will not break shit.
2163 ** card text does not actually have any styling...
2165 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2168 <a href="#" class="card-link">Card link</a>
2171 <div class="card-footer">
2172 <small class="text-muted">Last updated 3 mins ago</small>
2176 getAutoCreate : function(){
2184 if (this.weight.length && this.weight != 'light') {
2185 cfg.cls += ' text-white';
2187 cfg.cls += ' text-dark'; // need as it's nested..
2189 if (this.weight.length) {
2190 cfg.cls += ' bg-' + this.weight;
2193 cfg.cls += ' ' + this.layoutCls();
2196 var hdr_ctr = false;
2197 if (this.header.length) {
2199 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2214 if (this.collapsable) {
2217 cls : 'd-block user-select-none',
2221 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226 hdr.cn.push(hdr_ctr);
2231 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236 if (this.header_image.length) {
2239 cls : 'card-img-top',
2240 src: this.header_image // escape?
2245 cls : 'card-img-top d-none'
2251 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2255 if (this.collapsable || this.rotateable) {
2258 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2265 if (this.title.length) {
2269 src: this.title // escape?
2273 if (this.subtitle.length) {
2277 src: this.subtitle // escape?
2283 cls : 'roo-card-body-ctr'
2286 if (this.html.length) {
2292 // fixme ? handle objects?
2294 if (this.footer.length) {
2297 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302 cfg.cn.push({cls : 'card-footer d-none'});
2311 getCardHeader : function()
2313 var ret = this.el.select('.card-header',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardFooter : function()
2322 var ret = this.el.select('.card-footer',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardImageTop : function()
2331 var ret = this.header_imageEl;
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2339 getChildContainer : function()
2345 return this.el.select('.roo-card-body-ctr',true).first();
2348 initEvents: function()
2350 this.bodyEl = this.el.select('.card-body',true).first();
2351 this.containerEl = this.getChildContainer();
2353 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354 containerScroll: true,
2355 ddGroup: this.drag_group || 'default_card_drag_group'
2357 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359 if (this.dropable) {
2360 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361 containerScroll: true,
2362 ddGroup: this.drop_group || 'default_card_drag_group'
2364 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371 if (this.collapsable) {
2372 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374 if (this.rotateable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379 this.footerEl = this.el.select('.card-footer',true).first();
2380 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382 this.headerEl = this.el.select('.card-header',true).first();
2385 this.el.addClass('roo-card-rotated');
2386 this.fireEvent('rotate', this, true);
2388 this.header_imageEl = this.el.select('.card-img-top',true).first();
2389 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392 getDragData : function(e)
2394 var target = this.getEl();
2396 //this.handleSelection(e);
2401 nodes: this.getEl(),
2406 dragData.ddel = target.dom ; // the div element
2407 Roo.log(target.getWidth( ));
2408 dragData.ddel.style.width = target.getWidth() + 'px';
2415 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2416 * whole Element becomes the target, and this causes the drop gesture to append.
2418 * Returns an object:
2421 position : 'below' or 'above'
2422 card : relateive to card OBJECT (or true for no cards listed)
2423 items_n : relative to nth item in list
2424 card_n : relative to nth card in list
2429 getTargetFromEvent : function(e, dragged_card_el)
2431 var target = e.getTarget();
2432 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433 target = target.parentNode;
2444 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445 // see if target is one of the 'cards'...
2448 //Roo.log(this.items.length);
2451 var last_card_n = 0;
2453 for (var i = 0;i< this.items.length;i++) {
2455 if (!this.items[i].el.hasClass('card')) {
2458 pos = this.getDropPoint(e, this.items[i].el.dom);
2460 cards_len = ret.cards.length;
2461 //Roo.log(this.items[i].el.dom.id);
2462 ret.cards.push(this.items[i]);
2464 if (ret.card_n < 0 && pos == 'above') {
2465 ret.position = cards_len > 0 ? 'below' : pos;
2466 ret.items_n = i > 0 ? i - 1 : 0;
2467 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2468 ret.card = ret.cards[ret.card_n];
2471 if (!ret.cards.length) {
2473 ret.position = 'below';
2477 // could not find a card.. stick it at the end..
2478 if (ret.card_n < 0) {
2479 ret.card_n = last_card_n;
2480 ret.card = ret.cards[last_card_n];
2481 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482 ret.position = 'below';
2485 if (this.items[ret.items_n].el == dragged_card_el) {
2489 if (ret.position == 'below') {
2490 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492 if (card_after && card_after.el == dragged_card_el) {
2499 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501 if (card_before && card_before.el == dragged_card_el) {
2508 onNodeEnter : function(n, dd, e, data){
2511 onNodeOver : function(n, dd, e, data)
2514 var target_info = this.getTargetFromEvent(e,data.source.el);
2515 if (target_info === false) {
2516 this.dropPlaceHolder('hide');
2519 Roo.log(['getTargetFromEvent', target_info ]);
2522 if (this.fireEvent('cardover', this, [ data ]) === false) {
2526 this.dropPlaceHolder('show', target_info,data);
2530 onNodeOut : function(n, dd, e, data){
2531 this.dropPlaceHolder('hide');
2534 onNodeDrop : function(n, dd, e, data)
2537 // call drop - return false if
2539 // this could actually fail - if the Network drops..
2540 // we will ignore this at present..- client should probably reload
2541 // the whole set of cards if stuff like that fails.
2544 var info = this.getTargetFromEvent(e,data.source.el);
2545 if (info === false) {
2548 this.dropPlaceHolder('hide');
2552 this.acceptCard(data.source, info.position, info.card, info.items_n);
2556 firstChildCard : function()
2558 for (var i = 0;i< this.items.length;i++) {
2560 if (!this.items[i].el.hasClass('card')) {
2563 return this.items[i];
2565 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2572 acceptCard : function(move_card, position, next_to_card )
2574 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580 move_card.parent().removeCard(move_card);
2583 var dom = move_card.el.dom;
2584 dom.style.width = ''; // clear with - which is set by drag.
2586 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587 var cardel = next_to_card.el.dom;
2589 if (position == 'above' ) {
2590 cardel.parentNode.insertBefore(dom, cardel);
2591 } else if (cardel.nextSibling) {
2592 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594 cardel.parentNode.append(dom);
2597 // card container???
2598 this.containerEl.dom.append(dom);
2601 //FIXME HANDLE card = true
2603 // add this to the correct place in items.
2605 // remove Card from items.
2608 if (this.items.length) {
2610 //Roo.log([info.items_n, info.position, this.items.length]);
2611 for (var i =0; i < this.items.length; i++) {
2612 if (i == to_items_n && position == 'above') {
2613 nitems.push(move_card);
2615 nitems.push(this.items[i]);
2616 if (i == to_items_n && position == 'below') {
2617 nitems.push(move_card);
2620 this.items = nitems;
2621 Roo.log(this.items);
2623 this.items.push(move_card);
2626 move_card.parentId = this.id;
2632 removeCard : function(c)
2634 this.items = this.items.filter(function(e) { return e != c });
2637 dom.parentNode.removeChild(dom);
2638 dom.style.width = ''; // clear with - which is set by drag.
2643 /** Decide whether to drop above or below a View node. */
2644 getDropPoint : function(e, n, dd)
2649 if (n == this.containerEl.dom) {
2652 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653 var c = t + (b - t) / 2;
2654 var y = Roo.lib.Event.getPageY(e);
2661 onToggleCollapse : function(e)
2663 if (this.collapsed) {
2664 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665 this.collapsableEl.addClass('show');
2666 this.collapsed = false;
2669 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670 this.collapsableEl.removeClass('show');
2671 this.collapsed = true;
2676 onToggleRotate : function(e)
2678 this.collapsableEl.removeClass('show');
2679 this.footerEl.removeClass('d-none');
2680 this.el.removeClass('roo-card-rotated');
2681 this.el.removeClass('d-none');
2684 this.collapsableEl.addClass('show');
2685 this.rotated = false;
2686 this.fireEvent('rotate', this, this.rotated);
2689 this.el.addClass('roo-card-rotated');
2690 this.footerEl.addClass('d-none');
2691 this.el.select('.roo-collapsable').removeClass('show');
2693 this.rotated = true;
2694 this.fireEvent('rotate', this, this.rotated);
2698 dropPlaceHolder: function (action, info, data)
2700 if (this.dropEl === false) {
2701 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705 this.dropEl.removeClass(['d-none', 'd-block']);
2706 if (action == 'hide') {
2708 this.dropEl.addClass('d-none');
2711 // FIXME - info.card == true!!!
2712 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714 if (info.card !== true) {
2715 var cardel = info.card.el.dom;
2717 if (info.position == 'above') {
2718 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719 } else if (cardel.nextSibling) {
2720 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722 cardel.parentNode.append(this.dropEl.dom);
2725 // card container???
2726 this.containerEl.dom.append(this.dropEl.dom);
2729 this.dropEl.addClass('d-block roo-card-dropzone');
2731 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2738 setHeaderText: function(html)
2741 if (this.headerContainerEl) {
2742 this.headerContainerEl.dom.innerHTML = html;
2745 onHeaderImageLoad : function(ev, he)
2747 if (!this.header_image_fit_square) {
2751 var hw = he.naturalHeight / he.naturalWidth;
2754 //var w = he.dom.naturalWidth;
2757 he.style.position = 'relative';
2759 var nw = (ww * (1/hw));
2760 Roo.get(he).setSize( ww * (1/hw), ww);
2761 he.style.left = ((ww - nw)/ 2) + 'px';
2762 he.style.position = 'relative';
2773 * Card header - holder for the card header elements.
2778 * @class Roo.bootstrap.CardHeader
2779 * @extends Roo.bootstrap.Element
2780 * @parent Roo.bootstrap.Card
2781 * @children Roo.bootstrap.Component
2782 * Bootstrap CardHeader class
2784 * Create a new Card Header - that you can embed children into
2785 * @param {Object} config The config object
2788 Roo.bootstrap.CardHeader = function(config){
2789 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2795 container_method : 'getCardHeader'
2808 * Card footer - holder for the card footer elements.
2813 * @class Roo.bootstrap.CardFooter
2814 * @extends Roo.bootstrap.Element
2815 * @parent Roo.bootstrap.Card
2816 * @children Roo.bootstrap.Component
2817 * Bootstrap CardFooter class
2820 * Create a new Card Footer - that you can embed children into
2821 * @param {Object} config The config object
2824 Roo.bootstrap.CardFooter = function(config){
2825 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2831 container_method : 'getCardFooter'
2844 * Card header - holder for the card header elements.
2849 * @class Roo.bootstrap.CardImageTop
2850 * @extends Roo.bootstrap.Element
2851 * @parent Roo.bootstrap.Card
2852 * @children Roo.bootstrap.Component
2853 * Bootstrap CardImageTop class
2856 * Create a new Card Image Top container
2857 * @param {Object} config The config object
2860 Roo.bootstrap.CardImageTop = function(config){
2861 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2867 container_method : 'getCardImageTop'
2882 * @class Roo.bootstrap.ButtonUploader
2883 * @extends Roo.bootstrap.Button
2884 * Bootstrap Button Uploader class - it's a button which when you add files to it
2887 * @cfg {Number} errorTimeout default 3000
2888 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2889 * @cfg {Array} html The button text.
2890 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2893 * Create a new CardUploader
2894 * @param {Object} config The config object
2897 Roo.bootstrap.ButtonUploader = function(config){
2901 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2907 * @event beforeselect
2908 * When button is pressed, before show upload files dialog is shown
2909 * @param {Roo.bootstrap.UploaderButton} this
2912 'beforeselect' : true,
2914 * @event fired when files have been selected,
2915 * When a the download link is clicked
2916 * @param {Roo.bootstrap.UploaderButton} this
2917 * @param {Array} Array of files that have been uploaded
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2927 errorTimeout : 3000,
2931 fileCollection : false,
2936 getAutoCreate : function()
2941 cls : 'd-none roo-card-upload-selector'
2944 if (this.multiple) {
2945 im.multiple = 'multiple';
2951 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2961 initEvents : function()
2964 Roo.bootstrap.Button.prototype.initEvents.call(this);
2970 this.urlAPI = (window.createObjectURL && window) ||
2971 (window.URL && URL.revokeObjectURL && URL) ||
2972 (window.webkitURL && webkitURL);
2977 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2979 this.selectorEl.on('change', this.onFileSelected, this);
2986 onClick : function(e)
2990 if ( this.fireEvent('beforeselect', this) === false) {
2994 this.selectorEl.dom.click();
2998 onFileSelected : function(e)
3002 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3005 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3006 this.selectorEl.dom.value = '';// hopefully reset..
3008 this.fireEvent('uploaded', this, files );
3016 * addCard - add an Attachment to the uploader
3017 * @param data - the data about the image to upload
3021 title : "Title of file",
3022 is_uploaded : false,
3023 src : "http://.....",
3024 srcfile : { the File upload object },
3025 mimetype : file.type,
3028 .. any other data...
3053 * @class Roo.bootstrap.Img
3054 * @extends Roo.bootstrap.Component
3055 * Bootstrap Img class
3056 * @cfg {Boolean} imgResponsive false | true
3057 * @cfg {String} border rounded | circle | thumbnail
3058 * @cfg {String} src image source
3059 * @cfg {String} alt image alternative text
3060 * @cfg {String} href a tag href
3061 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3062 * @cfg {String} xsUrl xs image source
3063 * @cfg {String} smUrl sm image source
3064 * @cfg {String} mdUrl md image source
3065 * @cfg {String} lgUrl lg image source
3066 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3069 * Create a new Input
3070 * @param {Object} config The config object
3073 Roo.bootstrap.Img = function(config){
3074 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3080 * The img click event for the img.
3081 * @param {Roo.EventObject} e
3086 * The when any image loads
3087 * @param {Roo.EventObject} e
3093 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3095 imgResponsive: true,
3104 backgroundContain : false,
3106 getAutoCreate : function()
3108 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3109 return this.createSingleImg();
3114 cls: 'roo-image-responsive-group',
3119 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3121 if(!_this[size + 'Url']){
3127 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3128 html: _this.html || cfg.html,
3129 src: _this[size + 'Url']
3132 img.cls += ' roo-image-responsive-' + size;
3134 var s = ['xs', 'sm', 'md', 'lg'];
3136 s.splice(s.indexOf(size), 1);
3138 Roo.each(s, function(ss){
3139 img.cls += ' hidden-' + ss;
3142 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3143 cfg.cls += ' img-' + _this.border;
3147 cfg.alt = _this.alt;
3160 a.target = _this.target;
3164 cfg.cn.push((_this.href) ? a : img);
3171 createSingleImg : function()
3175 cls: (this.imgResponsive) ? 'img-responsive' : '',
3177 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3180 if (this.backgroundContain) {
3181 cfg.cls += ' background-contain';
3184 cfg.html = this.html || cfg.html;
3186 if (this.backgroundContain) {
3187 cfg.style="background-image: url(" + this.src + ')';
3189 cfg.src = this.src || cfg.src;
3192 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3193 cfg.cls += ' img-' + this.border;
3210 a.target = this.target;
3215 return (this.href) ? a : cfg;
3218 initEvents: function()
3221 this.el.on('click', this.onClick, this);
3223 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3224 this.el.on('load', this.onImageLoad, this);
3226 // not sure if this works.. not tested
3227 this.el.select('img', true).on('load', this.onImageLoad, this);
3232 onClick : function(e)
3234 Roo.log('img onclick');
3235 this.fireEvent('click', this, e);
3237 onImageLoad: function(e)
3239 Roo.log('img load');
3240 this.fireEvent('load', this, e);
3244 * Sets the url of the image - used to update it
3245 * @param {String} url the url of the image
3248 setSrc : function(url)
3252 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3253 if (this.backgroundContain) {
3254 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3256 this.el.dom.src = url;
3261 this.el.select('img', true).first().dom.src = url;
3277 * @class Roo.bootstrap.Link
3278 * @extends Roo.bootstrap.Component
3279 * @children Roo.bootstrap.Component
3280 * Bootstrap Link Class (eg. '<a href>')
3282 * @cfg {String} alt image alternative text
3283 * @cfg {String} href a tag href
3284 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3285 * @cfg {String} html the content of the link.
3286 * @cfg {String} anchor name for the anchor link
3287 * @cfg {String} fa - favicon
3289 * @cfg {Boolean} preventDefault (true | false) default false
3293 * Create a new Input
3294 * @param {Object} config The config object
3297 Roo.bootstrap.Link = function(config){
3298 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3304 * The img click event for the img.
3305 * @param {Roo.EventObject} e
3311 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3315 preventDefault: false,
3321 getAutoCreate : function()
3323 var html = this.html || '';
3325 if (this.fa !== false) {
3326 html = '<i class="fa fa-' + this.fa + '"></i>';
3331 // anchor's do not require html/href...
3332 if (this.anchor === false) {
3334 cfg.href = this.href || '#';
3336 cfg.name = this.anchor;
3337 if (this.html !== false || this.fa !== false) {
3340 if (this.href !== false) {
3341 cfg.href = this.href;
3345 if(this.alt !== false){
3350 if(this.target !== false) {
3351 cfg.target = this.target;
3357 initEvents: function() {
3359 if(!this.href || this.preventDefault){
3360 this.el.on('click', this.onClick, this);
3364 onClick : function(e)
3366 if(this.preventDefault){
3369 //Roo.log('img onclick');
3370 this.fireEvent('click', this, e);
3383 * @class Roo.bootstrap.Header
3384 * @extends Roo.bootstrap.Component
3385 * @children Roo.bootstrap.Component
3386 * Bootstrap Header class
3389 * @cfg {String} html content of header
3390 * @cfg {Number} level (1|2|3|4|5|6) default 1
3393 * Create a new Header
3394 * @param {Object} config The config object
3398 Roo.bootstrap.Header = function(config){
3399 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3402 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3410 getAutoCreate : function(){
3415 tag: 'h' + (1 *this.level),
3416 html: this.html || ''
3427 * @class Roo.bootstrap.MenuMgr
3429 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3432 Roo.bootstrap.menu.Manager = function(){
3433 var menus, active, groups = {}, attached = false, lastShow = new Date();
3435 // private - called when first menu is created
3438 active = new Roo.util.MixedCollection();
3439 Roo.get(document).addKeyListener(27, function(){
3440 if(active.length > 0){
3448 if(active && active.length > 0){
3449 var c = active.clone();
3459 if(active.length < 1){
3460 Roo.get(document).un("mouseup", onMouseDown);
3468 var last = active.last();
3469 lastShow = new Date();
3472 Roo.get(document).on("mouseup", onMouseDown);
3477 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3478 m.parentMenu.activeChild = m;
3479 }else if(last && last.isVisible()){
3480 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3485 function onBeforeHide(m){
3487 m.activeChild.hide();
3489 if(m.autoHideTimer){
3490 clearTimeout(m.autoHideTimer);
3491 delete m.autoHideTimer;
3496 function onBeforeShow(m){
3497 var pm = m.parentMenu;
3498 if(!pm && !m.allowOtherMenus){
3500 }else if(pm && pm.activeChild && active != m){
3501 pm.activeChild.hide();
3505 // private this should really trigger on mouseup..
3506 function onMouseDown(e){
3507 Roo.log("on Mouse Up");
3509 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3510 Roo.log("MenuManager hideAll");
3519 function onBeforeCheck(mi, state){
3521 var g = groups[mi.group];
3522 for(var i = 0, l = g.length; i < l; i++){
3524 g[i].setChecked(false);
3533 * Hides all menus that are currently visible
3535 hideAll : function(){
3540 register : function(menu){
3544 menus[menu.id] = menu;
3545 menu.on("beforehide", onBeforeHide);
3546 menu.on("hide", onHide);
3547 menu.on("beforeshow", onBeforeShow);
3548 menu.on("show", onShow);
3550 if(g && menu.events["checkchange"]){
3554 groups[g].push(menu);
3555 menu.on("checkchange", onCheck);
3560 * Returns a {@link Roo.menu.Menu} object
3561 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3562 * be used to generate and return a new Menu instance.
3564 get : function(menu){
3565 if(typeof menu == "string"){ // menu id
3567 }else if(menu.events){ // menu instance
3570 /*else if(typeof menu.length == 'number'){ // array of menu items?
3571 return new Roo.bootstrap.Menu({items:menu});
3572 }else{ // otherwise, must be a config
3573 return new Roo.bootstrap.Menu(menu);
3580 unregister : function(menu){
3581 delete menus[menu.id];
3582 menu.un("beforehide", onBeforeHide);
3583 menu.un("hide", onHide);
3584 menu.un("beforeshow", onBeforeShow);
3585 menu.un("show", onShow);
3587 if(g && menu.events["checkchange"]){
3588 groups[g].remove(menu);
3589 menu.un("checkchange", onCheck);
3594 registerCheckable : function(menuItem){
3595 var g = menuItem.group;
3600 groups[g].push(menuItem);
3601 menuItem.on("beforecheckchange", onBeforeCheck);
3606 unregisterCheckable : function(menuItem){
3607 var g = menuItem.group;
3609 groups[g].remove(menuItem);
3610 menuItem.un("beforecheckchange", onBeforeCheck);
3616 * @class Roo.bootstrap.menu.Menu
3617 * @extends Roo.bootstrap.Component
3619 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3621 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3623 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3624 * @cfg {bool} hidden if the menu should be hidden when rendered.
3625 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3626 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3627 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3628 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3632 * @param {Object} config The config objectQ
3636 Roo.bootstrap.menu.Menu = function(config){
3638 if (config.type == 'treeview') {
3639 // normally menu's are drawn attached to the document to handle layering etc..
3640 // however treeview (used by the docs menu is drawn into the parent element)
3641 this.container_method = 'getChildContainer';
3644 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3645 if (this.registerMenu && this.type != 'treeview') {
3646 Roo.bootstrap.menu.Manager.register(this);
3653 * Fires before this menu is displayed (return false to block)
3654 * @param {Roo.menu.Menu} this
3659 * Fires before this menu is hidden (return false to block)
3660 * @param {Roo.menu.Menu} this
3665 * Fires after this menu is displayed
3666 * @param {Roo.menu.Menu} this
3671 * Fires after this menu is hidden
3672 * @param {Roo.menu.Menu} this
3677 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3678 * @param {Roo.menu.Menu} this
3679 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * @param {Roo.EventObject} e
3685 * Fires when the mouse is hovering over this menu
3686 * @param {Roo.menu.Menu} this
3687 * @param {Roo.EventObject} e
3688 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3693 * Fires when the mouse exits this menu
3694 * @param {Roo.menu.Menu} this
3695 * @param {Roo.EventObject} e
3696 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3701 * Fires when a menu item contained in this menu is clicked
3702 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3703 * @param {Roo.EventObject} e
3707 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3710 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3714 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3717 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3719 registerMenu : true,
3721 menuItems :false, // stores the menu items..
3731 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3733 hideTrigger : false,
3738 getChildContainer : function() {
3742 getAutoCreate : function(){
3744 //if (['right'].indexOf(this.align)!==-1) {
3745 // cfg.cn[1].cls += ' pull-right'
3750 cls : 'dropdown-menu shadow' ,
3751 style : 'z-index:1000'
3755 if (this.type === 'submenu') {
3756 cfg.cls = 'submenu active';
3758 if (this.type === 'treeview') {
3759 cfg.cls = 'treeview-menu';
3764 initEvents : function() {
3766 // Roo.log("ADD event");
3767 // Roo.log(this.triggerEl.dom);
3768 if (this.triggerEl) {
3770 this.triggerEl.on('click', this.onTriggerClick, this);
3772 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3774 if (!this.hideTrigger) {
3775 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3776 // dropdown toggle on the 'a' in BS4?
3777 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3779 this.triggerEl.addClass('dropdown-toggle');
3785 this.el.on('touchstart' , this.onTouch, this);
3787 this.el.on('click' , this.onClick, this);
3789 this.el.on("mouseover", this.onMouseOver, this);
3790 this.el.on("mouseout", this.onMouseOut, this);
3794 findTargetItem : function(e)
3796 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3800 //Roo.log(t); Roo.log(t.id);
3802 //Roo.log(this.menuitems);
3803 return this.menuitems.get(t.id);
3805 //return this.items.get(t.menuItemId);
3811 onTouch : function(e)
3813 Roo.log("menu.onTouch");
3814 //e.stopEvent(); this make the user popdown broken
3818 onClick : function(e)
3820 Roo.log("menu.onClick");
3822 var t = this.findTargetItem(e);
3823 if(!t || t.isContainer){
3828 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3829 if(t == this.activeItem && t.shouldDeactivate(e)){
3830 this.activeItem.deactivate();
3831 delete this.activeItem;
3835 this.setActiveItem(t, true);
3843 Roo.log('pass click event');
3847 this.fireEvent("click", this, t, e);
3851 if(!t.href.length || t.href == '#'){
3852 (function() { _this.hide(); }).defer(100);
3857 onMouseOver : function(e){
3858 var t = this.findTargetItem(e);
3861 // if(t.canActivate && !t.disabled){
3862 // this.setActiveItem(t, true);
3866 this.fireEvent("mouseover", this, e, t);
3868 isVisible : function(){
3869 return !this.hidden;
3871 onMouseOut : function(e){
3872 var t = this.findTargetItem(e);
3875 // if(t == this.activeItem && t.shouldDeactivate(e)){
3876 // this.activeItem.deactivate();
3877 // delete this.activeItem;
3880 this.fireEvent("mouseout", this, e, t);
3885 * Displays this menu relative to another element
3886 * @param {String/HTMLElement/Roo.Element} element The element to align to
3887 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3888 * the element (defaults to this.defaultAlign)
3889 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3891 show : function(el, pos, parentMenu)
3893 if (false === this.fireEvent("beforeshow", this)) {
3894 Roo.log("show canceled");
3897 this.parentMenu = parentMenu;
3901 this.el.addClass('show'); // show otherwise we do not know how big we are..
3903 var xy = this.el.getAlignToXY(el, pos);
3905 // bl-tl << left align below
3906 // tl-bl << left align
3908 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3909 // if it goes to far to the right.. -> align left.
3910 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3913 // was left align - go right?
3914 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3917 // goes down the bottom
3918 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3920 var a = this.align.replace('?', '').split('-');
3921 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3925 this.showAt( xy , parentMenu, false);
3928 * Displays this menu at a specific xy position
3929 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3930 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3932 showAt : function(xy, parentMenu, /* private: */_e){
3933 this.parentMenu = parentMenu;
3938 this.fireEvent("beforeshow", this);
3939 //xy = this.el.adjustForConstraints(xy);
3943 this.hideMenuItems();
3944 this.hidden = false;
3945 if (this.triggerEl) {
3946 this.triggerEl.addClass('open');
3949 this.el.addClass('show');
3953 // reassign x when hitting right
3955 // reassign y when hitting bottom
3957 // but the list may align on trigger left or trigger top... should it be a properity?
3959 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3964 this.fireEvent("show", this);
3970 this.doFocus.defer(50, this);
3974 doFocus : function(){
3976 this.focusEl.focus();
3981 * Hides this menu and optionally all parent menus
3982 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3984 hide : function(deep)
3986 if (false === this.fireEvent("beforehide", this)) {
3987 Roo.log("hide canceled");
3990 this.hideMenuItems();
3991 if(this.el && this.isVisible()){
3993 if(this.activeItem){
3994 this.activeItem.deactivate();
3995 this.activeItem = null;
3997 if (this.triggerEl) {
3998 this.triggerEl.removeClass('open');
4001 this.el.removeClass('show');
4003 this.fireEvent("hide", this);
4005 if(deep === true && this.parentMenu){
4006 this.parentMenu.hide(true);
4010 onTriggerClick : function(e)
4012 Roo.log('trigger click');
4014 var target = e.getTarget();
4016 Roo.log(target.nodeName.toLowerCase());
4018 if(target.nodeName.toLowerCase() === 'i'){
4024 onTriggerPress : function(e)
4026 Roo.log('trigger press');
4027 //Roo.log(e.getTarget());
4028 // Roo.log(this.triggerEl.dom);
4030 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4031 var pel = Roo.get(e.getTarget());
4032 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4033 Roo.log('is treeview or dropdown?');
4037 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4041 if (this.isVisible()) {
4047 this.show(this.triggerEl, this.align, false);
4050 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4057 hideMenuItems : function()
4059 Roo.log("hide Menu Items");
4064 this.el.select('.open',true).each(function(aa) {
4066 aa.removeClass('open');
4070 addxtypeChild : function (tree, cntr) {
4071 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4073 this.menuitems.add(comp);
4085 this.getEl().dom.innerHTML = '';
4086 this.menuitems.clear();
4092 * @class Roo.bootstrap.menu.Item
4093 * @extends Roo.bootstrap.Component
4094 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4095 * @parent Roo.bootstrap.menu.Menu
4097 * Bootstrap MenuItem class
4099 * @cfg {String} html the menu label
4100 * @cfg {String} href the link
4101 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4102 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4103 * @cfg {Boolean} active used on sidebars to highlight active itesm
4104 * @cfg {String} fa favicon to show on left of menu item.
4105 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4109 * Create a new MenuItem
4110 * @param {Object} config The config object
4114 Roo.bootstrap.menu.Item = function(config){
4115 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4120 * The raw click event for the entire grid.
4121 * @param {Roo.bootstrap.menu.Item} this
4122 * @param {Roo.EventObject} e
4128 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4132 preventDefault: false,
4133 isContainer : false,
4137 getAutoCreate : function(){
4139 if(this.isContainer){
4142 cls: 'dropdown-menu-item '
4152 cls : 'dropdown-item',
4157 if (this.fa !== false) {
4160 cls : 'fa fa-' + this.fa
4169 cls: 'dropdown-menu-item',
4172 if (this.parent().type == 'treeview') {
4173 cfg.cls = 'treeview-menu';
4176 cfg.cls += ' active';
4181 anc.href = this.href || cfg.cn[0].href ;
4182 ctag.html = this.html || cfg.cn[0].html ;
4186 initEvents: function()
4188 if (this.parent().type == 'treeview') {
4189 this.el.select('a').on('click', this.onClick, this);
4193 this.menu.parentType = this.xtype;
4194 this.menu.triggerEl = this.el;
4195 this.menu = this.addxtype(Roo.apply({}, this.menu));
4199 onClick : function(e)
4201 //Roo.log('item on click ');
4203 if(this.href === false || this.preventDefault){
4206 //this.parent().hideMenuItems();
4208 this.fireEvent('click', this, e);
4222 * @class Roo.bootstrap.menu.Separator
4223 * @extends Roo.bootstrap.Component
4225 * @parent Roo.bootstrap.menu.Menu
4226 * Bootstrap Separator class
4229 * Create a new Separator
4230 * @param {Object} config The config object
4234 Roo.bootstrap.menu.Separator = function(config){
4235 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4238 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4240 getAutoCreate : function(){
4243 cls: 'dropdown-divider divider'
4259 * @class Roo.bootstrap.Modal
4260 * @extends Roo.bootstrap.Component
4261 * @parent none builder
4262 * @children Roo.bootstrap.Component
4263 * Bootstrap Modal class
4264 * @cfg {String} title Title of dialog
4265 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4266 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4267 * @cfg {Boolean} specificTitle default false
4268 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4269 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4270 * @cfg {Boolean} animate default true
4271 * @cfg {Boolean} allow_close default true
4272 * @cfg {Boolean} fitwindow default false
4273 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4274 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4275 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4276 * @cfg {String} size (sm|lg|xl) default empty
4277 * @cfg {Number} max_width set the max width of modal
4278 * @cfg {Boolean} editableTitle can the title be edited
4283 * Create a new Modal Dialog
4284 * @param {Object} config The config object
4287 Roo.bootstrap.Modal = function(config){
4288 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4293 * The raw btnclick event for the button
4294 * @param {Roo.EventObject} e
4299 * Fire when dialog resize
4300 * @param {Roo.bootstrap.Modal} this
4301 * @param {Roo.EventObject} e
4305 * @event titlechanged
4306 * Fire when the editable title has been changed
4307 * @param {Roo.bootstrap.Modal} this
4308 * @param {Roo.EventObject} value
4310 "titlechanged" : true
4313 this.buttons = this.buttons || [];
4316 this.tmpl = Roo.factory(this.tmpl);
4321 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4323 title : 'test dialog',
4333 specificTitle: false,
4335 buttonPosition: 'right',
4357 editableTitle : false,
4359 onRender : function(ct, position)
4361 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4364 var cfg = Roo.apply({}, this.getAutoCreate());
4367 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4369 //if (!cfg.name.length) {
4373 cfg.cls += ' ' + this.cls;
4376 cfg.style = this.style;
4378 this.el = Roo.get(document.body).createChild(cfg, position);
4380 //var type = this.el.dom.type;
4383 if(this.tabIndex !== undefined){
4384 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4387 this.dialogEl = this.el.select('.modal-dialog',true).first();
4388 this.bodyEl = this.el.select('.modal-body',true).first();
4389 this.closeEl = this.el.select('.modal-header .close', true).first();
4390 this.headerEl = this.el.select('.modal-header',true).first();
4391 this.titleEl = this.el.select('.modal-title',true).first();
4392 this.footerEl = this.el.select('.modal-footer',true).first();
4394 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4396 //this.el.addClass("x-dlg-modal");
4398 if (this.buttons.length) {
4399 Roo.each(this.buttons, function(bb) {
4400 var b = Roo.apply({}, bb);
4401 b.xns = b.xns || Roo.bootstrap;
4402 b.xtype = b.xtype || 'Button';
4403 if (typeof(b.listeners) == 'undefined') {
4404 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4407 var btn = Roo.factory(b);
4409 btn.render(this.getButtonContainer());
4413 // render the children.
4416 if(typeof(this.items) != 'undefined'){
4417 var items = this.items;
4420 for(var i =0;i < items.length;i++) {
4421 // we force children not to montor widnow resize - as we do that for them.
4422 items[i].monitorWindowResize = false;
4423 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4427 this.items = nitems;
4429 // where are these used - they used to be body/close/footer
4433 //this.el.addClass([this.fieldClass, this.cls]);
4437 getAutoCreate : function()
4439 // we will default to modal-body-overflow - might need to remove or make optional later.
4441 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4442 html : this.html || ''
4447 cls : 'modal-title',
4451 if(this.specificTitle){ // WTF is this?
4456 if (this.allow_close && Roo.bootstrap.version == 3) {
4466 if (this.editableTitle) {
4468 cls: 'form-control roo-editable-title d-none',
4474 if (this.allow_close && Roo.bootstrap.version == 4) {
4484 if(this.size.length){
4485 size = 'modal-' + this.size;
4488 var footer = Roo.bootstrap.version == 3 ?
4490 cls : 'modal-footer',
4494 cls: 'btn-' + this.buttonPosition
4499 { // BS4 uses mr-auto on left buttons....
4500 cls : 'modal-footer'
4511 cls: "modal-dialog " + size,
4514 cls : "modal-content",
4517 cls : 'modal-header',
4532 modal.cls += ' fade';
4538 getChildContainer : function() {
4543 getButtonContainer : function() {
4545 return Roo.bootstrap.version == 4 ?
4546 this.el.select('.modal-footer',true).first()
4547 : this.el.select('.modal-footer div',true).first();
4550 initEvents : function()
4552 if (this.allow_close) {
4553 this.closeEl.on('click', this.hide, this);
4555 Roo.EventManager.onWindowResize(this.resize, this, true);
4556 if (this.editableTitle) {
4557 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4558 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4559 this.headerEditEl.on('keyup', function(e) {
4560 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4561 this.toggleHeaderInput(false)
4564 this.headerEditEl.on('blur', function(e) {
4565 this.toggleHeaderInput(false)
4574 this.maskEl.setSize(
4575 Roo.lib.Dom.getViewWidth(true),
4576 Roo.lib.Dom.getViewHeight(true)
4579 if (this.fitwindow) {
4581 this.dialogEl.setStyle( { 'max-width' : '100%' });
4583 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4584 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4589 if(this.max_width !== 0) {
4591 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4594 this.setSize(w, this.height);
4598 if(this.max_height) {
4599 this.setSize(w,Math.min(
4601 Roo.lib.Dom.getViewportHeight(true) - 60
4607 if(!this.fit_content) {
4608 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4612 this.setSize(w, Math.min(
4614 this.headerEl.getHeight() +
4615 this.footerEl.getHeight() +
4616 this.getChildHeight(this.bodyEl.dom.childNodes),
4617 Roo.lib.Dom.getViewportHeight(true) - 60)
4623 setSize : function(w,h)
4630 // any layout/border etc.. resize..
4632 this.items.forEach( function(e) {
4633 e.layout ? e.layout() : false;
4642 if (!this.rendered) {
4645 this.toggleHeaderInput(false);
4646 //this.el.setStyle('display', 'block');
4647 this.el.removeClass('hideing');
4648 this.el.dom.style.display='block';
4650 Roo.get(document.body).addClass('modal-open');
4652 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4655 this.el.addClass('show');
4656 this.el.addClass('in');
4659 this.el.addClass('show');
4660 this.el.addClass('in');
4663 // not sure how we can show data in here..
4665 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4668 Roo.get(document.body).addClass("x-body-masked");
4670 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4671 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4672 this.maskEl.dom.style.display = 'block';
4673 this.maskEl.addClass('show');
4678 this.fireEvent('show', this);
4680 // set zindex here - otherwise it appears to be ignored...
4681 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4684 // this is for children that are... layout.Border
4686 this.items.forEach( function(e) {
4687 e.layout ? e.layout() : false;
4695 if(this.fireEvent("beforehide", this) !== false){
4697 this.maskEl.removeClass('show');
4699 this.maskEl.dom.style.display = '';
4700 Roo.get(document.body).removeClass("x-body-masked");
4701 this.el.removeClass('in');
4702 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4704 if(this.animate){ // why
4705 this.el.addClass('hideing');
4706 this.el.removeClass('show');
4708 if (!this.el.hasClass('hideing')) {
4709 return; // it's been shown again...
4712 this.el.dom.style.display='';
4714 Roo.get(document.body).removeClass('modal-open');
4715 this.el.removeClass('hideing');
4719 this.el.removeClass('show');
4720 this.el.dom.style.display='';
4721 Roo.get(document.body).removeClass('modal-open');
4724 this.fireEvent('hide', this);
4727 isVisible : function()
4730 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4734 addButton : function(str, cb)
4738 var b = Roo.apply({}, { html : str } );
4739 b.xns = b.xns || Roo.bootstrap;
4740 b.xtype = b.xtype || 'Button';
4741 if (typeof(b.listeners) == 'undefined') {
4742 b.listeners = { click : cb.createDelegate(this) };
4745 var btn = Roo.factory(b);
4747 btn.render(this.getButtonContainer());
4753 setDefaultButton : function(btn)
4755 //this.el.select('.modal-footer').()
4758 resizeTo: function(w,h)
4760 this.dialogEl.setWidth(w);
4762 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4764 this.bodyEl.setHeight(h - diff);
4766 this.fireEvent('resize', this);
4769 setContentSize : function(w, h)
4773 onButtonClick: function(btn,e)
4776 this.fireEvent('btnclick', btn.name, e);
4779 * Set the title of the Dialog
4780 * @param {String} str new Title
4782 setTitle: function(str) {
4783 this.titleEl.dom.innerHTML = str;
4787 * Set the body of the Dialog
4788 * @param {String} str new Title
4790 setBody: function(str) {
4791 this.bodyEl.dom.innerHTML = str;
4794 * Set the body of the Dialog using the template
4795 * @param {Obj} data - apply this data to the template and replace the body contents.
4797 applyBody: function(obj)
4800 Roo.log("Error - using apply Body without a template");
4803 this.tmpl.overwrite(this.bodyEl, obj);
4806 getChildHeight : function(child_nodes)
4810 child_nodes.length == 0
4815 var child_height = 0;
4817 for(var i = 0; i < child_nodes.length; i++) {
4820 * for modal with tabs...
4821 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4823 var layout_childs = child_nodes[i].childNodes;
4825 for(var j = 0; j < layout_childs.length; j++) {
4827 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4829 var layout_body_childs = layout_childs[j].childNodes;
4831 for(var k = 0; k < layout_body_childs.length; k++) {
4833 if(layout_body_childs[k].classList.contains('navbar')) {
4834 child_height += layout_body_childs[k].offsetHeight;
4838 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4840 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4842 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4844 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4845 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4860 child_height += child_nodes[i].offsetHeight;
4861 // Roo.log(child_nodes[i].offsetHeight);
4864 return child_height;
4866 toggleHeaderInput : function(is_edit)
4868 if (!this.editableTitle) {
4869 return; // not editable.
4871 if (is_edit && this.is_header_editing) {
4872 return; // already editing..
4876 this.headerEditEl.dom.value = this.title;
4877 this.headerEditEl.removeClass('d-none');
4878 this.headerEditEl.dom.focus();
4879 this.titleEl.addClass('d-none');
4881 this.is_header_editing = true;
4884 // flip back to not editing.
4885 this.title = this.headerEditEl.dom.value;
4886 this.headerEditEl.addClass('d-none');
4887 this.titleEl.removeClass('d-none');
4888 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4889 this.is_header_editing = false;
4890 this.fireEvent('titlechanged', this, this.title);
4899 Roo.apply(Roo.bootstrap.Modal, {
4901 * Button config that displays a single OK button
4910 * Button config that displays Yes and No buttons
4926 * Button config that displays OK and Cancel buttons
4941 * Button config that displays Yes, No and Cancel buttons
4966 * messagebox - can be used as a replace
4970 * @class Roo.MessageBox
4971 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4975 Roo.Msg.alert('Status', 'Changes saved successfully.');
4977 // Prompt for user data:
4978 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4980 // process text value...
4984 // Show a dialog using config options:
4986 title:'Save Changes?',
4987 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4988 buttons: Roo.Msg.YESNOCANCEL,
4995 Roo.bootstrap.MessageBox = function(){
4996 var dlg, opt, mask, waitTimer;
4997 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4998 var buttons, activeTextEl, bwidth;
5002 var handleButton = function(button){
5004 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5008 var handleHide = function(){
5010 dlg.el.removeClass(opt.cls);
5013 // Roo.TaskMgr.stop(waitTimer);
5014 // waitTimer = null;
5019 var updateButtons = function(b){
5022 buttons["ok"].hide();
5023 buttons["cancel"].hide();
5024 buttons["yes"].hide();
5025 buttons["no"].hide();
5026 dlg.footerEl.hide();
5030 dlg.footerEl.show();
5031 for(var k in buttons){
5032 if(typeof buttons[k] != "function"){
5035 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5036 width += buttons[k].el.getWidth()+15;
5046 var handleEsc = function(d, k, e){
5047 if(opt && opt.closable !== false){
5057 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5058 * @return {Roo.BasicDialog} The BasicDialog element
5060 getDialog : function(){
5062 dlg = new Roo.bootstrap.Modal( {
5065 //constraintoviewport:false,
5067 //collapsible : false,
5072 //buttonAlign:"center",
5073 closeClick : function(){
5074 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5077 handleButton("cancel");
5082 dlg.on("hide", handleHide);
5084 //dlg.addKeyListener(27, handleEsc);
5086 this.buttons = buttons;
5087 var bt = this.buttonText;
5088 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5089 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5090 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5091 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5093 bodyEl = dlg.bodyEl.createChild({
5095 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5096 '<textarea class="roo-mb-textarea"></textarea>' +
5097 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5099 msgEl = bodyEl.dom.firstChild;
5100 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5101 textboxEl.enableDisplayMode();
5102 textboxEl.addKeyListener([10,13], function(){
5103 if(dlg.isVisible() && opt && opt.buttons){
5106 }else if(opt.buttons.yes){
5107 handleButton("yes");
5111 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5112 textareaEl.enableDisplayMode();
5113 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5114 progressEl.enableDisplayMode();
5116 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5117 var pf = progressEl.dom.firstChild;
5119 pp = Roo.get(pf.firstChild);
5120 pp.setHeight(pf.offsetHeight);
5128 * Updates the message box body text
5129 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5130 * the XHTML-compliant non-breaking space character '&#160;')
5131 * @return {Roo.MessageBox} This message box
5133 updateText : function(text)
5135 if(!dlg.isVisible() && !opt.width){
5136 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5137 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5139 msgEl.innerHTML = text || ' ';
5141 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5142 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5144 Math.min(opt.width || cw , this.maxWidth),
5145 Math.max(opt.minWidth || this.minWidth, bwidth)
5148 activeTextEl.setWidth(w);
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = false;
5153 // to big, make it scroll. = But as usual stupid IE does not support
5156 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5157 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5158 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5160 bodyEl.dom.style.height = '';
5161 bodyEl.dom.style.overflowY = '';
5164 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5166 bodyEl.dom.style.overflowX = '';
5169 dlg.setContentSize(w, bodyEl.getHeight());
5170 if(dlg.isVisible()){
5171 dlg.fixedcenter = true;
5177 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5178 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5179 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5180 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5181 * @return {Roo.MessageBox} This message box
5183 updateProgress : function(value, text){
5185 this.updateText(text);
5188 if (pp) { // weird bug on my firefox - for some reason this is not defined
5189 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5190 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5196 * Returns true if the message box is currently displayed
5197 * @return {Boolean} True if the message box is visible, else false
5199 isVisible : function(){
5200 return dlg && dlg.isVisible();
5204 * Hides the message box if it is displayed
5207 if(this.isVisible()){
5213 * Displays a new message box, or reinitializes an existing message box, based on the config options
5214 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5215 * The following config object properties are supported:
5217 Property Type Description
5218 ---------- --------------- ------------------------------------------------------------------------------------
5219 animEl String/Element An id or Element from which the message box should animate as it opens and
5220 closes (defaults to undefined)
5221 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5222 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5223 closable Boolean False to hide the top-right close button (defaults to true). Note that
5224 progress and wait dialogs will ignore this property and always hide the
5225 close button as they can only be closed programmatically.
5226 cls String A custom CSS class to apply to the message box element
5227 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5228 displayed (defaults to 75)
5229 fn Function A callback function to execute after closing the dialog. The arguments to the
5230 function will be btn (the name of the button that was clicked, if applicable,
5231 e.g. "ok"), and text (the value of the active text field, if applicable).
5232 Progress and wait dialogs will ignore this option since they do not respond to
5233 user actions and can only be closed programmatically, so any required function
5234 should be called by the same code after it closes the dialog.
5235 icon String A CSS class that provides a background image to be used as an icon for
5236 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5237 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5238 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5239 modal Boolean False to allow user interaction with the page while the message box is
5240 displayed (defaults to true)
5241 msg String A string that will replace the existing message box body text (defaults
5242 to the XHTML-compliant non-breaking space character ' ')
5243 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5244 progress Boolean True to display a progress bar (defaults to false)
5245 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5246 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5247 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5248 title String The title text
5249 value String The string value to set into the active textbox element if displayed
5250 wait Boolean True to display a progress bar (defaults to false)
5251 width Number The width of the dialog in pixels
5258 msg: 'Please enter your address:',
5260 buttons: Roo.MessageBox.OKCANCEL,
5263 animEl: 'addAddressBtn'
5266 * @param {Object} config Configuration options
5267 * @return {Roo.MessageBox} This message box
5269 show : function(options)
5272 // this causes nightmares if you show one dialog after another
5273 // especially on callbacks..
5275 if(this.isVisible()){
5278 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5279 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5280 Roo.log("New Dialog Message:" + options.msg )
5281 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5282 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5285 var d = this.getDialog();
5287 d.setTitle(opt.title || " ");
5288 d.closeEl.setDisplayed(opt.closable !== false);
5289 activeTextEl = textboxEl;
5290 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5295 textareaEl.setHeight(typeof opt.multiline == "number" ?
5296 opt.multiline : this.defaultTextHeight);
5297 activeTextEl = textareaEl;
5306 progressEl.setDisplayed(opt.progress === true);
5308 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5310 this.updateProgress(0);
5311 activeTextEl.dom.value = opt.value || "";
5313 dlg.setDefaultButton(activeTextEl);
5315 var bs = opt.buttons;
5319 }else if(bs && bs.yes){
5320 db = buttons["yes"];
5322 dlg.setDefaultButton(db);
5324 bwidth = updateButtons(opt.buttons);
5325 this.updateText(opt.msg);
5327 d.el.addClass(opt.cls);
5329 d.proxyDrag = opt.proxyDrag === true;
5330 d.modal = opt.modal !== false;
5331 d.mask = opt.modal !== false ? mask : false;
5333 // force it to the end of the z-index stack so it gets a cursor in FF
5334 document.body.appendChild(dlg.el.dom);
5335 d.animateTarget = null;
5336 d.show(options.animEl);
5342 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5343 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5344 * and closing the message box when the process is complete.
5345 * @param {String} title The title bar text
5346 * @param {String} msg The message box body text
5347 * @return {Roo.MessageBox} This message box
5349 progress : function(title, msg){
5356 minWidth: this.minProgressWidth,
5363 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5364 * If a callback function is passed it will be called after the user clicks the button, and the
5365 * id of the button that was clicked will be passed as the only parameter to the callback
5366 * (could also be the top-right close button).
5367 * @param {String} title The title bar text
5368 * @param {String} msg The message box body text
5369 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370 * @param {Object} scope (optional) The scope of the callback function
5371 * @return {Roo.MessageBox} This message box
5373 alert : function(title, msg, fn, scope)
5388 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5389 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5390 * You are responsible for closing the message box when the process is complete.
5391 * @param {String} msg The message box body text
5392 * @param {String} title (optional) The title bar text
5393 * @return {Roo.MessageBox} This message box
5395 wait : function(msg, title){
5406 waitTimer = Roo.TaskMgr.start({
5408 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5416 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5417 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5418 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5419 * @param {String} title The title bar text
5420 * @param {String} msg The message box body text
5421 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5422 * @param {Object} scope (optional) The scope of the callback function
5423 * @return {Roo.MessageBox} This message box
5425 confirm : function(title, msg, fn, scope){
5429 buttons: this.YESNO,
5438 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5439 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5440 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5441 * (could also be the top-right close button) and the text that was entered will be passed as the two
5442 * parameters to the callback.
5443 * @param {String} title The title bar text
5444 * @param {String} msg The message box body text
5445 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5446 * @param {Object} scope (optional) The scope of the callback function
5447 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5448 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5449 * @return {Roo.MessageBox} This message box
5451 prompt : function(title, msg, fn, scope, multiline){
5455 buttons: this.OKCANCEL,
5460 multiline: multiline,
5467 * Button config that displays a single OK button
5472 * Button config that displays Yes and No buttons
5475 YESNO : {yes:true, no:true},
5477 * Button config that displays OK and Cancel buttons
5480 OKCANCEL : {ok:true, cancel:true},
5482 * Button config that displays Yes, No and Cancel buttons
5485 YESNOCANCEL : {yes:true, no:true, cancel:true},
5488 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5491 defaultTextHeight : 75,
5493 * The maximum width in pixels of the message box (defaults to 600)
5498 * The minimum width in pixels of the message box (defaults to 100)
5503 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5504 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5507 minProgressWidth : 250,
5509 * An object containing the default button text strings that can be overriden for localized language support.
5510 * Supported properties are: ok, cancel, yes and no.
5511 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5524 * Shorthand for {@link Roo.MessageBox}
5526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5527 Roo.Msg = Roo.Msg || Roo.MessageBox;
5536 * @class Roo.bootstrap.nav.Bar
5537 * @extends Roo.bootstrap.Component
5539 * Bootstrap Navbar class
5542 * Create a new Navbar
5543 * @param {Object} config The config object
5547 Roo.bootstrap.nav.Bar = function(config){
5548 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5552 * @event beforetoggle
5553 * Fire before toggle the menu
5554 * @param {Roo.EventObject} e
5556 "beforetoggle" : true
5560 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5569 getAutoCreate : function(){
5572 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5576 initEvents :function ()
5578 //Roo.log(this.el.select('.navbar-toggle',true));
5579 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5586 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5588 var size = this.el.getSize();
5589 this.maskEl.setSize(size.width, size.height);
5590 this.maskEl.enableDisplayMode("block");
5599 getChildContainer : function()
5601 if (this.el && this.el.select('.collapse').getCount()) {
5602 return this.el.select('.collapse',true).first();
5617 onToggle : function()
5620 if(this.fireEvent('beforetoggle', this) === false){
5623 var ce = this.el.select('.navbar-collapse',true).first();
5625 if (!ce.hasClass('show')) {
5635 * Expand the navbar pulldown
5637 expand : function ()
5640 var ce = this.el.select('.navbar-collapse',true).first();
5641 if (ce.hasClass('collapsing')) {
5644 ce.dom.style.height = '';
5646 ce.addClass('in'); // old...
5647 ce.removeClass('collapse');
5648 ce.addClass('show');
5649 var h = ce.getHeight();
5651 ce.removeClass('show');
5652 // at this point we should be able to see it..
5653 ce.addClass('collapsing');
5655 ce.setHeight(0); // resize it ...
5656 ce.on('transitionend', function() {
5657 //Roo.log('done transition');
5658 ce.removeClass('collapsing');
5659 ce.addClass('show');
5660 ce.removeClass('collapse');
5662 ce.dom.style.height = '';
5663 }, this, { single: true} );
5665 ce.dom.scrollTop = 0;
5668 * Collapse the navbar pulldown
5670 collapse : function()
5672 var ce = this.el.select('.navbar-collapse',true).first();
5674 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5675 // it's collapsed or collapsing..
5678 ce.removeClass('in'); // old...
5679 ce.setHeight(ce.getHeight());
5680 ce.removeClass('show');
5681 ce.addClass('collapsing');
5683 ce.on('transitionend', function() {
5684 ce.dom.style.height = '';
5685 ce.removeClass('collapsing');
5686 ce.addClass('collapse');
5687 }, this, { single: true} );
5707 * @class Roo.bootstrap.nav.Simplebar
5708 * @extends Roo.bootstrap.nav.Bar
5709 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5710 * Bootstrap Sidebar class
5712 * @cfg {Boolean} inverse is inverted color
5714 * @cfg {String} type (nav | pills | tabs)
5715 * @cfg {Boolean} arrangement stacked | justified
5716 * @cfg {String} align (left | right) alignment
5718 * @cfg {Boolean} main (true|false) main nav bar? default false
5719 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5721 * @cfg {String} tag (header|footer|nav|div) default is nav
5723 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5727 * Create a new Sidebar
5728 * @param {Object} config The config object
5732 Roo.bootstrap.nav.Simplebar = function(config){
5733 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5736 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5752 getAutoCreate : function(){
5756 tag : this.tag || 'div',
5757 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5759 if (['light','white'].indexOf(this.weight) > -1) {
5760 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5762 cfg.cls += ' bg-' + this.weight;
5765 cfg.cls += ' navbar-inverse';
5769 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5771 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5780 cls: 'nav nav-' + this.xtype,
5786 this.type = this.type || 'nav';
5787 if (['tabs','pills'].indexOf(this.type) != -1) {
5788 cfg.cn[0].cls += ' nav-' + this.type
5792 if (this.type!=='nav') {
5793 Roo.log('nav type must be nav/tabs/pills')
5795 cfg.cn[0].cls += ' navbar-nav'
5801 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5802 cfg.cn[0].cls += ' nav-' + this.arrangement;
5806 if (this.align === 'right') {
5807 cfg.cn[0].cls += ' navbar-right';
5832 * navbar-expand-md fixed-top
5836 * @class Roo.bootstrap.nav.Headerbar
5837 * @extends Roo.bootstrap.nav.Simplebar
5838 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5839 * Bootstrap Sidebar class
5841 * @cfg {String} brand what is brand
5842 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5843 * @cfg {String} brand_href href of the brand
5844 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5845 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5846 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5847 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5850 * Create a new Sidebar
5851 * @param {Object} config The config object
5855 Roo.bootstrap.nav.Headerbar = function(config){
5856 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5860 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5867 desktopCenter : false,
5870 getAutoCreate : function(){
5873 tag: this.nav || 'nav',
5874 cls: 'navbar navbar-expand-md',
5880 if (this.desktopCenter) {
5881 cn.push({cls : 'container', cn : []});
5889 cls: 'navbar-toggle navbar-toggler',
5890 'data-toggle': 'collapse',
5895 html: 'Toggle navigation'
5899 cls: 'icon-bar navbar-toggler-icon'
5912 cn.push( Roo.bootstrap.version == 4 ? btn : {
5914 cls: 'navbar-header',
5923 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5927 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5929 if (['light','white'].indexOf(this.weight) > -1) {
5930 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5932 cfg.cls += ' bg-' + this.weight;
5935 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5936 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5938 // tag can override this..
5940 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5943 if (this.brand !== '') {
5944 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5945 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5947 href: this.brand_href ? this.brand_href : '#',
5948 cls: 'navbar-brand',
5956 cfg.cls += ' main-nav';
5964 getHeaderChildContainer : function()
5966 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5967 return this.el.select('.navbar-header',true).first();
5970 return this.getChildContainer();
5973 getChildContainer : function()
5976 return this.el.select('.roo-navbar-collapse',true).first();
5981 initEvents : function()
5983 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5985 if (this.autohide) {
5990 Roo.get(document).on('scroll',function(e) {
5991 var ns = Roo.get(document).getScroll().top;
5992 var os = prevScroll;
5996 ft.removeClass('slideDown');
5997 ft.addClass('slideUp');
6000 ft.removeClass('slideUp');
6001 ft.addClass('slideDown');
6022 * @class Roo.bootstrap.nav.Sidebar
6023 * @extends Roo.bootstrap.nav.Bar
6024 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6025 * Bootstrap Sidebar class
6028 * Create a new Sidebar
6029 * @param {Object} config The config object
6033 Roo.bootstrap.nav.Sidebar = function(config){
6034 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6037 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6039 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6041 getAutoCreate : function(){
6046 cls: 'sidebar sidebar-nav'
6068 * @class Roo.bootstrap.nav.Group
6069 * @extends Roo.bootstrap.Component
6070 * @children Roo.bootstrap.nav.Item
6071 * Bootstrap NavGroup class
6072 * @cfg {String} align (left|right)
6073 * @cfg {Boolean} inverse
6074 * @cfg {String} type (nav|pills|tab) default nav
6075 * @cfg {String} navId - reference Id for navbar.
6076 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6079 * Create a new nav group
6080 * @param {Object} config The config object
6083 Roo.bootstrap.nav.Group = function(config){
6084 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6087 Roo.bootstrap.nav.Group.register(this);
6091 * Fires when the active item changes
6092 * @param {Roo.bootstrap.nav.Group} this
6093 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6094 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6101 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6113 getAutoCreate : function()
6115 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6121 if (Roo.bootstrap.version == 4) {
6122 if (['tabs','pills'].indexOf(this.type) != -1) {
6123 cfg.cls += ' nav-' + this.type;
6125 // trying to remove so header bar can right align top?
6126 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6127 // do not use on header bar...
6128 cfg.cls += ' navbar-nav';
6133 if (['tabs','pills'].indexOf(this.type) != -1) {
6134 cfg.cls += ' nav-' + this.type
6136 if (this.type !== 'nav') {
6137 Roo.log('nav type must be nav/tabs/pills')
6139 cfg.cls += ' navbar-nav'
6143 if (this.parent() && this.parent().sidebar) {
6146 cls: 'dashboard-menu sidebar-menu'
6152 if (this.form === true) {
6155 cls: 'navbar-form form-inline'
6157 //nav navbar-right ml-md-auto
6158 if (this.align === 'right') {
6159 cfg.cls += ' navbar-right ml-md-auto';
6161 cfg.cls += ' navbar-left';
6165 if (this.align === 'right') {
6166 cfg.cls += ' navbar-right ml-md-auto';
6168 cfg.cls += ' mr-auto';
6172 cfg.cls += ' navbar-inverse';
6180 * sets the active Navigation item
6181 * @param {Roo.bootstrap.nav.Item} the new current navitem
6183 setActiveItem : function(item)
6186 Roo.each(this.navItems, function(v){
6191 v.setActive(false, true);
6198 item.setActive(true, true);
6199 this.fireEvent('changed', this, item, prev);
6204 * gets the active Navigation item
6205 * @return {Roo.bootstrap.nav.Item} the current navitem
6207 getActive : function()
6211 Roo.each(this.navItems, function(v){
6222 indexOfNav : function()
6226 Roo.each(this.navItems, function(v,i){
6237 * adds a Navigation item
6238 * @param {Roo.bootstrap.nav.Item} the navitem to add
6240 addItem : function(cfg)
6242 if (this.form && Roo.bootstrap.version == 4) {
6245 var cn = new Roo.bootstrap.nav.Item(cfg);
6247 cn.parentId = this.id;
6248 cn.onRender(this.el, null);
6252 * register a Navigation item
6253 * @param {Roo.bootstrap.nav.Item} the navitem to add
6255 register : function(item)
6257 this.navItems.push( item);
6258 item.navId = this.navId;
6263 * clear all the Navigation item
6266 clearAll : function()
6269 this.el.dom.innerHTML = '';
6272 getNavItem: function(tabId)
6275 Roo.each(this.navItems, function(e) {
6276 if (e.tabId == tabId) {
6286 setActiveNext : function()
6288 var i = this.indexOfNav(this.getActive());
6289 if (i > this.navItems.length) {
6292 this.setActiveItem(this.navItems[i+1]);
6294 setActivePrev : function()
6296 var i = this.indexOfNav(this.getActive());
6300 this.setActiveItem(this.navItems[i-1]);
6302 clearWasActive : function(except) {
6303 Roo.each(this.navItems, function(e) {
6304 if (e.tabId != except.tabId && e.was_active) {
6305 e.was_active = false;
6312 getWasActive : function ()
6315 Roo.each(this.navItems, function(e) {
6330 Roo.apply(Roo.bootstrap.nav.Group, {
6334 * register a Navigation Group
6335 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6337 register : function(navgrp)
6339 this.groups[navgrp.navId] = navgrp;
6343 * fetch a Navigation Group based on the navigation ID
6344 * @param {string} the navgroup to add
6345 * @returns {Roo.bootstrap.nav.Group} the navgroup
6347 get: function(navId) {
6348 if (typeof(this.groups[navId]) == 'undefined') {
6350 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6352 return this.groups[navId] ;
6360 * @class Roo.bootstrap.nav.Item
6361 * @extends Roo.bootstrap.Component
6362 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6363 * @parent Roo.bootstrap.nav.Group
6365 * Bootstrap Navbar.NavItem class
6367 * @cfg {String} href link to
6368 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6369 * @cfg {Boolean} button_outline show and outlined button
6370 * @cfg {String} html content of button
6371 * @cfg {String} badge text inside badge
6372 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6373 * @cfg {String} glyphicon DEPRICATED - use fa
6374 * @cfg {String} icon DEPRICATED - use fa
6375 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6376 * @cfg {Boolean} active Is item active
6377 * @cfg {Boolean} disabled Is item disabled
6378 * @cfg {String} linkcls Link Class
6379 * @cfg {Boolean} preventDefault (true | false) default false
6380 * @cfg {String} tabId the tab that this item activates.
6381 * @cfg {String} tagtype (a|span) render as a href or span?
6382 * @cfg {Boolean} animateRef (true|false) link to element default false
6383 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6386 * Create a new Navbar Item
6387 * @param {Object} config The config object
6389 Roo.bootstrap.nav.Item = function(config){
6390 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6395 * The raw click event for the entire grid.
6396 * @param {Roo.EventObject} e
6401 * Fires when the active item active state changes
6402 * @param {Roo.bootstrap.nav.Item} this
6403 * @param {boolean} state the new state
6409 * Fires when scroll to element
6410 * @param {Roo.bootstrap.nav.Item} this
6411 * @param {Object} options
6412 * @param {Roo.EventObject} e
6420 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6429 preventDefault : false,
6437 button_outline : false,
6441 getAutoCreate : function(){
6448 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6451 cfg.cls += ' active' ;
6453 if (this.disabled) {
6454 cfg.cls += ' disabled';
6458 if (this.button_weight.length) {
6459 cfg.tag = this.href ? 'a' : 'button';
6460 cfg.html = this.html || '';
6461 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6463 cfg.href = this.href;
6466 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6468 cfg.cls += " nav-html";
6471 // menu .. should add dropdown-menu class - so no need for carat..
6473 if (this.badge !== '') {
6475 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6480 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6484 href : this.href || "#",
6485 html: this.html || '',
6489 if (this.tagtype == 'a') {
6490 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6494 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495 } else if (this.fa) {
6496 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6497 } else if(this.glyphicon) {
6498 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6500 cfg.cn[0].cls += " nav-html";
6504 cfg.cn[0].html += " <span class='caret'></span>";
6508 if (this.badge !== '') {
6509 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6517 onRender : function(ct, position)
6519 // Roo.log("Call onRender: " + this.xtype);
6520 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6524 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6525 this.navLink = this.el.select('.nav-link',true).first();
6526 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6531 initEvents: function()
6533 if (typeof (this.menu) != 'undefined') {
6534 this.menu.parentType = this.xtype;
6535 this.menu.triggerEl = this.el;
6536 this.menu = this.addxtype(Roo.apply({}, this.menu));
6539 this.el.on('click', this.onClick, this);
6541 //if(this.tagtype == 'span'){
6542 // this.el.select('span',true).on('click', this.onClick, this);
6545 // at this point parent should be available..
6546 this.parent().register(this);
6549 onClick : function(e)
6551 if (e.getTarget('.dropdown-menu-item')) {
6552 // did you click on a menu itemm.... - then don't trigger onclick..
6557 this.preventDefault ||
6558 this.href === false ||
6561 //Roo.log("NavItem - prevent Default?");
6565 if (this.disabled) {
6569 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6570 if (tg && tg.transition) {
6571 Roo.log("waiting for the transitionend");
6577 //Roo.log("fire event clicked");
6578 if(this.fireEvent('click', this, e) === false){
6582 if(this.tagtype == 'span'){
6586 //Roo.log(this.href);
6587 var ael = this.el.select('a',true).first();
6590 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6591 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6592 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6593 return; // ignore... - it's a 'hash' to another page.
6595 Roo.log("NavItem - prevent Default?");
6597 this.scrollToElement(e);
6601 var p = this.parent();
6603 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6604 if (typeof(p.setActiveItem) !== 'undefined') {
6605 p.setActiveItem(this);
6609 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6610 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6611 // remove the collapsed menu expand...
6612 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6616 isActive: function () {
6619 setActive : function(state, fire, is_was_active)
6621 if (this.active && !state && this.navId) {
6622 this.was_active = true;
6623 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6625 nv.clearWasActive(this);
6629 this.active = state;
6632 this.el.removeClass('active');
6633 this.navLink ? this.navLink.removeClass('active') : false;
6634 } else if (!this.el.hasClass('active')) {
6636 this.el.addClass('active');
6637 if (Roo.bootstrap.version == 4 && this.navLink ) {
6638 this.navLink.addClass('active');
6643 this.fireEvent('changed', this, state);
6646 // show a panel if it's registered and related..
6648 if (!this.navId || !this.tabId || !state || is_was_active) {
6652 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6656 var pan = tg.getPanelByName(this.tabId);
6660 // if we can not flip to new panel - go back to old nav highlight..
6661 if (false == tg.showPanel(pan)) {
6662 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6664 var onav = nv.getWasActive();
6666 onav.setActive(true, false, true);
6675 // this should not be here...
6676 setDisabled : function(state)
6678 this.disabled = state;
6680 this.el.removeClass('disabled');
6681 } else if (!this.el.hasClass('disabled')) {
6682 this.el.addClass('disabled');
6688 * Fetch the element to display the tooltip on.
6689 * @return {Roo.Element} defaults to this.el
6691 tooltipEl : function()
6693 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6696 scrollToElement : function(e)
6698 var c = document.body;
6701 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6703 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6704 c = document.documentElement;
6707 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6713 var o = target.calcOffsetsTo(c);
6720 this.fireEvent('scrollto', this, options, e);
6722 Roo.get(c).scrollTo('top', options.value, true);
6727 * Set the HTML (text content) of the item
6728 * @param {string} html content for the nav item
6730 setHtml : function(html)
6733 this.htmlEl.dom.innerHTML = html;
6745 * <span> icon </span>
6746 * <span> text </span>
6747 * <span>badge </span>
6751 * @class Roo.bootstrap.nav.SidebarItem
6752 * @extends Roo.bootstrap.nav.Item
6753 * Bootstrap Navbar.NavSidebarItem class
6755 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6756 * {Boolean} open is the menu open
6757 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6758 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6759 * {String} buttonSize (sm|md|lg)the extra classes for the button
6760 * {Boolean} showArrow show arrow next to the text (default true)
6762 * Create a new Navbar Button
6763 * @param {Object} config The config object
6765 Roo.bootstrap.nav.SidebarItem = function(config){
6766 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6771 * The raw click event for the entire grid.
6772 * @param {Roo.EventObject} e
6777 * Fires when the active item active state changes
6778 * @param {Roo.bootstrap.nav.SidebarItem} this
6779 * @param {boolean} state the new state
6787 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6789 badgeWeight : 'default',
6795 buttonWeight : 'default',
6801 getAutoCreate : function(){
6806 href : this.href || '#',
6812 if(this.buttonView){
6815 href : this.href || '#',
6816 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6829 cfg.cls += ' active';
6832 if (this.disabled) {
6833 cfg.cls += ' disabled';
6836 cfg.cls += ' open x-open';
6839 if (this.glyphicon || this.icon) {
6840 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6841 a.cn.push({ tag : 'i', cls : c }) ;
6844 if(!this.buttonView){
6847 html : this.html || ''
6854 if (this.badge !== '') {
6855 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6861 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6864 a.cls += ' dropdown-toggle treeview' ;
6870 initEvents : function()
6872 if (typeof (this.menu) != 'undefined') {
6873 this.menu.parentType = this.xtype;
6874 this.menu.triggerEl = this.el;
6875 this.menu = this.addxtype(Roo.apply({}, this.menu));
6878 this.el.on('click', this.onClick, this);
6880 if(this.badge !== ''){
6881 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6886 onClick : function(e)
6893 if(this.preventDefault){
6897 this.fireEvent('click', this, e);
6900 disable : function()
6902 this.setDisabled(true);
6907 this.setDisabled(false);
6910 setDisabled : function(state)
6912 if(this.disabled == state){
6916 this.disabled = state;
6919 this.el.addClass('disabled');
6923 this.el.removeClass('disabled');
6928 setActive : function(state)
6930 if(this.active == state){
6934 this.active = state;
6937 this.el.addClass('active');
6941 this.el.removeClass('active');
6946 isActive: function ()
6951 setBadge : function(str)
6957 this.badgeEl.dom.innerHTML = str;
6974 * @class Roo.bootstrap.nav.ProgressBar
6975 * @extends Roo.bootstrap.Component
6976 * @children Roo.bootstrap.nav.ProgressBarItem
6977 * Bootstrap NavProgressBar class
6980 * Create a new nav progress bar - a bar indicating step along a process
6981 * @param {Object} config The config object
6984 Roo.bootstrap.nav.ProgressBar = function(config){
6985 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6987 this.bullets = this.bullets || [];
6989 // Roo.bootstrap.nav.ProgressBar.register(this);
6993 * Fires when the active item changes
6994 * @param {Roo.bootstrap.nav.ProgressBar} this
6995 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6996 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7003 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7005 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7006 * Bullets for the Nav Progress bar for the toolbar
7011 getAutoCreate : function()
7013 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7017 cls : 'roo-navigation-bar-group',
7021 cls : 'roo-navigation-top-bar'
7025 cls : 'roo-navigation-bullets-bar',
7029 cls : 'roo-navigation-bar'
7036 cls : 'roo-navigation-bottom-bar'
7046 initEvents: function()
7051 onRender : function(ct, position)
7053 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7055 if(this.bullets.length){
7056 Roo.each(this.bullets, function(b){
7065 addItem : function(cfg)
7067 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7069 item.parentId = this.id;
7070 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7073 var top = new Roo.bootstrap.Element({
7075 cls : 'roo-navigation-bar-text'
7078 var bottom = new Roo.bootstrap.Element({
7080 cls : 'roo-navigation-bar-text'
7083 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7084 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7086 var topText = new Roo.bootstrap.Element({
7088 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7091 var bottomText = new Roo.bootstrap.Element({
7093 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7096 topText.onRender(top.el, null);
7097 bottomText.onRender(bottom.el, null);
7100 item.bottomEl = bottom;
7103 this.barItems.push(item);
7108 getActive : function()
7112 Roo.each(this.barItems, function(v){
7114 if (!v.isActive()) {
7126 setActiveItem : function(item)
7130 Roo.each(this.barItems, function(v){
7131 if (v.rid == item.rid) {
7141 item.setActive(true);
7143 this.fireEvent('changed', this, item, prev);
7146 getBarItem: function(rid)
7150 Roo.each(this.barItems, function(e) {
7162 indexOfItem : function(item)
7166 Roo.each(this.barItems, function(v, i){
7168 if (v.rid != item.rid) {
7179 setActiveNext : function()
7181 var i = this.indexOfItem(this.getActive());
7183 if (i > this.barItems.length) {
7187 this.setActiveItem(this.barItems[i+1]);
7190 setActivePrev : function()
7192 var i = this.indexOfItem(this.getActive());
7198 this.setActiveItem(this.barItems[i-1]);
7203 if(!this.barItems.length){
7207 var width = 100 / this.barItems.length;
7209 Roo.each(this.barItems, function(i){
7210 i.el.setStyle('width', width + '%');
7211 i.topEl.el.setStyle('width', width + '%');
7212 i.bottomEl.el.setStyle('width', width + '%');
7226 * @class Roo.bootstrap.nav.ProgressBarItem
7227 * @extends Roo.bootstrap.Component
7228 * Bootstrap NavProgressBarItem class
7229 * @cfg {String} rid the reference id
7230 * @cfg {Boolean} active (true|false) Is item active default false
7231 * @cfg {Boolean} disabled (true|false) Is item active default false
7232 * @cfg {String} html
7233 * @cfg {String} position (top|bottom) text position default bottom
7234 * @cfg {String} icon show icon instead of number
7237 * Create a new NavProgressBarItem
7238 * @param {Object} config The config object
7240 Roo.bootstrap.nav.ProgressBarItem = function(config){
7241 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7246 * The raw click event for the entire grid.
7247 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7248 * @param {Roo.EventObject} e
7255 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7261 position : 'bottom',
7264 getAutoCreate : function()
7266 var iconCls = 'roo-navigation-bar-item-icon';
7268 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7272 cls: 'roo-navigation-bar-item',
7282 cfg.cls += ' active';
7285 cfg.cls += ' disabled';
7291 disable : function()
7293 this.setDisabled(true);
7298 this.setDisabled(false);
7301 initEvents: function()
7303 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7305 this.iconEl.on('click', this.onClick, this);
7308 onClick : function(e)
7316 if(this.fireEvent('click', this, e) === false){
7320 this.parent().setActiveItem(this);
7323 isActive: function ()
7328 setActive : function(state)
7330 if(this.active == state){
7334 this.active = state;
7337 this.el.addClass('active');
7341 this.el.removeClass('active');
7346 setDisabled : function(state)
7348 if(this.disabled == state){
7352 this.disabled = state;
7355 this.el.addClass('disabled');
7359 this.el.removeClass('disabled');
7362 tooltipEl : function()
7364 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7375 Roo.namespace('Roo.bootstrap.breadcrumb');
7379 * @class Roo.bootstrap.breadcrumb.Nav
7380 * @extends Roo.bootstrap.Component
7381 * Bootstrap Breadcrumb Nav Class
7383 * @children Roo.bootstrap.breadcrumb.Item
7386 * Create a new breadcrumb.Nav
7387 * @param {Object} config The config object
7391 Roo.bootstrap.breadcrumb.Nav = function(config){
7392 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7397 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7399 getAutoCreate : function()
7416 initEvents: function()
7418 this.olEl = this.el.select('ol',true).first();
7420 getChildContainer : function()
7436 * @class Roo.bootstrap.breadcrumb.Nav
7437 * @extends Roo.bootstrap.Component
7438 * @children Roo.bootstrap.Component
7439 * @parent Roo.bootstrap.breadcrumb.Nav
7440 * Bootstrap Breadcrumb Nav Class
7443 * @cfg {String} html the content of the link.
7444 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7445 * @cfg {Boolean} active is it active
7449 * Create a new breadcrumb.Nav
7450 * @param {Object} config The config object
7453 Roo.bootstrap.breadcrumb.Item = function(config){
7454 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7459 * The img click event for the img.
7460 * @param {Roo.EventObject} e
7467 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7472 getAutoCreate : function()
7477 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7479 if (this.href !== false) {
7486 cfg.html = this.html;
7492 initEvents: function()
7495 this.el.select('a', true).first().on('click',this.onClick, this)
7499 onClick : function(e)
7502 this.fireEvent('click',this, e);
7515 * @class Roo.bootstrap.Row
7516 * @extends Roo.bootstrap.Component
7517 * @children Roo.bootstrap.Component
7518 * Bootstrap Row class (contains columns...)
7522 * @param {Object} config The config object
7525 Roo.bootstrap.Row = function(config){
7526 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7529 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7531 getAutoCreate : function(){
7550 * @class Roo.bootstrap.Pagination
7551 * @extends Roo.bootstrap.Component
7552 * @children Roo.bootstrap.Pagination
7553 * Bootstrap Pagination class
7555 * @cfg {String} size (xs|sm|md|lg|xl)
7556 * @cfg {Boolean} inverse
7559 * Create a new Pagination
7560 * @param {Object} config The config object
7563 Roo.bootstrap.Pagination = function(config){
7564 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7567 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7573 getAutoCreate : function(){
7579 cfg.cls += ' inverse';
7585 cfg.cls += " " + this.cls;
7603 * @class Roo.bootstrap.PaginationItem
7604 * @extends Roo.bootstrap.Component
7605 * Bootstrap PaginationItem class
7606 * @cfg {String} html text
7607 * @cfg {String} href the link
7608 * @cfg {Boolean} preventDefault (true | false) default true
7609 * @cfg {Boolean} active (true | false) default false
7610 * @cfg {Boolean} disabled default false
7614 * Create a new PaginationItem
7615 * @param {Object} config The config object
7619 Roo.bootstrap.PaginationItem = function(config){
7620 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7625 * The raw click event for the entire grid.
7626 * @param {Roo.EventObject} e
7632 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7636 preventDefault: true,
7641 getAutoCreate : function(){
7647 href : this.href ? this.href : '#',
7648 html : this.html ? this.html : ''
7658 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7662 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7668 initEvents: function() {
7670 this.el.on('click', this.onClick, this);
7673 onClick : function(e)
7675 Roo.log('PaginationItem on click ');
7676 if(this.preventDefault){
7684 this.fireEvent('click', this, e);
7700 * @class Roo.bootstrap.Slider
7701 * @extends Roo.bootstrap.Component
7702 * Bootstrap Slider class
7705 * Create a new Slider
7706 * @param {Object} config The config object
7709 Roo.bootstrap.Slider = function(config){
7710 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7713 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7715 getAutoCreate : function(){
7719 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7723 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7735 * Ext JS Library 1.1.1
7736 * Copyright(c) 2006-2007, Ext JS, LLC.
7738 * Originally Released Under LGPL - original licence link has changed is not relivant.
7741 * <script type="text/javascript">
7744 * @extends Roo.dd.DDProxy
7745 * @class Roo.grid.SplitDragZone
7746 * Support for Column Header resizing
7748 * @param {Object} config
7751 // This is a support class used internally by the Grid components
7752 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7754 this.view = grid.getView();
7755 this.proxy = this.view.resizeProxy;
7756 Roo.grid.SplitDragZone.superclass.constructor.call(
7759 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7761 dragElId : Roo.id(this.proxy.dom),
7766 this.setHandleElId(Roo.id(hd));
7767 if (hd2 !== false) {
7768 this.setOuterHandleElId(Roo.id(hd2));
7771 this.scroll = false;
7773 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7774 fly: Roo.Element.fly,
7776 b4StartDrag : function(x, y){
7777 this.view.headersDisabled = true;
7778 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7779 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7781 this.proxy.setHeight(h);
7783 // for old system colWidth really stored the actual width?
7784 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7785 // which in reality did not work.. - it worked only for fixed sizes
7786 // for resizable we need to use actual sizes.
7787 var w = this.cm.getColumnWidth(this.cellIndex);
7788 if (!this.view.mainWrap) {
7790 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7795 // this was w-this.grid.minColumnWidth;
7796 // doesnt really make sense? - w = thie curren width or the rendered one?
7797 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7798 this.resetConstraints();
7799 this.setXConstraint(minw, 1000);
7800 this.setYConstraint(0, 0);
7801 this.minX = x - minw;
7802 this.maxX = x + 1000;
7804 if (!this.view.mainWrap) { // this is Bootstrap code..
7805 this.getDragEl().style.display='block';
7808 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7812 handleMouseDown : function(e){
7813 ev = Roo.EventObject.setEvent(e);
7814 var t = this.fly(ev.getTarget());
7815 if(t.hasClass("x-grid-split")){
7816 this.cellIndex = this.view.getCellIndex(t.dom);
7818 this.cm = this.grid.colModel;
7819 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7820 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7825 endDrag : function(e){
7826 this.view.headersDisabled = false;
7827 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7828 var diff = endX - this.startPos;
7830 var w = this.cm.getColumnWidth(this.cellIndex);
7831 if (!this.view.mainWrap) {
7834 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7837 autoOffset : function(){
7842 * Ext JS Library 1.1.1
7843 * Copyright(c) 2006-2007, Ext JS, LLC.
7845 * Originally Released Under LGPL - original licence link has changed is not relivant.
7848 * <script type="text/javascript">
7852 * @class Roo.grid.AbstractSelectionModel
7853 * @extends Roo.util.Observable
7855 * Abstract base class for grid SelectionModels. It provides the interface that should be
7856 * implemented by descendant classes. This class should not be directly instantiated.
7859 Roo.grid.AbstractSelectionModel = function(){
7860 this.locked = false;
7861 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7864 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7865 /** @ignore Called by the grid automatically. Do not call directly. */
7866 init : function(grid){
7872 * Locks the selections.
7879 * Unlocks the selections.
7881 unlock : function(){
7882 this.locked = false;
7886 * Returns true if the selections are locked.
7889 isLocked : function(){
7894 * Ext JS Library 1.1.1
7895 * Copyright(c) 2006-2007, Ext JS, LLC.
7897 * Originally Released Under LGPL - original licence link has changed is not relivant.
7900 * <script type="text/javascript">
7903 * @extends Roo.grid.AbstractSelectionModel
7904 * @class Roo.grid.RowSelectionModel
7905 * The default SelectionModel used by {@link Roo.grid.Grid}.
7906 * It supports multiple selections and keyboard selection/navigation.
7908 * @param {Object} config
7910 Roo.grid.RowSelectionModel = function(config){
7911 Roo.apply(this, config);
7912 this.selections = new Roo.util.MixedCollection(false, function(o){
7917 this.lastActive = false;
7921 * @event selectionchange
7922 * Fires when the selection changes
7923 * @param {SelectionModel} this
7925 "selectionchange" : true,
7927 * @event afterselectionchange
7928 * Fires after the selection changes (eg. by key press or clicking)
7929 * @param {SelectionModel} this
7931 "afterselectionchange" : true,
7933 * @event beforerowselect
7934 * Fires when a row is selected being selected, return false to cancel.
7935 * @param {SelectionModel} this
7936 * @param {Number} rowIndex The selected index
7937 * @param {Boolean} keepExisting False if other selections will be cleared
7939 "beforerowselect" : true,
7942 * Fires when a row is selected.
7943 * @param {SelectionModel} this
7944 * @param {Number} rowIndex The selected index
7945 * @param {Roo.data.Record} r The record
7949 * @event rowdeselect
7950 * Fires when a row is deselected.
7951 * @param {SelectionModel} this
7952 * @param {Number} rowIndex The selected index
7954 "rowdeselect" : true
7956 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7957 this.locked = false;
7960 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7962 * @cfg {Boolean} singleSelect
7963 * True to allow selection of only one row at a time (defaults to false)
7965 singleSelect : false,
7968 initEvents : function(){
7970 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7971 this.grid.on("mousedown", this.handleMouseDown, this);
7972 }else{ // allow click to work like normal
7973 this.grid.on("rowclick", this.handleDragableRowClick, this);
7975 // bootstrap does not have a view..
7976 var view = this.grid.view ? this.grid.view : this.grid;
7977 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7980 this.selectPrevious(e.shiftKey);
7981 }else if(this.last !== false && this.lastActive !== false){
7982 var last = this.last;
7983 this.selectRange(this.last, this.lastActive-1);
7984 view.focusRow(this.lastActive);
7989 this.selectFirstRow();
7991 this.fireEvent("afterselectionchange", this);
7993 "down" : function(e){
7995 this.selectNext(e.shiftKey);
7996 }else if(this.last !== false && this.lastActive !== false){
7997 var last = this.last;
7998 this.selectRange(this.last, this.lastActive+1);
7999 view.focusRow(this.lastActive);
8004 this.selectFirstRow();
8006 this.fireEvent("afterselectionchange", this);
8012 view.on("refresh", this.onRefresh, this);
8013 view.on("rowupdated", this.onRowUpdated, this);
8014 view.on("rowremoved", this.onRemove, this);
8018 onRefresh : function(){
8019 var ds = this.grid.ds, i, v = this.grid.view;
8020 var s = this.selections;
8022 if((i = ds.indexOfId(r.id)) != -1){
8024 s.add(ds.getAt(i)); // updating the selection relate data
8032 onRemove : function(v, index, r){
8033 this.selections.remove(r);
8037 onRowUpdated : function(v, index, r){
8038 if(this.isSelected(r)){
8039 v.onRowSelect(index);
8045 * @param {Array} records The records to select
8046 * @param {Boolean} keepExisting (optional) True to keep existing selections
8048 selectRecords : function(records, keepExisting){
8050 this.clearSelections();
8052 var ds = this.grid.ds;
8053 for(var i = 0, len = records.length; i < len; i++){
8054 this.selectRow(ds.indexOf(records[i]), true);
8059 * Gets the number of selected rows.
8062 getCount : function(){
8063 return this.selections.length;
8067 * Selects the first row in the grid.
8069 selectFirstRow : function(){
8074 * Select the last row.
8075 * @param {Boolean} keepExisting (optional) True to keep existing selections
8077 selectLastRow : function(keepExisting){
8078 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8082 * Selects the row immediately following the last selected row.
8083 * @param {Boolean} keepExisting (optional) True to keep existing selections
8085 selectNext : function(keepExisting){
8086 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8087 this.selectRow(this.last+1, keepExisting);
8088 var view = this.grid.view ? this.grid.view : this.grid;
8089 view.focusRow(this.last);
8094 * Selects the row that precedes the last selected row.
8095 * @param {Boolean} keepExisting (optional) True to keep existing selections
8097 selectPrevious : function(keepExisting){
8099 this.selectRow(this.last-1, keepExisting);
8100 var view = this.grid.view ? this.grid.view : this.grid;
8101 view.focusRow(this.last);
8106 * Returns the selected records
8107 * @return {Array} Array of selected records
8109 getSelections : function(){
8110 return [].concat(this.selections.items);
8114 * Returns the first selected record.
8117 getSelected : function(){
8118 return this.selections.itemAt(0);
8123 * Clears all selections.
8125 clearSelections : function(fast){
8130 var ds = this.grid.ds;
8131 var s = this.selections;
8133 this.deselectRow(ds.indexOfId(r.id));
8137 this.selections.clear();
8146 selectAll : function(){
8150 this.selections.clear();
8151 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8152 this.selectRow(i, true);
8157 * Returns True if there is a selection.
8160 hasSelection : function(){
8161 return this.selections.length > 0;
8165 * Returns True if the specified row is selected.
8166 * @param {Number/Record} record The record or index of the record to check
8169 isSelected : function(index){
8170 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8171 return (r && this.selections.key(r.id) ? true : false);
8175 * Returns True if the specified record id is selected.
8176 * @param {String} id The id of record to check
8179 isIdSelected : function(id){
8180 return (this.selections.key(id) ? true : false);
8184 handleMouseDown : function(e, t)
8186 var view = this.grid.view ? this.grid.view : this.grid;
8188 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8191 if(e.shiftKey && this.last !== false){
8192 var last = this.last;
8193 this.selectRange(last, rowIndex, e.ctrlKey);
8194 this.last = last; // reset the last
8195 view.focusRow(rowIndex);
8197 var isSelected = this.isSelected(rowIndex);
8198 if(e.button !== 0 && isSelected){
8199 view.focusRow(rowIndex);
8200 }else if(e.ctrlKey && isSelected){
8201 this.deselectRow(rowIndex);
8202 }else if(!isSelected){
8203 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8204 view.focusRow(rowIndex);
8207 this.fireEvent("afterselectionchange", this);
8210 handleDragableRowClick : function(grid, rowIndex, e)
8212 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8213 this.selectRow(rowIndex, false);
8214 var view = this.grid.view ? this.grid.view : this.grid;
8215 view.focusRow(rowIndex);
8216 this.fireEvent("afterselectionchange", this);
8221 * Selects multiple rows.
8222 * @param {Array} rows Array of the indexes of the row to select
8223 * @param {Boolean} keepExisting (optional) True to keep existing selections
8225 selectRows : function(rows, keepExisting){
8227 this.clearSelections();
8229 for(var i = 0, len = rows.length; i < len; i++){
8230 this.selectRow(rows[i], true);
8235 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8236 * @param {Number} startRow The index of the first row in the range
8237 * @param {Number} endRow The index of the last row in the range
8238 * @param {Boolean} keepExisting (optional) True to retain existing selections
8240 selectRange : function(startRow, endRow, keepExisting){
8245 this.clearSelections();
8247 if(startRow <= endRow){
8248 for(var i = startRow; i <= endRow; i++){
8249 this.selectRow(i, true);
8252 for(var i = startRow; i >= endRow; i--){
8253 this.selectRow(i, true);
8259 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8260 * @param {Number} startRow The index of the first row in the range
8261 * @param {Number} endRow The index of the last row in the range
8263 deselectRange : function(startRow, endRow, preventViewNotify){
8267 for(var i = startRow; i <= endRow; i++){
8268 this.deselectRow(i, preventViewNotify);
8274 * @param {Number} row The index of the row to select
8275 * @param {Boolean} keepExisting (optional) True to keep existing selections
8277 selectRow : function(index, keepExisting, preventViewNotify){
8278 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8281 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8282 if(!keepExisting || this.singleSelect){
8283 this.clearSelections();
8285 var r = this.grid.ds.getAt(index);
8286 this.selections.add(r);
8287 this.last = this.lastActive = index;
8288 if(!preventViewNotify){
8289 var view = this.grid.view ? this.grid.view : this.grid;
8290 view.onRowSelect(index);
8292 this.fireEvent("rowselect", this, index, r);
8293 this.fireEvent("selectionchange", this);
8299 * @param {Number} row The index of the row to deselect
8301 deselectRow : function(index, preventViewNotify){
8305 if(this.last == index){
8308 if(this.lastActive == index){
8309 this.lastActive = false;
8311 var r = this.grid.ds.getAt(index);
8312 this.selections.remove(r);
8313 if(!preventViewNotify){
8314 var view = this.grid.view ? this.grid.view : this.grid;
8315 view.onRowDeselect(index);
8317 this.fireEvent("rowdeselect", this, index);
8318 this.fireEvent("selectionchange", this);
8322 restoreLast : function(){
8324 this.last = this._last;
8329 acceptsNav : function(row, col, cm){
8330 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8334 onEditorKey : function(field, e){
8335 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8340 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8342 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8344 }else if(k == e.ENTER && !e.ctrlKey){
8348 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8350 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8352 }else if(k == e.ESC){
8356 g.startEditing(newCell[0], newCell[1]);
8361 * Ext JS Library 1.1.1
8362 * Copyright(c) 2006-2007, Ext JS, LLC.
8364 * Originally Released Under LGPL - original licence link has changed is not relivant.
8367 * <script type="text/javascript">
8372 * @class Roo.grid.ColumnModel
8373 * @extends Roo.util.Observable
8374 * This is the default implementation of a ColumnModel used by the Grid. It defines
8375 * the columns in the grid.
8378 var colModel = new Roo.grid.ColumnModel([
8379 {header: "Ticker", width: 60, sortable: true, locked: true},
8380 {header: "Company Name", width: 150, sortable: true},
8381 {header: "Market Cap.", width: 100, sortable: true},
8382 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8383 {header: "Employees", width: 100, sortable: true, resizable: false}
8388 * The config options listed for this class are options which may appear in each
8389 * individual column definition.
8390 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8392 * @param {Object} config An Array of column config objects. See this class's
8393 * config objects for details.
8395 Roo.grid.ColumnModel = function(config){
8397 * The config passed into the constructor
8399 this.config = []; //config;
8402 // if no id, create one
8403 // if the column does not have a dataIndex mapping,
8404 // map it to the order it is in the config
8405 for(var i = 0, len = config.length; i < len; i++){
8406 this.addColumn(config[i]);
8411 * The width of columns which have no width specified (defaults to 100)
8414 this.defaultWidth = 100;
8417 * Default sortable of columns which have no sortable specified (defaults to false)
8420 this.defaultSortable = false;
8424 * @event widthchange
8425 * Fires when the width of a column changes.
8426 * @param {ColumnModel} this
8427 * @param {Number} columnIndex The column index
8428 * @param {Number} newWidth The new width
8430 "widthchange": true,
8432 * @event headerchange
8433 * Fires when the text of a header changes.
8434 * @param {ColumnModel} this
8435 * @param {Number} columnIndex The column index
8436 * @param {Number} newText The new header text
8438 "headerchange": true,
8440 * @event hiddenchange
8441 * Fires when a column is hidden or "unhidden".
8442 * @param {ColumnModel} this
8443 * @param {Number} columnIndex The column index
8444 * @param {Boolean} hidden true if hidden, false otherwise
8446 "hiddenchange": true,
8448 * @event columnmoved
8449 * Fires when a column is moved.
8450 * @param {ColumnModel} this
8451 * @param {Number} oldIndex
8452 * @param {Number} newIndex
8454 "columnmoved" : true,
8456 * @event columlockchange
8457 * Fires when a column's locked state is changed
8458 * @param {ColumnModel} this
8459 * @param {Number} colIndex
8460 * @param {Boolean} locked true if locked
8462 "columnlockchange" : true
8464 Roo.grid.ColumnModel.superclass.constructor.call(this);
8466 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8468 * @cfg {String} header The header text to display in the Grid view.
8471 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8474 * @cfg {String} smHeader Header at Bootsrap Small width
8477 * @cfg {String} mdHeader Header at Bootsrap Medium width
8480 * @cfg {String} lgHeader Header at Bootsrap Large width
8483 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8486 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8487 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8488 * specified, the column's index is used as an index into the Record's data Array.
8491 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8492 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8495 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8496 * Defaults to the value of the {@link #defaultSortable} property.
8497 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8500 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
8503 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
8506 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8509 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8512 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8513 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8514 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8515 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8518 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
8521 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
8524 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
8527 * @cfg {String} cursor (Optional)
8530 * @cfg {String} tooltip (Optional)
8533 * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8536 * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8539 * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8542 * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8545 * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8548 * Returns the id of the column at the specified index.
8549 * @param {Number} index The column index
8550 * @return {String} the id
8552 getColumnId : function(index){
8553 return this.config[index].id;
8557 * Returns the column for a specified id.
8558 * @param {String} id The column id
8559 * @return {Object} the column
8561 getColumnById : function(id){
8562 return this.lookup[id];
8567 * Returns the column Object for a specified dataIndex.
8568 * @param {String} dataIndex The column dataIndex
8569 * @return {Object|Boolean} the column or false if not found
8571 getColumnByDataIndex: function(dataIndex){
8572 var index = this.findColumnIndex(dataIndex);
8573 return index > -1 ? this.config[index] : false;
8577 * Returns the index for a specified column id.
8578 * @param {String} id The column id
8579 * @return {Number} the index, or -1 if not found
8581 getIndexById : function(id){
8582 for(var i = 0, len = this.config.length; i < len; i++){
8583 if(this.config[i].id == id){
8591 * Returns the index for a specified column dataIndex.
8592 * @param {String} dataIndex The column dataIndex
8593 * @return {Number} the index, or -1 if not found
8596 findColumnIndex : function(dataIndex){
8597 for(var i = 0, len = this.config.length; i < len; i++){
8598 if(this.config[i].dataIndex == dataIndex){
8606 moveColumn : function(oldIndex, newIndex){
8607 var c = this.config[oldIndex];
8608 this.config.splice(oldIndex, 1);
8609 this.config.splice(newIndex, 0, c);
8610 this.dataMap = null;
8611 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8614 isLocked : function(colIndex){
8615 return this.config[colIndex].locked === true;
8618 setLocked : function(colIndex, value, suppressEvent){
8619 if(this.isLocked(colIndex) == value){
8622 this.config[colIndex].locked = value;
8624 this.fireEvent("columnlockchange", this, colIndex, value);
8628 getTotalLockedWidth : function(){
8630 for(var i = 0; i < this.config.length; i++){
8631 if(this.isLocked(i) && !this.isHidden(i)){
8632 this.totalWidth += this.getColumnWidth(i);
8638 getLockedCount : function(){
8639 for(var i = 0, len = this.config.length; i < len; i++){
8640 if(!this.isLocked(i)){
8645 return this.config.length;
8649 * Returns the number of columns.
8652 getColumnCount : function(visibleOnly){
8653 if(visibleOnly === true){
8655 for(var i = 0, len = this.config.length; i < len; i++){
8656 if(!this.isHidden(i)){
8662 return this.config.length;
8666 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8667 * @param {Function} fn
8668 * @param {Object} scope (optional)
8669 * @return {Array} result
8671 getColumnsBy : function(fn, scope){
8673 for(var i = 0, len = this.config.length; i < len; i++){
8674 var c = this.config[i];
8675 if(fn.call(scope||this, c, i) === true){
8683 * Returns true if the specified column is sortable.
8684 * @param {Number} col The column index
8687 isSortable : function(col){
8688 if(typeof this.config[col].sortable == "undefined"){
8689 return this.defaultSortable;
8691 return this.config[col].sortable;
8695 * Returns the rendering (formatting) function defined for the column.
8696 * @param {Number} col The column index.
8697 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8699 getRenderer : function(col){
8700 if(!this.config[col].renderer){
8701 return Roo.grid.ColumnModel.defaultRenderer;
8703 return this.config[col].renderer;
8707 * Sets the rendering (formatting) function for a column.
8708 * @param {Number} col The column index
8709 * @param {Function} fn The function to use to process the cell's raw data
8710 * to return HTML markup for the grid view. The render function is called with
8711 * the following parameters:<ul>
8712 * <li>Data value.</li>
8713 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8714 * <li>css A CSS style string to apply to the table cell.</li>
8715 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8716 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8717 * <li>Row index</li>
8718 * <li>Column index</li>
8719 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8721 setRenderer : function(col, fn){
8722 this.config[col].renderer = fn;
8726 * Returns the width for the specified column.
8727 * @param {Number} col The column index
8728 * @param (optional) {String} gridSize bootstrap width size.
8731 getColumnWidth : function(col, gridSize)
8733 var cfg = this.config[col];
8735 if (typeof(gridSize) == 'undefined') {
8736 return cfg.width * 1 || this.defaultWidth;
8738 if (gridSize === false) { // if we set it..
8739 return cfg.width || false;
8741 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8743 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8744 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8747 return cfg[ sizes[i] ];
8754 * Sets the width for a column.
8755 * @param {Number} col The column index
8756 * @param {Number} width The new width
8758 setColumnWidth : function(col, width, suppressEvent){
8759 this.config[col].width = width;
8760 this.totalWidth = null;
8762 this.fireEvent("widthchange", this, col, width);
8767 * Returns the total width of all columns.
8768 * @param {Boolean} includeHidden True to include hidden column widths
8771 getTotalWidth : function(includeHidden){
8772 if(!this.totalWidth){
8773 this.totalWidth = 0;
8774 for(var i = 0, len = this.config.length; i < len; i++){
8775 if(includeHidden || !this.isHidden(i)){
8776 this.totalWidth += this.getColumnWidth(i);
8780 return this.totalWidth;
8784 * Returns the header for the specified column.
8785 * @param {Number} col The column index
8788 getColumnHeader : function(col){
8789 return this.config[col].header;
8793 * Sets the header for a column.
8794 * @param {Number} col The column index
8795 * @param {String} header The new header
8797 setColumnHeader : function(col, header){
8798 this.config[col].header = header;
8799 this.fireEvent("headerchange", this, col, header);
8803 * Returns the tooltip for the specified column.
8804 * @param {Number} col The column index
8807 getColumnTooltip : function(col){
8808 return this.config[col].tooltip;
8811 * Sets the tooltip for a column.
8812 * @param {Number} col The column index
8813 * @param {String} tooltip The new tooltip
8815 setColumnTooltip : function(col, tooltip){
8816 this.config[col].tooltip = tooltip;
8820 * Returns the dataIndex for the specified column.
8821 * @param {Number} col The column index
8824 getDataIndex : function(col){
8825 return this.config[col].dataIndex;
8829 * Sets the dataIndex for a column.
8830 * @param {Number} col The column index
8831 * @param {Number} dataIndex The new dataIndex
8833 setDataIndex : function(col, dataIndex){
8834 this.config[col].dataIndex = dataIndex;
8840 * Returns true if the cell is editable.
8841 * @param {Number} colIndex The column index
8842 * @param {Number} rowIndex The row index - this is nto actually used..?
8845 isCellEditable : function(colIndex, rowIndex){
8846 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8850 * Returns the editor defined for the cell/column.
8851 * return false or null to disable editing.
8852 * @param {Number} colIndex The column index
8853 * @param {Number} rowIndex The row index
8856 getCellEditor : function(colIndex, rowIndex){
8857 return this.config[colIndex].editor;
8861 * Sets if a column is editable.
8862 * @param {Number} col The column index
8863 * @param {Boolean} editable True if the column is editable
8865 setEditable : function(col, editable){
8866 this.config[col].editable = editable;
8871 * Returns true if the column is hidden.
8872 * @param {Number} colIndex The column index
8875 isHidden : function(colIndex){
8876 return this.config[colIndex].hidden;
8881 * Returns true if the column width cannot be changed
8883 isFixed : function(colIndex){
8884 return this.config[colIndex].fixed;
8888 * Returns true if the column can be resized
8891 isResizable : function(colIndex){
8892 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8895 * Sets if a column is hidden.
8896 * @param {Number} colIndex The column index
8897 * @param {Boolean} hidden True if the column is hidden
8899 setHidden : function(colIndex, hidden){
8900 this.config[colIndex].hidden = hidden;
8901 this.totalWidth = null;
8902 this.fireEvent("hiddenchange", this, colIndex, hidden);
8906 * Sets the editor for a column.
8907 * @param {Number} col The column index
8908 * @param {Object} editor The editor object
8910 setEditor : function(col, editor){
8911 this.config[col].editor = editor;
8914 * Add a column (experimental...) - defaults to adding to the end..
8915 * @param {Object} config
8917 addColumn : function(c)
8920 var i = this.config.length;
8923 if(typeof c.dataIndex == "undefined"){
8926 if(typeof c.renderer == "string"){
8927 c.renderer = Roo.util.Format[c.renderer];
8929 if(typeof c.id == "undefined"){
8932 if(c.editor && c.editor.xtype){
8933 c.editor = Roo.factory(c.editor, Roo.grid);
8935 if(c.editor && c.editor.isFormField){
8936 c.editor = new Roo.grid.GridEditor(c.editor);
8938 this.lookup[c.id] = c;
8943 Roo.grid.ColumnModel.defaultRenderer = function(value)
8945 if(typeof value == "object") {
8948 if(typeof value == "string" && value.length < 1){
8952 return String.format("{0}", value);
8955 // Alias for backwards compatibility
8956 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8959 * Ext JS Library 1.1.1
8960 * Copyright(c) 2006-2007, Ext JS, LLC.
8962 * Originally Released Under LGPL - original licence link has changed is not relivant.
8965 * <script type="text/javascript">
8969 * @class Roo.LoadMask
8970 * A simple utility class for generically masking elements while loading data. If the element being masked has
8971 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8972 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8973 * element's UpdateManager load indicator and will be destroyed after the initial load.
8975 * Create a new LoadMask
8976 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8977 * @param {Object} config The config object
8979 Roo.LoadMask = function(el, config){
8980 this.el = Roo.get(el);
8981 Roo.apply(this, config);
8983 this.store.on('beforeload', this.onBeforeLoad, this);
8984 this.store.on('load', this.onLoad, this);
8985 this.store.on('loadexception', this.onLoadException, this);
8986 this.removeMask = false;
8988 var um = this.el.getUpdateManager();
8989 um.showLoadIndicator = false; // disable the default indicator
8990 um.on('beforeupdate', this.onBeforeLoad, this);
8991 um.on('update', this.onLoad, this);
8992 um.on('failure', this.onLoad, this);
8993 this.removeMask = true;
8997 Roo.LoadMask.prototype = {
8999 * @cfg {Boolean} removeMask
9000 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9001 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9006 * The text to display in a centered loading message box (defaults to 'Loading...')
9010 * @cfg {String} msgCls
9011 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9013 msgCls : 'x-mask-loading',
9016 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9022 * Disables the mask to prevent it from being displayed
9024 disable : function(){
9025 this.disabled = true;
9029 * Enables the mask so that it can be displayed
9031 enable : function(){
9032 this.disabled = false;
9035 onLoadException : function()
9039 if (typeof(arguments[3]) != 'undefined') {
9040 Roo.MessageBox.alert("Error loading",arguments[3]);
9044 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9045 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9052 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9057 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9061 onBeforeLoad : function(){
9063 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9068 destroy : function(){
9070 this.store.un('beforeload', this.onBeforeLoad, this);
9071 this.store.un('load', this.onLoad, this);
9072 this.store.un('loadexception', this.onLoadException, this);
9074 var um = this.el.getUpdateManager();
9075 um.un('beforeupdate', this.onBeforeLoad, this);
9076 um.un('update', this.onLoad, this);
9077 um.un('failure', this.onLoad, this);
9081 * @class Roo.bootstrap.Table
9083 * @extends Roo.bootstrap.Component
9084 * @children Roo.bootstrap.TableBody
9085 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9086 * Similar to Roo.grid.Grid
9088 var table = Roo.factory({
9090 xns : Roo.bootstrap,
9091 autoSizeColumns: true,
9098 sortInfo : { direction : 'ASC', field: 'name' },
9100 xtype : 'HttpProxy',
9103 url : 'https://example.com/some.data.url.json'
9106 xtype : 'JsonReader',
9108 fields : [ 'id', 'name', whatever' ],
9115 xtype : 'ColumnModel',
9119 dataIndex : 'is_in_group',
9122 renderer : function(v, x , r) {
9124 return String.format("{0}", v)
9130 xtype : 'RowSelectionModel',
9131 xns : Roo.bootstrap.Table
9132 // you can add listeners to catch selection change here....
9138 grid.render(Roo.get("some-div"));
9141 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9146 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9147 * @cfg {Roo.data.Store} store The data store to use
9148 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9150 * @cfg {String} cls table class
9153 * @cfg {string} empty_results Text to display for no results
9154 * @cfg {boolean} striped Should the rows be alternative striped
9155 * @cfg {boolean} bordered Add borders to the table
9156 * @cfg {boolean} hover Add hover highlighting
9157 * @cfg {boolean} condensed Format condensed
9158 * @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,
9159 * also adds table-responsive (see bootstrap docs for details)
9160 * @cfg {Boolean} loadMask (true|false) default false
9161 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9162 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9163 * @cfg {Boolean} rowSelection (true|false) default false
9164 * @cfg {Boolean} cellSelection (true|false) default false
9165 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9166 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9167 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9168 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9169 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9172 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9175 * Create a new Table
9176 * @param {Object} config The config object
9179 Roo.bootstrap.Table = function(config)
9181 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9184 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9189 this.view = this; // compat with grid.
9191 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9193 this.sm.grid = this;
9194 this.selModel = Roo.factory(this.sm, Roo.grid);
9195 this.sm = this.selModel;
9196 this.sm.xmodule = this.xmodule || false;
9199 if (this.cm && typeof(this.cm.config) == 'undefined') {
9200 this.colModel = new Roo.grid.ColumnModel(this.cm);
9201 this.cm = this.colModel;
9202 this.cm.xmodule = this.xmodule || false;
9205 this.store= Roo.factory(this.store, Roo.data);
9206 this.ds = this.store;
9207 this.ds.xmodule = this.xmodule || false;
9210 if (this.footer && this.store) {
9211 this.footer.dataSource = this.ds;
9212 this.footer = Roo.factory(this.footer);
9219 * Fires when a cell is clicked
9220 * @param {Roo.bootstrap.Table} this
9221 * @param {Roo.Element} el
9222 * @param {Number} rowIndex
9223 * @param {Number} columnIndex
9224 * @param {Roo.EventObject} e
9228 * @event celldblclick
9229 * Fires when a cell is double clicked
9230 * @param {Roo.bootstrap.Table} this
9231 * @param {Roo.Element} el
9232 * @param {Number} rowIndex
9233 * @param {Number} columnIndex
9234 * @param {Roo.EventObject} e
9236 "celldblclick" : true,
9239 * Fires when a row is clicked
9240 * @param {Roo.bootstrap.Table} this
9241 * @param {Roo.Element} el
9242 * @param {Number} rowIndex
9243 * @param {Roo.EventObject} e
9247 * @event rowdblclick
9248 * Fires when a row is double clicked
9249 * @param {Roo.bootstrap.Table} this
9250 * @param {Roo.Element} el
9251 * @param {Number} rowIndex
9252 * @param {Roo.EventObject} e
9254 "rowdblclick" : true,
9257 * Fires when a mouseover occur
9258 * @param {Roo.bootstrap.Table} this
9259 * @param {Roo.Element} el
9260 * @param {Number} rowIndex
9261 * @param {Number} columnIndex
9262 * @param {Roo.EventObject} e
9267 * Fires when a mouseout occur
9268 * @param {Roo.bootstrap.Table} this
9269 * @param {Roo.Element} el
9270 * @param {Number} rowIndex
9271 * @param {Number} columnIndex
9272 * @param {Roo.EventObject} e
9277 * Fires when a row is rendered, so you can change add a style to it.
9278 * @param {Roo.bootstrap.Table} this
9279 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9283 * @event rowsrendered
9284 * Fires when all the rows have been rendered
9285 * @param {Roo.bootstrap.Table} this
9287 'rowsrendered' : true,
9289 * @event contextmenu
9290 * The raw contextmenu event for the entire grid.
9291 * @param {Roo.EventObject} e
9293 "contextmenu" : true,
9295 * @event rowcontextmenu
9296 * Fires when a row is right clicked
9297 * @param {Roo.bootstrap.Table} this
9298 * @param {Number} rowIndex
9299 * @param {Roo.EventObject} e
9301 "rowcontextmenu" : true,
9303 * @event cellcontextmenu
9304 * Fires when a cell is right clicked
9305 * @param {Roo.bootstrap.Table} this
9306 * @param {Number} rowIndex
9307 * @param {Number} cellIndex
9308 * @param {Roo.EventObject} e
9310 "cellcontextmenu" : true,
9312 * @event headercontextmenu
9313 * Fires when a header is right clicked
9314 * @param {Roo.bootstrap.Table} this
9315 * @param {Number} columnIndex
9316 * @param {Roo.EventObject} e
9318 "headercontextmenu" : true,
9321 * The raw mousedown event for the entire grid.
9322 * @param {Roo.EventObject} e
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9346 enableColumnResize: true,
9348 rowSelection : false,
9349 cellSelection : false,
9352 minColumnWidth : 50,
9354 // Roo.Element - the tbody
9355 bodyEl: false, // <tbody> Roo.Element - thead element
9356 headEl: false, // <thead> Roo.Element - thead element
9357 resizeProxy : false, // proxy element for dragging?
9361 container: false, // used by gridpanel...
9367 auto_hide_footer : false,
9369 view: false, // actually points to this..
9371 getAutoCreate : function()
9373 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9380 // this get's auto added by panel.Grid
9381 if (this.scrollBody) {
9382 cfg.cls += ' table-body-fixed';
9385 cfg.cls += ' table-striped';
9389 cfg.cls += ' table-hover';
9391 if (this.bordered) {
9392 cfg.cls += ' table-bordered';
9394 if (this.condensed) {
9395 cfg.cls += ' table-condensed';
9398 if (this.responsive) {
9399 cfg.cls += ' table-responsive';
9403 cfg.cls+= ' ' +this.cls;
9409 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9412 if(this.store || this.cm){
9413 if(this.headerShow){
9414 cfg.cn.push(this.renderHeader());
9417 cfg.cn.push(this.renderBody());
9419 if(this.footerShow){
9420 cfg.cn.push(this.renderFooter());
9422 // where does this come from?
9423 //cfg.cls+= ' TableGrid';
9426 return { cn : [ cfg ] };
9429 initEvents : function()
9431 if(!this.store || !this.cm){
9434 if (this.selModel) {
9435 this.selModel.initEvents();
9439 //Roo.log('initEvents with ds!!!!');
9441 this.bodyEl = this.el.select('tbody', true).first();
9442 this.headEl = this.el.select('thead', true).first();
9443 this.mainFoot = this.el.select('tfoot', true).first();
9448 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449 e.on('click', this.sort, this);
9453 // why is this done????? = it breaks dialogs??
9454 //this.parent().el.setStyle('position', 'relative');
9458 this.footer.parentId = this.id;
9459 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9462 this.el.select('tfoot tr td').first().addClass('hide');
9467 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9470 this.store.on('load', this.onLoad, this);
9471 this.store.on('beforeload', this.onBeforeLoad, this);
9472 this.store.on('update', this.onUpdate, this);
9473 this.store.on('add', this.onAdd, this);
9474 this.store.on("clear", this.clear, this);
9476 this.el.on("contextmenu", this.onContextMenu, this);
9479 this.cm.on("headerchange", this.onHeaderChange, this);
9480 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9482 //?? does bodyEl get replaced on render?
9483 this.bodyEl.on("click", this.onClick, this);
9484 this.bodyEl.on("dblclick", this.onDblClick, this);
9485 this.bodyEl.on('scroll', this.onBodyScroll, this);
9487 // guessing mainbody will work - this relays usually caught by selmodel at present.
9488 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9491 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9494 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9500 // Compatibility with grid - we implement all the view features at present.
9501 getView : function()
9506 initCSS : function()
9510 var cm = this.cm, styles = [];
9511 this.CSS.removeStyleSheet(this.id + '-cssrules');
9512 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9513 // we can honour xs/sm/md/xl as widths...
9514 // we first have to decide what widht we are currently at...
9515 var sz = Roo.getGridSize();
9519 var cols = []; // visable cols.
9521 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9522 var w = cm.getColumnWidth(i, false);
9524 cols.push( { rel : false, abs : 0 });
9528 cols.push( { rel : false, abs : w });
9530 last = i; // not really..
9533 var w = cm.getColumnWidth(i, sz);
9538 cols.push( { rel : w, abs : false });
9541 var avail = this.bodyEl.dom.clientWidth - total_abs;
9543 var unitWidth = Math.floor(avail / total);
9544 var rem = avail - (unitWidth * total);
9546 var hidden, width, pos = 0 , splithide , left;
9547 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9549 hidden = 'display:none;';
9551 width = 'width:0px;';
9553 if(!cm.isHidden(i)){
9557 // we can honour xs/sm/md/xl ?
9558 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9560 hidden = 'display:none;';
9562 // width should return a small number...
9564 w+=rem; // add the remaining with..
9567 left = "left:" + (pos -4) + "px;";
9568 width = "width:" + w+ "px;";
9571 if (this.responsive) {
9574 hidden = cm.isHidden(i) ? 'display:none;' : '';
9575 splithide = 'display: none;';
9578 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9581 splithide = 'display:none;';
9584 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9585 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9586 // this is the popover version..
9587 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9592 //Roo.log(styles.join(''));
9593 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9599 onContextMenu : function(e, t)
9601 this.processEvent("contextmenu", e);
9604 processEvent : function(name, e)
9606 if (name != 'touchstart' ) {
9607 this.fireEvent(name, e);
9610 var t = e.getTarget();
9612 var cell = Roo.get(t);
9618 if(cell.findParent('tfoot', false, true)){
9622 if(cell.findParent('thead', false, true)){
9624 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9625 cell = Roo.get(t).findParent('th', false, true);
9627 Roo.log("failed to find th in thead?");
9628 Roo.log(e.getTarget());
9633 var cellIndex = cell.dom.cellIndex;
9635 var ename = name == 'touchstart' ? 'click' : name;
9636 this.fireEvent("header" + ename, this, cellIndex, e);
9641 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9642 cell = Roo.get(t).findParent('td', false, true);
9644 Roo.log("failed to find th in tbody?");
9645 Roo.log(e.getTarget());
9650 var row = cell.findParent('tr', false, true);
9651 var cellIndex = cell.dom.cellIndex;
9652 var rowIndex = row.dom.rowIndex - 1;
9656 this.fireEvent("row" + name, this, rowIndex, e);
9660 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9666 onMouseover : function(e, el)
9668 var cell = Roo.get(el);
9674 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9675 cell = cell.findParent('td', false, true);
9678 var row = cell.findParent('tr', false, true);
9679 var cellIndex = cell.dom.cellIndex;
9680 var rowIndex = row.dom.rowIndex - 1; // start from 0
9682 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9686 onMouseout : function(e, el)
9688 var cell = Roo.get(el);
9694 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9695 cell = cell.findParent('td', false, true);
9698 var row = cell.findParent('tr', false, true);
9699 var cellIndex = cell.dom.cellIndex;
9700 var rowIndex = row.dom.rowIndex - 1; // start from 0
9702 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9706 onClick : function(e, el)
9708 var cell = Roo.get(el);
9710 if(!cell || (!this.cellSelection && !this.rowSelection)){
9714 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9715 cell = cell.findParent('td', false, true);
9718 if(!cell || typeof(cell) == 'undefined'){
9722 var row = cell.findParent('tr', false, true);
9724 if(!row || typeof(row) == 'undefined'){
9728 var cellIndex = cell.dom.cellIndex;
9729 var rowIndex = this.getRowIndex(row);
9731 // why??? - should these not be based on SelectionModel?
9732 //if(this.cellSelection){
9733 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9736 //if(this.rowSelection){
9737 this.fireEvent('rowclick', this, row, rowIndex, e);
9742 onDblClick : function(e,el)
9744 var cell = Roo.get(el);
9746 if(!cell || (!this.cellSelection && !this.rowSelection)){
9750 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9751 cell = cell.findParent('td', false, true);
9754 if(!cell || typeof(cell) == 'undefined'){
9758 var row = cell.findParent('tr', false, true);
9760 if(!row || typeof(row) == 'undefined'){
9764 var cellIndex = cell.dom.cellIndex;
9765 var rowIndex = this.getRowIndex(row);
9767 if(this.cellSelection){
9768 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9771 if(this.rowSelection){
9772 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9775 findRowIndex : function(el)
9777 var cell = Roo.get(el);
9781 var row = cell.findParent('tr', false, true);
9783 if(!row || typeof(row) == 'undefined'){
9786 return this.getRowIndex(row);
9788 sort : function(e,el)
9790 var col = Roo.get(el);
9792 if(!col.hasClass('sortable')){
9796 var sort = col.attr('sort');
9799 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9803 this.store.sortInfo = {field : sort, direction : dir};
9806 Roo.log("calling footer first");
9807 this.footer.onClick('first');
9810 this.store.load({ params : { start : 0 } });
9814 renderHeader : function()
9822 this.totalWidth = 0;
9824 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9826 var config = cm.config[i];
9830 cls : 'x-hcol-' + i,
9833 html: cm.getColumnHeader(i)
9836 var tooltip = cm.getColumnTooltip(i);
9838 c.tooltip = tooltip;
9844 if(typeof(config.sortable) != 'undefined' && config.sortable){
9845 c.cls += ' sortable';
9846 c.html = '<i class="fa"></i>' + c.html;
9849 // could use BS4 hidden-..-down
9851 if(typeof(config.lgHeader) != 'undefined'){
9852 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9855 if(typeof(config.mdHeader) != 'undefined'){
9856 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9859 if(typeof(config.smHeader) != 'undefined'){
9860 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9863 if(typeof(config.xsHeader) != 'undefined'){
9864 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9871 if(typeof(config.tooltip) != 'undefined'){
9872 c.tooltip = config.tooltip;
9875 if(typeof(config.colspan) != 'undefined'){
9876 c.colspan = config.colspan;
9879 // hidden is handled by CSS now
9881 if(typeof(config.dataIndex) != 'undefined'){
9882 c.sort = config.dataIndex;
9887 if(typeof(config.align) != 'undefined' && config.align.length){
9888 c.style += ' text-align:' + config.align + ';';
9891 /* width is done in CSS
9892 *if(typeof(config.width) != 'undefined'){
9893 c.style += ' width:' + config.width + 'px;';
9894 this.totalWidth += config.width;
9896 this.totalWidth += 100; // assume minimum of 100 per column?
9900 if(typeof(config.cls) != 'undefined'){
9901 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9903 // this is the bit that doesnt reall work at all...
9905 if (this.responsive) {
9908 ['xs','sm','md','lg'].map(function(size){
9910 if(typeof(config[size]) == 'undefined'){
9914 if (!config[size]) { // 0 = hidden
9915 // BS 4 '0' is treated as hide that column and below.
9916 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9920 c.cls += ' col-' + size + '-' + config[size] + (
9921 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9929 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9940 renderBody : function()
9950 colspan : this.cm.getColumnCount()
9960 renderFooter : function()
9970 colspan : this.cm.getColumnCount()
9984 // Roo.log('ds onload');
9989 var ds = this.store;
9991 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9992 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9993 if (_this.store.sortInfo) {
9995 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9996 e.select('i', true).addClass(['fa-arrow-up']);
9999 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10000 e.select('i', true).addClass(['fa-arrow-down']);
10005 var tbody = this.bodyEl;
10007 if(ds.getCount() > 0){
10008 ds.data.each(function(d,rowIndex){
10009 var row = this.renderRow(cm, ds, rowIndex);
10011 tbody.createChild(row);
10015 if(row.cellObjects.length){
10016 Roo.each(row.cellObjects, function(r){
10017 _this.renderCellObject(r);
10022 } else if (this.empty_results.length) {
10023 this.el.mask(this.empty_results, 'no-spinner');
10026 var tfoot = this.el.select('tfoot', true).first();
10028 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10030 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10032 var total = this.ds.getTotalCount();
10034 if(this.footer.pageSize < total){
10035 this.mainFoot.show();
10039 Roo.each(this.el.select('tbody td', true).elements, function(e){
10040 e.on('mouseover', _this.onMouseover, _this);
10043 Roo.each(this.el.select('tbody td', true).elements, function(e){
10044 e.on('mouseout', _this.onMouseout, _this);
10046 this.fireEvent('rowsrendered', this);
10050 this.initCSS(); /// resize cols
10056 onUpdate : function(ds,record)
10058 this.refreshRow(record);
10062 onRemove : function(ds, record, index, isUpdate){
10063 if(isUpdate !== true){
10064 this.fireEvent("beforerowremoved", this, index, record);
10066 var bt = this.bodyEl.dom;
10068 var rows = this.el.select('tbody > tr', true).elements;
10070 if(typeof(rows[index]) != 'undefined'){
10071 bt.removeChild(rows[index].dom);
10074 // if(bt.rows[index]){
10075 // bt.removeChild(bt.rows[index]);
10078 if(isUpdate !== true){
10079 //this.stripeRows(index);
10080 //this.syncRowHeights(index, index);
10082 this.fireEvent("rowremoved", this, index, record);
10086 onAdd : function(ds, records, rowIndex)
10088 //Roo.log('on Add called');
10089 // - note this does not handle multiple adding very well..
10090 var bt = this.bodyEl.dom;
10091 for (var i =0 ; i < records.length;i++) {
10092 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10093 //Roo.log(records[i]);
10094 //Roo.log(this.store.getAt(rowIndex+i));
10095 this.insertRow(this.store, rowIndex + i, false);
10102 refreshRow : function(record){
10103 var ds = this.store, index;
10104 if(typeof record == 'number'){
10106 record = ds.getAt(index);
10108 index = ds.indexOf(record);
10110 return; // should not happen - but seems to
10113 this.insertRow(ds, index, true);
10115 this.onRemove(ds, record, index+1, true);
10117 //this.syncRowHeights(index, index);
10119 this.fireEvent("rowupdated", this, index, record);
10121 // private - called by RowSelection
10122 onRowSelect : function(rowIndex){
10123 var row = this.getRowDom(rowIndex);
10124 row.addClass(['bg-info','info']);
10126 // private - called by RowSelection
10127 onRowDeselect : function(rowIndex)
10129 if (rowIndex < 0) {
10132 var row = this.getRowDom(rowIndex);
10133 row.removeClass(['bg-info','info']);
10136 * Focuses the specified row.
10137 * @param {Number} row The row index
10139 focusRow : function(row)
10141 //Roo.log('GridView.focusRow');
10142 var x = this.bodyEl.dom.scrollLeft;
10143 this.focusCell(row, 0, false);
10144 this.bodyEl.dom.scrollLeft = x;
10148 * Focuses the specified cell.
10149 * @param {Number} row The row index
10150 * @param {Number} col The column index
10151 * @param {Boolean} hscroll false to disable horizontal scrolling
10153 focusCell : function(row, col, hscroll)
10155 //Roo.log('GridView.focusCell');
10156 var el = this.ensureVisible(row, col, hscroll);
10157 // not sure what focusEL achives = it's a <a> pos relative
10158 //this.focusEl.alignTo(el, "tl-tl");
10160 // this.focusEl.focus();
10162 // this.focusEl.focus.defer(1, this.focusEl);
10167 * Scrolls the specified cell into view
10168 * @param {Number} row The row index
10169 * @param {Number} col The column index
10170 * @param {Boolean} hscroll false to disable horizontal scrolling
10172 ensureVisible : function(row, col, hscroll)
10174 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10175 //return null; //disable for testing.
10176 if(typeof row != "number"){
10177 row = row.rowIndex;
10179 if(row < 0 && row >= this.ds.getCount()){
10182 col = (col !== undefined ? col : 0);
10184 while(cm.isHidden(col)){
10188 var el = this.getCellDom(row, col);
10192 var c = this.bodyEl.dom;
10194 var ctop = parseInt(el.offsetTop, 10);
10195 var cleft = parseInt(el.offsetLeft, 10);
10196 var cbot = ctop + el.offsetHeight;
10197 var cright = cleft + el.offsetWidth;
10199 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10200 var ch = 0; //?? header is not withing the area?
10201 var stop = parseInt(c.scrollTop, 10);
10202 var sleft = parseInt(c.scrollLeft, 10);
10203 var sbot = stop + ch;
10204 var sright = sleft + c.clientWidth;
10206 Roo.log('GridView.ensureVisible:' +
10208 ' c.clientHeight:' + c.clientHeight +
10209 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10217 c.scrollTop = ctop;
10218 //Roo.log("set scrolltop to ctop DISABLE?");
10219 }else if(cbot > sbot){
10220 //Roo.log("set scrolltop to cbot-ch");
10221 c.scrollTop = cbot-ch;
10224 if(hscroll !== false){
10226 c.scrollLeft = cleft;
10227 }else if(cright > sright){
10228 c.scrollLeft = cright-c.clientWidth;
10236 insertRow : function(dm, rowIndex, isUpdate){
10239 this.fireEvent("beforerowsinserted", this, rowIndex);
10241 //var s = this.getScrollState();
10242 var row = this.renderRow(this.cm, this.store, rowIndex);
10243 // insert before rowIndex..
10244 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10248 if(row.cellObjects.length){
10249 Roo.each(row.cellObjects, function(r){
10250 _this.renderCellObject(r);
10255 this.fireEvent("rowsinserted", this, rowIndex);
10256 //this.syncRowHeights(firstRow, lastRow);
10257 //this.stripeRows(firstRow);
10264 getRowDom : function(rowIndex)
10266 var rows = this.el.select('tbody > tr', true).elements;
10268 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10271 getCellDom : function(rowIndex, colIndex)
10273 var row = this.getRowDom(rowIndex);
10274 if (row === false) {
10277 var cols = row.select('td', true).elements;
10278 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10282 // returns the object tree for a tr..
10285 renderRow : function(cm, ds, rowIndex)
10287 var d = ds.getAt(rowIndex);
10291 cls : 'x-row-' + rowIndex,
10295 var cellObjects = [];
10297 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10298 var config = cm.config[i];
10300 var renderer = cm.getRenderer(i);
10304 if(typeof(renderer) !== 'undefined'){
10305 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10307 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10308 // and are rendered into the cells after the row is rendered - using the id for the element.
10310 if(typeof(value) === 'object'){
10320 rowIndex : rowIndex,
10325 this.fireEvent('rowclass', this, rowcfg);
10329 // this might end up displaying HTML?
10330 // this is too messy... - better to only do it on columsn you know are going to be too long
10331 //tooltip : (typeof(value) === 'object') ? '' : value,
10332 cls : rowcfg.rowClass + ' x-col-' + i,
10334 html: (typeof(value) === 'object') ? '' : value
10341 if(typeof(config.colspan) != 'undefined'){
10342 td.colspan = config.colspan;
10347 if(typeof(config.align) != 'undefined' && config.align.length){
10348 td.style += ' text-align:' + config.align + ';';
10350 if(typeof(config.valign) != 'undefined' && config.valign.length){
10351 td.style += ' vertical-align:' + config.valign + ';';
10354 if(typeof(config.width) != 'undefined'){
10355 td.style += ' width:' + config.width + 'px;';
10359 if(typeof(config.cursor) != 'undefined'){
10360 td.style += ' cursor:' + config.cursor + ';';
10363 if(typeof(config.cls) != 'undefined'){
10364 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10366 if (this.responsive) {
10367 ['xs','sm','md','lg'].map(function(size){
10369 if(typeof(config[size]) == 'undefined'){
10375 if (!config[size]) { // 0 = hidden
10376 // BS 4 '0' is treated as hide that column and below.
10377 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10381 td.cls += ' col-' + size + '-' + config[size] + (
10382 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10392 row.cellObjects = cellObjects;
10400 onBeforeLoad : function()
10402 this.el.unmask(); // if needed.
10409 this.el.select('tbody', true).first().dom.innerHTML = '';
10412 * Show or hide a row.
10413 * @param {Number} rowIndex to show or hide
10414 * @param {Boolean} state hide
10416 setRowVisibility : function(rowIndex, state)
10418 var bt = this.bodyEl.dom;
10420 var rows = this.el.select('tbody > tr', true).elements;
10422 if(typeof(rows[rowIndex]) == 'undefined'){
10425 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10430 getSelectionModel : function(){
10431 if(!this.selModel){
10432 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10434 return this.selModel;
10437 * Render the Roo.bootstrap object from renderder
10439 renderCellObject : function(r)
10443 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10445 var t = r.cfg.render(r.container);
10448 Roo.each(r.cfg.cn, function(c){
10450 container: t.getChildContainer(),
10453 _this.renderCellObject(child);
10458 * get the Row Index from a dom element.
10459 * @param {Roo.Element} row The row to look for
10460 * @returns {Number} the row
10462 getRowIndex : function(row)
10466 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10477 * get the header TH element for columnIndex
10478 * @param {Number} columnIndex
10479 * @returns {Roo.Element}
10481 getHeaderIndex: function(colIndex)
10483 var cols = this.headEl.select('th', true).elements;
10484 return cols[colIndex];
10487 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10488 * @param {domElement} cell to look for
10489 * @returns {Number} the column
10491 getCellIndex : function(cell)
10493 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10495 return parseInt(id[1], 10);
10500 * Returns the grid's underlying element = used by panel.Grid
10501 * @return {Element} The element
10503 getGridEl : function(){
10507 * Forces a resize - used by panel.Grid
10508 * @return {Element} The element
10510 autoSize : function()
10512 //var ctr = Roo.get(this.container.dom.parentElement);
10513 var ctr = Roo.get(this.el.dom);
10515 var thd = this.getGridEl().select('thead',true).first();
10516 var tbd = this.getGridEl().select('tbody', true).first();
10517 var tfd = this.getGridEl().select('tfoot', true).first();
10519 var cw = ctr.getWidth();
10520 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10524 tbd.setWidth(ctr.getWidth());
10525 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10526 // this needs fixing for various usage - currently only hydra job advers I think..
10528 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10530 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10533 cw = Math.max(cw, this.totalWidth);
10534 this.getGridEl().select('tbody tr',true).setWidth(cw);
10537 // resize 'expandable coloumn?
10539 return; // we doe not have a view in this design..
10542 onBodyScroll: function()
10544 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10546 this.headEl.setStyle({
10547 'position' : 'relative',
10548 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10554 var scrollHeight = this.bodyEl.dom.scrollHeight;
10556 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10558 var height = this.bodyEl.getHeight();
10560 if(scrollHeight - height == scrollTop) {
10562 var total = this.ds.getTotalCount();
10564 if(this.footer.cursor + this.footer.pageSize < total){
10566 this.footer.ds.load({
10568 start : this.footer.cursor + this.footer.pageSize,
10569 limit : this.footer.pageSize
10578 onColumnSplitterMoved : function(i, diff)
10580 this.userResized = true;
10582 var cm = this.colModel;
10584 var w = this.getHeaderIndex(i).getWidth() + diff;
10587 cm.setColumnWidth(i, w, true);
10589 //var cid = cm.getColumnId(i); << not used in this version?
10590 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10592 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10593 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10594 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10596 //this.updateSplitters();
10597 //this.layout(); << ??
10598 this.fireEvent("columnresize", i, w);
10600 onHeaderChange : function()
10602 var header = this.renderHeader();
10603 var table = this.el.select('table', true).first();
10605 this.headEl.remove();
10606 this.headEl = table.createChild(header, this.bodyEl, false);
10608 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10609 e.on('click', this.sort, this);
10612 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10613 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10618 onHiddenChange : function(colModel, colIndex, hidden)
10621 this.cm.setHidden()
10622 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10623 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10625 this.CSS.updateRule(thSelector, "display", "");
10626 this.CSS.updateRule(tdSelector, "display", "");
10629 this.CSS.updateRule(thSelector, "display", "none");
10630 this.CSS.updateRule(tdSelector, "display", "none");
10633 // onload calls initCSS()
10634 this.onHeaderChange();
10638 setColumnWidth: function(col_index, width)
10640 // width = "md-2 xs-2..."
10641 if(!this.colModel.config[col_index]) {
10645 var w = width.split(" ");
10647 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10649 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10652 for(var j = 0; j < w.length; j++) {
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(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10672 h_row[0].classList.replace(
10673 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10674 "col-"+size_cls[0]+"-"+size_cls[1]
10677 for(var i = 0; i < rows.length; i++) {
10679 var size_cls = w[j].split("-");
10681 if(!Number.isInteger(size_cls[1] * 1)) {
10685 if(!this.colModel.config[col_index][size_cls[0]]) {
10689 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10693 rows[i].classList.replace(
10694 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10695 "col-"+size_cls[0]+"-"+size_cls[1]
10699 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10704 // currently only used to find the split on drag..
10705 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10710 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10711 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10720 * @class Roo.bootstrap.TableCell
10721 * @extends Roo.bootstrap.Component
10722 * @children Roo.bootstrap.Component
10723 * @parent Roo.bootstrap.TableRow
10724 * Bootstrap TableCell class
10726 * @cfg {String} html cell contain text
10727 * @cfg {String} cls cell class
10728 * @cfg {String} tag cell tag (td|th) default td
10729 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10730 * @cfg {String} align Aligns the content in a cell
10731 * @cfg {String} axis Categorizes cells
10732 * @cfg {String} bgcolor Specifies the background color of a cell
10733 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10734 * @cfg {Number} colspan Specifies the number of columns a cell should span
10735 * @cfg {String} headers Specifies one or more header cells a cell is related to
10736 * @cfg {Number} height Sets the height of a cell
10737 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10738 * @cfg {Number} rowspan Sets the number of rows a cell should span
10739 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10740 * @cfg {String} valign Vertical aligns the content in a cell
10741 * @cfg {Number} width Specifies the width of a cell
10744 * Create a new TableCell
10745 * @param {Object} config The config object
10748 Roo.bootstrap.TableCell = function(config){
10749 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10752 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10772 getAutoCreate : function(){
10773 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10780 cfg.tag = this.tag;
10793 cfg.align=this.align
10798 if (this.bgcolor) {
10799 cfg.bgcolor=this.bgcolor
10801 if (this.charoff) {
10802 cfg.charoff=this.charoff
10804 if (this.colspan) {
10805 cfg.colspan=this.colspan
10807 if (this.headers) {
10808 cfg.headers=this.headers
10811 cfg.height=this.height
10814 cfg.nowrap=this.nowrap
10816 if (this.rowspan) {
10817 cfg.rowspan=this.rowspan
10820 cfg.scope=this.scope
10823 cfg.valign=this.valign
10826 cfg.width=this.width
10845 * @class Roo.bootstrap.TableRow
10846 * @extends Roo.bootstrap.Component
10847 * @children Roo.bootstrap.TableCell
10848 * @parent Roo.bootstrap.TableBody
10849 * Bootstrap TableRow class
10850 * @cfg {String} cls row class
10851 * @cfg {String} align Aligns the content in a table row
10852 * @cfg {String} bgcolor Specifies a background color for a table row
10853 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10854 * @cfg {String} valign Vertical aligns the content in a table row
10857 * Create a new TableRow
10858 * @param {Object} config The config object
10861 Roo.bootstrap.TableRow = function(config){
10862 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10865 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10873 getAutoCreate : function(){
10874 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10881 cfg.cls = this.cls;
10884 cfg.align = this.align;
10887 cfg.bgcolor = this.bgcolor;
10890 cfg.charoff = this.charoff;
10893 cfg.valign = this.valign;
10911 * @class Roo.bootstrap.TableBody
10912 * @extends Roo.bootstrap.Component
10913 * @children Roo.bootstrap.TableRow
10914 * @parent Roo.bootstrap.Table
10915 * Bootstrap TableBody class
10916 * @cfg {String} cls element class
10917 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10918 * @cfg {String} align Aligns the content inside the element
10919 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10920 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10923 * Create a new TableBody
10924 * @param {Object} config The config object
10927 Roo.bootstrap.TableBody = function(config){
10928 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10931 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10939 getAutoCreate : function(){
10940 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10950 cfg.tag = this.tag;
10954 cfg.align = this.align;
10957 cfg.charoff = this.charoff;
10960 cfg.valign = this.valign;
10967 // initEvents : function()
10970 // if(!this.store){
10974 // this.store = Roo.factory(this.store, Roo.data);
10975 // this.store.on('load', this.onLoad, this);
10977 // this.store.load();
10981 // onLoad: function ()
10983 // this.fireEvent('load', this);
10993 * Ext JS Library 1.1.1
10994 * Copyright(c) 2006-2007, Ext JS, LLC.
10996 * Originally Released Under LGPL - original licence link has changed is not relivant.
10999 * <script type="text/javascript">
11002 // as we use this in bootstrap.
11003 Roo.namespace('Roo.form');
11005 * @class Roo.form.Action
11006 * Internal Class used to handle form actions
11008 * @param {Roo.form.BasicForm} el The form element or its id
11009 * @param {Object} config Configuration options
11014 // define the action interface
11015 Roo.form.Action = function(form, options){
11017 this.options = options || {};
11020 * Client Validation Failed
11023 Roo.form.Action.CLIENT_INVALID = 'client';
11025 * Server Validation Failed
11028 Roo.form.Action.SERVER_INVALID = 'server';
11030 * Connect to Server Failed
11033 Roo.form.Action.CONNECT_FAILURE = 'connect';
11035 * Reading Data from Server Failed
11038 Roo.form.Action.LOAD_FAILURE = 'load';
11040 Roo.form.Action.prototype = {
11042 failureType : undefined,
11043 response : undefined,
11044 result : undefined,
11046 // interface method
11047 run : function(options){
11051 // interface method
11052 success : function(response){
11056 // interface method
11057 handleResponse : function(response){
11061 // default connection failure
11062 failure : function(response){
11064 this.response = response;
11065 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11066 this.form.afterAction(this, false);
11069 processResponse : function(response){
11070 this.response = response;
11071 if(!response.responseText){
11074 this.result = this.handleResponse(response);
11075 return this.result;
11078 // utility functions used internally
11079 getUrl : function(appendParams){
11080 var url = this.options.url || this.form.url || this.form.el.dom.action;
11082 var p = this.getParams();
11084 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11090 getMethod : function(){
11091 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11094 getParams : function(){
11095 var bp = this.form.baseParams;
11096 var p = this.options.params;
11098 if(typeof p == "object"){
11099 p = Roo.urlEncode(Roo.applyIf(p, bp));
11100 }else if(typeof p == 'string' && bp){
11101 p += '&' + Roo.urlEncode(bp);
11104 p = Roo.urlEncode(bp);
11109 createCallback : function(){
11111 success: this.success,
11112 failure: this.failure,
11114 timeout: (this.form.timeout*1000),
11115 upload: this.form.fileUpload ? this.success : undefined
11120 Roo.form.Action.Submit = function(form, options){
11121 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11124 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11127 haveProgress : false,
11128 uploadComplete : false,
11130 // uploadProgress indicator.
11131 uploadProgress : function()
11133 if (!this.form.progressUrl) {
11137 if (!this.haveProgress) {
11138 Roo.MessageBox.progress("Uploading", "Uploading");
11140 if (this.uploadComplete) {
11141 Roo.MessageBox.hide();
11145 this.haveProgress = true;
11147 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11149 var c = new Roo.data.Connection();
11151 url : this.form.progressUrl,
11156 success : function(req){
11157 //console.log(data);
11161 rdata = Roo.decode(req.responseText)
11163 Roo.log("Invalid data from server..");
11167 if (!rdata || !rdata.success) {
11169 Roo.MessageBox.alert(Roo.encode(rdata));
11172 var data = rdata.data;
11174 if (this.uploadComplete) {
11175 Roo.MessageBox.hide();
11180 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11181 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11184 this.uploadProgress.defer(2000,this);
11187 failure: function(data) {
11188 Roo.log('progress url failed ');
11199 // run get Values on the form, so it syncs any secondary forms.
11200 this.form.getValues();
11202 var o = this.options;
11203 var method = this.getMethod();
11204 var isPost = method == 'POST';
11205 if(o.clientValidation === false || this.form.isValid()){
11207 if (this.form.progressUrl) {
11208 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11209 (new Date() * 1) + '' + Math.random());
11214 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11215 form:this.form.el.dom,
11216 url:this.getUrl(!isPost),
11218 params:isPost ? this.getParams() : null,
11219 isUpload: this.form.fileUpload,
11220 formData : this.form.formData
11223 this.uploadProgress();
11225 }else if (o.clientValidation !== false){ // client validation failed
11226 this.failureType = Roo.form.Action.CLIENT_INVALID;
11227 this.form.afterAction(this, false);
11231 success : function(response)
11233 this.uploadComplete= true;
11234 if (this.haveProgress) {
11235 Roo.MessageBox.hide();
11239 var result = this.processResponse(response);
11240 if(result === true || result.success){
11241 this.form.afterAction(this, true);
11245 this.form.markInvalid(result.errors);
11246 this.failureType = Roo.form.Action.SERVER_INVALID;
11248 this.form.afterAction(this, false);
11250 failure : function(response)
11252 this.uploadComplete= true;
11253 if (this.haveProgress) {
11254 Roo.MessageBox.hide();
11257 this.response = response;
11258 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11259 this.form.afterAction(this, false);
11262 handleResponse : function(response){
11263 if(this.form.errorReader){
11264 var rs = this.form.errorReader.read(response);
11267 for(var i = 0, len = rs.records.length; i < len; i++) {
11268 var r = rs.records[i];
11269 errors[i] = r.data;
11272 if(errors.length < 1){
11276 success : rs.success,
11282 ret = Roo.decode(response.responseText);
11286 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11296 Roo.form.Action.Load = function(form, options){
11297 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11298 this.reader = this.form.reader;
11301 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11306 Roo.Ajax.request(Roo.apply(
11307 this.createCallback(), {
11308 method:this.getMethod(),
11309 url:this.getUrl(false),
11310 params:this.getParams()
11314 success : function(response){
11316 var result = this.processResponse(response);
11317 if(result === true || !result.success || !result.data){
11318 this.failureType = Roo.form.Action.LOAD_FAILURE;
11319 this.form.afterAction(this, false);
11322 this.form.clearInvalid();
11323 this.form.setValues(result.data);
11324 this.form.afterAction(this, true);
11327 handleResponse : function(response){
11328 if(this.form.reader){
11329 var rs = this.form.reader.read(response);
11330 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11332 success : rs.success,
11336 return Roo.decode(response.responseText);
11340 Roo.form.Action.ACTION_TYPES = {
11341 'load' : Roo.form.Action.Load,
11342 'submit' : Roo.form.Action.Submit
11351 * @class Roo.bootstrap.form.Form
11352 * @extends Roo.bootstrap.Component
11353 * @children Roo.bootstrap.Component
11354 * Bootstrap Form class
11355 * @cfg {String} method GET | POST (default POST)
11356 * @cfg {String} labelAlign top | left (default top)
11357 * @cfg {String} align left | right - for navbars
11358 * @cfg {Boolean} loadMask load mask when submit (default true)
11362 * Create a new Form
11363 * @param {Object} config The config object
11367 Roo.bootstrap.form.Form = function(config){
11369 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11371 Roo.bootstrap.form.Form.popover.apply();
11375 * @event clientvalidation
11376 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11377 * @param {Form} this
11378 * @param {Boolean} valid true if the form has passed client-side validation
11380 clientvalidation: true,
11382 * @event beforeaction
11383 * Fires before any action is performed. Return false to cancel the action.
11384 * @param {Form} this
11385 * @param {Action} action The action to be performed
11387 beforeaction: true,
11389 * @event actionfailed
11390 * Fires when an action fails.
11391 * @param {Form} this
11392 * @param {Action} action The action that failed
11394 actionfailed : true,
11396 * @event actioncomplete
11397 * Fires when an action is completed.
11398 * @param {Form} this
11399 * @param {Action} action The action that completed
11401 actioncomplete : true
11405 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11408 * @cfg {String} method
11409 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11413 * @cfg {String} url
11414 * The URL to use for form actions if one isn't supplied in the action options.
11417 * @cfg {Boolean} fileUpload
11418 * Set to true if this form is a file upload.
11422 * @cfg {Object} baseParams
11423 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11427 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11431 * @cfg {Sting} align (left|right) for navbar forms
11436 activeAction : null,
11439 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11440 * element by passing it or its id or mask the form itself by passing in true.
11443 waitMsgTarget : false,
11448 * @cfg {Boolean} errorMask (true|false) default false
11453 * @cfg {Number} maskOffset Default 100
11458 * @cfg {Boolean} maskBody
11462 getAutoCreate : function(){
11466 method : this.method || 'POST',
11467 id : this.id || Roo.id(),
11470 if (this.parent().xtype.match(/^Nav/)) {
11471 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11475 if (this.labelAlign == 'left' ) {
11476 cfg.cls += ' form-horizontal';
11482 initEvents : function()
11484 this.el.on('submit', this.onSubmit, this);
11485 // this was added as random key presses on the form where triggering form submit.
11486 this.el.on('keypress', function(e) {
11487 if (e.getCharCode() != 13) {
11490 // we might need to allow it for textareas.. and some other items.
11491 // check e.getTarget().
11493 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11497 Roo.log("keypress blocked");
11499 e.preventDefault();
11505 onSubmit : function(e){
11510 * Returns true if client-side validation on the form is successful.
11513 isValid : function(){
11514 var items = this.getItems();
11516 var target = false;
11518 items.each(function(f){
11524 Roo.log('invalid field: ' + f.name);
11528 if(!target && f.el.isVisible(true)){
11534 if(this.errorMask && !valid){
11535 Roo.bootstrap.form.Form.popover.mask(this, target);
11542 * Returns true if any fields in this form have changed since their original load.
11545 isDirty : function(){
11547 var items = this.getItems();
11548 items.each(function(f){
11558 * Performs a predefined action (submit or load) or custom actions you define on this form.
11559 * @param {String} actionName The name of the action type
11560 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11561 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11562 * accept other config options):
11564 Property Type Description
11565 ---------------- --------------- ----------------------------------------------------------------------------------
11566 url String The url for the action (defaults to the form's url)
11567 method String The form method to use (defaults to the form's method, or POST if not defined)
11568 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11569 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11570 validate the form on the client (defaults to false)
11572 * @return {BasicForm} this
11574 doAction : function(action, options){
11575 if(typeof action == 'string'){
11576 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11578 if(this.fireEvent('beforeaction', this, action) !== false){
11579 this.beforeAction(action);
11580 action.run.defer(100, action);
11586 beforeAction : function(action){
11587 var o = action.options;
11592 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11594 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11597 // not really supported yet.. ??
11599 //if(this.waitMsgTarget === true){
11600 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11601 //}else if(this.waitMsgTarget){
11602 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11603 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11605 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11611 afterAction : function(action, success){
11612 this.activeAction = null;
11613 var o = action.options;
11618 Roo.get(document.body).unmask();
11624 //if(this.waitMsgTarget === true){
11625 // this.el.unmask();
11626 //}else if(this.waitMsgTarget){
11627 // this.waitMsgTarget.unmask();
11629 // Roo.MessageBox.updateProgress(1);
11630 // Roo.MessageBox.hide();
11637 Roo.callback(o.success, o.scope, [this, action]);
11638 this.fireEvent('actioncomplete', this, action);
11642 // failure condition..
11643 // we have a scenario where updates need confirming.
11644 // eg. if a locking scenario exists..
11645 // we look for { errors : { needs_confirm : true }} in the response.
11647 (typeof(action.result) != 'undefined') &&
11648 (typeof(action.result.errors) != 'undefined') &&
11649 (typeof(action.result.errors.needs_confirm) != 'undefined')
11652 Roo.log("not supported yet");
11655 Roo.MessageBox.confirm(
11656 "Change requires confirmation",
11657 action.result.errorMsg,
11662 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11672 Roo.callback(o.failure, o.scope, [this, action]);
11673 // show an error message if no failed handler is set..
11674 if (!this.hasListener('actionfailed')) {
11675 Roo.log("need to add dialog support");
11677 Roo.MessageBox.alert("Error",
11678 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11679 action.result.errorMsg :
11680 "Saving Failed, please check your entries or try again"
11685 this.fireEvent('actionfailed', this, action);
11690 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11691 * @param {String} id The value to search for
11694 findField : function(id){
11695 var items = this.getItems();
11696 var field = items.get(id);
11698 items.each(function(f){
11699 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11706 return field || null;
11709 * Mark fields in this form invalid in bulk.
11710 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11711 * @return {BasicForm} this
11713 markInvalid : function(errors){
11714 if(errors instanceof Array){
11715 for(var i = 0, len = errors.length; i < len; i++){
11716 var fieldError = errors[i];
11717 var f = this.findField(fieldError.id);
11719 f.markInvalid(fieldError.msg);
11725 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11726 field.markInvalid(errors[id]);
11730 //Roo.each(this.childForms || [], function (f) {
11731 // f.markInvalid(errors);
11738 * Set values for fields in this form in bulk.
11739 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11740 * @return {BasicForm} this
11742 setValues : function(values){
11743 if(values instanceof Array){ // array of objects
11744 for(var i = 0, len = values.length; i < len; i++){
11746 var f = this.findField(v.id);
11748 f.setValue(v.value);
11749 if(this.trackResetOnLoad){
11750 f.originalValue = f.getValue();
11754 }else{ // object hash
11757 if(typeof values[id] != 'function' && (field = this.findField(id))){
11759 if (field.setFromData &&
11760 field.valueField &&
11761 field.displayField &&
11762 // combos' with local stores can
11763 // be queried via setValue()
11764 // to set their value..
11765 (field.store && !field.store.isLocal)
11769 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11770 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11771 field.setFromData(sd);
11773 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11775 field.setFromData(values);
11778 field.setValue(values[id]);
11782 if(this.trackResetOnLoad){
11783 field.originalValue = field.getValue();
11789 //Roo.each(this.childForms || [], function (f) {
11790 // f.setValues(values);
11797 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11798 * they are returned as an array.
11799 * @param {Boolean} asString
11802 getValues : function(asString){
11803 //if (this.childForms) {
11804 // copy values from the child forms
11805 // Roo.each(this.childForms, function (f) {
11806 // this.setValues(f.getValues());
11812 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11813 if(asString === true){
11816 return Roo.urlDecode(fs);
11820 * Returns the fields in this form as an object with key/value pairs.
11821 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11824 getFieldValues : function(with_hidden)
11826 var items = this.getItems();
11828 items.each(function(f){
11830 if (!f.getName()) {
11834 var v = f.getValue();
11836 if (f.inputType =='radio') {
11837 if (typeof(ret[f.getName()]) == 'undefined') {
11838 ret[f.getName()] = ''; // empty..
11841 if (!f.el.dom.checked) {
11845 v = f.el.dom.value;
11849 if(f.xtype == 'MoneyField'){
11850 ret[f.currencyName] = f.getCurrency();
11853 // not sure if this supported any more..
11854 if ((typeof(v) == 'object') && f.getRawValue) {
11855 v = f.getRawValue() ; // dates..
11857 // combo boxes where name != hiddenName...
11858 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11859 ret[f.name] = f.getRawValue();
11861 ret[f.getName()] = v;
11868 * Clears all invalid messages in this form.
11869 * @return {BasicForm} this
11871 clearInvalid : function(){
11872 var items = this.getItems();
11874 items.each(function(f){
11882 * Resets this form.
11883 * @return {BasicForm} this
11885 reset : function(){
11886 var items = this.getItems();
11887 items.each(function(f){
11891 Roo.each(this.childForms || [], function (f) {
11899 getItems : function()
11901 var r=new Roo.util.MixedCollection(false, function(o){
11902 return o.id || (o.id = Roo.id());
11904 var iter = function(el) {
11911 Roo.each(el.items,function(e) {
11920 hideFields : function(items)
11922 Roo.each(items, function(i){
11924 var f = this.findField(i);
11935 showFields : function(items)
11937 Roo.each(items, function(i){
11939 var f = this.findField(i);
11952 Roo.apply(Roo.bootstrap.form.Form, {
11968 intervalID : false,
11974 if(this.isApplied){
11979 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11980 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11981 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11982 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11985 this.maskEl.top.enableDisplayMode("block");
11986 this.maskEl.left.enableDisplayMode("block");
11987 this.maskEl.bottom.enableDisplayMode("block");
11988 this.maskEl.right.enableDisplayMode("block");
11990 this.toolTip = new Roo.bootstrap.Tooltip({
11991 cls : 'roo-form-error-popover',
11993 'left' : ['r-l', [-2,0], 'right'],
11994 'right' : ['l-r', [2,0], 'left'],
11995 'bottom' : ['tl-bl', [0,2], 'top'],
11996 'top' : [ 'bl-tl', [0,-2], 'bottom']
12000 this.toolTip.render(Roo.get(document.body));
12002 this.toolTip.el.enableDisplayMode("block");
12004 Roo.get(document.body).on('click', function(){
12008 Roo.get(document.body).on('touchstart', function(){
12012 this.isApplied = true
12015 mask : function(form, target)
12019 this.target = target;
12021 if(!this.form.errorMask || !target.el){
12025 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12027 Roo.log(scrollable);
12029 var ot = this.target.el.calcOffsetsTo(scrollable);
12031 var scrollTo = ot[1] - this.form.maskOffset;
12033 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12035 scrollable.scrollTo('top', scrollTo);
12037 var box = this.target.el.getBox();
12039 var zIndex = Roo.bootstrap.Modal.zIndex++;
12042 this.maskEl.top.setStyle('position', 'absolute');
12043 this.maskEl.top.setStyle('z-index', zIndex);
12044 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12045 this.maskEl.top.setLeft(0);
12046 this.maskEl.top.setTop(0);
12047 this.maskEl.top.show();
12049 this.maskEl.left.setStyle('position', 'absolute');
12050 this.maskEl.left.setStyle('z-index', zIndex);
12051 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12052 this.maskEl.left.setLeft(0);
12053 this.maskEl.left.setTop(box.y - this.padding);
12054 this.maskEl.left.show();
12056 this.maskEl.bottom.setStyle('position', 'absolute');
12057 this.maskEl.bottom.setStyle('z-index', zIndex);
12058 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12059 this.maskEl.bottom.setLeft(0);
12060 this.maskEl.bottom.setTop(box.bottom + this.padding);
12061 this.maskEl.bottom.show();
12063 this.maskEl.right.setStyle('position', 'absolute');
12064 this.maskEl.right.setStyle('z-index', zIndex);
12065 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12066 this.maskEl.right.setLeft(box.right + this.padding);
12067 this.maskEl.right.setTop(box.y - this.padding);
12068 this.maskEl.right.show();
12070 this.toolTip.bindEl = this.target.el;
12072 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12074 var tip = this.target.blankText;
12076 if(this.target.getValue() !== '' ) {
12078 if (this.target.invalidText.length) {
12079 tip = this.target.invalidText;
12080 } else if (this.target.regexText.length){
12081 tip = this.target.regexText;
12085 this.toolTip.show(tip);
12087 this.intervalID = window.setInterval(function() {
12088 Roo.bootstrap.form.Form.popover.unmask();
12091 window.onwheel = function(){ return false;};
12093 (function(){ this.isMasked = true; }).defer(500, this);
12097 unmask : function()
12099 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12103 this.maskEl.top.setStyle('position', 'absolute');
12104 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12105 this.maskEl.top.hide();
12107 this.maskEl.left.setStyle('position', 'absolute');
12108 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12109 this.maskEl.left.hide();
12111 this.maskEl.bottom.setStyle('position', 'absolute');
12112 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12113 this.maskEl.bottom.hide();
12115 this.maskEl.right.setStyle('position', 'absolute');
12116 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12117 this.maskEl.right.hide();
12119 this.toolTip.hide();
12121 this.toolTip.el.hide();
12123 window.onwheel = function(){ return true;};
12125 if(this.intervalID){
12126 window.clearInterval(this.intervalID);
12127 this.intervalID = false;
12130 this.isMasked = false;
12140 * Ext JS Library 1.1.1
12141 * Copyright(c) 2006-2007, Ext JS, LLC.
12143 * Originally Released Under LGPL - original licence link has changed is not relivant.
12146 * <script type="text/javascript">
12149 * @class Roo.form.VTypes
12150 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12153 Roo.form.VTypes = function(){
12154 // closure these in so they are only created once.
12155 var alpha = /^[a-zA-Z_]+$/;
12156 var alphanum = /^[a-zA-Z0-9_]+$/;
12157 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12158 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12160 // All these messages and functions are configurable
12163 * The function used to validate email addresses
12164 * @param {String} value The email address
12166 'email' : function(v){
12167 return email.test(v);
12170 * The error text to display when the email validation function returns false
12173 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12175 * The keystroke filter mask to be applied on email input
12178 'emailMask' : /[a-z0-9_\.\-@]/i,
12181 * The function used to validate URLs
12182 * @param {String} value The URL
12184 'url' : function(v){
12185 return url.test(v);
12188 * The error text to display when the url validation function returns false
12191 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12194 * The function used to validate alpha values
12195 * @param {String} value The value
12197 'alpha' : function(v){
12198 return alpha.test(v);
12201 * The error text to display when the alpha validation function returns false
12204 'alphaText' : 'This field should only contain letters and _',
12206 * The keystroke filter mask to be applied on alpha input
12209 'alphaMask' : /[a-z_]/i,
12212 * The function used to validate alphanumeric values
12213 * @param {String} value The value
12215 'alphanum' : function(v){
12216 return alphanum.test(v);
12219 * The error text to display when the alphanumeric validation function returns false
12222 'alphanumText' : 'This field should only contain letters, numbers and _',
12224 * The keystroke filter mask to be applied on alphanumeric input
12227 'alphanumMask' : /[a-z0-9_]/i
12237 * @class Roo.bootstrap.form.Input
12238 * @extends Roo.bootstrap.Component
12239 * Bootstrap Input class
12240 * @cfg {Boolean} disabled is it disabled
12241 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12242 * @cfg {String} name name of the input
12243 * @cfg {string} fieldLabel - the label associated
12244 * @cfg {string} placeholder - placeholder to put in text.
12245 * @cfg {string} before - input group add on before
12246 * @cfg {string} after - input group add on after
12247 * @cfg {string} size - (lg|sm) or leave empty..
12248 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12249 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12250 * @cfg {Number} md colspan out of 12 for computer-sized screens
12251 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12252 * @cfg {string} value default value of the input
12253 * @cfg {Number} labelWidth set the width of label
12254 * @cfg {Number} labellg set the width of label (1-12)
12255 * @cfg {Number} labelmd set the width of label (1-12)
12256 * @cfg {Number} labelsm set the width of label (1-12)
12257 * @cfg {Number} labelxs set the width of label (1-12)
12258 * @cfg {String} labelAlign (top|left)
12259 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12260 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12261 * @cfg {String} indicatorpos (left|right) default left
12262 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12263 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12264 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12265 * @cfg {Roo.bootstrap.Button} before Button to show before
12266 * @cfg {Roo.bootstrap.Button} afterButton to show before
12267 * @cfg {String} align (left|center|right) Default left
12268 * @cfg {Boolean} forceFeedback (true|false) Default false
12271 * Create a new Input
12272 * @param {Object} config The config object
12275 Roo.bootstrap.form.Input = function(config){
12277 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12282 * Fires when this field receives input focus.
12283 * @param {Roo.form.Field} this
12288 * Fires when this field loses input focus.
12289 * @param {Roo.form.Field} this
12293 * @event specialkey
12294 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12295 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12296 * @param {Roo.form.Field} this
12297 * @param {Roo.EventObject} e The event object
12302 * Fires just before the field blurs if the field value has changed.
12303 * @param {Roo.form.Field} this
12304 * @param {Mixed} newValue The new value
12305 * @param {Mixed} oldValue The original value
12310 * Fires after the field has been marked as invalid.
12311 * @param {Roo.form.Field} this
12312 * @param {String} msg The validation message
12317 * Fires after the field has been validated with no errors.
12318 * @param {Roo.form.Field} this
12323 * Fires after the key up
12324 * @param {Roo.form.Field} this
12325 * @param {Roo.EventObject} e The event Object
12330 * Fires after the user pastes into input
12331 * @param {Roo.form.Field} this
12332 * @param {Roo.EventObject} e The event Object
12338 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12340 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12341 automatic validation (defaults to "keyup").
12343 validationEvent : "keyup",
12345 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12347 validateOnBlur : true,
12349 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12351 validationDelay : 250,
12353 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12355 focusClass : "x-form-focus", // not needed???
12359 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12361 invalidClass : "has-warning",
12364 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12366 validClass : "has-success",
12369 * @cfg {Boolean} hasFeedback (true|false) default true
12371 hasFeedback : true,
12374 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12376 invalidFeedbackClass : "glyphicon-warning-sign",
12379 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12381 validFeedbackClass : "glyphicon-ok",
12384 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12386 selectOnFocus : false,
12389 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12393 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12398 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12400 disableKeyFilter : false,
12403 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12407 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12411 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12413 blankText : "Please complete this mandatory field",
12416 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12420 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12422 maxLength : Number.MAX_VALUE,
12424 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12426 minLengthText : "The minimum length for this field is {0}",
12428 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12430 maxLengthText : "The maximum length for this field is {0}",
12434 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12435 * If available, this function will be called only after the basic validators all return true, and will be passed the
12436 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12440 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12441 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12442 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12446 * @cfg {String} regexText -- Depricated - use Invalid Text
12451 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12457 autocomplete: false,
12461 inputType : 'text',
12464 placeholder: false,
12469 preventMark: false,
12470 isFormField : true,
12473 labelAlign : false,
12476 formatedValue : false,
12477 forceFeedback : false,
12479 indicatorpos : 'left',
12489 parentLabelAlign : function()
12492 while (parent.parent()) {
12493 parent = parent.parent();
12494 if (typeof(parent.labelAlign) !='undefined') {
12495 return parent.labelAlign;
12502 getAutoCreate : function()
12504 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12510 if(this.inputType != 'hidden'){
12511 cfg.cls = 'form-group' //input-group
12517 type : this.inputType,
12518 value : this.value,
12519 cls : 'form-control',
12520 placeholder : this.placeholder || '',
12521 autocomplete : this.autocomplete || 'new-password'
12523 if (this.inputType == 'file') {
12524 input.style = 'overflow:hidden'; // why not in CSS?
12527 if(this.capture.length){
12528 input.capture = this.capture;
12531 if(this.accept.length){
12532 input.accept = this.accept + "/*";
12536 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12539 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12540 input.maxLength = this.maxLength;
12543 if (this.disabled) {
12544 input.disabled=true;
12547 if (this.readOnly) {
12548 input.readonly=true;
12552 input.name = this.name;
12556 input.cls += ' input-' + this.size;
12560 ['xs','sm','md','lg'].map(function(size){
12561 if (settings[size]) {
12562 cfg.cls += ' col-' + size + '-' + settings[size];
12566 var inputblock = input;
12570 cls: 'glyphicon form-control-feedback'
12573 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12576 cls : 'has-feedback',
12584 if (this.before || this.after) {
12587 cls : 'input-group',
12591 if (this.before && typeof(this.before) == 'string') {
12593 inputblock.cn.push({
12595 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12599 if (this.before && typeof(this.before) == 'object') {
12600 this.before = Roo.factory(this.before);
12602 inputblock.cn.push({
12604 cls : 'roo-input-before input-group-prepend input-group-' +
12605 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12609 inputblock.cn.push(input);
12611 if (this.after && typeof(this.after) == 'string') {
12612 inputblock.cn.push({
12614 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12618 if (this.after && typeof(this.after) == 'object') {
12619 this.after = Roo.factory(this.after);
12621 inputblock.cn.push({
12623 cls : 'roo-input-after input-group-append input-group-' +
12624 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12628 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12629 inputblock.cls += ' has-feedback';
12630 inputblock.cn.push(feedback);
12635 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12636 tooltip : 'This field is required'
12638 if (this.allowBlank ) {
12639 indicator.style = this.allowBlank ? ' display:none' : '';
12641 if (align ==='left' && this.fieldLabel.length) {
12643 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12650 cls : 'control-label col-form-label',
12651 html : this.fieldLabel
12662 var labelCfg = cfg.cn[1];
12663 var contentCfg = cfg.cn[2];
12665 if(this.indicatorpos == 'right'){
12670 cls : 'control-label col-form-label',
12674 html : this.fieldLabel
12688 labelCfg = cfg.cn[0];
12689 contentCfg = cfg.cn[1];
12693 if(this.labelWidth > 12){
12694 labelCfg.style = "width: " + this.labelWidth + 'px';
12697 if(this.labelWidth < 13 && this.labelmd == 0){
12698 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12701 if(this.labellg > 0){
12702 labelCfg.cls += ' col-lg-' + this.labellg;
12703 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12706 if(this.labelmd > 0){
12707 labelCfg.cls += ' col-md-' + this.labelmd;
12708 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12711 if(this.labelsm > 0){
12712 labelCfg.cls += ' col-sm-' + this.labelsm;
12713 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12716 if(this.labelxs > 0){
12717 labelCfg.cls += ' col-xs-' + this.labelxs;
12718 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12722 } else if ( this.fieldLabel.length) {
12729 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12730 tooltip : 'This field is required',
12731 style : this.allowBlank ? ' display:none' : ''
12735 //cls : 'input-group-addon',
12736 html : this.fieldLabel
12744 if(this.indicatorpos == 'right'){
12749 //cls : 'input-group-addon',
12750 html : this.fieldLabel
12755 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12756 tooltip : 'This field is required',
12757 style : this.allowBlank ? ' display:none' : ''
12777 if (this.parentType === 'Navbar' && this.parent().bar) {
12778 cfg.cls += ' navbar-form';
12781 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12782 // on BS4 we do this only if not form
12783 cfg.cls += ' navbar-form';
12791 * return the real input element.
12793 inputEl: function ()
12795 return this.el.select('input.form-control',true).first();
12798 tooltipEl : function()
12800 return this.inputEl();
12803 indicatorEl : function()
12805 if (Roo.bootstrap.version == 4) {
12806 return false; // not enabled in v4 yet.
12809 var indicator = this.el.select('i.roo-required-indicator',true).first();
12819 setDisabled : function(v)
12821 var i = this.inputEl().dom;
12823 i.removeAttribute('disabled');
12827 i.setAttribute('disabled','true');
12829 initEvents : function()
12832 this.inputEl().on("keydown" , this.fireKey, this);
12833 this.inputEl().on("focus", this.onFocus, this);
12834 this.inputEl().on("blur", this.onBlur, this);
12836 this.inputEl().relayEvent('keyup', this);
12837 this.inputEl().relayEvent('paste', this);
12839 this.indicator = this.indicatorEl();
12841 if(this.indicator){
12842 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12845 // reference to original value for reset
12846 this.originalValue = this.getValue();
12847 //Roo.form.TextField.superclass.initEvents.call(this);
12848 if(this.validationEvent == 'keyup'){
12849 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12850 this.inputEl().on('keyup', this.filterValidation, this);
12852 else if(this.validationEvent !== false){
12853 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12856 if(this.selectOnFocus){
12857 this.on("focus", this.preFocus, this);
12860 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12861 this.inputEl().on("keypress", this.filterKeys, this);
12863 this.inputEl().relayEvent('keypress', this);
12866 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12867 this.el.on("click", this.autoSize, this);
12870 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12871 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12874 if (typeof(this.before) == 'object') {
12875 this.before.render(this.el.select('.roo-input-before',true).first());
12877 if (typeof(this.after) == 'object') {
12878 this.after.render(this.el.select('.roo-input-after',true).first());
12881 this.inputEl().on('change', this.onChange, this);
12884 filterValidation : function(e){
12885 if(!e.isNavKeyPress()){
12886 this.validationTask.delay(this.validationDelay);
12890 * Validates the field value
12891 * @return {Boolean} True if the value is valid, else false
12893 validate : function(){
12894 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12895 if(this.disabled || this.validateValue(this.getRawValue())){
12900 this.markInvalid();
12906 * Validates a value according to the field's validation rules and marks the field as invalid
12907 * if the validation fails
12908 * @param {Mixed} value The value to validate
12909 * @return {Boolean} True if the value is valid, else false
12911 validateValue : function(value)
12913 if(this.getVisibilityEl().hasClass('hidden')){
12917 if(value.length < 1) { // if it's blank
12918 if(this.allowBlank){
12924 if(value.length < this.minLength){
12927 if(value.length > this.maxLength){
12931 var vt = Roo.form.VTypes;
12932 if(!vt[this.vtype](value, this)){
12936 if(typeof this.validator == "function"){
12937 var msg = this.validator(value);
12941 if (typeof(msg) == 'string') {
12942 this.invalidText = msg;
12946 if(this.regex && !this.regex.test(value)){
12954 fireKey : function(e){
12955 //Roo.log('field ' + e.getKey());
12956 if(e.isNavKeyPress()){
12957 this.fireEvent("specialkey", this, e);
12960 focus : function (selectText){
12962 this.inputEl().focus();
12963 if(selectText === true){
12964 this.inputEl().dom.select();
12970 onFocus : function(){
12971 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12972 // this.el.addClass(this.focusClass);
12974 if(!this.hasFocus){
12975 this.hasFocus = true;
12976 this.startValue = this.getValue();
12977 this.fireEvent("focus", this);
12981 beforeBlur : Roo.emptyFn,
12985 onBlur : function(){
12987 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12988 //this.el.removeClass(this.focusClass);
12990 this.hasFocus = false;
12991 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12994 var v = this.getValue();
12995 if(String(v) !== String(this.startValue)){
12996 this.fireEvent('change', this, v, this.startValue);
12998 this.fireEvent("blur", this);
13001 onChange : function(e)
13003 var v = this.getValue();
13004 if(String(v) !== String(this.startValue)){
13005 this.fireEvent('change', this, v, this.startValue);
13011 * Resets the current field value to the originally loaded value and clears any validation messages
13013 reset : function(){
13014 this.setValue(this.originalValue);
13018 * Returns the name of the field
13019 * @return {Mixed} name The name field
13021 getName: function(){
13025 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13026 * @return {Mixed} value The field value
13028 getValue : function(){
13030 var v = this.inputEl().getValue();
13035 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13036 * @return {Mixed} value The field value
13038 getRawValue : function(){
13039 var v = this.inputEl().getValue();
13045 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13046 * @param {Mixed} value The value to set
13048 setRawValue : function(v){
13049 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13052 selectText : function(start, end){
13053 var v = this.getRawValue();
13055 start = start === undefined ? 0 : start;
13056 end = end === undefined ? v.length : end;
13057 var d = this.inputEl().dom;
13058 if(d.setSelectionRange){
13059 d.setSelectionRange(start, end);
13060 }else if(d.createTextRange){
13061 var range = d.createTextRange();
13062 range.moveStart("character", start);
13063 range.moveEnd("character", v.length-end);
13070 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13071 * @param {Mixed} value The value to set
13073 setValue : function(v){
13076 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13082 processValue : function(value){
13083 if(this.stripCharsRe){
13084 var newValue = value.replace(this.stripCharsRe, '');
13085 if(newValue !== value){
13086 this.setRawValue(newValue);
13093 preFocus : function(){
13095 if(this.selectOnFocus){
13096 this.inputEl().dom.select();
13099 filterKeys : function(e){
13100 var k = e.getKey();
13101 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13104 var c = e.getCharCode(), cc = String.fromCharCode(c);
13105 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13108 if(!this.maskRe.test(cc)){
13113 * Clear any invalid styles/messages for this field
13115 clearInvalid : function(){
13117 if(!this.el || this.preventMark){ // not rendered
13122 this.el.removeClass([this.invalidClass, 'is-invalid']);
13124 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13126 var feedback = this.el.select('.form-control-feedback', true).first();
13129 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13134 if(this.indicator){
13135 this.indicator.removeClass('visible');
13136 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13139 this.fireEvent('valid', this);
13143 * Mark this field as valid
13145 markValid : function()
13147 if(!this.el || this.preventMark){ // not rendered...
13151 this.el.removeClass([this.invalidClass, this.validClass]);
13152 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13154 var feedback = this.el.select('.form-control-feedback', true).first();
13157 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13160 if(this.indicator){
13161 this.indicator.removeClass('visible');
13162 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13170 if(this.allowBlank && !this.getRawValue().length){
13173 if (Roo.bootstrap.version == 3) {
13174 this.el.addClass(this.validClass);
13176 this.inputEl().addClass('is-valid');
13179 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13181 var feedback = this.el.select('.form-control-feedback', true).first();
13184 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13185 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13190 this.fireEvent('valid', this);
13194 * Mark this field as invalid
13195 * @param {String} msg The validation message
13197 markInvalid : function(msg)
13199 if(!this.el || this.preventMark){ // not rendered
13203 this.el.removeClass([this.invalidClass, this.validClass]);
13204 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13206 var feedback = this.el.select('.form-control-feedback', true).first();
13209 this.el.select('.form-control-feedback', true).first().removeClass(
13210 [this.invalidFeedbackClass, this.validFeedbackClass]);
13217 if(this.allowBlank && !this.getRawValue().length){
13221 if(this.indicator){
13222 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13223 this.indicator.addClass('visible');
13225 if (Roo.bootstrap.version == 3) {
13226 this.el.addClass(this.invalidClass);
13228 this.inputEl().addClass('is-invalid');
13233 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13235 var feedback = this.el.select('.form-control-feedback', true).first();
13238 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13240 if(this.getValue().length || this.forceFeedback){
13241 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13248 this.fireEvent('invalid', this, msg);
13251 SafariOnKeyDown : function(event)
13253 // this is a workaround for a password hang bug on chrome/ webkit.
13254 if (this.inputEl().dom.type != 'password') {
13258 var isSelectAll = false;
13260 if(this.inputEl().dom.selectionEnd > 0){
13261 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13263 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13264 event.preventDefault();
13269 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13271 event.preventDefault();
13272 // this is very hacky as keydown always get's upper case.
13274 var cc = String.fromCharCode(event.getCharCode());
13275 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13279 adjustWidth : function(tag, w){
13280 tag = tag.toLowerCase();
13281 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13282 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13283 if(tag == 'input'){
13286 if(tag == 'textarea'){
13289 }else if(Roo.isOpera){
13290 if(tag == 'input'){
13293 if(tag == 'textarea'){
13301 setFieldLabel : function(v)
13303 if(!this.rendered){
13307 if(this.indicatorEl()){
13308 var ar = this.el.select('label > span',true);
13310 if (ar.elements.length) {
13311 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13312 this.fieldLabel = v;
13316 var br = this.el.select('label',true);
13318 if(br.elements.length) {
13319 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13320 this.fieldLabel = v;
13324 Roo.log('Cannot Found any of label > span || label in input');
13328 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13329 this.fieldLabel = v;
13344 * @class Roo.bootstrap.form.TextArea
13345 * @extends Roo.bootstrap.form.Input
13346 * Bootstrap TextArea class
13347 * @cfg {Number} cols Specifies the visible width of a text area
13348 * @cfg {Number} rows Specifies the visible number of lines in a text area
13349 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13350 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13351 * @cfg {string} html text
13354 * Create a new TextArea
13355 * @param {Object} config The config object
13358 Roo.bootstrap.form.TextArea = function(config){
13359 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13363 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13373 getAutoCreate : function(){
13375 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13381 if(this.inputType != 'hidden'){
13382 cfg.cls = 'form-group' //input-group
13390 value : this.value || '',
13391 html: this.html || '',
13392 cls : 'form-control',
13393 placeholder : this.placeholder || ''
13397 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13398 input.maxLength = this.maxLength;
13402 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13406 input.cols = this.cols;
13409 if (this.readOnly) {
13410 input.readonly = true;
13414 input.name = this.name;
13418 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13422 ['xs','sm','md','lg'].map(function(size){
13423 if (settings[size]) {
13424 cfg.cls += ' col-' + size + '-' + settings[size];
13428 var inputblock = input;
13430 if(this.hasFeedback && !this.allowBlank){
13434 cls: 'glyphicon form-control-feedback'
13438 cls : 'has-feedback',
13447 if (this.before || this.after) {
13450 cls : 'input-group',
13454 inputblock.cn.push({
13456 cls : 'input-group-addon',
13461 inputblock.cn.push(input);
13463 if(this.hasFeedback && !this.allowBlank){
13464 inputblock.cls += ' has-feedback';
13465 inputblock.cn.push(feedback);
13469 inputblock.cn.push({
13471 cls : 'input-group-addon',
13478 if (align ==='left' && this.fieldLabel.length) {
13483 cls : 'control-label',
13484 html : this.fieldLabel
13495 if(this.labelWidth > 12){
13496 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13499 if(this.labelWidth < 13 && this.labelmd == 0){
13500 this.labelmd = this.labelWidth;
13503 if(this.labellg > 0){
13504 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13505 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13508 if(this.labelmd > 0){
13509 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13510 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13513 if(this.labelsm > 0){
13514 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13515 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13518 if(this.labelxs > 0){
13519 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13520 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13523 } else if ( this.fieldLabel.length) {
13528 //cls : 'input-group-addon',
13529 html : this.fieldLabel
13547 if (this.disabled) {
13548 input.disabled=true;
13555 * return the real textarea element.
13557 inputEl: function ()
13559 return this.el.select('textarea.form-control',true).first();
13563 * Clear any invalid styles/messages for this field
13565 clearInvalid : function()
13568 if(!this.el || this.preventMark){ // not rendered
13572 var label = this.el.select('label', true).first();
13573 var icon = this.el.select('i.fa-star', true).first();
13578 this.el.removeClass( this.validClass);
13579 this.inputEl().removeClass('is-invalid');
13581 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13583 var feedback = this.el.select('.form-control-feedback', true).first();
13586 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13591 this.fireEvent('valid', this);
13595 * Mark this field as valid
13597 markValid : function()
13599 if(!this.el || this.preventMark){ // not rendered
13603 this.el.removeClass([this.invalidClass, this.validClass]);
13604 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13606 var feedback = this.el.select('.form-control-feedback', true).first();
13609 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13612 if(this.disabled || this.allowBlank){
13616 var label = this.el.select('label', true).first();
13617 var icon = this.el.select('i.fa-star', true).first();
13622 if (Roo.bootstrap.version == 3) {
13623 this.el.addClass(this.validClass);
13625 this.inputEl().addClass('is-valid');
13629 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13631 var feedback = this.el.select('.form-control-feedback', true).first();
13634 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13635 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13640 this.fireEvent('valid', this);
13644 * Mark this field as invalid
13645 * @param {String} msg The validation message
13647 markInvalid : function(msg)
13649 if(!this.el || this.preventMark){ // not rendered
13653 this.el.removeClass([this.invalidClass, this.validClass]);
13654 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13656 var feedback = this.el.select('.form-control-feedback', true).first();
13659 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13662 if(this.disabled || this.allowBlank){
13666 var label = this.el.select('label', true).first();
13667 var icon = this.el.select('i.fa-star', true).first();
13669 if(!this.getValue().length && label && !icon){
13670 this.el.createChild({
13672 cls : 'text-danger fa fa-lg fa-star',
13673 tooltip : 'This field is required',
13674 style : 'margin-right:5px;'
13678 if (Roo.bootstrap.version == 3) {
13679 this.el.addClass(this.invalidClass);
13681 this.inputEl().addClass('is-invalid');
13684 // fixme ... this may be depricated need to test..
13685 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13687 var feedback = this.el.select('.form-control-feedback', true).first();
13690 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13692 if(this.getValue().length || this.forceFeedback){
13693 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13700 this.fireEvent('invalid', this, msg);
13708 * trigger field - base class for combo..
13713 * @class Roo.bootstrap.form.TriggerField
13714 * @extends Roo.bootstrap.form.Input
13715 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13716 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13717 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13718 * for which you can provide a custom implementation. For example:
13720 var trigger = new Roo.bootstrap.form.TriggerField();
13721 trigger.onTriggerClick = myTriggerFn;
13722 trigger.applyTo('my-field');
13725 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13726 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13727 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13728 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13729 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13732 * Create a new TriggerField.
13733 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13734 * to the base TextField)
13736 Roo.bootstrap.form.TriggerField = function(config){
13737 this.mimicing = false;
13738 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13741 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13743 * @cfg {String} triggerClass A CSS class to apply to the trigger
13746 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13751 * @cfg {Boolean} removable (true|false) special filter default false
13755 /** @cfg {Boolean} grow @hide */
13756 /** @cfg {Number} growMin @hide */
13757 /** @cfg {Number} growMax @hide */
13763 autoSize: Roo.emptyFn,
13767 deferHeight : true,
13770 actionMode : 'wrap',
13775 getAutoCreate : function(){
13777 var align = this.labelAlign || this.parentLabelAlign();
13782 cls: 'form-group' //input-group
13789 type : this.inputType,
13790 cls : 'form-control',
13791 autocomplete: 'new-password',
13792 placeholder : this.placeholder || ''
13796 input.name = this.name;
13799 input.cls += ' input-' + this.size;
13802 if (this.disabled) {
13803 input.disabled=true;
13806 var inputblock = input;
13808 if(this.hasFeedback && !this.allowBlank){
13812 cls: 'glyphicon form-control-feedback'
13815 if(this.removable && !this.editable ){
13817 cls : 'has-feedback',
13823 cls : 'roo-combo-removable-btn close'
13830 cls : 'has-feedback',
13839 if(this.removable && !this.editable ){
13841 cls : 'roo-removable',
13847 cls : 'roo-combo-removable-btn close'
13854 if (this.before || this.after) {
13857 cls : 'input-group',
13861 inputblock.cn.push({
13863 cls : 'input-group-addon input-group-prepend input-group-text',
13868 inputblock.cn.push(input);
13870 if(this.hasFeedback && !this.allowBlank){
13871 inputblock.cls += ' has-feedback';
13872 inputblock.cn.push(feedback);
13876 inputblock.cn.push({
13878 cls : 'input-group-addon input-group-append input-group-text',
13887 var ibwrap = inputblock;
13892 cls: 'roo-select2-choices',
13896 cls: 'roo-select2-search-field',
13908 cls: 'roo-select2-container input-group',
13913 cls: 'form-hidden-field'
13919 if(!this.multiple && this.showToggleBtn){
13925 if (this.caret != false) {
13928 cls: 'fa fa-' + this.caret
13935 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13937 Roo.bootstrap.version == 3 ? caret : '',
13940 cls: 'combobox-clear',
13954 combobox.cls += ' roo-select2-container-multi';
13958 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13959 tooltip : 'This field is required'
13961 if (Roo.bootstrap.version == 4) {
13964 style : 'display:none'
13969 if (align ==='left' && this.fieldLabel.length) {
13971 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13978 cls : 'control-label',
13979 html : this.fieldLabel
13991 var labelCfg = cfg.cn[1];
13992 var contentCfg = cfg.cn[2];
13994 if(this.indicatorpos == 'right'){
13999 cls : 'control-label',
14003 html : this.fieldLabel
14017 labelCfg = cfg.cn[0];
14018 contentCfg = cfg.cn[1];
14021 if(this.labelWidth > 12){
14022 labelCfg.style = "width: " + this.labelWidth + 'px';
14025 if(this.labelWidth < 13 && this.labelmd == 0){
14026 this.labelmd = this.labelWidth;
14029 if(this.labellg > 0){
14030 labelCfg.cls += ' col-lg-' + this.labellg;
14031 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14034 if(this.labelmd > 0){
14035 labelCfg.cls += ' col-md-' + this.labelmd;
14036 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14039 if(this.labelsm > 0){
14040 labelCfg.cls += ' col-sm-' + this.labelsm;
14041 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14044 if(this.labelxs > 0){
14045 labelCfg.cls += ' col-xs-' + this.labelxs;
14046 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14049 } else if ( this.fieldLabel.length) {
14050 // Roo.log(" label");
14055 //cls : 'input-group-addon',
14056 html : this.fieldLabel
14064 if(this.indicatorpos == 'right'){
14072 html : this.fieldLabel
14086 // Roo.log(" no label && no align");
14093 ['xs','sm','md','lg'].map(function(size){
14094 if (settings[size]) {
14095 cfg.cls += ' col-' + size + '-' + settings[size];
14106 onResize : function(w, h){
14107 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14108 // if(typeof w == 'number'){
14109 // var x = w - this.trigger.getWidth();
14110 // this.inputEl().setWidth(this.adjustWidth('input', x));
14111 // this.trigger.setStyle('left', x+'px');
14116 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14119 getResizeEl : function(){
14120 return this.inputEl();
14124 getPositionEl : function(){
14125 return this.inputEl();
14129 alignErrorIcon : function(){
14130 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14134 initEvents : function(){
14138 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14139 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14140 if(!this.multiple && this.showToggleBtn){
14141 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14142 if(this.hideTrigger){
14143 this.trigger.setDisplayed(false);
14145 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14149 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14152 if(this.removable && !this.editable && !this.tickable){
14153 var close = this.closeTriggerEl();
14156 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14157 close.on('click', this.removeBtnClick, this, close);
14161 //this.trigger.addClassOnOver('x-form-trigger-over');
14162 //this.trigger.addClassOnClick('x-form-trigger-click');
14165 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14169 closeTriggerEl : function()
14171 var close = this.el.select('.roo-combo-removable-btn', true).first();
14172 return close ? close : false;
14175 removeBtnClick : function(e, h, el)
14177 e.preventDefault();
14179 if(this.fireEvent("remove", this) !== false){
14181 this.fireEvent("afterremove", this)
14185 createList : function()
14187 this.list = Roo.get(document.body).createChild({
14188 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14189 cls: 'typeahead typeahead-long dropdown-menu shadow',
14190 style: 'display:none'
14193 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14198 initTrigger : function(){
14203 onDestroy : function(){
14205 this.trigger.removeAllListeners();
14206 // this.trigger.remove();
14209 // this.wrap.remove();
14211 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14215 onFocus : function(){
14216 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14218 if(!this.mimicing){
14219 this.wrap.addClass('x-trigger-wrap-focus');
14220 this.mimicing = true;
14221 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14222 if(this.monitorTab){
14223 this.el.on("keydown", this.checkTab, this);
14230 checkTab : function(e){
14231 if(e.getKey() == e.TAB){
14232 this.triggerBlur();
14237 onBlur : function(){
14242 mimicBlur : function(e, t){
14244 if(!this.wrap.contains(t) && this.validateBlur()){
14245 this.triggerBlur();
14251 triggerBlur : function(){
14252 this.mimicing = false;
14253 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14254 if(this.monitorTab){
14255 this.el.un("keydown", this.checkTab, this);
14257 //this.wrap.removeClass('x-trigger-wrap-focus');
14258 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14262 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14263 validateBlur : function(e, t){
14268 onDisable : function(){
14269 this.inputEl().dom.disabled = true;
14270 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14272 // this.wrap.addClass('x-item-disabled');
14277 onEnable : function(){
14278 this.inputEl().dom.disabled = false;
14279 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14281 // this.el.removeClass('x-item-disabled');
14286 onShow : function(){
14287 var ae = this.getActionEl();
14290 ae.dom.style.display = '';
14291 ae.dom.style.visibility = 'visible';
14297 onHide : function(){
14298 var ae = this.getActionEl();
14299 ae.dom.style.display = 'none';
14303 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14304 * by an implementing function.
14306 * @param {EventObject} e
14308 onTriggerClick : Roo.emptyFn
14316 * @class Roo.bootstrap.form.CardUploader
14317 * @extends Roo.bootstrap.Button
14318 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14319 * @cfg {Number} errorTimeout default 3000
14320 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14321 * @cfg {Array} html The button text.
14325 * Create a new CardUploader
14326 * @param {Object} config The config object
14329 Roo.bootstrap.form.CardUploader = function(config){
14333 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14336 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14344 * When a image is clicked on - and needs to display a slideshow or similar..
14345 * @param {Roo.bootstrap.Card} this
14346 * @param {Object} The image information data
14352 * When a the download link is clicked
14353 * @param {Roo.bootstrap.Card} this
14354 * @param {Object} The image information data contains
14361 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14364 errorTimeout : 3000,
14368 fileCollection : false,
14371 getAutoCreate : function()
14375 cls :'form-group' ,
14380 //cls : 'input-group-addon',
14381 html : this.fieldLabel
14389 value : this.value,
14390 cls : 'd-none form-control'
14395 multiple : 'multiple',
14397 cls : 'd-none roo-card-upload-selector'
14401 cls : 'roo-card-uploader-button-container w-100 mb-2'
14404 cls : 'card-columns roo-card-uploader-container'
14414 getChildContainer : function() /// what children are added to.
14416 return this.containerEl;
14419 getButtonContainer : function() /// what children are added to.
14421 return this.el.select(".roo-card-uploader-button-container").first();
14424 initEvents : function()
14427 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14431 xns: Roo.bootstrap,
14434 container_method : 'getButtonContainer' ,
14435 html : this.html, // fix changable?
14438 'click' : function(btn, e) {
14447 this.urlAPI = (window.createObjectURL && window) ||
14448 (window.URL && URL.revokeObjectURL && URL) ||
14449 (window.webkitURL && webkitURL);
14454 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14456 this.selectorEl.on('change', this.onFileSelected, this);
14459 this.images.forEach(function(img) {
14462 this.images = false;
14464 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14470 onClick : function(e)
14472 e.preventDefault();
14474 this.selectorEl.dom.click();
14478 onFileSelected : function(e)
14480 e.preventDefault();
14482 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14486 Roo.each(this.selectorEl.dom.files, function(file){
14487 this.addFile(file);
14496 addFile : function(file)
14499 if(typeof(file) === 'string'){
14500 throw "Add file by name?"; // should not happen
14504 if(!file || !this.urlAPI){
14514 var url = _this.urlAPI.createObjectURL( file);
14517 id : Roo.bootstrap.form.CardUploader.ID--,
14518 is_uploaded : false,
14522 mimetype : file.type,
14530 * addCard - add an Attachment to the uploader
14531 * @param data - the data about the image to upload
14535 title : "Title of file",
14536 is_uploaded : false,
14537 src : "http://.....",
14538 srcfile : { the File upload object },
14539 mimetype : file.type,
14542 .. any other data...
14548 addCard : function (data)
14550 // hidden input element?
14551 // if the file is not an image...
14552 //then we need to use something other that and header_image
14557 xns : Roo.bootstrap,
14558 xtype : 'CardFooter',
14561 xns : Roo.bootstrap,
14567 xns : Roo.bootstrap,
14569 html : String.format("<small>{0}</small>", data.title),
14570 cls : 'col-10 text-left',
14575 click : function() {
14577 t.fireEvent( "download", t, data );
14583 xns : Roo.bootstrap,
14585 style: 'max-height: 28px; ',
14591 click : function() {
14592 t.removeCard(data.id)
14604 var cn = this.addxtype(
14607 xns : Roo.bootstrap,
14610 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14611 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14612 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14617 initEvents : function() {
14618 Roo.bootstrap.Card.prototype.initEvents.call(this);
14620 this.imgEl = this.el.select('.card-img-top').first();
14622 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14623 this.imgEl.set({ 'pointer' : 'cursor' });
14626 this.getCardFooter().addClass('p-1');
14633 // dont' really need ot update items.
14634 // this.items.push(cn);
14635 this.fileCollection.add(cn);
14637 if (!data.srcfile) {
14638 this.updateInput();
14643 var reader = new FileReader();
14644 reader.addEventListener("load", function() {
14645 data.srcdata = reader.result;
14648 reader.readAsDataURL(data.srcfile);
14653 removeCard : function(id)
14656 var card = this.fileCollection.get(id);
14657 card.data.is_deleted = 1;
14658 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14659 //this.fileCollection.remove(card);
14660 //this.items = this.items.filter(function(e) { return e != card });
14661 // dont' really need ot update items.
14662 card.el.dom.parentNode.removeChild(card.el.dom);
14663 this.updateInput();
14669 this.fileCollection.each(function(card) {
14670 if (card.el.dom && card.el.dom.parentNode) {
14671 card.el.dom.parentNode.removeChild(card.el.dom);
14674 this.fileCollection.clear();
14675 this.updateInput();
14678 updateInput : function()
14681 this.fileCollection.each(function(e) {
14685 this.inputEl().dom.value = JSON.stringify(data);
14695 Roo.bootstrap.form.CardUploader.ID = -1;/*
14697 * Ext JS Library 1.1.1
14698 * Copyright(c) 2006-2007, Ext JS, LLC.
14700 * Originally Released Under LGPL - original licence link has changed is not relivant.
14703 * <script type="text/javascript">
14708 * @class Roo.data.SortTypes
14710 * Defines the default sorting (casting?) comparison functions used when sorting data.
14712 Roo.data.SortTypes = {
14714 * Default sort that does nothing
14715 * @param {Mixed} s The value being converted
14716 * @return {Mixed} The comparison value
14718 none : function(s){
14723 * The regular expression used to strip tags
14727 stripTagsRE : /<\/?[^>]+>/gi,
14730 * Strips all HTML tags to sort on text only
14731 * @param {Mixed} s The value being converted
14732 * @return {String} The comparison value
14734 asText : function(s){
14735 return String(s).replace(this.stripTagsRE, "");
14739 * Strips all HTML tags to sort on text only - Case insensitive
14740 * @param {Mixed} s The value being converted
14741 * @return {String} The comparison value
14743 asUCText : function(s){
14744 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14748 * Case insensitive string
14749 * @param {Mixed} s The value being converted
14750 * @return {String} The comparison value
14752 asUCString : function(s) {
14753 return String(s).toUpperCase();
14758 * @param {Mixed} s The value being converted
14759 * @return {Number} The comparison value
14761 asDate : function(s) {
14765 if(s instanceof Date){
14766 return s.getTime();
14768 return Date.parse(String(s));
14773 * @param {Mixed} s The value being converted
14774 * @return {Float} The comparison value
14776 asFloat : function(s) {
14777 var val = parseFloat(String(s).replace(/,/g, ""));
14786 * @param {Mixed} s The value being converted
14787 * @return {Number} The comparison value
14789 asInt : function(s) {
14790 var val = parseInt(String(s).replace(/,/g, ""));
14798 * Ext JS Library 1.1.1
14799 * Copyright(c) 2006-2007, Ext JS, LLC.
14801 * Originally Released Under LGPL - original licence link has changed is not relivant.
14804 * <script type="text/javascript">
14808 * @class Roo.data.Record
14809 * Instances of this class encapsulate both record <em>definition</em> information, and record
14810 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14811 * to access Records cached in an {@link Roo.data.Store} object.<br>
14813 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14814 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14817 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14819 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14820 * {@link #create}. The parameters are the same.
14821 * @param {Array} data An associative Array of data values keyed by the field name.
14822 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14823 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14824 * not specified an integer id is generated.
14826 Roo.data.Record = function(data, id){
14827 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14832 * Generate a constructor for a specific record layout.
14833 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14834 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14835 * Each field definition object may contain the following properties: <ul>
14836 * <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,
14837 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14838 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14839 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14840 * is being used, then this is a string containing the javascript expression to reference the data relative to
14841 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14842 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14843 * this may be omitted.</p></li>
14844 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14845 * <ul><li>auto (Default, implies no conversion)</li>
14850 * <li>date</li></ul></p></li>
14851 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14852 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14853 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14854 * by the Reader into an object that will be stored in the Record. It is passed the
14855 * following parameters:<ul>
14856 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14858 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14860 * <br>usage:<br><pre><code>
14861 var TopicRecord = Roo.data.Record.create(
14862 {name: 'title', mapping: 'topic_title'},
14863 {name: 'author', mapping: 'username'},
14864 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14865 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14866 {name: 'lastPoster', mapping: 'user2'},
14867 {name: 'excerpt', mapping: 'post_text'}
14870 var myNewRecord = new TopicRecord({
14871 title: 'Do my job please',
14874 lastPost: new Date(),
14875 lastPoster: 'Animal',
14876 excerpt: 'No way dude!'
14878 myStore.add(myNewRecord);
14883 Roo.data.Record.create = function(o){
14884 var f = function(){
14885 f.superclass.constructor.apply(this, arguments);
14887 Roo.extend(f, Roo.data.Record);
14888 var p = f.prototype;
14889 p.fields = new Roo.util.MixedCollection(false, function(field){
14892 for(var i = 0, len = o.length; i < len; i++){
14893 p.fields.add(new Roo.data.Field(o[i]));
14895 f.getField = function(name){
14896 return p.fields.get(name);
14901 Roo.data.Record.AUTO_ID = 1000;
14902 Roo.data.Record.EDIT = 'edit';
14903 Roo.data.Record.REJECT = 'reject';
14904 Roo.data.Record.COMMIT = 'commit';
14906 Roo.data.Record.prototype = {
14908 * Readonly flag - true if this record has been modified.
14917 join : function(store){
14918 this.store = store;
14922 * Set the named field to the specified value.
14923 * @param {String} name The name of the field to set.
14924 * @param {Object} value The value to set the field to.
14926 set : function(name, value){
14927 if(this.data[name] == value){
14931 if(!this.modified){
14932 this.modified = {};
14934 if(typeof this.modified[name] == 'undefined'){
14935 this.modified[name] = this.data[name];
14937 this.data[name] = value;
14938 if(!this.editing && this.store){
14939 this.store.afterEdit(this);
14944 * Get the value of the named field.
14945 * @param {String} name The name of the field to get the value of.
14946 * @return {Object} The value of the field.
14948 get : function(name){
14949 return this.data[name];
14953 beginEdit : function(){
14954 this.editing = true;
14955 this.modified = {};
14959 cancelEdit : function(){
14960 this.editing = false;
14961 delete this.modified;
14965 endEdit : function(){
14966 this.editing = false;
14967 if(this.dirty && this.store){
14968 this.store.afterEdit(this);
14973 * Usually called by the {@link Roo.data.Store} which owns the Record.
14974 * Rejects all changes made to the Record since either creation, or the last commit operation.
14975 * Modified fields are reverted to their original values.
14977 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14978 * of reject operations.
14980 reject : function(){
14981 var m = this.modified;
14983 if(typeof m[n] != "function"){
14984 this.data[n] = m[n];
14987 this.dirty = false;
14988 delete this.modified;
14989 this.editing = false;
14991 this.store.afterReject(this);
14996 * Usually called by the {@link Roo.data.Store} which owns the Record.
14997 * Commits all changes made to the Record since either creation, or the last commit operation.
14999 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15000 * of commit operations.
15002 commit : function(){
15003 this.dirty = false;
15004 delete this.modified;
15005 this.editing = false;
15007 this.store.afterCommit(this);
15012 hasError : function(){
15013 return this.error != null;
15017 clearError : function(){
15022 * Creates a copy of this record.
15023 * @param {String} id (optional) A new record id if you don't want to use this record's id
15026 copy : function(newId) {
15027 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15031 * Ext JS Library 1.1.1
15032 * Copyright(c) 2006-2007, Ext JS, LLC.
15034 * Originally Released Under LGPL - original licence link has changed is not relivant.
15037 * <script type="text/javascript">
15043 * @class Roo.data.Store
15044 * @extends Roo.util.Observable
15045 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15046 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15048 * 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
15049 * has no knowledge of the format of the data returned by the Proxy.<br>
15051 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15052 * instances from the data object. These records are cached and made available through accessor functions.
15054 * Creates a new Store.
15055 * @param {Object} config A config object containing the objects needed for the Store to access data,
15056 * and read the data into Records.
15058 Roo.data.Store = function(config){
15059 this.data = new Roo.util.MixedCollection(false);
15060 this.data.getKey = function(o){
15063 this.baseParams = {};
15065 this.paramNames = {
15070 "multisort" : "_multisort"
15073 if(config && config.data){
15074 this.inlineData = config.data;
15075 delete config.data;
15078 Roo.apply(this, config);
15080 if(this.reader){ // reader passed
15081 this.reader = Roo.factory(this.reader, Roo.data);
15082 this.reader.xmodule = this.xmodule || false;
15083 if(!this.recordType){
15084 this.recordType = this.reader.recordType;
15086 if(this.reader.onMetaChange){
15087 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15091 if(this.recordType){
15092 this.fields = this.recordType.prototype.fields;
15094 this.modified = [];
15098 * @event datachanged
15099 * Fires when the data cache has changed, and a widget which is using this Store
15100 * as a Record cache should refresh its view.
15101 * @param {Store} this
15103 datachanged : true,
15105 * @event metachange
15106 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15107 * @param {Store} this
15108 * @param {Object} meta The JSON metadata
15113 * Fires when Records have been added to the Store
15114 * @param {Store} this
15115 * @param {Roo.data.Record[]} records The array of Records added
15116 * @param {Number} index The index at which the record(s) were added
15121 * Fires when a Record has been removed from the Store
15122 * @param {Store} this
15123 * @param {Roo.data.Record} record The Record that was removed
15124 * @param {Number} index The index at which the record was removed
15129 * Fires when a Record has been updated
15130 * @param {Store} this
15131 * @param {Roo.data.Record} record The Record that was updated
15132 * @param {String} operation The update operation being performed. Value may be one of:
15134 Roo.data.Record.EDIT
15135 Roo.data.Record.REJECT
15136 Roo.data.Record.COMMIT
15142 * Fires when the data cache has been cleared.
15143 * @param {Store} this
15147 * @event beforeload
15148 * Fires before a request is made for a new data object. If the beforeload handler returns false
15149 * the load action will be canceled.
15150 * @param {Store} this
15151 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15155 * @event beforeloadadd
15156 * Fires after a new set of Records has been loaded.
15157 * @param {Store} this
15158 * @param {Roo.data.Record[]} records The Records that were loaded
15159 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15161 beforeloadadd : true,
15164 * Fires after a new set of Records has been loaded, before they are added to the store.
15165 * @param {Store} this
15166 * @param {Roo.data.Record[]} records The Records that were loaded
15167 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15168 * @params {Object} return from reader
15172 * @event loadexception
15173 * Fires if an exception occurs in the Proxy during loading.
15174 * Called with the signature of the Proxy's "loadexception" event.
15175 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15178 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15179 * @param {Object} load options
15180 * @param {Object} jsonData from your request (normally this contains the Exception)
15182 loadexception : true
15186 this.proxy = Roo.factory(this.proxy, Roo.data);
15187 this.proxy.xmodule = this.xmodule || false;
15188 this.relayEvents(this.proxy, ["loadexception"]);
15190 this.sortToggle = {};
15191 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15193 Roo.data.Store.superclass.constructor.call(this);
15195 if(this.inlineData){
15196 this.loadData(this.inlineData);
15197 delete this.inlineData;
15201 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15203 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15204 * without a remote query - used by combo/forms at present.
15208 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15211 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15214 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15215 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15218 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15219 * on any HTTP request
15222 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15225 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15229 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15230 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15232 remoteSort : false,
15235 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15236 * loaded or when a record is removed. (defaults to false).
15238 pruneModifiedRecords : false,
15241 lastOptions : null,
15244 * Add Records to the Store and fires the add event.
15245 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15247 add : function(records){
15248 records = [].concat(records);
15249 for(var i = 0, len = records.length; i < len; i++){
15250 records[i].join(this);
15252 var index = this.data.length;
15253 this.data.addAll(records);
15254 this.fireEvent("add", this, records, index);
15258 * Remove a Record from the Store and fires the remove event.
15259 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15261 remove : function(record){
15262 var index = this.data.indexOf(record);
15263 this.data.removeAt(index);
15265 if(this.pruneModifiedRecords){
15266 this.modified.remove(record);
15268 this.fireEvent("remove", this, record, index);
15272 * Remove all Records from the Store and fires the clear event.
15274 removeAll : function(){
15276 if(this.pruneModifiedRecords){
15277 this.modified = [];
15279 this.fireEvent("clear", this);
15283 * Inserts Records to the Store at the given index and fires the add event.
15284 * @param {Number} index The start index at which to insert the passed Records.
15285 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15287 insert : function(index, records){
15288 records = [].concat(records);
15289 for(var i = 0, len = records.length; i < len; i++){
15290 this.data.insert(index, records[i]);
15291 records[i].join(this);
15293 this.fireEvent("add", this, records, index);
15297 * Get the index within the cache of the passed Record.
15298 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15299 * @return {Number} The index of the passed Record. Returns -1 if not found.
15301 indexOf : function(record){
15302 return this.data.indexOf(record);
15306 * Get the index within the cache of the Record with the passed id.
15307 * @param {String} id The id of the Record to find.
15308 * @return {Number} The index of the Record. Returns -1 if not found.
15310 indexOfId : function(id){
15311 return this.data.indexOfKey(id);
15315 * Get the Record with the specified id.
15316 * @param {String} id The id of the Record to find.
15317 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15319 getById : function(id){
15320 return this.data.key(id);
15324 * Get the Record at the specified index.
15325 * @param {Number} index The index of the Record to find.
15326 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15328 getAt : function(index){
15329 return this.data.itemAt(index);
15333 * Returns a range of Records between specified indices.
15334 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15335 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15336 * @return {Roo.data.Record[]} An array of Records
15338 getRange : function(start, end){
15339 return this.data.getRange(start, end);
15343 storeOptions : function(o){
15344 o = Roo.apply({}, o);
15347 this.lastOptions = o;
15351 * Loads the Record cache from the configured Proxy using the configured Reader.
15353 * If using remote paging, then the first load call must specify the <em>start</em>
15354 * and <em>limit</em> properties in the options.params property to establish the initial
15355 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15357 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15358 * and this call will return before the new data has been loaded. Perform any post-processing
15359 * in a callback function, or in a "load" event handler.</strong>
15361 * @param {Object} options An object containing properties which control loading options:<ul>
15362 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15363 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15364 * passed the following arguments:<ul>
15365 * <li>r : Roo.data.Record[]</li>
15366 * <li>options: Options object from the load call</li>
15367 * <li>success: Boolean success indicator</li></ul></li>
15368 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15369 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15372 load : function(options){
15373 options = options || {};
15374 if(this.fireEvent("beforeload", this, options) !== false){
15375 this.storeOptions(options);
15376 var p = Roo.apply(options.params || {}, this.baseParams);
15377 // if meta was not loaded from remote source.. try requesting it.
15378 if (!this.reader.metaFromRemote) {
15379 p._requestMeta = 1;
15381 if(this.sortInfo && this.remoteSort){
15382 var pn = this.paramNames;
15383 p[pn["sort"]] = this.sortInfo.field;
15384 p[pn["dir"]] = this.sortInfo.direction;
15386 if (this.multiSort) {
15387 var pn = this.paramNames;
15388 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15391 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15396 * Reloads the Record cache from the configured Proxy using the configured Reader and
15397 * the options from the last load operation performed.
15398 * @param {Object} options (optional) An object containing properties which may override the options
15399 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15400 * the most recently used options are reused).
15402 reload : function(options){
15403 this.load(Roo.applyIf(options||{}, this.lastOptions));
15407 // Called as a callback by the Reader during a load operation.
15408 loadRecords : function(o, options, success){
15411 if(success !== false){
15412 this.fireEvent("load", this, [], options, o);
15414 if(options.callback){
15415 options.callback.call(options.scope || this, [], options, false);
15419 // if data returned failure - throw an exception.
15420 if (o.success === false) {
15421 // show a message if no listener is registered.
15422 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15423 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15425 // loadmask wil be hooked into this..
15426 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15429 var r = o.records, t = o.totalRecords || r.length;
15431 this.fireEvent("beforeloadadd", this, r, options, o);
15433 if(!options || options.add !== true){
15434 if(this.pruneModifiedRecords){
15435 this.modified = [];
15437 for(var i = 0, len = r.length; i < len; i++){
15441 this.data = this.snapshot;
15442 delete this.snapshot;
15445 this.data.addAll(r);
15446 this.totalLength = t;
15448 this.fireEvent("datachanged", this);
15450 this.totalLength = Math.max(t, this.data.length+r.length);
15454 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15456 var e = new Roo.data.Record({});
15458 e.set(this.parent.displayField, this.parent.emptyTitle);
15459 e.set(this.parent.valueField, '');
15464 this.fireEvent("load", this, r, options, o);
15465 if(options.callback){
15466 options.callback.call(options.scope || this, r, options, true);
15472 * Loads data from a passed data block. A Reader which understands the format of the data
15473 * must have been configured in the constructor.
15474 * @param {Object} data The data block from which to read the Records. The format of the data expected
15475 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15476 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15478 loadData : function(o, append){
15479 var r = this.reader.readRecords(o);
15480 this.loadRecords(r, {add: append}, true);
15484 * using 'cn' the nested child reader read the child array into it's child stores.
15485 * @param {Object} rec The record with a 'children array
15487 loadDataFromChildren : function(rec)
15489 this.loadData(this.reader.toLoadData(rec));
15494 * Gets the number of cached records.
15496 * <em>If using paging, this may not be the total size of the dataset. If the data object
15497 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15498 * the data set size</em>
15500 getCount : function(){
15501 return this.data.length || 0;
15505 * Gets the total number of records in the dataset as returned by the server.
15507 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15508 * the dataset size</em>
15510 getTotalCount : function(){
15511 return this.totalLength || 0;
15515 * Returns the sort state of the Store as an object with two properties:
15517 field {String} The name of the field by which the Records are sorted
15518 direction {String} The sort order, "ASC" or "DESC"
15521 getSortState : function(){
15522 return this.sortInfo;
15526 applySort : function(){
15527 if(this.sortInfo && !this.remoteSort){
15528 var s = this.sortInfo, f = s.field;
15529 var st = this.fields.get(f).sortType;
15530 var fn = function(r1, r2){
15531 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15532 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15534 this.data.sort(s.direction, fn);
15535 if(this.snapshot && this.snapshot != this.data){
15536 this.snapshot.sort(s.direction, fn);
15542 * Sets the default sort column and order to be used by the next load operation.
15543 * @param {String} fieldName The name of the field to sort by.
15544 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15546 setDefaultSort : function(field, dir){
15547 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15551 * Sort the Records.
15552 * If remote sorting is used, the sort is performed on the server, and the cache is
15553 * reloaded. If local sorting is used, the cache is sorted internally.
15554 * @param {String} fieldName The name of the field to sort by.
15555 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15557 sort : function(fieldName, dir){
15558 var f = this.fields.get(fieldName);
15560 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15562 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15563 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15568 this.sortToggle[f.name] = dir;
15569 this.sortInfo = {field: f.name, direction: dir};
15570 if(!this.remoteSort){
15572 this.fireEvent("datachanged", this);
15574 this.load(this.lastOptions);
15579 * Calls the specified function for each of the Records in the cache.
15580 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15581 * Returning <em>false</em> aborts and exits the iteration.
15582 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15584 each : function(fn, scope){
15585 this.data.each(fn, scope);
15589 * Gets all records modified since the last commit. Modified records are persisted across load operations
15590 * (e.g., during paging).
15591 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15593 getModifiedRecords : function(){
15594 return this.modified;
15598 createFilterFn : function(property, value, anyMatch){
15599 if(!value.exec){ // not a regex
15600 value = String(value);
15601 if(value.length == 0){
15604 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15606 return function(r){
15607 return value.test(r.data[property]);
15612 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15613 * @param {String} property A field on your records
15614 * @param {Number} start The record index to start at (defaults to 0)
15615 * @param {Number} end The last record index to include (defaults to length - 1)
15616 * @return {Number} The sum
15618 sum : function(property, start, end){
15619 var rs = this.data.items, v = 0;
15620 start = start || 0;
15621 end = (end || end === 0) ? end : rs.length-1;
15623 for(var i = start; i <= end; i++){
15624 v += (rs[i].data[property] || 0);
15630 * Filter the records by a specified property.
15631 * @param {String} field A field on your records
15632 * @param {String/RegExp} value Either a string that the field
15633 * should start with or a RegExp to test against the field
15634 * @param {Boolean} anyMatch True to match any part not just the beginning
15636 filter : function(property, value, anyMatch){
15637 var fn = this.createFilterFn(property, value, anyMatch);
15638 return fn ? this.filterBy(fn) : this.clearFilter();
15642 * Filter by a function. The specified function will be called with each
15643 * record in this data source. If the function returns true the record is included,
15644 * otherwise it is filtered.
15645 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15646 * @param {Object} scope (optional) The scope of the function (defaults to this)
15648 filterBy : function(fn, scope){
15649 this.snapshot = this.snapshot || this.data;
15650 this.data = this.queryBy(fn, scope||this);
15651 this.fireEvent("datachanged", this);
15655 * Query the records by a specified property.
15656 * @param {String} field A field on your records
15657 * @param {String/RegExp} value Either a string that the field
15658 * should start with or a RegExp to test against the field
15659 * @param {Boolean} anyMatch True to match any part not just the beginning
15660 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15662 query : function(property, value, anyMatch){
15663 var fn = this.createFilterFn(property, value, anyMatch);
15664 return fn ? this.queryBy(fn) : this.data.clone();
15668 * Query by a function. The specified function will be called with each
15669 * record in this data source. If the function returns true the record is included
15671 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15672 * @param {Object} scope (optional) The scope of the function (defaults to this)
15673 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15675 queryBy : function(fn, scope){
15676 var data = this.snapshot || this.data;
15677 return data.filterBy(fn, scope||this);
15681 * Collects unique values for a particular dataIndex from this store.
15682 * @param {String} dataIndex The property to collect
15683 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15684 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15685 * @return {Array} An array of the unique values
15687 collect : function(dataIndex, allowNull, bypassFilter){
15688 var d = (bypassFilter === true && this.snapshot) ?
15689 this.snapshot.items : this.data.items;
15690 var v, sv, r = [], l = {};
15691 for(var i = 0, len = d.length; i < len; i++){
15692 v = d[i].data[dataIndex];
15694 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15703 * Revert to a view of the Record cache with no filtering applied.
15704 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15706 clearFilter : function(suppressEvent){
15707 if(this.snapshot && this.snapshot != this.data){
15708 this.data = this.snapshot;
15709 delete this.snapshot;
15710 if(suppressEvent !== true){
15711 this.fireEvent("datachanged", this);
15717 afterEdit : function(record){
15718 if(this.modified.indexOf(record) == -1){
15719 this.modified.push(record);
15721 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15725 afterReject : function(record){
15726 this.modified.remove(record);
15727 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15731 afterCommit : function(record){
15732 this.modified.remove(record);
15733 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15737 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15738 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15740 commitChanges : function(){
15741 var m = this.modified.slice(0);
15742 this.modified = [];
15743 for(var i = 0, len = m.length; i < len; i++){
15749 * Cancel outstanding changes on all changed records.
15751 rejectChanges : function(){
15752 var m = this.modified.slice(0);
15753 this.modified = [];
15754 for(var i = 0, len = m.length; i < len; i++){
15759 onMetaChange : function(meta, rtype, o){
15760 this.recordType = rtype;
15761 this.fields = rtype.prototype.fields;
15762 delete this.snapshot;
15763 this.sortInfo = meta.sortInfo || this.sortInfo;
15764 this.modified = [];
15765 this.fireEvent('metachange', this, this.reader.meta);
15768 moveIndex : function(data, type)
15770 var index = this.indexOf(data);
15772 var newIndex = index + type;
15776 this.insert(newIndex, data);
15781 * Ext JS Library 1.1.1
15782 * Copyright(c) 2006-2007, Ext JS, LLC.
15784 * Originally Released Under LGPL - original licence link has changed is not relivant.
15787 * <script type="text/javascript">
15791 * @class Roo.data.SimpleStore
15792 * @extends Roo.data.Store
15793 * Small helper class to make creating Stores from Array data easier.
15794 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15795 * @cfg {Array} fields An array of field definition objects, or field name strings.
15796 * @cfg {Object} an existing reader (eg. copied from another store)
15797 * @cfg {Array} data The multi-dimensional array of data
15798 * @cfg {Roo.data.DataProxy} proxy [not-required]
15799 * @cfg {Roo.data.Reader} reader [not-required]
15801 * @param {Object} config
15803 Roo.data.SimpleStore = function(config)
15805 Roo.data.SimpleStore.superclass.constructor.call(this, {
15807 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15810 Roo.data.Record.create(config.fields)
15812 proxy : new Roo.data.MemoryProxy(config.data)
15816 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15818 * Ext JS Library 1.1.1
15819 * Copyright(c) 2006-2007, Ext JS, LLC.
15821 * Originally Released Under LGPL - original licence link has changed is not relivant.
15824 * <script type="text/javascript">
15829 * @extends Roo.data.Store
15830 * @class Roo.data.JsonStore
15831 * Small helper class to make creating Stores for JSON data easier. <br/>
15833 var store = new Roo.data.JsonStore({
15834 url: 'get-images.php',
15836 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15839 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15840 * JsonReader and HttpProxy (unless inline data is provided).</b>
15841 * @cfg {Array} fields An array of field definition objects, or field name strings.
15843 * @param {Object} config
15845 Roo.data.JsonStore = function(c){
15846 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15847 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15848 reader: new Roo.data.JsonReader(c, c.fields)
15851 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15853 * Ext JS Library 1.1.1
15854 * Copyright(c) 2006-2007, Ext JS, LLC.
15856 * Originally Released Under LGPL - original licence link has changed is not relivant.
15859 * <script type="text/javascript">
15863 Roo.data.Field = function(config){
15864 if(typeof config == "string"){
15865 config = {name: config};
15867 Roo.apply(this, config);
15870 this.type = "auto";
15873 var st = Roo.data.SortTypes;
15874 // named sortTypes are supported, here we look them up
15875 if(typeof this.sortType == "string"){
15876 this.sortType = st[this.sortType];
15879 // set default sortType for strings and dates
15880 if(!this.sortType){
15883 this.sortType = st.asUCString;
15886 this.sortType = st.asDate;
15889 this.sortType = st.none;
15894 var stripRe = /[\$,%]/g;
15896 // prebuilt conversion function for this field, instead of
15897 // switching every time we're reading a value
15899 var cv, dateFormat = this.dateFormat;
15904 cv = function(v){ return v; };
15907 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15911 return v !== undefined && v !== null && v !== '' ?
15912 parseInt(String(v).replace(stripRe, ""), 10) : '';
15917 return v !== undefined && v !== null && v !== '' ?
15918 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15923 cv = function(v){ return v === true || v === "true" || v == 1; };
15930 if(v instanceof Date){
15934 if(dateFormat == "timestamp"){
15935 return new Date(v*1000);
15937 return Date.parseDate(v, dateFormat);
15939 var parsed = Date.parse(v);
15940 return parsed ? new Date(parsed) : null;
15949 Roo.data.Field.prototype = {
15957 * Ext JS Library 1.1.1
15958 * Copyright(c) 2006-2007, Ext JS, LLC.
15960 * Originally Released Under LGPL - original licence link has changed is not relivant.
15963 * <script type="text/javascript">
15966 // Base class for reading structured data from a data source. This class is intended to be
15967 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15970 * @class Roo.data.DataReader
15972 * Base class for reading structured data from a data source. This class is intended to be
15973 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15976 Roo.data.DataReader = function(meta, recordType){
15980 this.recordType = recordType instanceof Array ?
15981 Roo.data.Record.create(recordType) : recordType;
15984 Roo.data.DataReader.prototype = {
15987 readerType : 'Data',
15989 * Create an empty record
15990 * @param {Object} data (optional) - overlay some values
15991 * @return {Roo.data.Record} record created.
15993 newRow : function(d) {
15995 this.recordType.prototype.fields.each(function(c) {
15997 case 'int' : da[c.name] = 0; break;
15998 case 'date' : da[c.name] = new Date(); break;
15999 case 'float' : da[c.name] = 0.0; break;
16000 case 'boolean' : da[c.name] = false; break;
16001 default : da[c.name] = ""; break;
16005 return new this.recordType(Roo.apply(da, d));
16011 * Ext JS Library 1.1.1
16012 * Copyright(c) 2006-2007, Ext JS, LLC.
16014 * Originally Released Under LGPL - original licence link has changed is not relivant.
16017 * <script type="text/javascript">
16021 * @class Roo.data.DataProxy
16022 * @extends Roo.util.Observable
16024 * This class is an abstract base class for implementations which provide retrieval of
16025 * unformatted data objects.<br>
16027 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16028 * (of the appropriate type which knows how to parse the data object) to provide a block of
16029 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16031 * Custom implementations must implement the load method as described in
16032 * {@link Roo.data.HttpProxy#load}.
16034 Roo.data.DataProxy = function(){
16037 * @event beforeload
16038 * Fires before a network request is made to retrieve a data object.
16039 * @param {Object} This DataProxy object.
16040 * @param {Object} params The params parameter to the load function.
16045 * Fires before the load method's callback is called.
16046 * @param {Object} This DataProxy object.
16047 * @param {Object} o The data object.
16048 * @param {Object} arg The callback argument object passed to the load function.
16052 * @event loadexception
16053 * Fires if an Exception occurs during data retrieval.
16054 * @param {Object} This DataProxy object.
16055 * @param {Object} o The data object.
16056 * @param {Object} arg The callback argument object passed to the load function.
16057 * @param {Object} e The Exception.
16059 loadexception : true
16061 Roo.data.DataProxy.superclass.constructor.call(this);
16064 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16067 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16071 * Ext JS Library 1.1.1
16072 * Copyright(c) 2006-2007, Ext JS, LLC.
16074 * Originally Released Under LGPL - original licence link has changed is not relivant.
16077 * <script type="text/javascript">
16080 * @class Roo.data.MemoryProxy
16081 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16082 * to the Reader when its load method is called.
16084 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16086 Roo.data.MemoryProxy = function(data){
16090 Roo.data.MemoryProxy.superclass.constructor.call(this);
16094 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16097 * Load data from the requested source (in this case an in-memory
16098 * data object passed to the constructor), read the data object into
16099 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16100 * process that block using the passed callback.
16101 * @param {Object} params This parameter is not used by the MemoryProxy class.
16102 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16103 * object into a block of Roo.data.Records.
16104 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16105 * The function must be passed <ul>
16106 * <li>The Record block object</li>
16107 * <li>The "arg" argument from the load function</li>
16108 * <li>A boolean success indicator</li>
16110 * @param {Object} scope The scope in which to call the callback
16111 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16113 load : function(params, reader, callback, scope, arg){
16114 params = params || {};
16117 result = reader.readRecords(params.data ? params.data :this.data);
16119 this.fireEvent("loadexception", this, arg, null, e);
16120 callback.call(scope, null, arg, false);
16123 callback.call(scope, result, arg, true);
16127 update : function(params, records){
16132 * Ext JS Library 1.1.1
16133 * Copyright(c) 2006-2007, Ext JS, LLC.
16135 * Originally Released Under LGPL - original licence link has changed is not relivant.
16138 * <script type="text/javascript">
16141 * @class Roo.data.HttpProxy
16142 * @extends Roo.data.DataProxy
16143 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16144 * configured to reference a certain URL.<br><br>
16146 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16147 * from which the running page was served.<br><br>
16149 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16151 * Be aware that to enable the browser to parse an XML document, the server must set
16152 * the Content-Type header in the HTTP response to "text/xml".
16154 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16155 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16156 * will be used to make the request.
16158 Roo.data.HttpProxy = function(conn){
16159 Roo.data.HttpProxy.superclass.constructor.call(this);
16160 // is conn a conn config or a real conn?
16162 this.useAjax = !conn || !conn.events;
16166 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16167 // thse are take from connection...
16170 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16173 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16174 * extra parameters to each request made by this object. (defaults to undefined)
16177 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16178 * to each request made by this object. (defaults to undefined)
16181 * @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)
16184 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16187 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16193 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16197 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16198 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16199 * a finer-grained basis than the DataProxy events.
16201 getConnection : function(){
16202 return this.useAjax ? Roo.Ajax : this.conn;
16206 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16207 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16208 * process that block using the passed callback.
16209 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16210 * for the request to the remote server.
16211 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16212 * object into a block of Roo.data.Records.
16213 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16214 * The function must be passed <ul>
16215 * <li>The Record block object</li>
16216 * <li>The "arg" argument from the load function</li>
16217 * <li>A boolean success indicator</li>
16219 * @param {Object} scope The scope in which to call the callback
16220 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16222 load : function(params, reader, callback, scope, arg){
16223 if(this.fireEvent("beforeload", this, params) !== false){
16225 params : params || {},
16227 callback : callback,
16232 callback : this.loadResponse,
16236 Roo.applyIf(o, this.conn);
16237 if(this.activeRequest){
16238 Roo.Ajax.abort(this.activeRequest);
16240 this.activeRequest = Roo.Ajax.request(o);
16242 this.conn.request(o);
16245 callback.call(scope||this, null, arg, false);
16250 loadResponse : function(o, success, response){
16251 delete this.activeRequest;
16253 this.fireEvent("loadexception", this, o, response);
16254 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16259 result = o.reader.read(response);
16262 o.raw = { errorMsg : response.responseText };
16263 this.fireEvent("loadexception", this, o, response, e);
16264 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16268 this.fireEvent("load", this, o, o.request.arg);
16269 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16273 update : function(dataSet){
16278 updateResponse : function(dataSet){
16283 * Ext JS Library 1.1.1
16284 * Copyright(c) 2006-2007, Ext JS, LLC.
16286 * Originally Released Under LGPL - original licence link has changed is not relivant.
16289 * <script type="text/javascript">
16293 * @class Roo.data.ScriptTagProxy
16294 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16295 * other than the originating domain of the running page.<br><br>
16297 * <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
16298 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16300 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16301 * source code that is used as the source inside a <script> tag.<br><br>
16303 * In order for the browser to process the returned data, the server must wrap the data object
16304 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16305 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16306 * depending on whether the callback name was passed:
16309 boolean scriptTag = false;
16310 String cb = request.getParameter("callback");
16313 response.setContentType("text/javascript");
16315 response.setContentType("application/x-json");
16317 Writer out = response.getWriter();
16319 out.write(cb + "(");
16321 out.print(dataBlock.toJsonString());
16328 * @param {Object} config A configuration object.
16330 Roo.data.ScriptTagProxy = function(config){
16331 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16332 Roo.apply(this, config);
16333 this.head = document.getElementsByTagName("head")[0];
16336 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16338 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16340 * @cfg {String} url The URL from which to request the data object.
16343 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16347 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16348 * the server the name of the callback function set up by the load call to process the returned data object.
16349 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16350 * javascript output which calls this named function passing the data object as its only parameter.
16352 callbackParam : "callback",
16354 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16355 * name to the request.
16360 * Load data from the configured URL, read the data object into
16361 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16362 * process that block using the passed callback.
16363 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16364 * for the request to the remote server.
16365 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16366 * object into a block of Roo.data.Records.
16367 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16368 * The function must be passed <ul>
16369 * <li>The Record block object</li>
16370 * <li>The "arg" argument from the load function</li>
16371 * <li>A boolean success indicator</li>
16373 * @param {Object} scope The scope in which to call the callback
16374 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16376 load : function(params, reader, callback, scope, arg){
16377 if(this.fireEvent("beforeload", this, params) !== false){
16379 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16381 var url = this.url;
16382 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16384 url += "&_dc=" + (new Date().getTime());
16386 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16389 cb : "stcCallback"+transId,
16390 scriptId : "stcScript"+transId,
16394 callback : callback,
16400 window[trans.cb] = function(o){
16401 conn.handleResponse(o, trans);
16404 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16406 if(this.autoAbort !== false){
16410 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16412 var script = document.createElement("script");
16413 script.setAttribute("src", url);
16414 script.setAttribute("type", "text/javascript");
16415 script.setAttribute("id", trans.scriptId);
16416 this.head.appendChild(script);
16418 this.trans = trans;
16420 callback.call(scope||this, null, arg, false);
16425 isLoading : function(){
16426 return this.trans ? true : false;
16430 * Abort the current server request.
16432 abort : function(){
16433 if(this.isLoading()){
16434 this.destroyTrans(this.trans);
16439 destroyTrans : function(trans, isLoaded){
16440 this.head.removeChild(document.getElementById(trans.scriptId));
16441 clearTimeout(trans.timeoutId);
16443 window[trans.cb] = undefined;
16445 delete window[trans.cb];
16448 // if hasn't been loaded, wait for load to remove it to prevent script error
16449 window[trans.cb] = function(){
16450 window[trans.cb] = undefined;
16452 delete window[trans.cb];
16459 handleResponse : function(o, trans){
16460 this.trans = false;
16461 this.destroyTrans(trans, true);
16464 result = trans.reader.readRecords(o);
16466 this.fireEvent("loadexception", this, o, trans.arg, e);
16467 trans.callback.call(trans.scope||window, null, trans.arg, false);
16470 this.fireEvent("load", this, o, trans.arg);
16471 trans.callback.call(trans.scope||window, result, trans.arg, true);
16475 handleFailure : function(trans){
16476 this.trans = false;
16477 this.destroyTrans(trans, false);
16478 this.fireEvent("loadexception", this, null, trans.arg);
16479 trans.callback.call(trans.scope||window, null, trans.arg, false);
16483 * Ext JS Library 1.1.1
16484 * Copyright(c) 2006-2007, Ext JS, LLC.
16486 * Originally Released Under LGPL - original licence link has changed is not relivant.
16489 * <script type="text/javascript">
16493 * @class Roo.data.JsonReader
16494 * @extends Roo.data.DataReader
16495 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16496 * based on mappings in a provided Roo.data.Record constructor.
16498 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16499 * in the reply previously.
16504 var RecordDef = Roo.data.Record.create([
16505 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16506 {name: 'occupation'} // This field will use "occupation" as the mapping.
16508 var myReader = new Roo.data.JsonReader({
16509 totalProperty: "results", // The property which contains the total dataset size (optional)
16510 root: "rows", // The property which contains an Array of row objects
16511 id: "id" // The property within each row object that provides an ID for the record (optional)
16515 * This would consume a JSON file like this:
16517 { 'results': 2, 'rows': [
16518 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16519 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16522 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16523 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16524 * paged from the remote server.
16525 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16526 * @cfg {String} root name of the property which contains the Array of row objects.
16527 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16528 * @cfg {Array} fields Array of field definition objects
16530 * Create a new JsonReader
16531 * @param {Object} meta Metadata configuration options
16532 * @param {Object} recordType Either an Array of field definition objects,
16533 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16535 Roo.data.JsonReader = function(meta, recordType){
16538 // set some defaults:
16539 Roo.applyIf(meta, {
16540 totalProperty: 'total',
16541 successProperty : 'success',
16546 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16548 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16550 readerType : 'Json',
16553 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16554 * Used by Store query builder to append _requestMeta to params.
16557 metaFromRemote : false,
16559 * This method is only used by a DataProxy which has retrieved data from a remote server.
16560 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16561 * @return {Object} data A data block which is used by an Roo.data.Store object as
16562 * a cache of Roo.data.Records.
16564 read : function(response){
16565 var json = response.responseText;
16567 var o = /* eval:var:o */ eval("("+json+")");
16569 throw {message: "JsonReader.read: Json object not found"};
16575 this.metaFromRemote = true;
16576 this.meta = o.metaData;
16577 this.recordType = Roo.data.Record.create(o.metaData.fields);
16578 this.onMetaChange(this.meta, this.recordType, o);
16580 return this.readRecords(o);
16583 // private function a store will implement
16584 onMetaChange : function(meta, recordType, o){
16591 simpleAccess: function(obj, subsc) {
16598 getJsonAccessor: function(){
16600 return function(expr) {
16602 return(re.test(expr))
16603 ? new Function("obj", "return obj." + expr)
16608 return Roo.emptyFn;
16613 * Create a data block containing Roo.data.Records from an XML document.
16614 * @param {Object} o An object which contains an Array of row objects in the property specified
16615 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16616 * which contains the total size of the dataset.
16617 * @return {Object} data A data block which is used by an Roo.data.Store object as
16618 * a cache of Roo.data.Records.
16620 readRecords : function(o){
16622 * After any data loads, the raw JSON data is available for further custom processing.
16626 var s = this.meta, Record = this.recordType,
16627 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16629 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16631 if(s.totalProperty) {
16632 this.getTotal = this.getJsonAccessor(s.totalProperty);
16634 if(s.successProperty) {
16635 this.getSuccess = this.getJsonAccessor(s.successProperty);
16637 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16639 var g = this.getJsonAccessor(s.id);
16640 this.getId = function(rec) {
16642 return (r === undefined || r === "") ? null : r;
16645 this.getId = function(){return null;};
16648 for(var jj = 0; jj < fl; jj++){
16650 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16651 this.ef[jj] = this.getJsonAccessor(map);
16655 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16656 if(s.totalProperty){
16657 var vt = parseInt(this.getTotal(o), 10);
16662 if(s.successProperty){
16663 var vs = this.getSuccess(o);
16664 if(vs === false || vs === 'false'){
16669 for(var i = 0; i < c; i++){
16672 var id = this.getId(n);
16673 for(var j = 0; j < fl; j++){
16675 var v = this.ef[j](n);
16677 Roo.log('missing convert for ' + f.name);
16681 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16685 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16691 var record = new Record(values, id);
16693 records[i] = record;
16699 totalRecords : totalRecords
16702 // used when loading children.. @see loadDataFromChildren
16703 toLoadData: function(rec)
16705 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16706 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16707 return { data : data, total : data.length };
16712 * Ext JS Library 1.1.1
16713 * Copyright(c) 2006-2007, Ext JS, LLC.
16715 * Originally Released Under LGPL - original licence link has changed is not relivant.
16718 * <script type="text/javascript">
16722 * @class Roo.data.ArrayReader
16723 * @extends Roo.data.DataReader
16724 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16725 * Each element of that Array represents a row of data fields. The
16726 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16727 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16731 var RecordDef = Roo.data.Record.create([
16732 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16733 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16735 var myReader = new Roo.data.ArrayReader({
16736 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16740 * This would consume an Array like this:
16742 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16746 * Create a new JsonReader
16747 * @param {Object} meta Metadata configuration options.
16748 * @param {Object|Array} recordType Either an Array of field definition objects
16750 * @cfg {Array} fields Array of field definition objects
16751 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16752 * as specified to {@link Roo.data.Record#create},
16753 * or an {@link Roo.data.Record} object
16756 * created using {@link Roo.data.Record#create}.
16758 Roo.data.ArrayReader = function(meta, recordType)
16760 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16763 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16766 * Create a data block containing Roo.data.Records from an XML document.
16767 * @param {Object} o An Array of row objects which represents the dataset.
16768 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16769 * a cache of Roo.data.Records.
16771 readRecords : function(o)
16773 var sid = this.meta ? this.meta.id : null;
16774 var recordType = this.recordType, fields = recordType.prototype.fields;
16777 for(var i = 0; i < root.length; i++){
16780 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16781 for(var j = 0, jlen = fields.length; j < jlen; j++){
16782 var f = fields.items[j];
16783 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16784 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16786 values[f.name] = v;
16788 var record = new recordType(values, id);
16790 records[records.length] = record;
16794 totalRecords : records.length
16797 // used when loading children.. @see loadDataFromChildren
16798 toLoadData: function(rec)
16800 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16801 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16812 * @class Roo.bootstrap.form.ComboBox
16813 * @extends Roo.bootstrap.form.TriggerField
16814 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16815 * @cfg {Boolean} append (true|false) default false
16816 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16817 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16818 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16819 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16820 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16821 * @cfg {Boolean} animate default true
16822 * @cfg {Boolean} emptyResultText only for touch device
16823 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16824 * @cfg {String} emptyTitle default ''
16825 * @cfg {Number} width fixed with? experimental
16827 * Create a new ComboBox.
16828 * @param {Object} config Configuration options
16830 Roo.bootstrap.form.ComboBox = function(config){
16831 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16835 * Fires when the dropdown list is expanded
16836 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16841 * Fires when the dropdown list is collapsed
16842 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16846 * @event beforeselect
16847 * Fires before a list item is selected. Return false to cancel the selection.
16848 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16849 * @param {Roo.data.Record} record The data record returned from the underlying store
16850 * @param {Number} index The index of the selected item in the dropdown list
16852 'beforeselect' : true,
16855 * Fires when a list item is selected
16856 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16857 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16858 * @param {Number} index The index of the selected item in the dropdown list
16862 * @event beforequery
16863 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16864 * The event object passed has these properties:
16865 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16866 * @param {String} query The query
16867 * @param {Boolean} forceAll true to force "all" query
16868 * @param {Boolean} cancel true to cancel the query
16869 * @param {Object} e The query event object
16871 'beforequery': true,
16874 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16875 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16880 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16881 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16882 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16887 * Fires when the remove value from the combobox array
16888 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892 * @event afterremove
16893 * Fires when the remove value from the combobox array
16894 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896 'afterremove' : true,
16898 * @event specialfilter
16899 * Fires when specialfilter
16900 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902 'specialfilter' : true,
16905 * Fires when tick the element
16906 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16910 * @event touchviewdisplay
16911 * Fires when touch view require special display (default is using displayField)
16912 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16913 * @param {Object} cfg set html .
16915 'touchviewdisplay' : true
16920 this.tickItems = [];
16922 this.selectedIndex = -1;
16923 if(this.mode == 'local'){
16924 if(config.queryDelay === undefined){
16925 this.queryDelay = 10;
16927 if(config.minChars === undefined){
16933 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16936 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16937 * rendering into an Roo.Editor, defaults to false)
16940 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16941 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16944 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16947 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16948 * the dropdown list (defaults to undefined, with no header element)
16952 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16956 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16958 listWidth: undefined,
16960 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16961 * mode = 'remote' or 'text' if mode = 'local')
16963 displayField: undefined,
16966 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16967 * mode = 'remote' or 'value' if mode = 'local').
16968 * Note: use of a valueField requires the user make a selection
16969 * in order for a value to be mapped.
16971 valueField: undefined,
16973 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16978 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16979 * field's data value (defaults to the underlying DOM element's name)
16981 hiddenName: undefined,
16983 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16987 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16989 selectedClass: 'active',
16992 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16996 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16997 * anchor positions (defaults to 'tl-bl')
16999 listAlign: 'tl-bl?',
17001 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17005 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17006 * query specified by the allQuery config option (defaults to 'query')
17008 triggerAction: 'query',
17010 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17011 * (defaults to 4, does not apply if editable = false)
17015 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17016 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17020 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17021 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17025 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17026 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17030 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17031 * when editable = true (defaults to false)
17033 selectOnFocus:false,
17035 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17037 queryParam: 'query',
17039 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17040 * when mode = 'remote' (defaults to 'Loading...')
17042 loadingText: 'Loading...',
17044 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17048 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17052 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17053 * traditional select (defaults to true)
17057 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17061 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17065 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17066 * listWidth has a higher value)
17070 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17071 * allow the user to set arbitrary text into the field (defaults to false)
17073 forceSelection:false,
17075 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17076 * if typeAhead = true (defaults to 250)
17078 typeAheadDelay : 250,
17080 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17081 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17083 valueNotFoundText : undefined,
17085 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17087 blockFocus : false,
17090 * @cfg {Boolean} disableClear Disable showing of clear button.
17092 disableClear : false,
17094 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17096 alwaysQuery : false,
17099 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17104 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17106 invalidClass : "has-warning",
17109 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17111 validClass : "has-success",
17114 * @cfg {Boolean} specialFilter (true|false) special filter default false
17116 specialFilter : false,
17119 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17121 mobileTouchView : true,
17124 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17126 useNativeIOS : false,
17129 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17131 mobile_restrict_height : false,
17133 ios_options : false,
17145 btnPosition : 'right',
17146 triggerList : true,
17147 showToggleBtn : true,
17149 emptyResultText: 'Empty',
17150 triggerText : 'Select',
17154 // element that contains real text value.. (when hidden is used..)
17156 getAutoCreate : function()
17161 * Render classic select for iso
17164 if(Roo.isIOS && this.useNativeIOS){
17165 cfg = this.getAutoCreateNativeIOS();
17173 if(Roo.isTouch && this.mobileTouchView){
17174 cfg = this.getAutoCreateTouchView();
17181 if(!this.tickable){
17182 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17187 * ComboBox with tickable selections
17190 var align = this.labelAlign || this.parentLabelAlign();
17193 cls : 'form-group roo-combobox-tickable' //input-group
17196 var btn_text_select = '';
17197 var btn_text_done = '';
17198 var btn_text_cancel = '';
17200 if (this.btn_text_show) {
17201 btn_text_select = 'Select';
17202 btn_text_done = 'Done';
17203 btn_text_cancel = 'Cancel';
17208 cls : 'tickable-buttons',
17213 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17214 //html : this.triggerText
17215 html: btn_text_select
17221 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17223 html: btn_text_done
17229 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17231 html: btn_text_cancel
17237 buttons.cn.unshift({
17239 cls: 'roo-select2-search-field-input'
17245 Roo.each(buttons.cn, function(c){
17247 c.cls += ' btn-' + _this.size;
17250 if (_this.disabled) {
17257 style : 'display: contents',
17262 cls: 'form-hidden-field'
17266 cls: 'roo-select2-choices',
17270 cls: 'roo-select2-search-field',
17281 cls: 'roo-select2-container input-group roo-select2-container-multi',
17287 // cls: 'typeahead typeahead-long dropdown-menu',
17288 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17293 if(this.hasFeedback && !this.allowBlank){
17297 cls: 'glyphicon form-control-feedback'
17300 combobox.cn.push(feedback);
17307 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17308 tooltip : 'This field is required'
17310 if (Roo.bootstrap.version == 4) {
17313 style : 'display:none'
17316 if (align ==='left' && this.fieldLabel.length) {
17318 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17325 cls : 'control-label col-form-label',
17326 html : this.fieldLabel
17338 var labelCfg = cfg.cn[1];
17339 var contentCfg = cfg.cn[2];
17342 if(this.indicatorpos == 'right'){
17348 cls : 'control-label col-form-label',
17352 html : this.fieldLabel
17368 labelCfg = cfg.cn[0];
17369 contentCfg = cfg.cn[1];
17373 if(this.labelWidth > 12){
17374 labelCfg.style = "width: " + this.labelWidth + 'px';
17376 if(this.width * 1 > 0){
17377 contentCfg.style = "width: " + this.width + 'px';
17379 if(this.labelWidth < 13 && this.labelmd == 0){
17380 this.labelmd = this.labelWidth;
17383 if(this.labellg > 0){
17384 labelCfg.cls += ' col-lg-' + this.labellg;
17385 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17388 if(this.labelmd > 0){
17389 labelCfg.cls += ' col-md-' + this.labelmd;
17390 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17393 if(this.labelsm > 0){
17394 labelCfg.cls += ' col-sm-' + this.labelsm;
17395 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17398 if(this.labelxs > 0){
17399 labelCfg.cls += ' col-xs-' + this.labelxs;
17400 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17404 } else if ( this.fieldLabel.length) {
17405 // Roo.log(" label");
17410 //cls : 'input-group-addon',
17411 html : this.fieldLabel
17416 if(this.indicatorpos == 'right'){
17420 //cls : 'input-group-addon',
17421 html : this.fieldLabel
17431 // Roo.log(" no label && no align");
17438 ['xs','sm','md','lg'].map(function(size){
17439 if (settings[size]) {
17440 cfg.cls += ' col-' + size + '-' + settings[size];
17448 _initEventsCalled : false,
17451 initEvents: function()
17453 if (this._initEventsCalled) { // as we call render... prevent looping...
17456 this._initEventsCalled = true;
17459 throw "can not find store for combo";
17462 this.indicator = this.indicatorEl();
17464 this.store = Roo.factory(this.store, Roo.data);
17465 this.store.parent = this;
17467 // if we are building from html. then this element is so complex, that we can not really
17468 // use the rendered HTML.
17469 // so we have to trash and replace the previous code.
17470 if (Roo.XComponent.build_from_html) {
17471 // remove this element....
17472 var e = this.el.dom, k=0;
17473 while (e ) { e = e.previousSibling; ++k;}
17478 this.rendered = false;
17480 this.render(this.parent().getChildContainer(true), k);
17483 if(Roo.isIOS && this.useNativeIOS){
17484 this.initIOSView();
17492 if(Roo.isTouch && this.mobileTouchView){
17493 this.initTouchView();
17498 this.initTickableEvents();
17502 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17504 if(this.hiddenName){
17506 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17508 this.hiddenField.dom.value =
17509 this.hiddenValue !== undefined ? this.hiddenValue :
17510 this.value !== undefined ? this.value : '';
17512 // prevent input submission
17513 this.el.dom.removeAttribute('name');
17514 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17519 // this.el.dom.setAttribute('autocomplete', 'off');
17522 var cls = 'x-combo-list';
17524 //this.list = new Roo.Layer({
17525 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17531 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17532 _this.list.setWidth(lw);
17535 this.list.on('mouseover', this.onViewOver, this);
17536 this.list.on('mousemove', this.onViewMove, this);
17537 this.list.on('scroll', this.onViewScroll, this);
17540 this.list.swallowEvent('mousewheel');
17541 this.assetHeight = 0;
17544 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17545 this.assetHeight += this.header.getHeight();
17548 this.innerList = this.list.createChild({cls:cls+'-inner'});
17549 this.innerList.on('mouseover', this.onViewOver, this);
17550 this.innerList.on('mousemove', this.onViewMove, this);
17551 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17553 if(this.allowBlank && !this.pageSize && !this.disableClear){
17554 this.footer = this.list.createChild({cls:cls+'-ft'});
17555 this.pageTb = new Roo.Toolbar(this.footer);
17559 this.footer = this.list.createChild({cls:cls+'-ft'});
17560 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17561 {pageSize: this.pageSize});
17565 if (this.pageTb && this.allowBlank && !this.disableClear) {
17567 this.pageTb.add(new Roo.Toolbar.Fill(), {
17568 cls: 'x-btn-icon x-btn-clear',
17570 handler: function()
17573 _this.clearValue();
17574 _this.onSelect(false, -1);
17579 this.assetHeight += this.footer.getHeight();
17584 this.tpl = Roo.bootstrap.version == 4 ?
17585 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17586 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17589 this.view = new Roo.View(this.list, this.tpl, {
17590 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17592 //this.view.wrapEl.setDisplayed(false);
17593 this.view.on('click', this.onViewClick, this);
17596 this.store.on('beforeload', this.onBeforeLoad, this);
17597 this.store.on('load', this.onLoad, this);
17598 this.store.on('loadexception', this.onLoadException, this);
17600 if(this.resizable){
17601 this.resizer = new Roo.Resizable(this.list, {
17602 pinned:true, handles:'se'
17604 this.resizer.on('resize', function(r, w, h){
17605 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17606 this.listWidth = w;
17607 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17608 this.restrictHeight();
17610 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17613 if(!this.editable){
17614 this.editable = true;
17615 this.setEditable(false);
17620 if (typeof(this.events.add.listeners) != 'undefined') {
17622 this.addicon = this.wrap.createChild(
17623 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17625 this.addicon.on('click', function(e) {
17626 this.fireEvent('add', this);
17629 if (typeof(this.events.edit.listeners) != 'undefined') {
17631 this.editicon = this.wrap.createChild(
17632 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17633 if (this.addicon) {
17634 this.editicon.setStyle('margin-left', '40px');
17636 this.editicon.on('click', function(e) {
17638 // we fire even if inothing is selected..
17639 this.fireEvent('edit', this, this.lastData );
17645 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17646 "up" : function(e){
17647 this.inKeyMode = true;
17651 "down" : function(e){
17652 if(!this.isExpanded()){
17653 this.onTriggerClick();
17655 this.inKeyMode = true;
17660 "enter" : function(e){
17661 // this.onViewClick();
17665 if(this.fireEvent("specialkey", this, e)){
17666 this.onViewClick(false);
17672 "esc" : function(e){
17676 "tab" : function(e){
17679 if(this.fireEvent("specialkey", this, e)){
17680 this.onViewClick(false);
17688 doRelay : function(foo, bar, hname){
17689 if(hname == 'down' || this.scope.isExpanded()){
17690 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17699 this.queryDelay = Math.max(this.queryDelay || 10,
17700 this.mode == 'local' ? 10 : 250);
17703 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17705 if(this.typeAhead){
17706 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17708 if(this.editable !== false){
17709 this.inputEl().on("keyup", this.onKeyUp, this);
17711 if(this.forceSelection){
17712 this.inputEl().on('blur', this.doForce, this);
17716 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17717 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17721 initTickableEvents: function()
17725 if(this.hiddenName){
17727 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17729 this.hiddenField.dom.value =
17730 this.hiddenValue !== undefined ? this.hiddenValue :
17731 this.value !== undefined ? this.value : '';
17733 // prevent input submission
17734 this.el.dom.removeAttribute('name');
17735 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17740 // this.list = this.el.select('ul.dropdown-menu',true).first();
17742 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17743 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17744 if(this.triggerList){
17745 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17748 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17749 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17751 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17752 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17754 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17755 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17757 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17758 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17759 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17762 this.cancelBtn.hide();
17767 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17768 _this.list.setWidth(lw);
17771 this.list.on('mouseover', this.onViewOver, this);
17772 this.list.on('mousemove', this.onViewMove, this);
17774 this.list.on('scroll', this.onViewScroll, this);
17777 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17778 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17781 this.view = new Roo.View(this.list, this.tpl, {
17786 selectedClass: this.selectedClass
17789 //this.view.wrapEl.setDisplayed(false);
17790 this.view.on('click', this.onViewClick, this);
17794 this.store.on('beforeload', this.onBeforeLoad, this);
17795 this.store.on('load', this.onLoad, this);
17796 this.store.on('loadexception', this.onLoadException, this);
17799 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17800 "up" : function(e){
17801 this.inKeyMode = true;
17805 "down" : function(e){
17806 this.inKeyMode = true;
17810 "enter" : function(e){
17811 if(this.fireEvent("specialkey", this, e)){
17812 this.onViewClick(false);
17818 "esc" : function(e){
17819 this.onTickableFooterButtonClick(e, false, false);
17822 "tab" : function(e){
17823 this.fireEvent("specialkey", this, e);
17825 this.onTickableFooterButtonClick(e, false, false);
17832 doRelay : function(e, fn, key){
17833 if(this.scope.isExpanded()){
17834 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17843 this.queryDelay = Math.max(this.queryDelay || 10,
17844 this.mode == 'local' ? 10 : 250);
17847 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17849 if(this.typeAhead){
17850 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17853 if(this.editable !== false){
17854 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17857 this.indicator = this.indicatorEl();
17859 if(this.indicator){
17860 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17861 this.indicator.hide();
17866 onDestroy : function(){
17868 this.view.setStore(null);
17869 this.view.el.removeAllListeners();
17870 this.view.el.remove();
17871 this.view.purgeListeners();
17874 this.list.dom.innerHTML = '';
17878 this.store.un('beforeload', this.onBeforeLoad, this);
17879 this.store.un('load', this.onLoad, this);
17880 this.store.un('loadexception', this.onLoadException, this);
17882 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17886 fireKey : function(e){
17887 if(e.isNavKeyPress() && !this.list.isVisible()){
17888 this.fireEvent("specialkey", this, e);
17893 onResize: function(w, h)
17897 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17899 // if(typeof w != 'number'){
17900 // // we do not handle it!?!?
17903 // var tw = this.trigger.getWidth();
17904 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17905 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17907 // this.inputEl().setWidth( this.adjustWidth('input', x));
17909 // //this.trigger.setStyle('left', x+'px');
17911 // if(this.list && this.listWidth === undefined){
17912 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17913 // this.list.setWidth(lw);
17914 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17922 * Allow or prevent the user from directly editing the field text. If false is passed,
17923 * the user will only be able to select from the items defined in the dropdown list. This method
17924 * is the runtime equivalent of setting the 'editable' config option at config time.
17925 * @param {Boolean} value True to allow the user to directly edit the field text
17927 setEditable : function(value){
17928 if(value == this.editable){
17931 this.editable = value;
17933 this.inputEl().dom.setAttribute('readOnly', true);
17934 this.inputEl().on('mousedown', this.onTriggerClick, this);
17935 this.inputEl().addClass('x-combo-noedit');
17937 this.inputEl().dom.removeAttribute('readOnly');
17938 this.inputEl().un('mousedown', this.onTriggerClick, this);
17939 this.inputEl().removeClass('x-combo-noedit');
17945 onBeforeLoad : function(combo,opts){
17946 if(!this.hasFocus){
17950 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17952 this.restrictHeight();
17953 this.selectedIndex = -1;
17957 onLoad : function(){
17959 this.hasQuery = false;
17961 if(!this.hasFocus){
17965 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17966 this.loading.hide();
17969 if(this.store.getCount() > 0){
17972 this.restrictHeight();
17973 if(this.lastQuery == this.allQuery){
17974 if(this.editable && !this.tickable){
17975 this.inputEl().dom.select();
17979 !this.selectByValue(this.value, true) &&
17982 !this.store.lastOptions ||
17983 typeof(this.store.lastOptions.add) == 'undefined' ||
17984 this.store.lastOptions.add != true
17987 this.select(0, true);
17990 if(this.autoFocus){
17993 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17994 this.taTask.delay(this.typeAheadDelay);
17998 this.onEmptyResults();
18004 onLoadException : function()
18006 this.hasQuery = false;
18008 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18009 this.loading.hide();
18012 if(this.tickable && this.editable){
18017 // only causes errors at present
18018 //Roo.log(this.store.reader.jsonData);
18019 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18021 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18027 onTypeAhead : function(){
18028 if(this.store.getCount() > 0){
18029 var r = this.store.getAt(0);
18030 var newValue = r.data[this.displayField];
18031 var len = newValue.length;
18032 var selStart = this.getRawValue().length;
18034 if(selStart != len){
18035 this.setRawValue(newValue);
18036 this.selectText(selStart, newValue.length);
18042 onSelect : function(record, index){
18044 if(this.fireEvent('beforeselect', this, record, index) !== false){
18046 this.setFromData(index > -1 ? record.data : false);
18049 this.fireEvent('select', this, record, index);
18054 * Returns the currently selected field value or empty string if no value is set.
18055 * @return {String} value The selected value
18057 getValue : function()
18059 if(Roo.isIOS && this.useNativeIOS){
18060 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18064 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18067 if(this.valueField){
18068 return typeof this.value != 'undefined' ? this.value : '';
18070 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18074 getRawValue : function()
18076 if(Roo.isIOS && this.useNativeIOS){
18077 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18080 var v = this.inputEl().getValue();
18086 * Clears any text/value currently set in the field
18088 clearValue : function(){
18090 if(this.hiddenField){
18091 this.hiddenField.dom.value = '';
18094 this.setRawValue('');
18095 this.lastSelectionText = '';
18096 this.lastData = false;
18098 var close = this.closeTriggerEl();
18109 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18110 * will be displayed in the field. If the value does not match the data value of an existing item,
18111 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18112 * Otherwise the field will be blank (although the value will still be set).
18113 * @param {String} value The value to match
18115 setValue : function(v)
18117 if(Roo.isIOS && this.useNativeIOS){
18118 this.setIOSValue(v);
18128 if(this.valueField){
18129 var r = this.findRecord(this.valueField, v);
18131 text = r.data[this.displayField];
18132 }else if(this.valueNotFoundText !== undefined){
18133 text = this.valueNotFoundText;
18136 this.lastSelectionText = text;
18137 if(this.hiddenField){
18138 this.hiddenField.dom.value = v;
18140 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18143 var close = this.closeTriggerEl();
18146 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18152 * @property {Object} the last set data for the element
18157 * Sets the value of the field based on a object which is related to the record format for the store.
18158 * @param {Object} value the value to set as. or false on reset?
18160 setFromData : function(o){
18167 var dv = ''; // display value
18168 var vv = ''; // value value..
18170 if (this.displayField) {
18171 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18173 // this is an error condition!!!
18174 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18177 if(this.valueField){
18178 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18181 var close = this.closeTriggerEl();
18184 if(dv.length || vv * 1 > 0){
18186 this.blockFocus=true;
18192 if(this.hiddenField){
18193 this.hiddenField.dom.value = vv;
18195 this.lastSelectionText = dv;
18196 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18200 // no hidden field.. - we store the value in 'value', but still display
18201 // display field!!!!
18202 this.lastSelectionText = dv;
18203 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18210 reset : function(){
18211 // overridden so that last data is reset..
18218 this.setValue(this.originalValue);
18219 //this.clearInvalid();
18220 this.lastData = false;
18222 this.view.clearSelections();
18228 findRecord : function(prop, value){
18230 if(this.store.getCount() > 0){
18231 this.store.each(function(r){
18232 if(r.data[prop] == value){
18242 getName: function()
18244 // returns hidden if it's set..
18245 if (!this.rendered) {return ''};
18246 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18250 onViewMove : function(e, t){
18251 this.inKeyMode = false;
18255 onViewOver : function(e, t){
18256 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18259 var item = this.view.findItemFromChild(t);
18262 var index = this.view.indexOf(item);
18263 this.select(index, false);
18268 onViewClick : function(view, doFocus, el, e)
18270 var index = this.view.getSelectedIndexes()[0];
18272 var r = this.store.getAt(index);
18276 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18283 Roo.each(this.tickItems, function(v,k){
18285 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18287 _this.tickItems.splice(k, 1);
18289 if(typeof(e) == 'undefined' && view == false){
18290 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18302 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18303 this.tickItems.push(r.data);
18306 if(typeof(e) == 'undefined' && view == false){
18307 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18314 this.onSelect(r, index);
18316 if(doFocus !== false && !this.blockFocus){
18317 this.inputEl().focus();
18322 restrictHeight : function(){
18323 //this.innerList.dom.style.height = '';
18324 //var inner = this.innerList.dom;
18325 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18326 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18327 //this.list.beginUpdate();
18328 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18329 this.list.alignTo(this.inputEl(), this.listAlign);
18330 this.list.alignTo(this.inputEl(), this.listAlign);
18331 //this.list.endUpdate();
18335 onEmptyResults : function(){
18337 if(this.tickable && this.editable){
18338 this.hasFocus = false;
18339 this.restrictHeight();
18347 * Returns true if the dropdown list is expanded, else false.
18349 isExpanded : function(){
18350 return this.list.isVisible();
18354 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18355 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18356 * @param {String} value The data value of the item to select
18357 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18358 * selected item if it is not currently in view (defaults to true)
18359 * @return {Boolean} True if the value matched an item in the list, else false
18361 selectByValue : function(v, scrollIntoView){
18362 if(v !== undefined && v !== null){
18363 var r = this.findRecord(this.valueField || this.displayField, v);
18365 this.select(this.store.indexOf(r), scrollIntoView);
18373 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18374 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18375 * @param {Number} index The zero-based index of the list item to select
18376 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18377 * selected item if it is not currently in view (defaults to true)
18379 select : function(index, scrollIntoView){
18380 this.selectedIndex = index;
18381 this.view.select(index);
18382 if(scrollIntoView !== false){
18383 var el = this.view.getNode(index);
18385 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18388 this.list.scrollChildIntoView(el, false);
18394 selectNext : function(){
18395 var ct = this.store.getCount();
18397 if(this.selectedIndex == -1){
18399 }else if(this.selectedIndex < ct-1){
18400 this.select(this.selectedIndex+1);
18406 selectPrev : function(){
18407 var ct = this.store.getCount();
18409 if(this.selectedIndex == -1){
18411 }else if(this.selectedIndex != 0){
18412 this.select(this.selectedIndex-1);
18418 onKeyUp : function(e){
18419 if(this.editable !== false && !e.isSpecialKey()){
18420 this.lastKey = e.getKey();
18421 this.dqTask.delay(this.queryDelay);
18426 validateBlur : function(){
18427 return !this.list || !this.list.isVisible();
18431 initQuery : function(){
18433 var v = this.getRawValue();
18435 if(this.tickable && this.editable){
18436 v = this.tickableInputEl().getValue();
18443 doForce : function(){
18444 if(this.inputEl().dom.value.length > 0){
18445 this.inputEl().dom.value =
18446 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18452 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18453 * query allowing the query action to be canceled if needed.
18454 * @param {String} query The SQL query to execute
18455 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18456 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18457 * saved in the current store (defaults to false)
18459 doQuery : function(q, forceAll){
18461 if(q === undefined || q === null){
18466 forceAll: forceAll,
18470 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18475 forceAll = qe.forceAll;
18476 if(forceAll === true || (q.length >= this.minChars)){
18478 this.hasQuery = true;
18480 if(this.lastQuery != q || this.alwaysQuery){
18481 this.lastQuery = q;
18482 if(this.mode == 'local'){
18483 this.selectedIndex = -1;
18485 this.store.clearFilter();
18488 if(this.specialFilter){
18489 this.fireEvent('specialfilter', this);
18494 this.store.filter(this.displayField, q);
18497 this.store.fireEvent("datachanged", this.store);
18504 this.store.baseParams[this.queryParam] = q;
18506 var options = {params : this.getParams(q)};
18509 options.add = true;
18510 options.params.start = this.page * this.pageSize;
18513 this.store.load(options);
18516 * this code will make the page width larger, at the beginning, the list not align correctly,
18517 * we should expand the list on onLoad
18518 * so command out it
18523 this.selectedIndex = -1;
18528 this.loadNext = false;
18532 getParams : function(q){
18534 //p[this.queryParam] = q;
18538 p.limit = this.pageSize;
18544 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18546 collapse : function(){
18547 if(!this.isExpanded()){
18553 this.hasFocus = false;
18557 this.cancelBtn.hide();
18558 this.trigger.show();
18561 this.tickableInputEl().dom.value = '';
18562 this.tickableInputEl().blur();
18567 Roo.get(document).un('mousedown', this.collapseIf, this);
18568 Roo.get(document).un('mousewheel', this.collapseIf, this);
18569 if (!this.editable) {
18570 Roo.get(document).un('keydown', this.listKeyPress, this);
18572 this.fireEvent('collapse', this);
18578 collapseIf : function(e){
18579 var in_combo = e.within(this.el);
18580 var in_list = e.within(this.list);
18581 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18583 if (in_combo || in_list || is_list) {
18584 //e.stopPropagation();
18589 this.onTickableFooterButtonClick(e, false, false);
18597 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18599 expand : function(){
18601 if(this.isExpanded() || !this.hasFocus){
18605 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18606 this.list.setWidth(lw);
18612 this.restrictHeight();
18616 this.tickItems = Roo.apply([], this.item);
18619 this.cancelBtn.show();
18620 this.trigger.hide();
18623 this.tickableInputEl().focus();
18628 Roo.get(document).on('mousedown', this.collapseIf, this);
18629 Roo.get(document).on('mousewheel', this.collapseIf, this);
18630 if (!this.editable) {
18631 Roo.get(document).on('keydown', this.listKeyPress, this);
18634 this.fireEvent('expand', this);
18638 // Implements the default empty TriggerField.onTriggerClick function
18639 onTriggerClick : function(e)
18641 Roo.log('trigger click');
18643 if(this.disabled || !this.triggerList){
18648 this.loadNext = false;
18650 if(this.isExpanded()){
18652 if (!this.blockFocus) {
18653 this.inputEl().focus();
18657 this.hasFocus = true;
18658 if(this.triggerAction == 'all') {
18659 this.doQuery(this.allQuery, true);
18661 this.doQuery(this.getRawValue());
18663 if (!this.blockFocus) {
18664 this.inputEl().focus();
18669 onTickableTriggerClick : function(e)
18676 this.loadNext = false;
18677 this.hasFocus = true;
18679 if(this.triggerAction == 'all') {
18680 this.doQuery(this.allQuery, true);
18682 this.doQuery(this.getRawValue());
18686 onSearchFieldClick : function(e)
18688 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18689 this.onTickableFooterButtonClick(e, false, false);
18693 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18698 this.loadNext = false;
18699 this.hasFocus = true;
18701 if(this.triggerAction == 'all') {
18702 this.doQuery(this.allQuery, true);
18704 this.doQuery(this.getRawValue());
18708 listKeyPress : function(e)
18710 //Roo.log('listkeypress');
18711 // scroll to first matching element based on key pres..
18712 if (e.isSpecialKey()) {
18715 var k = String.fromCharCode(e.getKey()).toUpperCase();
18718 var csel = this.view.getSelectedNodes();
18719 var cselitem = false;
18721 var ix = this.view.indexOf(csel[0]);
18722 cselitem = this.store.getAt(ix);
18723 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18729 this.store.each(function(v) {
18731 // start at existing selection.
18732 if (cselitem.id == v.id) {
18738 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18739 match = this.store.indexOf(v);
18745 if (match === false) {
18746 return true; // no more action?
18749 this.view.select(match);
18750 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18751 sn.scrollIntoView(sn.dom.parentNode, false);
18754 onViewScroll : function(e, t){
18756 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){
18760 this.hasQuery = true;
18762 this.loading = this.list.select('.loading', true).first();
18764 if(this.loading === null){
18765 this.list.createChild({
18767 cls: 'loading roo-select2-more-results roo-select2-active',
18768 html: 'Loading more results...'
18771 this.loading = this.list.select('.loading', true).first();
18773 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18775 this.loading.hide();
18778 this.loading.show();
18783 this.loadNext = true;
18785 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18790 addItem : function(o)
18792 var dv = ''; // display value
18794 if (this.displayField) {
18795 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18797 // this is an error condition!!!
18798 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18805 var choice = this.choices.createChild({
18807 cls: 'roo-select2-search-choice',
18816 cls: 'roo-select2-search-choice-close fa fa-times',
18821 }, this.searchField);
18823 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18825 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18833 this.inputEl().dom.value = '';
18838 onRemoveItem : function(e, _self, o)
18840 e.preventDefault();
18842 this.lastItem = Roo.apply([], this.item);
18844 var index = this.item.indexOf(o.data) * 1;
18847 Roo.log('not this item?!');
18851 this.item.splice(index, 1);
18856 this.fireEvent('remove', this, e);
18862 syncValue : function()
18864 if(!this.item.length){
18871 Roo.each(this.item, function(i){
18872 if(_this.valueField){
18873 value.push(i[_this.valueField]);
18880 this.value = value.join(',');
18882 if(this.hiddenField){
18883 this.hiddenField.dom.value = this.value;
18886 this.store.fireEvent("datachanged", this.store);
18891 clearItem : function()
18893 if(!this.multiple){
18899 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18907 if(this.tickable && !Roo.isTouch){
18908 this.view.refresh();
18912 inputEl: function ()
18914 if(Roo.isIOS && this.useNativeIOS){
18915 return this.el.select('select.roo-ios-select', true).first();
18918 if(Roo.isTouch && this.mobileTouchView){
18919 return this.el.select('input.form-control',true).first();
18923 return this.searchField;
18926 return this.el.select('input.form-control',true).first();
18929 onTickableFooterButtonClick : function(e, btn, el)
18931 e.preventDefault();
18933 this.lastItem = Roo.apply([], this.item);
18935 if(btn && btn.name == 'cancel'){
18936 this.tickItems = Roo.apply([], this.item);
18945 Roo.each(this.tickItems, function(o){
18953 validate : function()
18955 if(this.getVisibilityEl().hasClass('hidden')){
18959 var v = this.getRawValue();
18962 v = this.getValue();
18965 if(this.disabled || this.allowBlank || v.length){
18970 this.markInvalid();
18974 tickableInputEl : function()
18976 if(!this.tickable || !this.editable){
18977 return this.inputEl();
18980 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18984 getAutoCreateTouchView : function()
18989 cls: 'form-group' //input-group
18995 type : this.inputType,
18996 cls : 'form-control x-combo-noedit',
18997 autocomplete: 'new-password',
18998 placeholder : this.placeholder || '',
19003 input.name = this.name;
19007 input.cls += ' input-' + this.size;
19010 if (this.disabled) {
19011 input.disabled = true;
19015 cls : 'roo-combobox-wrap',
19022 inputblock.cls += ' input-group';
19024 inputblock.cn.unshift({
19026 cls : 'input-group-addon input-group-prepend input-group-text',
19031 if(this.removable && !this.multiple){
19032 inputblock.cls += ' roo-removable';
19034 inputblock.cn.push({
19037 cls : 'roo-combo-removable-btn close'
19041 if(this.hasFeedback && !this.allowBlank){
19043 inputblock.cls += ' has-feedback';
19045 inputblock.cn.push({
19047 cls: 'glyphicon form-control-feedback'
19054 inputblock.cls += (this.before) ? '' : ' input-group';
19056 inputblock.cn.push({
19058 cls : 'input-group-addon input-group-append input-group-text',
19064 var ibwrap = inputblock;
19069 cls: 'roo-select2-choices',
19073 cls: 'roo-select2-search-field',
19086 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19091 cls: 'form-hidden-field'
19097 if(!this.multiple && this.showToggleBtn){
19103 if (this.caret != false) {
19106 cls: 'fa fa-' + this.caret
19113 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19115 Roo.bootstrap.version == 3 ? caret : '',
19118 cls: 'combobox-clear',
19132 combobox.cls += ' roo-select2-container-multi';
19135 var required = this.allowBlank ? {
19137 style: 'display: none'
19140 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19141 tooltip : 'This field is required'
19144 var align = this.labelAlign || this.parentLabelAlign();
19146 if (align ==='left' && this.fieldLabel.length) {
19152 cls : 'control-label col-form-label',
19153 html : this.fieldLabel
19157 cls : 'roo-combobox-wrap ',
19164 var labelCfg = cfg.cn[1];
19165 var contentCfg = cfg.cn[2];
19168 if(this.indicatorpos == 'right'){
19173 cls : 'control-label col-form-label',
19177 html : this.fieldLabel
19183 cls : "roo-combobox-wrap ",
19191 labelCfg = cfg.cn[0];
19192 contentCfg = cfg.cn[1];
19197 if(this.labelWidth > 12){
19198 labelCfg.style = "width: " + this.labelWidth + 'px';
19201 if(this.labelWidth < 13 && this.labelmd == 0){
19202 this.labelmd = this.labelWidth;
19205 if(this.labellg > 0){
19206 labelCfg.cls += ' col-lg-' + this.labellg;
19207 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19210 if(this.labelmd > 0){
19211 labelCfg.cls += ' col-md-' + this.labelmd;
19212 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19215 if(this.labelsm > 0){
19216 labelCfg.cls += ' col-sm-' + this.labelsm;
19217 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19220 if(this.labelxs > 0){
19221 labelCfg.cls += ' col-xs-' + this.labelxs;
19222 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19226 } else if ( this.fieldLabel.length) {
19231 cls : 'control-label',
19232 html : this.fieldLabel
19243 if(this.indicatorpos == 'right'){
19247 cls : 'control-label',
19248 html : this.fieldLabel,
19266 var settings = this;
19268 ['xs','sm','md','lg'].map(function(size){
19269 if (settings[size]) {
19270 cfg.cls += ' col-' + size + '-' + settings[size];
19277 initTouchView : function()
19279 this.renderTouchView();
19281 this.touchViewEl.on('scroll', function(){
19282 this.el.dom.scrollTop = 0;
19285 this.originalValue = this.getValue();
19287 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19289 this.inputEl().on("click", this.showTouchView, this);
19290 if (this.triggerEl) {
19291 this.triggerEl.on("click", this.showTouchView, this);
19295 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19296 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19298 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19300 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19301 this.store.on('load', this.onTouchViewLoad, this);
19302 this.store.on('loadexception', this.onTouchViewLoadException, this);
19304 if(this.hiddenName){
19306 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19308 this.hiddenField.dom.value =
19309 this.hiddenValue !== undefined ? this.hiddenValue :
19310 this.value !== undefined ? this.value : '';
19312 this.el.dom.removeAttribute('name');
19313 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19317 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19318 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19321 if(this.removable && !this.multiple){
19322 var close = this.closeTriggerEl();
19324 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19325 close.on('click', this.removeBtnClick, this, close);
19329 * fix the bug in Safari iOS8
19331 this.inputEl().on("focus", function(e){
19332 document.activeElement.blur();
19335 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19342 renderTouchView : function()
19344 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19345 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19347 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19348 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19350 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19351 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19352 this.touchViewBodyEl.setStyle('overflow', 'auto');
19354 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19355 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19357 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19358 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19362 showTouchView : function()
19368 this.touchViewHeaderEl.hide();
19370 if(this.modalTitle.length){
19371 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19372 this.touchViewHeaderEl.show();
19375 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19376 this.touchViewEl.show();
19378 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19380 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19381 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19383 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19385 if(this.modalTitle.length){
19386 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19389 this.touchViewBodyEl.setHeight(bodyHeight);
19393 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19395 this.touchViewEl.addClass(['in','show']);
19398 if(this._touchViewMask){
19399 Roo.get(document.body).addClass("x-body-masked");
19400 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19401 this._touchViewMask.setStyle('z-index', 10000);
19402 this._touchViewMask.addClass('show');
19405 this.doTouchViewQuery();
19409 hideTouchView : function()
19411 this.touchViewEl.removeClass(['in','show']);
19415 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19417 this.touchViewEl.setStyle('display', 'none');
19420 if(this._touchViewMask){
19421 this._touchViewMask.removeClass('show');
19422 Roo.get(document.body).removeClass("x-body-masked");
19426 setTouchViewValue : function()
19433 Roo.each(this.tickItems, function(o){
19438 this.hideTouchView();
19441 doTouchViewQuery : function()
19450 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19454 if(!this.alwaysQuery || this.mode == 'local'){
19455 this.onTouchViewLoad();
19462 onTouchViewBeforeLoad : function(combo,opts)
19468 onTouchViewLoad : function()
19470 if(this.store.getCount() < 1){
19471 this.onTouchViewEmptyResults();
19475 this.clearTouchView();
19477 var rawValue = this.getRawValue();
19479 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19481 this.tickItems = [];
19483 this.store.data.each(function(d, rowIndex){
19484 var row = this.touchViewListGroup.createChild(template);
19486 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19487 row.addClass(d.data.cls);
19490 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19493 html : d.data[this.displayField]
19496 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19497 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19500 row.removeClass('selected');
19501 if(!this.multiple && this.valueField &&
19502 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19505 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19506 row.addClass('selected');
19509 if(this.multiple && this.valueField &&
19510 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19514 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19515 this.tickItems.push(d.data);
19518 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19522 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19524 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19526 if(this.modalTitle.length){
19527 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19530 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19532 if(this.mobile_restrict_height && listHeight < bodyHeight){
19533 this.touchViewBodyEl.setHeight(listHeight);
19538 if(firstChecked && listHeight > bodyHeight){
19539 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19544 onTouchViewLoadException : function()
19546 this.hideTouchView();
19549 onTouchViewEmptyResults : function()
19551 this.clearTouchView();
19553 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19555 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19559 clearTouchView : function()
19561 this.touchViewListGroup.dom.innerHTML = '';
19564 onTouchViewClick : function(e, el, o)
19566 e.preventDefault();
19569 var rowIndex = o.rowIndex;
19571 var r = this.store.getAt(rowIndex);
19573 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19575 if(!this.multiple){
19576 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19577 c.dom.removeAttribute('checked');
19580 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19582 this.setFromData(r.data);
19584 var close = this.closeTriggerEl();
19590 this.hideTouchView();
19592 this.fireEvent('select', this, r, rowIndex);
19597 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19598 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19599 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19603 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19604 this.addItem(r.data);
19605 this.tickItems.push(r.data);
19609 getAutoCreateNativeIOS : function()
19612 cls: 'form-group' //input-group,
19617 cls : 'roo-ios-select'
19621 combobox.name = this.name;
19624 if (this.disabled) {
19625 combobox.disabled = true;
19628 var settings = this;
19630 ['xs','sm','md','lg'].map(function(size){
19631 if (settings[size]) {
19632 cfg.cls += ' col-' + size + '-' + settings[size];
19642 initIOSView : function()
19644 this.store.on('load', this.onIOSViewLoad, this);
19649 onIOSViewLoad : function()
19651 if(this.store.getCount() < 1){
19655 this.clearIOSView();
19657 if(this.allowBlank) {
19659 var default_text = '-- SELECT --';
19661 if(this.placeholder.length){
19662 default_text = this.placeholder;
19665 if(this.emptyTitle.length){
19666 default_text += ' - ' + this.emptyTitle + ' -';
19669 var opt = this.inputEl().createChild({
19672 html : default_text
19676 o[this.valueField] = 0;
19677 o[this.displayField] = default_text;
19679 this.ios_options.push({
19686 this.store.data.each(function(d, rowIndex){
19690 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19691 html = d.data[this.displayField];
19696 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19697 value = d.data[this.valueField];
19706 if(this.value == d.data[this.valueField]){
19707 option['selected'] = true;
19710 var opt = this.inputEl().createChild(option);
19712 this.ios_options.push({
19719 this.inputEl().on('change', function(){
19720 this.fireEvent('select', this);
19725 clearIOSView: function()
19727 this.inputEl().dom.innerHTML = '';
19729 this.ios_options = [];
19732 setIOSValue: function(v)
19736 if(!this.ios_options){
19740 Roo.each(this.ios_options, function(opts){
19742 opts.el.dom.removeAttribute('selected');
19744 if(opts.data[this.valueField] != v){
19748 opts.el.dom.setAttribute('selected', true);
19754 * @cfg {Boolean} grow
19758 * @cfg {Number} growMin
19762 * @cfg {Number} growMax
19771 Roo.apply(Roo.bootstrap.form.ComboBox, {
19775 cls: 'modal-header',
19797 cls: 'list-group-item',
19801 cls: 'roo-combobox-list-group-item-value'
19805 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19819 listItemCheckbox : {
19821 cls: 'list-group-item',
19825 cls: 'roo-combobox-list-group-item-value'
19829 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19845 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19850 cls: 'modal-footer',
19858 cls: 'col-xs-6 text-left',
19861 cls: 'btn btn-danger roo-touch-view-cancel',
19867 cls: 'col-xs-6 text-right',
19870 cls: 'btn btn-success roo-touch-view-ok',
19881 Roo.apply(Roo.bootstrap.form.ComboBox, {
19883 touchViewTemplate : {
19885 cls: 'modal fade roo-combobox-touch-view',
19889 cls: 'modal-dialog',
19890 style : 'position:fixed', // we have to fix position....
19894 cls: 'modal-content',
19896 Roo.bootstrap.form.ComboBox.header,
19897 Roo.bootstrap.form.ComboBox.body,
19898 Roo.bootstrap.form.ComboBox.footer
19907 * Ext JS Library 1.1.1
19908 * Copyright(c) 2006-2007, Ext JS, LLC.
19910 * Originally Released Under LGPL - original licence link has changed is not relivant.
19913 * <script type="text/javascript">
19918 * @extends Roo.util.Observable
19919 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19920 * This class also supports single and multi selection modes. <br>
19921 * Create a data model bound view:
19923 var store = new Roo.data.Store(...);
19925 var view = new Roo.View({
19927 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19929 singleSelect: true,
19930 selectedClass: "ydataview-selected",
19934 // listen for node click?
19935 view.on("click", function(vw, index, node, e){
19936 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19940 dataModel.load("foobar.xml");
19942 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19944 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19945 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19947 * Note: old style constructor is still suported (container, template, config)
19950 * Create a new View
19951 * @param {Object} config The config object
19954 Roo.View = function(config, depreciated_tpl, depreciated_config){
19956 this.parent = false;
19958 if (typeof(depreciated_tpl) == 'undefined') {
19959 // new way.. - universal constructor.
19960 Roo.apply(this, config);
19961 this.el = Roo.get(this.el);
19964 this.el = Roo.get(config);
19965 this.tpl = depreciated_tpl;
19966 Roo.apply(this, depreciated_config);
19968 this.wrapEl = this.el.wrap().wrap();
19969 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19972 if(typeof(this.tpl) == "string"){
19973 this.tpl = new Roo.Template(this.tpl);
19975 // support xtype ctors..
19976 this.tpl = new Roo.factory(this.tpl, Roo);
19980 this.tpl.compile();
19985 * @event beforeclick
19986 * Fires before a click is processed. Returns false to cancel the default action.
19987 * @param {Roo.View} this
19988 * @param {Number} index The index of the target node
19989 * @param {HTMLElement} node The target node
19990 * @param {Roo.EventObject} e The raw event object
19992 "beforeclick" : true,
19995 * Fires when a template node is clicked.
19996 * @param {Roo.View} this
19997 * @param {Number} index The index of the target node
19998 * @param {HTMLElement} node The target node
19999 * @param {Roo.EventObject} e The raw event object
20004 * Fires when a template node is double clicked.
20005 * @param {Roo.View} this
20006 * @param {Number} index The index of the target node
20007 * @param {HTMLElement} node The target node
20008 * @param {Roo.EventObject} e The raw event object
20012 * @event contextmenu
20013 * Fires when a template node is right clicked.
20014 * @param {Roo.View} this
20015 * @param {Number} index The index of the target node
20016 * @param {HTMLElement} node The target node
20017 * @param {Roo.EventObject} e The raw event object
20019 "contextmenu" : true,
20021 * @event selectionchange
20022 * Fires when the selected nodes change.
20023 * @param {Roo.View} this
20024 * @param {Array} selections Array of the selected nodes
20026 "selectionchange" : true,
20029 * @event beforeselect
20030 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20031 * @param {Roo.View} this
20032 * @param {HTMLElement} node The node to be selected
20033 * @param {Array} selections Array of currently selected nodes
20035 "beforeselect" : true,
20037 * @event preparedata
20038 * Fires on every row to render, to allow you to change the data.
20039 * @param {Roo.View} this
20040 * @param {Object} data to be rendered (change this)
20042 "preparedata" : true
20050 "click": this.onClick,
20051 "dblclick": this.onDblClick,
20052 "contextmenu": this.onContextMenu,
20056 this.selections = [];
20058 this.cmp = new Roo.CompositeElementLite([]);
20060 this.store = Roo.factory(this.store, Roo.data);
20061 this.setStore(this.store, true);
20064 if ( this.footer && this.footer.xtype) {
20066 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20068 this.footer.dataSource = this.store;
20069 this.footer.container = fctr;
20070 this.footer = Roo.factory(this.footer, Roo);
20071 fctr.insertFirst(this.el);
20073 // this is a bit insane - as the paging toolbar seems to detach the el..
20074 // dom.parentNode.parentNode.parentNode
20075 // they get detached?
20079 Roo.View.superclass.constructor.call(this);
20084 Roo.extend(Roo.View, Roo.util.Observable, {
20087 * @cfg {Roo.data.Store} store Data store to load data from.
20092 * @cfg {String|Roo.Element} el The container element.
20097 * @cfg {String|Roo.Template} tpl The template used by this View
20101 * @cfg {String} dataName the named area of the template to use as the data area
20102 * Works with domtemplates roo-name="name"
20106 * @cfg {String} selectedClass The css class to add to selected nodes
20108 selectedClass : "x-view-selected",
20110 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20115 * @cfg {String} text to display on mask (default Loading)
20119 * @cfg {Boolean} multiSelect Allow multiple selection
20121 multiSelect : false,
20123 * @cfg {Boolean} singleSelect Allow single selection
20125 singleSelect: false,
20128 * @cfg {Boolean} toggleSelect - selecting
20130 toggleSelect : false,
20133 * @cfg {Boolean} tickable - selecting
20138 * Returns the element this view is bound to.
20139 * @return {Roo.Element}
20141 getEl : function(){
20142 return this.wrapEl;
20148 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20150 refresh : function(){
20151 //Roo.log('refresh');
20154 // if we are using something like 'domtemplate', then
20155 // the what gets used is:
20156 // t.applySubtemplate(NAME, data, wrapping data..)
20157 // the outer template then get' applied with
20158 // the store 'extra data'
20159 // and the body get's added to the
20160 // roo-name="data" node?
20161 // <span class='roo-tpl-{name}'></span> ?????
20165 this.clearSelections();
20166 this.el.update("");
20168 var records = this.store.getRange();
20169 if(records.length < 1) {
20171 // is this valid?? = should it render a template??
20173 this.el.update(this.emptyText);
20177 if (this.dataName) {
20178 this.el.update(t.apply(this.store.meta)); //????
20179 el = this.el.child('.roo-tpl-' + this.dataName);
20182 for(var i = 0, len = records.length; i < len; i++){
20183 var data = this.prepareData(records[i].data, i, records[i]);
20184 this.fireEvent("preparedata", this, data, i, records[i]);
20186 var d = Roo.apply({}, data);
20189 Roo.apply(d, {'roo-id' : Roo.id()});
20193 Roo.each(this.parent.item, function(item){
20194 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20197 Roo.apply(d, {'roo-data-checked' : 'checked'});
20201 html[html.length] = Roo.util.Format.trim(
20203 t.applySubtemplate(this.dataName, d, this.store.meta) :
20210 el.update(html.join(""));
20211 this.nodes = el.dom.childNodes;
20212 this.updateIndexes(0);
20217 * Function to override to reformat the data that is sent to
20218 * the template for each node.
20219 * DEPRICATED - use the preparedata event handler.
20220 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20221 * a JSON object for an UpdateManager bound view).
20223 prepareData : function(data, index, record)
20225 this.fireEvent("preparedata", this, data, index, record);
20229 onUpdate : function(ds, record){
20230 // Roo.log('on update');
20231 this.clearSelections();
20232 var index = this.store.indexOf(record);
20233 var n = this.nodes[index];
20234 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20235 n.parentNode.removeChild(n);
20236 this.updateIndexes(index, index);
20242 onAdd : function(ds, records, index)
20244 //Roo.log(['on Add', ds, records, index] );
20245 this.clearSelections();
20246 if(this.nodes.length == 0){
20250 var n = this.nodes[index];
20251 for(var i = 0, len = records.length; i < len; i++){
20252 var d = this.prepareData(records[i].data, i, records[i]);
20254 this.tpl.insertBefore(n, d);
20257 this.tpl.append(this.el, d);
20260 this.updateIndexes(index);
20263 onRemove : function(ds, record, index){
20264 // Roo.log('onRemove');
20265 this.clearSelections();
20266 var el = this.dataName ?
20267 this.el.child('.roo-tpl-' + this.dataName) :
20270 el.dom.removeChild(this.nodes[index]);
20271 this.updateIndexes(index);
20275 * Refresh an individual node.
20276 * @param {Number} index
20278 refreshNode : function(index){
20279 this.onUpdate(this.store, this.store.getAt(index));
20282 updateIndexes : function(startIndex, endIndex){
20283 var ns = this.nodes;
20284 startIndex = startIndex || 0;
20285 endIndex = endIndex || ns.length - 1;
20286 for(var i = startIndex; i <= endIndex; i++){
20287 ns[i].nodeIndex = i;
20292 * Changes the data store this view uses and refresh the view.
20293 * @param {Store} store
20295 setStore : function(store, initial){
20296 if(!initial && this.store){
20297 this.store.un("datachanged", this.refresh);
20298 this.store.un("add", this.onAdd);
20299 this.store.un("remove", this.onRemove);
20300 this.store.un("update", this.onUpdate);
20301 this.store.un("clear", this.refresh);
20302 this.store.un("beforeload", this.onBeforeLoad);
20303 this.store.un("load", this.onLoad);
20304 this.store.un("loadexception", this.onLoad);
20308 store.on("datachanged", this.refresh, this);
20309 store.on("add", this.onAdd, this);
20310 store.on("remove", this.onRemove, this);
20311 store.on("update", this.onUpdate, this);
20312 store.on("clear", this.refresh, this);
20313 store.on("beforeload", this.onBeforeLoad, this);
20314 store.on("load", this.onLoad, this);
20315 store.on("loadexception", this.onLoad, this);
20323 * onbeforeLoad - masks the loading area.
20326 onBeforeLoad : function(store,opts)
20328 //Roo.log('onBeforeLoad');
20330 this.el.update("");
20332 this.el.mask(this.mask ? this.mask : "Loading" );
20334 onLoad : function ()
20341 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20342 * @param {HTMLElement} node
20343 * @return {HTMLElement} The template node
20345 findItemFromChild : function(node){
20346 var el = this.dataName ?
20347 this.el.child('.roo-tpl-' + this.dataName,true) :
20350 if(!node || node.parentNode == el){
20353 var p = node.parentNode;
20354 while(p && p != el){
20355 if(p.parentNode == el){
20364 onClick : function(e){
20365 var item = this.findItemFromChild(e.getTarget());
20367 var index = this.indexOf(item);
20368 if(this.onItemClick(item, index, e) !== false){
20369 this.fireEvent("click", this, index, item, e);
20372 this.clearSelections();
20377 onContextMenu : function(e){
20378 var item = this.findItemFromChild(e.getTarget());
20380 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20385 onDblClick : function(e){
20386 var item = this.findItemFromChild(e.getTarget());
20388 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20392 onItemClick : function(item, index, e)
20394 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20397 if (this.toggleSelect) {
20398 var m = this.isSelected(item) ? 'unselect' : 'select';
20401 _t[m](item, true, false);
20404 if(this.multiSelect || this.singleSelect){
20405 if(this.multiSelect && e.shiftKey && this.lastSelection){
20406 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20408 this.select(item, this.multiSelect && e.ctrlKey);
20409 this.lastSelection = item;
20412 if(!this.tickable){
20413 e.preventDefault();
20421 * Get the number of selected nodes.
20424 getSelectionCount : function(){
20425 return this.selections.length;
20429 * Get the currently selected nodes.
20430 * @return {Array} An array of HTMLElements
20432 getSelectedNodes : function(){
20433 return this.selections;
20437 * Get the indexes of the selected nodes.
20440 getSelectedIndexes : function(){
20441 var indexes = [], s = this.selections;
20442 for(var i = 0, len = s.length; i < len; i++){
20443 indexes.push(s[i].nodeIndex);
20449 * Clear all selections
20450 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20452 clearSelections : function(suppressEvent){
20453 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20454 this.cmp.elements = this.selections;
20455 this.cmp.removeClass(this.selectedClass);
20456 this.selections = [];
20457 if(!suppressEvent){
20458 this.fireEvent("selectionchange", this, this.selections);
20464 * Returns true if the passed node is selected
20465 * @param {HTMLElement/Number} node The node or node index
20466 * @return {Boolean}
20468 isSelected : function(node){
20469 var s = this.selections;
20473 node = this.getNode(node);
20474 return s.indexOf(node) !== -1;
20479 * @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
20480 * @param {Boolean} keepExisting (optional) true to keep existing selections
20481 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20483 select : function(nodeInfo, keepExisting, suppressEvent){
20484 if(nodeInfo instanceof Array){
20486 this.clearSelections(true);
20488 for(var i = 0, len = nodeInfo.length; i < len; i++){
20489 this.select(nodeInfo[i], true, true);
20493 var node = this.getNode(nodeInfo);
20494 if(!node || this.isSelected(node)){
20495 return; // already selected.
20498 this.clearSelections(true);
20501 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20502 Roo.fly(node).addClass(this.selectedClass);
20503 this.selections.push(node);
20504 if(!suppressEvent){
20505 this.fireEvent("selectionchange", this, this.selections);
20513 * @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
20514 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20515 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20517 unselect : function(nodeInfo, keepExisting, suppressEvent)
20519 if(nodeInfo instanceof Array){
20520 Roo.each(this.selections, function(s) {
20521 this.unselect(s, nodeInfo);
20525 var node = this.getNode(nodeInfo);
20526 if(!node || !this.isSelected(node)){
20527 //Roo.log("not selected");
20528 return; // not selected.
20532 Roo.each(this.selections, function(s) {
20534 Roo.fly(node).removeClass(this.selectedClass);
20541 this.selections= ns;
20542 this.fireEvent("selectionchange", this, this.selections);
20546 * Gets a template node.
20547 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20548 * @return {HTMLElement} The node or null if it wasn't found
20550 getNode : function(nodeInfo){
20551 if(typeof nodeInfo == "string"){
20552 return document.getElementById(nodeInfo);
20553 }else if(typeof nodeInfo == "number"){
20554 return this.nodes[nodeInfo];
20560 * Gets a range template nodes.
20561 * @param {Number} startIndex
20562 * @param {Number} endIndex
20563 * @return {Array} An array of nodes
20565 getNodes : function(start, end){
20566 var ns = this.nodes;
20567 start = start || 0;
20568 end = typeof end == "undefined" ? ns.length - 1 : end;
20571 for(var i = start; i <= end; i++){
20575 for(var i = start; i >= end; i--){
20583 * Finds the index of the passed node
20584 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20585 * @return {Number} The index of the node or -1
20587 indexOf : function(node){
20588 node = this.getNode(node);
20589 if(typeof node.nodeIndex == "number"){
20590 return node.nodeIndex;
20592 var ns = this.nodes;
20593 for(var i = 0, len = ns.length; i < len; i++){
20604 * based on jquery fullcalendar
20608 Roo.bootstrap = Roo.bootstrap || {};
20610 * @class Roo.bootstrap.Calendar
20611 * @extends Roo.bootstrap.Component
20612 * Bootstrap Calendar class
20613 * @cfg {Boolean} loadMask (true|false) default false
20614 * @cfg {Object} header generate the user specific header of the calendar, default false
20617 * Create a new Container
20618 * @param {Object} config The config object
20623 Roo.bootstrap.Calendar = function(config){
20624 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20628 * Fires when a date is selected
20629 * @param {DatePicker} this
20630 * @param {Date} date The selected date
20634 * @event monthchange
20635 * Fires when the displayed month changes
20636 * @param {DatePicker} this
20637 * @param {Date} date The selected month
20639 'monthchange': true,
20641 * @event evententer
20642 * Fires when mouse over an event
20643 * @param {Calendar} this
20644 * @param {event} Event
20646 'evententer': true,
20648 * @event eventleave
20649 * Fires when the mouse leaves an
20650 * @param {Calendar} this
20653 'eventleave': true,
20655 * @event eventclick
20656 * Fires when the mouse click an
20657 * @param {Calendar} this
20666 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20669 * @cfg {Roo.data.Store} store
20670 * The data source for the calendar
20674 * @cfg {Number} startDay
20675 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20683 getAutoCreate : function(){
20686 var fc_button = function(name, corner, style, content ) {
20687 return Roo.apply({},{
20689 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20691 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20694 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20705 style : 'width:100%',
20712 cls : 'fc-header-left',
20714 fc_button('prev', 'left', 'arrow', '‹' ),
20715 fc_button('next', 'right', 'arrow', '›' ),
20716 { tag: 'span', cls: 'fc-header-space' },
20717 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20725 cls : 'fc-header-center',
20729 cls: 'fc-header-title',
20732 html : 'month / year'
20740 cls : 'fc-header-right',
20742 /* fc_button('month', 'left', '', 'month' ),
20743 fc_button('week', '', '', 'week' ),
20744 fc_button('day', 'right', '', 'day' )
20756 header = this.header;
20759 var cal_heads = function() {
20761 // fixme - handle this.
20763 for (var i =0; i < Date.dayNames.length; i++) {
20764 var d = Date.dayNames[i];
20767 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20768 html : d.substring(0,3)
20772 ret[0].cls += ' fc-first';
20773 ret[6].cls += ' fc-last';
20776 var cal_cell = function(n) {
20779 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20784 cls: 'fc-day-number',
20788 cls: 'fc-day-content',
20792 style: 'position: relative;' // height: 17px;
20804 var cal_rows = function() {
20807 for (var r = 0; r < 6; r++) {
20814 for (var i =0; i < Date.dayNames.length; i++) {
20815 var d = Date.dayNames[i];
20816 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20819 row.cn[0].cls+=' fc-first';
20820 row.cn[0].cn[0].style = 'min-height:90px';
20821 row.cn[6].cls+=' fc-last';
20825 ret[0].cls += ' fc-first';
20826 ret[4].cls += ' fc-prev-last';
20827 ret[5].cls += ' fc-last';
20834 cls: 'fc-border-separate',
20835 style : 'width:100%',
20843 cls : 'fc-first fc-last',
20861 cls : 'fc-content',
20862 style : "position: relative;",
20865 cls : 'fc-view fc-view-month fc-grid',
20866 style : 'position: relative',
20867 unselectable : 'on',
20870 cls : 'fc-event-container',
20871 style : 'position:absolute;z-index:8;top:0;left:0;'
20889 initEvents : function()
20892 throw "can not find store for calendar";
20898 style: "text-align:center",
20902 style: "background-color:white;width:50%;margin:250 auto",
20906 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20917 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20919 var size = this.el.select('.fc-content', true).first().getSize();
20920 this.maskEl.setSize(size.width, size.height);
20921 this.maskEl.enableDisplayMode("block");
20922 if(!this.loadMask){
20923 this.maskEl.hide();
20926 this.store = Roo.factory(this.store, Roo.data);
20927 this.store.on('load', this.onLoad, this);
20928 this.store.on('beforeload', this.onBeforeLoad, this);
20932 this.cells = this.el.select('.fc-day',true);
20933 //Roo.log(this.cells);
20934 this.textNodes = this.el.query('.fc-day-number');
20935 this.cells.addClassOnOver('fc-state-hover');
20937 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20938 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20939 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20940 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20942 this.on('monthchange', this.onMonthChange, this);
20944 this.update(new Date().clearTime());
20947 resize : function() {
20948 var sz = this.el.getSize();
20950 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20951 this.el.select('.fc-day-content div',true).setHeight(34);
20956 showPrevMonth : function(e){
20957 this.update(this.activeDate.add("mo", -1));
20959 showToday : function(e){
20960 this.update(new Date().clearTime());
20963 showNextMonth : function(e){
20964 this.update(this.activeDate.add("mo", 1));
20968 showPrevYear : function(){
20969 this.update(this.activeDate.add("y", -1));
20973 showNextYear : function(){
20974 this.update(this.activeDate.add("y", 1));
20979 update : function(date)
20981 var vd = this.activeDate;
20982 this.activeDate = date;
20983 // if(vd && this.el){
20984 // var t = date.getTime();
20985 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20986 // Roo.log('using add remove');
20988 // this.fireEvent('monthchange', this, date);
20990 // this.cells.removeClass("fc-state-highlight");
20991 // this.cells.each(function(c){
20992 // if(c.dateValue == t){
20993 // c.addClass("fc-state-highlight");
20994 // setTimeout(function(){
20995 // try{c.dom.firstChild.focus();}catch(e){}
21005 var days = date.getDaysInMonth();
21007 var firstOfMonth = date.getFirstDateOfMonth();
21008 var startingPos = firstOfMonth.getDay()-this.startDay;
21010 if(startingPos < this.startDay){
21014 var pm = date.add(Date.MONTH, -1);
21015 var prevStart = pm.getDaysInMonth()-startingPos;
21017 this.cells = this.el.select('.fc-day',true);
21018 this.textNodes = this.el.query('.fc-day-number');
21019 this.cells.addClassOnOver('fc-state-hover');
21021 var cells = this.cells.elements;
21022 var textEls = this.textNodes;
21024 Roo.each(cells, function(cell){
21025 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21028 days += startingPos;
21030 // convert everything to numbers so it's fast
21031 var day = 86400000;
21032 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21035 //Roo.log(prevStart);
21037 var today = new Date().clearTime().getTime();
21038 var sel = date.clearTime().getTime();
21039 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21040 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21041 var ddMatch = this.disabledDatesRE;
21042 var ddText = this.disabledDatesText;
21043 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21044 var ddaysText = this.disabledDaysText;
21045 var format = this.format;
21047 var setCellClass = function(cal, cell){
21051 //Roo.log('set Cell Class');
21053 var t = d.getTime();
21057 cell.dateValue = t;
21059 cell.className += " fc-today";
21060 cell.className += " fc-state-highlight";
21061 cell.title = cal.todayText;
21064 // disable highlight in other month..
21065 //cell.className += " fc-state-highlight";
21070 cell.className = " fc-state-disabled";
21071 cell.title = cal.minText;
21075 cell.className = " fc-state-disabled";
21076 cell.title = cal.maxText;
21080 if(ddays.indexOf(d.getDay()) != -1){
21081 cell.title = ddaysText;
21082 cell.className = " fc-state-disabled";
21085 if(ddMatch && format){
21086 var fvalue = d.dateFormat(format);
21087 if(ddMatch.test(fvalue)){
21088 cell.title = ddText.replace("%0", fvalue);
21089 cell.className = " fc-state-disabled";
21093 if (!cell.initialClassName) {
21094 cell.initialClassName = cell.dom.className;
21097 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21102 for(; i < startingPos; i++) {
21103 textEls[i].innerHTML = (++prevStart);
21104 d.setDate(d.getDate()+1);
21106 cells[i].className = "fc-past fc-other-month";
21107 setCellClass(this, cells[i]);
21112 for(; i < days; i++){
21113 intDay = i - startingPos + 1;
21114 textEls[i].innerHTML = (intDay);
21115 d.setDate(d.getDate()+1);
21117 cells[i].className = ''; // "x-date-active";
21118 setCellClass(this, cells[i]);
21122 for(; i < 42; i++) {
21123 textEls[i].innerHTML = (++extraDays);
21124 d.setDate(d.getDate()+1);
21126 cells[i].className = "fc-future fc-other-month";
21127 setCellClass(this, cells[i]);
21130 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21132 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21134 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21135 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21137 if(totalRows != 6){
21138 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21139 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21142 this.fireEvent('monthchange', this, date);
21146 if(!this.internalRender){
21147 var main = this.el.dom.firstChild;
21148 var w = main.offsetWidth;
21149 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21150 Roo.fly(main).setWidth(w);
21151 this.internalRender = true;
21152 // opera does not respect the auto grow header center column
21153 // then, after it gets a width opera refuses to recalculate
21154 // without a second pass
21155 if(Roo.isOpera && !this.secondPass){
21156 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21157 this.secondPass = true;
21158 this.update.defer(10, this, [date]);
21165 findCell : function(dt) {
21166 dt = dt.clearTime().getTime();
21168 this.cells.each(function(c){
21169 //Roo.log("check " +c.dateValue + '?=' + dt);
21170 if(c.dateValue == dt){
21180 findCells : function(ev) {
21181 var s = ev.start.clone().clearTime().getTime();
21183 var e= ev.end.clone().clearTime().getTime();
21186 this.cells.each(function(c){
21187 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21189 if(c.dateValue > e){
21192 if(c.dateValue < s){
21201 // findBestRow: function(cells)
21205 // for (var i =0 ; i < cells.length;i++) {
21206 // ret = Math.max(cells[i].rows || 0,ret);
21213 addItem : function(ev)
21215 // look for vertical location slot in
21216 var cells = this.findCells(ev);
21218 // ev.row = this.findBestRow(cells);
21220 // work out the location.
21224 for(var i =0; i < cells.length; i++) {
21226 cells[i].row = cells[0].row;
21229 cells[i].row = cells[i].row + 1;
21239 if (crow.start.getY() == cells[i].getY()) {
21241 crow.end = cells[i];
21258 cells[0].events.push(ev);
21260 this.calevents.push(ev);
21263 clearEvents: function() {
21265 if(!this.calevents){
21269 Roo.each(this.cells.elements, function(c){
21275 Roo.each(this.calevents, function(e) {
21276 Roo.each(e.els, function(el) {
21277 el.un('mouseenter' ,this.onEventEnter, this);
21278 el.un('mouseleave' ,this.onEventLeave, this);
21283 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21289 renderEvents: function()
21293 this.cells.each(function(c) {
21302 if(c.row != c.events.length){
21303 r = 4 - (4 - (c.row - c.events.length));
21306 c.events = ev.slice(0, r);
21307 c.more = ev.slice(r);
21309 if(c.more.length && c.more.length == 1){
21310 c.events.push(c.more.pop());
21313 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21317 this.cells.each(function(c) {
21319 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21322 for (var e = 0; e < c.events.length; e++){
21323 var ev = c.events[e];
21324 var rows = ev.rows;
21326 for(var i = 0; i < rows.length; i++) {
21328 // how many rows should it span..
21331 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21332 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21334 unselectable : "on",
21337 cls: 'fc-event-inner',
21341 // cls: 'fc-event-time',
21342 // html : cells.length > 1 ? '' : ev.time
21346 cls: 'fc-event-title',
21347 html : String.format('{0}', ev.title)
21354 cls: 'ui-resizable-handle ui-resizable-e',
21355 html : '  '
21362 cfg.cls += ' fc-event-start';
21364 if ((i+1) == rows.length) {
21365 cfg.cls += ' fc-event-end';
21368 var ctr = _this.el.select('.fc-event-container',true).first();
21369 var cg = ctr.createChild(cfg);
21371 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21372 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21374 var r = (c.more.length) ? 1 : 0;
21375 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21376 cg.setWidth(ebox.right - sbox.x -2);
21378 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21379 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21380 cg.on('click', _this.onEventClick, _this, ev);
21391 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21392 style : 'position: absolute',
21393 unselectable : "on",
21396 cls: 'fc-event-inner',
21400 cls: 'fc-event-title',
21408 cls: 'ui-resizable-handle ui-resizable-e',
21409 html : '  '
21415 var ctr = _this.el.select('.fc-event-container',true).first();
21416 var cg = ctr.createChild(cfg);
21418 var sbox = c.select('.fc-day-content',true).first().getBox();
21419 var ebox = c.select('.fc-day-content',true).first().getBox();
21421 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21422 cg.setWidth(ebox.right - sbox.x -2);
21424 cg.on('click', _this.onMoreEventClick, _this, c.more);
21434 onEventEnter: function (e, el,event,d) {
21435 this.fireEvent('evententer', this, el, event);
21438 onEventLeave: function (e, el,event,d) {
21439 this.fireEvent('eventleave', this, el, event);
21442 onEventClick: function (e, el,event,d) {
21443 this.fireEvent('eventclick', this, el, event);
21446 onMonthChange: function () {
21450 onMoreEventClick: function(e, el, more)
21454 this.calpopover.placement = 'right';
21455 this.calpopover.setTitle('More');
21457 this.calpopover.setContent('');
21459 var ctr = this.calpopover.el.select('.popover-content', true).first();
21461 Roo.each(more, function(m){
21463 cls : 'fc-event-hori fc-event-draggable',
21466 var cg = ctr.createChild(cfg);
21468 cg.on('click', _this.onEventClick, _this, m);
21471 this.calpopover.show(el);
21476 onLoad: function ()
21478 this.calevents = [];
21481 if(this.store.getCount() > 0){
21482 this.store.data.each(function(d){
21485 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21486 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21487 time : d.data.start_time,
21488 title : d.data.title,
21489 description : d.data.description,
21490 venue : d.data.venue
21495 this.renderEvents();
21497 if(this.calevents.length && this.loadMask){
21498 this.maskEl.hide();
21502 onBeforeLoad: function()
21504 this.clearEvents();
21506 this.maskEl.show();
21520 * @class Roo.bootstrap.Popover
21521 * @extends Roo.bootstrap.Component
21522 * @parent none builder
21523 * @children Roo.bootstrap.Component
21524 * Bootstrap Popover class
21525 * @cfg {String} html contents of the popover (or false to use children..)
21526 * @cfg {String} title of popover (or false to hide)
21527 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21528 * @cfg {String} trigger click || hover (or false to trigger manually)
21529 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21530 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21531 * - if false and it has a 'parent' then it will be automatically added to that element
21532 * - if string - Roo.get will be called
21533 * @cfg {Number} delay - delay before showing
21536 * Create a new Popover
21537 * @param {Object} config The config object
21540 Roo.bootstrap.Popover = function(config){
21541 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21547 * After the popover show
21549 * @param {Roo.bootstrap.Popover} this
21554 * After the popover hide
21556 * @param {Roo.bootstrap.Popover} this
21562 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21567 placement : 'right',
21568 trigger : 'hover', // hover
21574 can_build_overlaid : false,
21576 maskEl : false, // the mask element
21579 alignEl : false, // when show is called with an element - this get's stored.
21581 getChildContainer : function()
21583 return this.contentEl;
21586 getPopoverHeader : function()
21588 this.title = true; // flag not to hide it..
21589 this.headerEl.addClass('p-0');
21590 return this.headerEl
21594 getAutoCreate : function(){
21597 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21598 style: 'display:block',
21604 cls : 'popover-inner ',
21608 cls: 'popover-title popover-header',
21609 html : this.title === false ? '' : this.title
21612 cls : 'popover-content popover-body ' + (this.cls || ''),
21613 html : this.html || ''
21624 * @param {string} the title
21626 setTitle: function(str)
21630 this.headerEl.dom.innerHTML = str;
21635 * @param {string} the body content
21637 setContent: function(str)
21640 if (this.contentEl) {
21641 this.contentEl.dom.innerHTML = str;
21645 // as it get's added to the bottom of the page.
21646 onRender : function(ct, position)
21648 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21653 var cfg = Roo.apply({}, this.getAutoCreate());
21657 cfg.cls += ' ' + this.cls;
21660 cfg.style = this.style;
21662 //Roo.log("adding to ");
21663 this.el = Roo.get(document.body).createChild(cfg, position);
21664 // Roo.log(this.el);
21667 this.contentEl = this.el.select('.popover-content',true).first();
21668 this.headerEl = this.el.select('.popover-title',true).first();
21671 if(typeof(this.items) != 'undefined'){
21672 var items = this.items;
21675 for(var i =0;i < items.length;i++) {
21676 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21680 this.items = nitems;
21682 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21683 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21690 resizeMask : function()
21692 this.maskEl.setSize(
21693 Roo.lib.Dom.getViewWidth(true),
21694 Roo.lib.Dom.getViewHeight(true)
21698 initEvents : function()
21702 Roo.bootstrap.Popover.register(this);
21705 this.arrowEl = this.el.select('.arrow',true).first();
21706 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21707 this.el.enableDisplayMode('block');
21711 if (this.over === false && !this.parent()) {
21714 if (this.triggers === false) {
21719 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21720 var triggers = this.trigger ? this.trigger.split(' ') : [];
21721 Roo.each(triggers, function(trigger) {
21723 if (trigger == 'click') {
21724 on_el.on('click', this.toggle, this);
21725 } else if (trigger != 'manual') {
21726 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21727 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21729 on_el.on(eventIn ,this.enter, this);
21730 on_el.on(eventOut, this.leave, this);
21740 toggle : function () {
21741 this.hoverState == 'in' ? this.leave() : this.enter();
21744 enter : function () {
21746 clearTimeout(this.timeout);
21748 this.hoverState = 'in';
21750 if (!this.delay || !this.delay.show) {
21755 this.timeout = setTimeout(function () {
21756 if (_t.hoverState == 'in') {
21759 }, this.delay.show)
21762 leave : function() {
21763 clearTimeout(this.timeout);
21765 this.hoverState = 'out';
21767 if (!this.delay || !this.delay.hide) {
21772 this.timeout = setTimeout(function () {
21773 if (_t.hoverState == 'out') {
21776 }, this.delay.hide)
21780 * update the position of the dialog
21781 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21786 doAlign : function()
21789 if (this.alignEl) {
21790 this.updatePosition(this.placement, true);
21793 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21794 var es = this.el.getSize();
21795 var x = Roo.lib.Dom.getViewWidth()/2;
21796 var y = Roo.lib.Dom.getViewHeight()/2;
21797 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21809 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21810 * @param {string} (left|right|top|bottom) position
21812 show : function (on_el, placement)
21814 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21815 on_el = on_el || false; // default to false
21818 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21819 on_el = this.parent().el;
21820 } else if (this.over) {
21821 on_el = Roo.get(this.over);
21826 this.alignEl = Roo.get( on_el );
21829 this.render(document.body);
21835 if (this.title === false) {
21836 this.headerEl.hide();
21841 this.el.dom.style.display = 'block';
21845 //var arrow = this.el.select('.arrow',true).first();
21846 //arrow.set(align[2],
21848 this.el.addClass('in');
21852 this.hoverState = 'in';
21855 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21856 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21857 this.maskEl.dom.style.display = 'block';
21858 this.maskEl.addClass('show');
21860 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21862 this.fireEvent('show', this);
21866 * fire this manually after loading a grid in the table for example
21867 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21868 * @param {Boolean} try and move it if we cant get right position.
21870 updatePosition : function(placement, try_move)
21872 // allow for calling with no parameters
21873 placement = placement ? placement : this.placement;
21874 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21876 this.el.removeClass([
21877 'fade','top','bottom', 'left', 'right','in',
21878 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21880 this.el.addClass(placement + ' bs-popover-' + placement);
21882 if (!this.alignEl ) {
21886 switch (placement) {
21888 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21889 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21890 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21891 //normal display... or moved up/down.
21892 this.el.setXY(offset);
21893 var xy = this.alignEl.getAnchorXY('tr', false);
21895 this.arrowEl.setXY(xy);
21898 // continue through...
21899 return this.updatePosition('left', false);
21903 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21904 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21905 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21906 //normal display... or moved up/down.
21907 this.el.setXY(offset);
21908 var xy = this.alignEl.getAnchorXY('tl', false);
21909 xy[0]-=10;xy[1]+=5; // << fix me
21910 this.arrowEl.setXY(xy);
21914 return this.updatePosition('right', false);
21917 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21918 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21919 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21920 //normal display... or moved up/down.
21921 this.el.setXY(offset);
21922 var xy = this.alignEl.getAnchorXY('t', false);
21923 xy[1]-=10; // << fix me
21924 this.arrowEl.setXY(xy);
21928 return this.updatePosition('bottom', false);
21931 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21932 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21933 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21934 //normal display... or moved up/down.
21935 this.el.setXY(offset);
21936 var xy = this.alignEl.getAnchorXY('b', false);
21937 xy[1]+=2; // << fix me
21938 this.arrowEl.setXY(xy);
21942 return this.updatePosition('top', false);
21953 this.el.setXY([0,0]);
21954 this.el.removeClass('in');
21956 this.hoverState = null;
21957 this.maskEl.hide(); // always..
21958 this.fireEvent('hide', this);
21964 Roo.apply(Roo.bootstrap.Popover, {
21967 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21968 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21969 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21970 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21975 clickHander : false,
21979 onMouseDown : function(e)
21981 if (this.popups.length && !e.getTarget(".roo-popover")) {
21982 /// what is nothing is showing..
21991 register : function(popup)
21993 if (!Roo.bootstrap.Popover.clickHandler) {
21994 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21996 // hide other popups.
21997 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
21998 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
21999 this.hideAll(); //<< why?
22000 //this.popups.push(popup);
22002 hideAll : function()
22004 this.popups.forEach(function(p) {
22008 onShow : function() {
22009 Roo.bootstrap.Popover.popups.push(this);
22011 onHide : function() {
22012 Roo.bootstrap.Popover.popups.remove(this);
22017 * @class Roo.bootstrap.PopoverNav
22018 * @extends Roo.bootstrap.nav.Simplebar
22019 * @parent Roo.bootstrap.Popover
22020 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22022 * Bootstrap Popover header navigation class
22023 * FIXME? should this go under nav?
22027 * Create a new Popover Header Navigation
22028 * @param {Object} config The config object
22031 Roo.bootstrap.PopoverNav = function(config){
22032 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22035 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22038 container_method : 'getPopoverHeader'
22056 * @class Roo.bootstrap.Progress
22057 * @extends Roo.bootstrap.Component
22058 * @children Roo.bootstrap.ProgressBar
22059 * Bootstrap Progress class
22060 * @cfg {Boolean} striped striped of the progress bar
22061 * @cfg {Boolean} active animated of the progress bar
22065 * Create a new Progress
22066 * @param {Object} config The config object
22069 Roo.bootstrap.Progress = function(config){
22070 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22073 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22078 getAutoCreate : function(){
22086 cfg.cls += ' progress-striped';
22090 cfg.cls += ' active';
22109 * @class Roo.bootstrap.ProgressBar
22110 * @extends Roo.bootstrap.Component
22111 * Bootstrap ProgressBar class
22112 * @cfg {Number} aria_valuenow aria-value now
22113 * @cfg {Number} aria_valuemin aria-value min
22114 * @cfg {Number} aria_valuemax aria-value max
22115 * @cfg {String} label label for the progress bar
22116 * @cfg {String} panel (success | info | warning | danger )
22117 * @cfg {String} role role of the progress bar
22118 * @cfg {String} sr_only text
22122 * Create a new ProgressBar
22123 * @param {Object} config The config object
22126 Roo.bootstrap.ProgressBar = function(config){
22127 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22130 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22134 aria_valuemax : 100,
22140 getAutoCreate : function()
22145 cls: 'progress-bar',
22146 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22158 cfg.role = this.role;
22161 if(this.aria_valuenow){
22162 cfg['aria-valuenow'] = this.aria_valuenow;
22165 if(this.aria_valuemin){
22166 cfg['aria-valuemin'] = this.aria_valuemin;
22169 if(this.aria_valuemax){
22170 cfg['aria-valuemax'] = this.aria_valuemax;
22173 if(this.label && !this.sr_only){
22174 cfg.html = this.label;
22178 cfg.cls += ' progress-bar-' + this.panel;
22184 update : function(aria_valuenow)
22186 this.aria_valuenow = aria_valuenow;
22188 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22196 * @class Roo.bootstrap.TabGroup
22197 * @extends Roo.bootstrap.Column
22198 * @children Roo.bootstrap.TabPanel
22199 * Bootstrap Column class
22200 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22201 * @cfg {Boolean} carousel true to make the group behave like a carousel
22202 * @cfg {Boolean} bullets show bullets for the panels
22203 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22204 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22205 * @cfg {Boolean} showarrow (true|false) show arrow default true
22208 * Create a new TabGroup
22209 * @param {Object} config The config object
22212 Roo.bootstrap.TabGroup = function(config){
22213 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22215 this.navId = Roo.id();
22218 Roo.bootstrap.TabGroup.register(this);
22222 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22225 transition : false,
22230 slideOnTouch : false,
22233 getAutoCreate : function()
22235 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22237 cfg.cls += ' tab-content';
22239 if (this.carousel) {
22240 cfg.cls += ' carousel slide';
22243 cls : 'carousel-inner',
22247 if(this.bullets && !Roo.isTouch){
22250 cls : 'carousel-bullets',
22254 if(this.bullets_cls){
22255 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22262 cfg.cn[0].cn.push(bullets);
22265 if(this.showarrow){
22266 cfg.cn[0].cn.push({
22268 class : 'carousel-arrow',
22272 class : 'carousel-prev',
22276 class : 'fa fa-chevron-left'
22282 class : 'carousel-next',
22286 class : 'fa fa-chevron-right'
22299 initEvents: function()
22301 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22302 // this.el.on("touchstart", this.onTouchStart, this);
22305 if(this.autoslide){
22308 this.slideFn = window.setInterval(function() {
22309 _this.showPanelNext();
22313 if(this.showarrow){
22314 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22315 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22321 // onTouchStart : function(e, el, o)
22323 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22327 // this.showPanelNext();
22331 getChildContainer : function()
22333 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22337 * register a Navigation item
22338 * @param {Roo.bootstrap.nav.Item} the navitem to add
22340 register : function(item)
22342 this.tabs.push( item);
22343 item.navId = this.navId; // not really needed..
22348 getActivePanel : function()
22351 Roo.each(this.tabs, function(t) {
22361 getPanelByName : function(n)
22364 Roo.each(this.tabs, function(t) {
22365 if (t.tabId == n) {
22373 indexOfPanel : function(p)
22376 Roo.each(this.tabs, function(t,i) {
22377 if (t.tabId == p.tabId) {
22386 * show a specific panel
22387 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22388 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22390 showPanel : function (pan)
22392 if(this.transition || typeof(pan) == 'undefined'){
22393 Roo.log("waiting for the transitionend");
22397 if (typeof(pan) == 'number') {
22398 pan = this.tabs[pan];
22401 if (typeof(pan) == 'string') {
22402 pan = this.getPanelByName(pan);
22405 var cur = this.getActivePanel();
22408 Roo.log('pan or acitve pan is undefined');
22412 if (pan.tabId == this.getActivePanel().tabId) {
22416 if (false === cur.fireEvent('beforedeactivate')) {
22420 if(this.bullets > 0 && !Roo.isTouch){
22421 this.setActiveBullet(this.indexOfPanel(pan));
22424 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22426 //class="carousel-item carousel-item-next carousel-item-left"
22428 this.transition = true;
22429 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22430 var lr = dir == 'next' ? 'left' : 'right';
22431 pan.el.addClass(dir); // or prev
22432 pan.el.addClass('carousel-item-' + dir); // or prev
22433 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22434 cur.el.addClass(lr); // or right
22435 pan.el.addClass(lr);
22436 cur.el.addClass('carousel-item-' +lr); // or right
22437 pan.el.addClass('carousel-item-' +lr);
22441 cur.el.on('transitionend', function() {
22442 Roo.log("trans end?");
22444 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22445 pan.setActive(true);
22447 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22448 cur.setActive(false);
22450 _this.transition = false;
22452 }, this, { single: true } );
22457 cur.setActive(false);
22458 pan.setActive(true);
22463 showPanelNext : function()
22465 var i = this.indexOfPanel(this.getActivePanel());
22467 if (i >= this.tabs.length - 1 && !this.autoslide) {
22471 if (i >= this.tabs.length - 1 && this.autoslide) {
22475 this.showPanel(this.tabs[i+1]);
22478 showPanelPrev : function()
22480 var i = this.indexOfPanel(this.getActivePanel());
22482 if (i < 1 && !this.autoslide) {
22486 if (i < 1 && this.autoslide) {
22487 i = this.tabs.length;
22490 this.showPanel(this.tabs[i-1]);
22494 addBullet: function()
22496 if(!this.bullets || Roo.isTouch){
22499 var ctr = this.el.select('.carousel-bullets',true).first();
22500 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22501 var bullet = ctr.createChild({
22502 cls : 'bullet bullet-' + i
22503 },ctr.dom.lastChild);
22508 bullet.on('click', (function(e, el, o, ii, t){
22510 e.preventDefault();
22512 this.showPanel(ii);
22514 if(this.autoslide && this.slideFn){
22515 clearInterval(this.slideFn);
22516 this.slideFn = window.setInterval(function() {
22517 _this.showPanelNext();
22521 }).createDelegate(this, [i, bullet], true));
22526 setActiveBullet : function(i)
22532 Roo.each(this.el.select('.bullet', true).elements, function(el){
22533 el.removeClass('selected');
22536 var bullet = this.el.select('.bullet-' + i, true).first();
22542 bullet.addClass('selected');
22553 Roo.apply(Roo.bootstrap.TabGroup, {
22557 * register a Navigation Group
22558 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22560 register : function(navgrp)
22562 this.groups[navgrp.navId] = navgrp;
22566 * fetch a Navigation Group based on the navigation ID
22567 * if one does not exist , it will get created.
22568 * @param {string} the navgroup to add
22569 * @returns {Roo.bootstrap.nav.Group} the navgroup
22571 get: function(navId) {
22572 if (typeof(this.groups[navId]) == 'undefined') {
22573 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22575 return this.groups[navId] ;
22590 * @class Roo.bootstrap.TabPanel
22591 * @extends Roo.bootstrap.Component
22592 * @children Roo.bootstrap.Component
22593 * Bootstrap TabPanel class
22594 * @cfg {Boolean} active panel active
22595 * @cfg {String} html panel content
22596 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22597 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22598 * @cfg {String} href click to link..
22599 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22603 * Create a new TabPanel
22604 * @param {Object} config The config object
22607 Roo.bootstrap.TabPanel = function(config){
22608 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22612 * Fires when the active status changes
22613 * @param {Roo.bootstrap.TabPanel} this
22614 * @param {Boolean} state the new state
22619 * @event beforedeactivate
22620 * Fires before a tab is de-activated - can be used to do validation on a form.
22621 * @param {Roo.bootstrap.TabPanel} this
22622 * @return {Boolean} false if there is an error
22625 'beforedeactivate': true
22628 this.tabId = this.tabId || Roo.id();
22632 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22639 touchSlide : false,
22640 getAutoCreate : function(){
22645 // item is needed for carousel - not sure if it has any effect otherwise
22646 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22647 html: this.html || ''
22651 cfg.cls += ' active';
22655 cfg.tabId = this.tabId;
22663 initEvents: function()
22665 var p = this.parent();
22667 this.navId = this.navId || p.navId;
22669 if (typeof(this.navId) != 'undefined') {
22670 // not really needed.. but just in case.. parent should be a NavGroup.
22671 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22675 var i = tg.tabs.length - 1;
22677 if(this.active && tg.bullets > 0 && i < tg.bullets){
22678 tg.setActiveBullet(i);
22682 this.el.on('click', this.onClick, this);
22684 if(Roo.isTouch && this.touchSlide){
22685 this.el.on("touchstart", this.onTouchStart, this);
22686 this.el.on("touchmove", this.onTouchMove, this);
22687 this.el.on("touchend", this.onTouchEnd, this);
22692 onRender : function(ct, position)
22694 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22697 setActive : function(state)
22699 Roo.log("panel - set active " + this.tabId + "=" + state);
22701 this.active = state;
22703 this.el.removeClass('active');
22705 } else if (!this.el.hasClass('active')) {
22706 this.el.addClass('active');
22709 this.fireEvent('changed', this, state);
22712 onClick : function(e)
22714 e.preventDefault();
22716 if(!this.href.length){
22720 window.location.href = this.href;
22729 onTouchStart : function(e)
22731 this.swiping = false;
22733 this.startX = e.browserEvent.touches[0].clientX;
22734 this.startY = e.browserEvent.touches[0].clientY;
22737 onTouchMove : function(e)
22739 this.swiping = true;
22741 this.endX = e.browserEvent.touches[0].clientX;
22742 this.endY = e.browserEvent.touches[0].clientY;
22745 onTouchEnd : function(e)
22752 var tabGroup = this.parent();
22754 if(this.endX > this.startX){ // swiping right
22755 tabGroup.showPanelPrev();
22759 if(this.startX > this.endX){ // swiping left
22760 tabGroup.showPanelNext();
22779 * @class Roo.bootstrap.form.DateField
22780 * @extends Roo.bootstrap.form.Input
22781 * Bootstrap DateField class
22782 * @cfg {Number} weekStart default 0
22783 * @cfg {String} viewMode default empty, (months|years)
22784 * @cfg {String} minViewMode default empty, (months|years)
22785 * @cfg {Number} startDate default -Infinity
22786 * @cfg {Number} endDate default Infinity
22787 * @cfg {Boolean} todayHighlight default false
22788 * @cfg {Boolean} todayBtn default false
22789 * @cfg {Boolean} calendarWeeks default false
22790 * @cfg {Object} daysOfWeekDisabled default empty
22791 * @cfg {Boolean} singleMode default false (true | false)
22793 * @cfg {Boolean} keyboardNavigation default true
22794 * @cfg {String} language default en
22797 * Create a new DateField
22798 * @param {Object} config The config object
22801 Roo.bootstrap.form.DateField = function(config){
22802 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22806 * Fires when this field show.
22807 * @param {Roo.bootstrap.form.DateField} this
22808 * @param {Mixed} date The date value
22813 * Fires when this field hide.
22814 * @param {Roo.bootstrap.form.DateField} this
22815 * @param {Mixed} date The date value
22820 * Fires when select a date.
22821 * @param {Roo.bootstrap.form.DateField} this
22822 * @param {Mixed} date The date value
22826 * @event beforeselect
22827 * Fires when before select a date.
22828 * @param {Roo.bootstrap.form.DateField} this
22829 * @param {Mixed} date The date value
22831 beforeselect : true
22835 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22838 * @cfg {String} format
22839 * The default date format string which can be overriden for localization support. The format must be
22840 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22844 * @cfg {String} altFormats
22845 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22846 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22848 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22856 todayHighlight : false,
22862 keyboardNavigation: true,
22864 calendarWeeks: false,
22866 startDate: -Infinity,
22870 daysOfWeekDisabled: [],
22874 singleMode : false,
22876 UTCDate: function()
22878 return new Date(Date.UTC.apply(Date, arguments));
22881 UTCToday: function()
22883 var today = new Date();
22884 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22887 getDate: function() {
22888 var d = this.getUTCDate();
22889 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22892 getUTCDate: function() {
22896 setDate: function(d) {
22897 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22900 setUTCDate: function(d) {
22902 this.setValue(this.formatDate(this.date));
22905 onRender: function(ct, position)
22908 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22910 this.language = this.language || 'en';
22911 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22912 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22914 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22915 this.format = this.format || 'm/d/y';
22916 this.isInline = false;
22917 this.isInput = true;
22918 this.component = this.el.select('.add-on', true).first() || false;
22919 this.component = (this.component && this.component.length === 0) ? false : this.component;
22920 this.hasInput = this.component && this.inputEl().length;
22922 if (typeof(this.minViewMode === 'string')) {
22923 switch (this.minViewMode) {
22925 this.minViewMode = 1;
22928 this.minViewMode = 2;
22931 this.minViewMode = 0;
22936 if (typeof(this.viewMode === 'string')) {
22937 switch (this.viewMode) {
22950 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22952 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22954 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22956 this.picker().on('mousedown', this.onMousedown, this);
22957 this.picker().on('click', this.onClick, this);
22959 this.picker().addClass('datepicker-dropdown');
22961 this.startViewMode = this.viewMode;
22963 if(this.singleMode){
22964 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22965 v.setVisibilityMode(Roo.Element.DISPLAY);
22969 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22970 v.setStyle('width', '189px');
22974 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22975 if(!this.calendarWeeks){
22980 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22981 v.attr('colspan', function(i, val){
22982 return parseInt(val) + 1;
22987 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22989 this.setStartDate(this.startDate);
22990 this.setEndDate(this.endDate);
22992 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22999 if(this.isInline) {
23004 picker : function()
23006 return this.pickerEl;
23007 // return this.el.select('.datepicker', true).first();
23010 fillDow: function()
23012 var dowCnt = this.weekStart;
23021 if(this.calendarWeeks){
23029 while (dowCnt < this.weekStart + 7) {
23033 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23037 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23040 fillMonths: function()
23043 var months = this.picker().select('>.datepicker-months td', true).first();
23045 months.dom.innerHTML = '';
23051 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23054 months.createChild(month);
23061 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;
23063 if (this.date < this.startDate) {
23064 this.viewDate = new Date(this.startDate);
23065 } else if (this.date > this.endDate) {
23066 this.viewDate = new Date(this.endDate);
23068 this.viewDate = new Date(this.date);
23076 var d = new Date(this.viewDate),
23077 year = d.getUTCFullYear(),
23078 month = d.getUTCMonth(),
23079 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23080 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23081 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23082 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23083 currentDate = this.date && this.date.valueOf(),
23084 today = this.UTCToday();
23086 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23088 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23090 // this.picker.select('>tfoot th.today').
23091 // .text(dates[this.language].today)
23092 // .toggle(this.todayBtn !== false);
23094 this.updateNavArrows();
23097 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23099 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23101 prevMonth.setUTCDate(day);
23103 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23105 var nextMonth = new Date(prevMonth);
23107 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23109 nextMonth = nextMonth.valueOf();
23111 var fillMonths = false;
23113 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23115 while(prevMonth.valueOf() <= nextMonth) {
23118 if (prevMonth.getUTCDay() === this.weekStart) {
23120 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23128 if(this.calendarWeeks){
23129 // ISO 8601: First week contains first thursday.
23130 // ISO also states week starts on Monday, but we can be more abstract here.
23132 // Start of current week: based on weekstart/current date
23133 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23134 // Thursday of this week
23135 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23136 // First Thursday of year, year from thursday
23137 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23138 // Calendar week: ms between thursdays, div ms per day, div 7 days
23139 calWeek = (th - yth) / 864e5 / 7 + 1;
23141 fillMonths.cn.push({
23149 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23151 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23154 if (this.todayHighlight &&
23155 prevMonth.getUTCFullYear() == today.getFullYear() &&
23156 prevMonth.getUTCMonth() == today.getMonth() &&
23157 prevMonth.getUTCDate() == today.getDate()) {
23158 clsName += ' today';
23161 if (currentDate && prevMonth.valueOf() === currentDate) {
23162 clsName += ' active';
23165 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23166 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23167 clsName += ' disabled';
23170 fillMonths.cn.push({
23172 cls: 'day ' + clsName,
23173 html: prevMonth.getDate()
23176 prevMonth.setDate(prevMonth.getDate()+1);
23179 var currentYear = this.date && this.date.getUTCFullYear();
23180 var currentMonth = this.date && this.date.getUTCMonth();
23182 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23184 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23185 v.removeClass('active');
23187 if(currentYear === year && k === currentMonth){
23188 v.addClass('active');
23191 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23192 v.addClass('disabled');
23198 year = parseInt(year/10, 10) * 10;
23200 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23202 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23205 for (var i = -1; i < 11; i++) {
23206 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23208 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23216 showMode: function(dir)
23219 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23222 Roo.each(this.picker().select('>div',true).elements, function(v){
23223 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23226 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23231 if(this.isInline) {
23235 this.picker().removeClass(['bottom', 'top']);
23237 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23239 * place to the top of element!
23243 this.picker().addClass('top');
23244 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23249 this.picker().addClass('bottom');
23251 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23254 parseDate : function(value)
23256 if(!value || value instanceof Date){
23259 var v = Date.parseDate(value, this.format);
23260 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23261 v = Date.parseDate(value, 'Y-m-d');
23263 if(!v && this.altFormats){
23264 if(!this.altFormatsArray){
23265 this.altFormatsArray = this.altFormats.split("|");
23267 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23268 v = Date.parseDate(value, this.altFormatsArray[i]);
23274 formatDate : function(date, fmt)
23276 return (!date || !(date instanceof Date)) ?
23277 date : date.dateFormat(fmt || this.format);
23280 onFocus : function()
23282 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23286 onBlur : function()
23288 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23290 var d = this.inputEl().getValue();
23297 showPopup : function()
23299 this.picker().show();
23303 this.fireEvent('showpopup', this, this.date);
23306 hidePopup : function()
23308 if(this.isInline) {
23311 this.picker().hide();
23312 this.viewMode = this.startViewMode;
23315 this.fireEvent('hidepopup', this, this.date);
23319 onMousedown: function(e)
23321 e.stopPropagation();
23322 e.preventDefault();
23327 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23331 setValue: function(v)
23333 if(this.fireEvent('beforeselect', this, v) !== false){
23334 var d = new Date(this.parseDate(v) ).clearTime();
23336 if(isNaN(d.getTime())){
23337 this.date = this.viewDate = '';
23338 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23342 v = this.formatDate(d);
23344 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23346 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23350 this.fireEvent('select', this, this.date);
23354 getValue: function()
23356 return this.formatDate(this.date);
23359 fireKey: function(e)
23361 if (!this.picker().isVisible()){
23362 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23368 var dateChanged = false,
23370 newDate, newViewDate;
23375 e.preventDefault();
23379 if (!this.keyboardNavigation) {
23382 dir = e.keyCode == 37 ? -1 : 1;
23385 newDate = this.moveYear(this.date, dir);
23386 newViewDate = this.moveYear(this.viewDate, dir);
23387 } else if (e.shiftKey){
23388 newDate = this.moveMonth(this.date, dir);
23389 newViewDate = this.moveMonth(this.viewDate, dir);
23391 newDate = new Date(this.date);
23392 newDate.setUTCDate(this.date.getUTCDate() + dir);
23393 newViewDate = new Date(this.viewDate);
23394 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23396 if (this.dateWithinRange(newDate)){
23397 this.date = newDate;
23398 this.viewDate = newViewDate;
23399 this.setValue(this.formatDate(this.date));
23401 e.preventDefault();
23402 dateChanged = true;
23407 if (!this.keyboardNavigation) {
23410 dir = e.keyCode == 38 ? -1 : 1;
23412 newDate = this.moveYear(this.date, dir);
23413 newViewDate = this.moveYear(this.viewDate, dir);
23414 } else if (e.shiftKey){
23415 newDate = this.moveMonth(this.date, dir);
23416 newViewDate = this.moveMonth(this.viewDate, dir);
23418 newDate = new Date(this.date);
23419 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23420 newViewDate = new Date(this.viewDate);
23421 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23423 if (this.dateWithinRange(newDate)){
23424 this.date = newDate;
23425 this.viewDate = newViewDate;
23426 this.setValue(this.formatDate(this.date));
23428 e.preventDefault();
23429 dateChanged = true;
23433 this.setValue(this.formatDate(this.date));
23435 e.preventDefault();
23438 this.setValue(this.formatDate(this.date));
23452 onClick: function(e)
23454 e.stopPropagation();
23455 e.preventDefault();
23457 var target = e.getTarget();
23459 if(target.nodeName.toLowerCase() === 'i'){
23460 target = Roo.get(target).dom.parentNode;
23463 var nodeName = target.nodeName;
23464 var className = target.className;
23465 var html = target.innerHTML;
23466 //Roo.log(nodeName);
23468 switch(nodeName.toLowerCase()) {
23470 switch(className) {
23476 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23477 switch(this.viewMode){
23479 this.viewDate = this.moveMonth(this.viewDate, dir);
23483 this.viewDate = this.moveYear(this.viewDate, dir);
23489 var date = new Date();
23490 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23492 this.setValue(this.formatDate(this.date));
23499 if (className.indexOf('disabled') < 0) {
23500 if (!this.viewDate) {
23501 this.viewDate = new Date();
23503 this.viewDate.setUTCDate(1);
23504 if (className.indexOf('month') > -1) {
23505 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23507 var year = parseInt(html, 10) || 0;
23508 this.viewDate.setUTCFullYear(year);
23512 if(this.singleMode){
23513 this.setValue(this.formatDate(this.viewDate));
23524 //Roo.log(className);
23525 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23526 var day = parseInt(html, 10) || 1;
23527 var year = (this.viewDate || new Date()).getUTCFullYear(),
23528 month = (this.viewDate || new Date()).getUTCMonth();
23530 if (className.indexOf('old') > -1) {
23537 } else if (className.indexOf('new') > -1) {
23545 //Roo.log([year,month,day]);
23546 this.date = this.UTCDate(year, month, day,0,0,0,0);
23547 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23549 //Roo.log(this.formatDate(this.date));
23550 this.setValue(this.formatDate(this.date));
23557 setStartDate: function(startDate)
23559 this.startDate = startDate || -Infinity;
23560 if (this.startDate !== -Infinity) {
23561 this.startDate = this.parseDate(this.startDate);
23564 this.updateNavArrows();
23567 setEndDate: function(endDate)
23569 this.endDate = endDate || Infinity;
23570 if (this.endDate !== Infinity) {
23571 this.endDate = this.parseDate(this.endDate);
23574 this.updateNavArrows();
23577 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23579 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23580 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23581 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23583 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23584 return parseInt(d, 10);
23587 this.updateNavArrows();
23590 updateNavArrows: function()
23592 if(this.singleMode){
23596 var d = new Date(this.viewDate),
23597 year = d.getUTCFullYear(),
23598 month = d.getUTCMonth();
23600 Roo.each(this.picker().select('.prev', true).elements, function(v){
23602 switch (this.viewMode) {
23605 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23611 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23618 Roo.each(this.picker().select('.next', true).elements, function(v){
23620 switch (this.viewMode) {
23623 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23629 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23637 moveMonth: function(date, dir)
23642 var new_date = new Date(date.valueOf()),
23643 day = new_date.getUTCDate(),
23644 month = new_date.getUTCMonth(),
23645 mag = Math.abs(dir),
23647 dir = dir > 0 ? 1 : -1;
23650 // If going back one month, make sure month is not current month
23651 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23653 return new_date.getUTCMonth() == month;
23655 // If going forward one month, make sure month is as expected
23656 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23658 return new_date.getUTCMonth() != new_month;
23660 new_month = month + dir;
23661 new_date.setUTCMonth(new_month);
23662 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23663 if (new_month < 0 || new_month > 11) {
23664 new_month = (new_month + 12) % 12;
23667 // For magnitudes >1, move one month at a time...
23668 for (var i=0; i<mag; i++) {
23669 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23670 new_date = this.moveMonth(new_date, dir);
23672 // ...then reset the day, keeping it in the new month
23673 new_month = new_date.getUTCMonth();
23674 new_date.setUTCDate(day);
23676 return new_month != new_date.getUTCMonth();
23679 // Common date-resetting loop -- if date is beyond end of month, make it
23682 new_date.setUTCDate(--day);
23683 new_date.setUTCMonth(new_month);
23688 moveYear: function(date, dir)
23690 return this.moveMonth(date, dir*12);
23693 dateWithinRange: function(date)
23695 return date >= this.startDate && date <= this.endDate;
23701 this.picker().remove();
23704 validateValue : function(value)
23706 if(this.getVisibilityEl().hasClass('hidden')){
23710 if(value.length < 1) {
23711 if(this.allowBlank){
23717 if(value.length < this.minLength){
23720 if(value.length > this.maxLength){
23724 var vt = Roo.form.VTypes;
23725 if(!vt[this.vtype](value, this)){
23729 if(typeof this.validator == "function"){
23730 var msg = this.validator(value);
23736 if(this.regex && !this.regex.test(value)){
23740 if(typeof(this.parseDate(value)) == 'undefined'){
23744 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23748 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23758 this.date = this.viewDate = '';
23760 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23765 Roo.apply(Roo.bootstrap.form.DateField, {
23776 html: '<i class="fa fa-arrow-left"/>'
23786 html: '<i class="fa fa-arrow-right"/>'
23828 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23829 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23830 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23831 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23832 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23845 navFnc: 'FullYear',
23850 navFnc: 'FullYear',
23855 Roo.apply(Roo.bootstrap.form.DateField, {
23859 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23863 cls: 'datepicker-days',
23867 cls: 'table-condensed',
23869 Roo.bootstrap.form.DateField.head,
23873 Roo.bootstrap.form.DateField.footer
23880 cls: 'datepicker-months',
23884 cls: 'table-condensed',
23886 Roo.bootstrap.form.DateField.head,
23887 Roo.bootstrap.form.DateField.content,
23888 Roo.bootstrap.form.DateField.footer
23895 cls: 'datepicker-years',
23899 cls: 'table-condensed',
23901 Roo.bootstrap.form.DateField.head,
23902 Roo.bootstrap.form.DateField.content,
23903 Roo.bootstrap.form.DateField.footer
23922 * @class Roo.bootstrap.form.TimeField
23923 * @extends Roo.bootstrap.form.Input
23924 * Bootstrap DateField class
23928 * Create a new TimeField
23929 * @param {Object} config The config object
23932 Roo.bootstrap.form.TimeField = function(config){
23933 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23937 * Fires when this field show.
23938 * @param {Roo.bootstrap.form.DateField} thisthis
23939 * @param {Mixed} date The date value
23944 * Fires when this field hide.
23945 * @param {Roo.bootstrap.form.DateField} this
23946 * @param {Mixed} date The date value
23951 * Fires when select a date.
23952 * @param {Roo.bootstrap.form.DateField} this
23953 * @param {Mixed} date The date value
23959 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23962 * @cfg {String} format
23963 * The default time format string which can be overriden for localization support. The format must be
23964 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23968 getAutoCreate : function()
23970 this.after = '<i class="fa far fa-clock"></i>';
23971 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23975 onRender: function(ct, position)
23978 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23980 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23982 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23984 this.pop = this.picker().select('>.datepicker-time',true).first();
23985 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23987 this.picker().on('mousedown', this.onMousedown, this);
23988 this.picker().on('click', this.onClick, this);
23990 this.picker().addClass('datepicker-dropdown');
23995 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23996 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23997 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23998 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23999 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24000 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24004 fireKey: function(e){
24005 if (!this.picker().isVisible()){
24006 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24012 e.preventDefault();
24020 this.onTogglePeriod();
24023 this.onIncrementMinutes();
24026 this.onDecrementMinutes();
24035 onClick: function(e) {
24036 e.stopPropagation();
24037 e.preventDefault();
24040 picker : function()
24042 return this.pickerEl;
24045 fillTime: function()
24047 var time = this.pop.select('tbody', true).first();
24049 time.dom.innerHTML = '';
24064 cls: 'hours-up fa fas fa-chevron-up'
24084 cls: 'minutes-up fa fas fa-chevron-up'
24105 cls: 'timepicker-hour',
24120 cls: 'timepicker-minute',
24135 cls: 'btn btn-primary period',
24157 cls: 'hours-down fa fas fa-chevron-down'
24177 cls: 'minutes-down fa fas fa-chevron-down'
24195 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24202 var hours = this.time.getHours();
24203 var minutes = this.time.getMinutes();
24216 hours = hours - 12;
24220 hours = '0' + hours;
24224 minutes = '0' + minutes;
24227 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24228 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24229 this.pop.select('button', true).first().dom.innerHTML = period;
24235 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24237 var cls = ['bottom'];
24239 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24246 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24250 //this.picker().setXY(20000,20000);
24251 this.picker().addClass(cls.join('-'));
24255 Roo.each(cls, function(c){
24260 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24261 //_this.picker().setTop(_this.inputEl().getHeight());
24265 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24267 //_this.picker().setTop(0 - _this.picker().getHeight());
24272 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24276 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24284 onFocus : function()
24286 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24290 onBlur : function()
24292 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24298 this.picker().show();
24303 this.fireEvent('show', this, this.date);
24308 this.picker().hide();
24311 this.fireEvent('hide', this, this.date);
24314 setTime : function()
24317 this.setValue(this.time.format(this.format));
24319 this.fireEvent('select', this, this.date);
24324 onMousedown: function(e){
24325 e.stopPropagation();
24326 e.preventDefault();
24329 onIncrementHours: function()
24331 Roo.log('onIncrementHours');
24332 this.time = this.time.add(Date.HOUR, 1);
24337 onDecrementHours: function()
24339 Roo.log('onDecrementHours');
24340 this.time = this.time.add(Date.HOUR, -1);
24344 onIncrementMinutes: function()
24346 Roo.log('onIncrementMinutes');
24347 this.time = this.time.add(Date.MINUTE, 1);
24351 onDecrementMinutes: function()
24353 Roo.log('onDecrementMinutes');
24354 this.time = this.time.add(Date.MINUTE, -1);
24358 onTogglePeriod: function()
24360 Roo.log('onTogglePeriod');
24361 this.time = this.time.add(Date.HOUR, 12);
24369 Roo.apply(Roo.bootstrap.form.TimeField, {
24373 cls: 'datepicker dropdown-menu',
24377 cls: 'datepicker-time',
24381 cls: 'table-condensed',
24410 cls: 'btn btn-info ok',
24438 * @class Roo.bootstrap.form.MonthField
24439 * @extends Roo.bootstrap.form.Input
24440 * Bootstrap MonthField class
24442 * @cfg {String} language default en
24445 * Create a new MonthField
24446 * @param {Object} config The config object
24449 Roo.bootstrap.form.MonthField = function(config){
24450 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24455 * Fires when this field show.
24456 * @param {Roo.bootstrap.form.MonthField} this
24457 * @param {Mixed} date The date value
24462 * Fires when this field hide.
24463 * @param {Roo.bootstrap.form.MonthField} this
24464 * @param {Mixed} date The date value
24469 * Fires when select a date.
24470 * @param {Roo.bootstrap.form.MonthField} this
24471 * @param {String} oldvalue The old value
24472 * @param {String} newvalue The new value
24478 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24480 onRender: function(ct, position)
24483 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24485 this.language = this.language || 'en';
24486 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24487 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24489 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24490 this.isInline = false;
24491 this.isInput = true;
24492 this.component = this.el.select('.add-on', true).first() || false;
24493 this.component = (this.component && this.component.length === 0) ? false : this.component;
24494 this.hasInput = this.component && this.inputEL().length;
24496 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24498 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24500 this.picker().on('mousedown', this.onMousedown, this);
24501 this.picker().on('click', this.onClick, this);
24503 this.picker().addClass('datepicker-dropdown');
24505 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24506 v.setStyle('width', '189px');
24513 if(this.isInline) {
24519 setValue: function(v, suppressEvent)
24521 var o = this.getValue();
24523 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24527 if(suppressEvent !== true){
24528 this.fireEvent('select', this, o, v);
24533 getValue: function()
24538 onClick: function(e)
24540 e.stopPropagation();
24541 e.preventDefault();
24543 var target = e.getTarget();
24545 if(target.nodeName.toLowerCase() === 'i'){
24546 target = Roo.get(target).dom.parentNode;
24549 var nodeName = target.nodeName;
24550 var className = target.className;
24551 var html = target.innerHTML;
24553 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24557 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24559 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24565 picker : function()
24567 return this.pickerEl;
24570 fillMonths: function()
24573 var months = this.picker().select('>.datepicker-months td', true).first();
24575 months.dom.innerHTML = '';
24581 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24584 months.createChild(month);
24593 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24594 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24597 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24598 e.removeClass('active');
24600 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24601 e.addClass('active');
24608 if(this.isInline) {
24612 this.picker().removeClass(['bottom', 'top']);
24614 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24616 * place to the top of element!
24620 this.picker().addClass('top');
24621 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24626 this.picker().addClass('bottom');
24628 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24631 onFocus : function()
24633 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24637 onBlur : function()
24639 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24641 var d = this.inputEl().getValue();
24650 this.picker().show();
24651 this.picker().select('>.datepicker-months', true).first().show();
24655 this.fireEvent('show', this, this.date);
24660 if(this.isInline) {
24663 this.picker().hide();
24664 this.fireEvent('hide', this, this.date);
24668 onMousedown: function(e)
24670 e.stopPropagation();
24671 e.preventDefault();
24676 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24680 fireKey: function(e)
24682 if (!this.picker().isVisible()){
24683 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24694 e.preventDefault();
24698 dir = e.keyCode == 37 ? -1 : 1;
24700 this.vIndex = this.vIndex + dir;
24702 if(this.vIndex < 0){
24706 if(this.vIndex > 11){
24710 if(isNaN(this.vIndex)){
24714 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24720 dir = e.keyCode == 38 ? -1 : 1;
24722 this.vIndex = this.vIndex + dir * 4;
24724 if(this.vIndex < 0){
24728 if(this.vIndex > 11){
24732 if(isNaN(this.vIndex)){
24736 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24741 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24742 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24746 e.preventDefault();
24749 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24750 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24766 this.picker().remove();
24771 Roo.apply(Roo.bootstrap.form.MonthField, {
24790 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24791 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24796 Roo.apply(Roo.bootstrap.form.MonthField, {
24800 cls: 'datepicker dropdown-menu roo-dynamic',
24804 cls: 'datepicker-months',
24808 cls: 'table-condensed',
24810 Roo.bootstrap.form.DateField.content
24830 * @class Roo.bootstrap.form.CheckBox
24831 * @extends Roo.bootstrap.form.Input
24832 * Bootstrap CheckBox class
24834 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24835 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24836 * @cfg {String} boxLabel The text that appears beside the checkbox
24837 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24838 * @cfg {Boolean} checked initnal the element
24839 * @cfg {Boolean} inline inline the element (default false)
24840 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24841 * @cfg {String} tooltip label tooltip
24844 * Create a new CheckBox
24845 * @param {Object} config The config object
24848 Roo.bootstrap.form.CheckBox = function(config){
24849 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24854 * Fires when the element is checked or unchecked.
24855 * @param {Roo.bootstrap.form.CheckBox} this This input
24856 * @param {Boolean} checked The new checked value
24861 * Fires when the element is click.
24862 * @param {Roo.bootstrap.form.CheckBox} this This input
24869 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24871 inputType: 'checkbox',
24880 // checkbox success does not make any sense really..
24885 getAutoCreate : function()
24887 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24893 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24896 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24902 type : this.inputType,
24903 value : this.inputValue,
24904 cls : 'roo-' + this.inputType, //'form-box',
24905 placeholder : this.placeholder || ''
24909 if(this.inputType != 'radio'){
24913 cls : 'roo-hidden-value',
24914 value : this.checked ? this.inputValue : this.valueOff
24919 if (this.weight) { // Validity check?
24920 cfg.cls += " " + this.inputType + "-" + this.weight;
24923 if (this.disabled) {
24924 input.disabled=true;
24928 input.checked = this.checked;
24933 input.name = this.name;
24935 if(this.inputType != 'radio'){
24936 hidden.name = this.name;
24937 input.name = '_hidden_' + this.name;
24942 input.cls += ' input-' + this.size;
24947 ['xs','sm','md','lg'].map(function(size){
24948 if (settings[size]) {
24949 cfg.cls += ' col-' + size + '-' + settings[size];
24953 var inputblock = input;
24955 if (this.before || this.after) {
24958 cls : 'input-group',
24963 inputblock.cn.push({
24965 cls : 'input-group-addon',
24970 inputblock.cn.push(input);
24972 if(this.inputType != 'radio'){
24973 inputblock.cn.push(hidden);
24977 inputblock.cn.push({
24979 cls : 'input-group-addon',
24985 var boxLabelCfg = false;
24991 //'for': id, // box label is handled by onclick - so no for...
24993 html: this.boxLabel
24996 boxLabelCfg.tooltip = this.tooltip;
25002 if (align ==='left' && this.fieldLabel.length) {
25003 // Roo.log("left and has label");
25008 cls : 'control-label',
25009 html : this.fieldLabel
25020 cfg.cn[1].cn.push(boxLabelCfg);
25023 if(this.labelWidth > 12){
25024 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25027 if(this.labelWidth < 13 && this.labelmd == 0){
25028 this.labelmd = this.labelWidth;
25031 if(this.labellg > 0){
25032 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25033 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25036 if(this.labelmd > 0){
25037 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25038 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25041 if(this.labelsm > 0){
25042 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25043 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25046 if(this.labelxs > 0){
25047 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25048 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25051 } else if ( this.fieldLabel.length) {
25052 // Roo.log(" label");
25056 tag: this.boxLabel ? 'span' : 'label',
25058 cls: 'control-label box-input-label',
25059 //cls : 'input-group-addon',
25060 html : this.fieldLabel
25067 cfg.cn.push(boxLabelCfg);
25072 // Roo.log(" no label && no align");
25073 cfg.cn = [ inputblock ] ;
25075 cfg.cn.push(boxLabelCfg);
25083 if(this.inputType != 'radio'){
25084 cfg.cn.push(hidden);
25092 * return the real input element.
25094 inputEl: function ()
25096 return this.el.select('input.roo-' + this.inputType,true).first();
25098 hiddenEl: function ()
25100 return this.el.select('input.roo-hidden-value',true).first();
25103 labelEl: function()
25105 return this.el.select('label.control-label',true).first();
25107 /* depricated... */
25111 return this.labelEl();
25114 boxLabelEl: function()
25116 return this.el.select('label.box-label',true).first();
25119 initEvents : function()
25121 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25123 this.inputEl().on('click', this.onClick, this);
25125 if (this.boxLabel) {
25126 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25129 this.startValue = this.getValue();
25132 Roo.bootstrap.form.CheckBox.register(this);
25136 onClick : function(e)
25138 if(this.fireEvent('click', this, e) !== false){
25139 this.setChecked(!this.checked);
25144 setChecked : function(state,suppressEvent)
25146 this.startValue = this.getValue();
25148 if(this.inputType == 'radio'){
25150 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25151 e.dom.checked = false;
25154 this.inputEl().dom.checked = true;
25156 this.inputEl().dom.value = this.inputValue;
25158 if(suppressEvent !== true){
25159 this.fireEvent('check', this, true);
25167 this.checked = state;
25169 this.inputEl().dom.checked = state;
25172 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25174 if(suppressEvent !== true){
25175 this.fireEvent('check', this, state);
25181 getValue : function()
25183 if(this.inputType == 'radio'){
25184 return this.getGroupValue();
25187 return this.hiddenEl().dom.value;
25191 getGroupValue : function()
25193 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25197 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25200 setValue : function(v,suppressEvent)
25202 if(this.inputType == 'radio'){
25203 this.setGroupValue(v, suppressEvent);
25207 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25212 setGroupValue : function(v, suppressEvent)
25214 this.startValue = this.getValue();
25216 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25217 e.dom.checked = false;
25219 if(e.dom.value == v){
25220 e.dom.checked = true;
25224 if(suppressEvent !== true){
25225 this.fireEvent('check', this, true);
25233 validate : function()
25235 if(this.getVisibilityEl().hasClass('hidden')){
25241 (this.inputType == 'radio' && this.validateRadio()) ||
25242 (this.inputType == 'checkbox' && this.validateCheckbox())
25248 this.markInvalid();
25252 validateRadio : function()
25254 if(this.getVisibilityEl().hasClass('hidden')){
25258 if(this.allowBlank){
25264 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25265 if(!e.dom.checked){
25277 validateCheckbox : function()
25280 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25281 //return (this.getValue() == this.inputValue) ? true : false;
25284 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25292 for(var i in group){
25293 if(group[i].el.isVisible(true)){
25301 for(var i in group){
25306 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25313 * Mark this field as valid
25315 markValid : function()
25319 this.fireEvent('valid', this);
25321 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25324 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25331 if(this.inputType == 'radio'){
25332 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25333 var fg = e.findParent('.form-group', false, true);
25334 if (Roo.bootstrap.version == 3) {
25335 fg.removeClass([_this.invalidClass, _this.validClass]);
25336 fg.addClass(_this.validClass);
25338 fg.removeClass(['is-valid', 'is-invalid']);
25339 fg.addClass('is-valid');
25347 var fg = this.el.findParent('.form-group', false, true);
25348 if (Roo.bootstrap.version == 3) {
25349 fg.removeClass([this.invalidClass, this.validClass]);
25350 fg.addClass(this.validClass);
25352 fg.removeClass(['is-valid', 'is-invalid']);
25353 fg.addClass('is-valid');
25358 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25364 for(var i in group){
25365 var fg = group[i].el.findParent('.form-group', false, true);
25366 if (Roo.bootstrap.version == 3) {
25367 fg.removeClass([this.invalidClass, this.validClass]);
25368 fg.addClass(this.validClass);
25370 fg.removeClass(['is-valid', 'is-invalid']);
25371 fg.addClass('is-valid');
25377 * Mark this field as invalid
25378 * @param {String} msg The validation message
25380 markInvalid : function(msg)
25382 if(this.allowBlank){
25388 this.fireEvent('invalid', this, msg);
25390 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25393 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25397 label.markInvalid();
25400 if(this.inputType == 'radio'){
25402 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25403 var fg = e.findParent('.form-group', false, true);
25404 if (Roo.bootstrap.version == 3) {
25405 fg.removeClass([_this.invalidClass, _this.validClass]);
25406 fg.addClass(_this.invalidClass);
25408 fg.removeClass(['is-invalid', 'is-valid']);
25409 fg.addClass('is-invalid');
25417 var fg = this.el.findParent('.form-group', false, true);
25418 if (Roo.bootstrap.version == 3) {
25419 fg.removeClass([_this.invalidClass, _this.validClass]);
25420 fg.addClass(_this.invalidClass);
25422 fg.removeClass(['is-invalid', 'is-valid']);
25423 fg.addClass('is-invalid');
25428 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25434 for(var i in group){
25435 var fg = group[i].el.findParent('.form-group', false, true);
25436 if (Roo.bootstrap.version == 3) {
25437 fg.removeClass([_this.invalidClass, _this.validClass]);
25438 fg.addClass(_this.invalidClass);
25440 fg.removeClass(['is-invalid', 'is-valid']);
25441 fg.addClass('is-invalid');
25447 clearInvalid : function()
25449 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25451 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25453 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25455 if (label && label.iconEl) {
25456 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25457 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25461 disable : function()
25463 if(this.inputType != 'radio'){
25464 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25471 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25472 _this.getActionEl().addClass(this.disabledClass);
25473 e.dom.disabled = true;
25477 this.disabled = true;
25478 this.fireEvent("disable", this);
25482 enable : function()
25484 if(this.inputType != 'radio'){
25485 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25492 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25493 _this.getActionEl().removeClass(this.disabledClass);
25494 e.dom.disabled = false;
25498 this.disabled = false;
25499 this.fireEvent("enable", this);
25503 setBoxLabel : function(v)
25508 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25514 Roo.apply(Roo.bootstrap.form.CheckBox, {
25519 * register a CheckBox Group
25520 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25522 register : function(checkbox)
25524 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25525 this.groups[checkbox.groupId] = {};
25528 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25532 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25536 * fetch a CheckBox Group based on the group ID
25537 * @param {string} the group ID
25538 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25540 get: function(groupId) {
25541 if (typeof(this.groups[groupId]) == 'undefined') {
25545 return this.groups[groupId] ;
25558 * @class Roo.bootstrap.form.Radio
25559 * @extends Roo.bootstrap.Component
25560 * Bootstrap Radio class
25561 * @cfg {String} boxLabel - the label associated
25562 * @cfg {String} value - the value of radio
25565 * Create a new Radio
25566 * @param {Object} config The config object
25568 Roo.bootstrap.form.Radio = function(config){
25569 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25573 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25579 getAutoCreate : function()
25583 cls : 'form-group radio',
25588 html : this.boxLabel
25596 initEvents : function()
25598 this.parent().register(this);
25600 this.el.on('click', this.onClick, this);
25604 onClick : function(e)
25606 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25607 this.setChecked(true);
25611 setChecked : function(state, suppressEvent)
25613 this.parent().setValue(this.value, suppressEvent);
25617 setBoxLabel : function(v)
25622 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25637 * @class Roo.bootstrap.form.SecurePass
25638 * @extends Roo.bootstrap.form.Input
25639 * Bootstrap SecurePass class
25643 * Create a new SecurePass
25644 * @param {Object} config The config object
25647 Roo.bootstrap.form.SecurePass = function (config) {
25648 // these go here, so the translation tool can replace them..
25650 PwdEmpty: "Please type a password, and then retype it to confirm.",
25651 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25652 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25653 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25654 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25655 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25656 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25657 TooWeak: "Your password is Too Weak."
25659 this.meterLabel = "Password strength:";
25660 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25661 this.meterClass = [
25662 "roo-password-meter-tooweak",
25663 "roo-password-meter-weak",
25664 "roo-password-meter-medium",
25665 "roo-password-meter-strong",
25666 "roo-password-meter-grey"
25671 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25674 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25676 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25678 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25679 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25680 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25681 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25682 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25683 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25684 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25694 * @cfg {String/Object} Label for the strength meter (defaults to
25695 * 'Password strength:')
25700 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25701 * ['Weak', 'Medium', 'Strong'])
25704 pwdStrengths: false,
25717 initEvents: function ()
25719 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25721 if (this.el.is('input[type=password]') && Roo.isSafari) {
25722 this.el.on('keydown', this.SafariOnKeyDown, this);
25725 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25728 onRender: function (ct, position)
25730 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25731 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25732 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25734 this.trigger.createChild({
25739 cls: 'roo-password-meter-grey col-xs-12',
25742 //width: this.meterWidth + 'px'
25746 cls: 'roo-password-meter-text'
25752 if (this.hideTrigger) {
25753 this.trigger.setDisplayed(false);
25755 this.setSize(this.width || '', this.height || '');
25758 onDestroy: function ()
25760 if (this.trigger) {
25761 this.trigger.removeAllListeners();
25762 this.trigger.remove();
25765 this.wrap.remove();
25767 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25770 checkStrength: function ()
25772 var pwd = this.inputEl().getValue();
25773 if (pwd == this._lastPwd) {
25778 if (this.ClientSideStrongPassword(pwd)) {
25780 } else if (this.ClientSideMediumPassword(pwd)) {
25782 } else if (this.ClientSideWeakPassword(pwd)) {
25788 Roo.log('strength1: ' + strength);
25790 //var pm = this.trigger.child('div/div/div').dom;
25791 var pm = this.trigger.child('div/div');
25792 pm.removeClass(this.meterClass);
25793 pm.addClass(this.meterClass[strength]);
25796 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25798 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25800 this._lastPwd = pwd;
25804 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25806 this._lastPwd = '';
25808 var pm = this.trigger.child('div/div');
25809 pm.removeClass(this.meterClass);
25810 pm.addClass('roo-password-meter-grey');
25813 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25816 this.inputEl().dom.type='password';
25819 validateValue: function (value)
25821 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25824 if (value.length == 0) {
25825 if (this.allowBlank) {
25826 this.clearInvalid();
25830 this.markInvalid(this.errors.PwdEmpty);
25831 this.errorMsg = this.errors.PwdEmpty;
25839 if (!value.match(/[\x21-\x7e]+/)) {
25840 this.markInvalid(this.errors.PwdBadChar);
25841 this.errorMsg = this.errors.PwdBadChar;
25844 if (value.length < 6) {
25845 this.markInvalid(this.errors.PwdShort);
25846 this.errorMsg = this.errors.PwdShort;
25849 if (value.length > 16) {
25850 this.markInvalid(this.errors.PwdLong);
25851 this.errorMsg = this.errors.PwdLong;
25855 if (this.ClientSideStrongPassword(value)) {
25857 } else if (this.ClientSideMediumPassword(value)) {
25859 } else if (this.ClientSideWeakPassword(value)) {
25866 if (strength < 2) {
25867 //this.markInvalid(this.errors.TooWeak);
25868 this.errorMsg = this.errors.TooWeak;
25873 console.log('strength2: ' + strength);
25875 //var pm = this.trigger.child('div/div/div').dom;
25877 var pm = this.trigger.child('div/div');
25878 pm.removeClass(this.meterClass);
25879 pm.addClass(this.meterClass[strength]);
25881 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25883 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25885 this.errorMsg = '';
25889 CharacterSetChecks: function (type)
25892 this.fResult = false;
25895 isctype: function (character, type)
25898 case this.kCapitalLetter:
25899 if (character >= 'A' && character <= 'Z') {
25904 case this.kSmallLetter:
25905 if (character >= 'a' && character <= 'z') {
25911 if (character >= '0' && character <= '9') {
25916 case this.kPunctuation:
25917 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25928 IsLongEnough: function (pwd, size)
25930 return !(pwd == null || isNaN(size) || pwd.length < size);
25933 SpansEnoughCharacterSets: function (word, nb)
25935 if (!this.IsLongEnough(word, nb))
25940 var characterSetChecks = new Array(
25941 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25942 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25945 for (var index = 0; index < word.length; ++index) {
25946 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25947 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25948 characterSetChecks[nCharSet].fResult = true;
25955 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25956 if (characterSetChecks[nCharSet].fResult) {
25961 if (nCharSets < nb) {
25967 ClientSideStrongPassword: function (pwd)
25969 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25972 ClientSideMediumPassword: function (pwd)
25974 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25977 ClientSideWeakPassword: function (pwd)
25979 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25982 })//<script type="text/javascript">
25985 * Based Ext JS Library 1.1.1
25986 * Copyright(c) 2006-2007, Ext JS, LLC.
25992 * @class Roo.HtmlEditorCore
25993 * @extends Roo.Component
25994 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25996 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25999 Roo.HtmlEditorCore = function(config){
26002 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
26007 * @event initialize
26008 * Fires when the editor is fully initialized (including the iframe)
26009 * @param {Roo.HtmlEditorCore} this
26014 * Fires when the editor is first receives the focus. Any insertion must wait
26015 * until after this event.
26016 * @param {Roo.HtmlEditorCore} this
26020 * @event beforesync
26021 * Fires before the textarea is updated with content from the editor iframe. Return false
26022 * to cancel the sync.
26023 * @param {Roo.HtmlEditorCore} this
26024 * @param {String} html
26028 * @event beforepush
26029 * Fires before the iframe editor is updated with content from the textarea. Return false
26030 * to cancel the push.
26031 * @param {Roo.HtmlEditorCore} this
26032 * @param {String} html
26037 * Fires when the textarea is updated with content from the editor iframe.
26038 * @param {Roo.HtmlEditorCore} this
26039 * @param {String} html
26044 * Fires when the iframe editor is updated with content from the textarea.
26045 * @param {Roo.HtmlEditorCore} this
26046 * @param {String} html
26051 * @event editorevent
26052 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26053 * @param {Roo.HtmlEditorCore} this
26059 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
26061 // defaults : white / black...
26062 this.applyBlacklists();
26069 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
26073 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
26079 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26084 * @cfg {Number} height (in pixels)
26088 * @cfg {Number} width (in pixels)
26093 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26096 stylesheets: false,
26099 * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
26101 allowComments: false,
26105 // private properties
26106 validationEvent : false,
26108 initialized : false,
26110 sourceEditMode : false,
26111 onFocus : Roo.emptyFn,
26113 hideMode:'offsets',
26117 // blacklist + whitelisted elements..
26124 * Protected method that will not generally be called directly. It
26125 * is called when the editor initializes the iframe with HTML contents. Override this method if you
26126 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
26128 getDocMarkup : function(){
26132 // inherit styels from page...??
26133 if (this.stylesheets === false) {
26135 Roo.get(document.head).select('style').each(function(node) {
26136 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26139 Roo.get(document.head).select('link').each(function(node) {
26140 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
26143 } else if (!this.stylesheets.length) {
26145 st = '<style type="text/css">' +
26146 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26149 for (var i in this.stylesheets) {
26150 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
26155 st += '<style type="text/css">' +
26156 'IMG { cursor: pointer } ' +
26159 var cls = 'roo-htmleditor-body';
26161 if(this.bodyCls.length){
26162 cls += ' ' + this.bodyCls;
26165 return '<html><head>' + st +
26166 //<style type="text/css">' +
26167 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
26169 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
26173 onRender : function(ct, position)
26176 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
26177 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
26180 this.el.dom.style.border = '0 none';
26181 this.el.dom.setAttribute('tabIndex', -1);
26182 this.el.addClass('x-hidden hide');
26186 if(Roo.isIE){ // fix IE 1px bogus margin
26187 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
26191 this.frameId = Roo.id();
26195 var iframe = this.owner.wrap.createChild({
26197 cls: 'form-control', // bootstrap..
26199 name: this.frameId,
26200 frameBorder : 'no',
26201 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
26206 this.iframe = iframe.dom;
26208 this.assignDocWin();
26210 this.doc.designMode = 'on';
26213 this.doc.write(this.getDocMarkup());
26217 var task = { // must defer to wait for browser to be ready
26219 //console.log("run task?" + this.doc.readyState);
26220 this.assignDocWin();
26221 if(this.doc.body || this.doc.readyState == 'complete'){
26223 this.doc.designMode="on";
26227 Roo.TaskMgr.stop(task);
26228 this.initEditor.defer(10, this);
26235 Roo.TaskMgr.start(task);
26240 onResize : function(w, h)
26242 Roo.log('resize: ' +w + ',' + h );
26243 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
26247 if(typeof w == 'number'){
26249 this.iframe.style.width = w + 'px';
26251 if(typeof h == 'number'){
26253 this.iframe.style.height = h + 'px';
26255 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
26262 * Toggles the editor between standard and source edit mode.
26263 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26265 toggleSourceEdit : function(sourceEditMode){
26267 this.sourceEditMode = sourceEditMode === true;
26269 if(this.sourceEditMode){
26271 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
26274 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
26275 //this.iframe.className = '';
26278 //this.setSize(this.owner.wrap.getSize());
26279 //this.fireEvent('editmodechange', this, this.sourceEditMode);
26286 * Protected method that will not generally be called directly. If you need/want
26287 * custom HTML cleanup, this is the method you should override.
26288 * @param {String} html The HTML to be cleaned
26289 * return {String} The cleaned HTML
26291 cleanHtml : function(html){
26292 html = String(html);
26293 if(html.length > 5){
26294 if(Roo.isSafari){ // strip safari nonsense
26295 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
26298 if(html == ' '){
26305 * HTML Editor -> Textarea
26306 * Protected method that will not generally be called directly. Syncs the contents
26307 * of the editor iframe with the textarea.
26309 syncValue : function(){
26310 if(this.initialized){
26311 var bd = (this.doc.body || this.doc.documentElement);
26312 //this.cleanUpPaste(); -- this is done else where and causes havoc..
26313 var html = bd.innerHTML;
26315 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
26316 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
26318 html = '<div style="'+m[0]+'">' + html + '</div>';
26321 html = this.cleanHtml(html);
26322 // fix up the special chars.. normaly like back quotes in word...
26323 // however we do not want to do this with chinese..
26324 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
26326 var cc = match.charCodeAt();
26328 // Get the character value, handling surrogate pairs
26329 if (match.length == 2) {
26330 // It's a surrogate pair, calculate the Unicode code point
26331 var high = match.charCodeAt(0) - 0xD800;
26332 var low = match.charCodeAt(1) - 0xDC00;
26333 cc = (high * 0x400) + low + 0x10000;
26335 (cc >= 0x4E00 && cc < 0xA000 ) ||
26336 (cc >= 0x3400 && cc < 0x4E00 ) ||
26337 (cc >= 0xf900 && cc < 0xfb00 )
26342 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
26343 return "&#" + cc + ";";
26350 if(this.owner.fireEvent('beforesync', this, html) !== false){
26351 this.el.dom.value = html;
26352 this.owner.fireEvent('sync', this, html);
26358 * Protected method that will not generally be called directly. Pushes the value of the textarea
26359 * into the iframe editor.
26361 pushValue : function(){
26362 if(this.initialized){
26363 var v = this.el.dom.value.trim();
26365 // if(v.length < 1){
26369 if(this.owner.fireEvent('beforepush', this, v) !== false){
26370 var d = (this.doc.body || this.doc.documentElement);
26372 this.cleanUpPaste();
26373 this.el.dom.value = d.innerHTML;
26374 this.owner.fireEvent('push', this, v);
26380 deferFocus : function(){
26381 this.focus.defer(10, this);
26385 focus : function(){
26386 if(this.win && !this.sourceEditMode){
26393 assignDocWin: function()
26395 var iframe = this.iframe;
26398 this.doc = iframe.contentWindow.document;
26399 this.win = iframe.contentWindow;
26401 // if (!Roo.get(this.frameId)) {
26404 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26405 // this.win = Roo.get(this.frameId).dom.contentWindow;
26407 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
26411 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
26412 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
26417 initEditor : function(){
26418 //console.log("INIT EDITOR");
26419 this.assignDocWin();
26423 this.doc.designMode="on";
26425 this.doc.write(this.getDocMarkup());
26428 var dbody = (this.doc.body || this.doc.documentElement);
26429 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
26430 // this copies styles from the containing element into thsi one..
26431 // not sure why we need all of this..
26432 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
26434 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
26435 //ss['background-attachment'] = 'fixed'; // w3c
26436 dbody.bgProperties = 'fixed'; // ie
26437 //Roo.DomHelper.applyStyles(dbody, ss);
26438 Roo.EventManager.on(this.doc, {
26439 //'mousedown': this.onEditorEvent,
26440 'mouseup': this.onEditorEvent,
26441 'dblclick': this.onEditorEvent,
26442 'click': this.onEditorEvent,
26443 'keyup': this.onEditorEvent,
26448 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
26450 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
26451 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
26453 this.initialized = true;
26455 this.owner.fireEvent('initialize', this);
26460 onDestroy : function(){
26466 //for (var i =0; i < this.toolbars.length;i++) {
26467 // // fixme - ask toolbars for heights?
26468 // this.toolbars[i].onDestroy();
26471 //this.wrap.dom.innerHTML = '';
26472 //this.wrap.remove();
26477 onFirstFocus : function(){
26479 this.assignDocWin();
26482 this.activated = true;
26485 if(Roo.isGecko){ // prevent silly gecko errors
26487 var s = this.win.getSelection();
26488 if(!s.focusNode || s.focusNode.nodeType != 3){
26489 var r = s.getRangeAt(0);
26490 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26495 this.execCmd('useCSS', true);
26496 this.execCmd('styleWithCSS', false);
26499 this.owner.fireEvent('activate', this);
26503 adjustFont: function(btn){
26504 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26505 //if(Roo.isSafari){ // safari
26508 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26509 if(Roo.isSafari){ // safari
26510 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26511 v = (v < 10) ? 10 : v;
26512 v = (v > 48) ? 48 : v;
26513 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26518 v = Math.max(1, v+adjust);
26520 this.execCmd('FontSize', v );
26523 onEditorEvent : function(e)
26525 this.owner.fireEvent('editorevent', this, e);
26526 // this.updateToolbar();
26527 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26530 insertTag : function(tg)
26532 // could be a bit smarter... -> wrap the current selected tRoo..
26533 if (tg.toLowerCase() == 'span' ||
26534 tg.toLowerCase() == 'code' ||
26535 tg.toLowerCase() == 'sup' ||
26536 tg.toLowerCase() == 'sub'
26539 range = this.createRange(this.getSelection());
26540 var wrappingNode = this.doc.createElement(tg.toLowerCase());
26541 wrappingNode.appendChild(range.extractContents());
26542 range.insertNode(wrappingNode);
26549 this.execCmd("formatblock", tg);
26553 insertText : function(txt)
26557 var range = this.createRange();
26558 range.deleteContents();
26559 //alert(Sender.getAttribute('label'));
26561 range.insertNode(this.doc.createTextNode(txt));
26567 * Executes a Midas editor command on the editor document and performs necessary focus and
26568 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26569 * @param {String} cmd The Midas command
26570 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26572 relayCmd : function(cmd, value){
26574 this.execCmd(cmd, value);
26575 this.owner.fireEvent('editorevent', this);
26576 //this.updateToolbar();
26577 this.owner.deferFocus();
26581 * Executes a Midas editor command directly on the editor document.
26582 * For visual commands, you should use {@link #relayCmd} instead.
26583 * <b>This should only be called after the editor is initialized.</b>
26584 * @param {String} cmd The Midas command
26585 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26587 execCmd : function(cmd, value){
26588 this.doc.execCommand(cmd, false, value === undefined ? null : value);
26595 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26597 * @param {String} text | dom node..
26599 insertAtCursor : function(text)
26602 if(!this.activated){
26608 var r = this.doc.selection.createRange();
26619 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26623 // from jquery ui (MIT licenced)
26625 var win = this.win;
26627 if (win.getSelection && win.getSelection().getRangeAt) {
26628 range = win.getSelection().getRangeAt(0);
26629 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26630 range.insertNode(node);
26631 } else if (win.document.selection && win.document.selection.createRange) {
26632 // no firefox support
26633 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26634 win.document.selection.createRange().pasteHTML(txt);
26636 // no firefox support
26637 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26638 this.execCmd('InsertHTML', txt);
26647 mozKeyPress : function(e){
26649 var c = e.getCharCode(), cmd;
26652 c = String.fromCharCode(c).toLowerCase();
26666 this.cleanUpPaste.defer(100, this);
26674 e.preventDefault();
26682 fixKeys : function(){ // load time branching for fastest keydown performance
26684 return function(e){
26685 var k = e.getKey(), r;
26688 r = this.doc.selection.createRange();
26691 r.pasteHTML('    ');
26698 r = this.doc.selection.createRange();
26700 var target = r.parentElement();
26701 if(!target || target.tagName.toLowerCase() != 'li'){
26703 r.pasteHTML('<br />');
26709 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26710 this.cleanUpPaste.defer(100, this);
26716 }else if(Roo.isOpera){
26717 return function(e){
26718 var k = e.getKey();
26722 this.execCmd('InsertHTML','    ');
26725 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26726 this.cleanUpPaste.defer(100, this);
26731 }else if(Roo.isSafari){
26732 return function(e){
26733 var k = e.getKey();
26737 this.execCmd('InsertText','\t');
26741 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26742 this.cleanUpPaste.defer(100, this);
26750 getAllAncestors: function()
26752 var p = this.getSelectedNode();
26755 a.push(p); // push blank onto stack..
26756 p = this.getParentElement();
26760 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26764 a.push(this.doc.body);
26768 lastSelNode : false,
26771 getSelection : function()
26773 this.assignDocWin();
26774 return Roo.isIE ? this.doc.selection : this.win.getSelection();
26777 getSelectedNode: function()
26779 // this may only work on Gecko!!!
26781 // should we cache this!!!!
26786 var range = this.createRange(this.getSelection()).cloneRange();
26789 var parent = range.parentElement();
26791 var testRange = range.duplicate();
26792 testRange.moveToElementText(parent);
26793 if (testRange.inRange(range)) {
26796 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26799 parent = parent.parentElement;
26804 // is ancestor a text element.
26805 var ac = range.commonAncestorContainer;
26806 if (ac.nodeType == 3) {
26807 ac = ac.parentNode;
26810 var ar = ac.childNodes;
26813 var other_nodes = [];
26814 var has_other_nodes = false;
26815 for (var i=0;i<ar.length;i++) {
26816 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
26819 // fullly contained node.
26821 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26826 // probably selected..
26827 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26828 other_nodes.push(ar[i]);
26832 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
26837 has_other_nodes = true;
26839 if (!nodes.length && other_nodes.length) {
26840 nodes= other_nodes;
26842 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26848 createRange: function(sel)
26850 // this has strange effects when using with
26851 // top toolbar - not sure if it's a great idea.
26852 //this.editor.contentWindow.focus();
26853 if (typeof sel != "undefined") {
26855 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26857 return this.doc.createRange();
26860 return this.doc.createRange();
26863 getParentElement: function()
26866 this.assignDocWin();
26867 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26869 var range = this.createRange(sel);
26872 var p = range.commonAncestorContainer;
26873 while (p.nodeType == 3) { // text node
26884 * Range intersection.. the hard stuff...
26888 * [ -- selected range --- ]
26892 * if end is before start or hits it. fail.
26893 * if start is after end or hits it fail.
26895 * if either hits (but other is outside. - then it's not
26901 // @see http://www.thismuchiknow.co.uk/?p=64.
26902 rangeIntersectsNode : function(range, node)
26904 var nodeRange = node.ownerDocument.createRange();
26906 nodeRange.selectNode(node);
26908 nodeRange.selectNodeContents(node);
26911 var rangeStartRange = range.cloneRange();
26912 rangeStartRange.collapse(true);
26914 var rangeEndRange = range.cloneRange();
26915 rangeEndRange.collapse(false);
26917 var nodeStartRange = nodeRange.cloneRange();
26918 nodeStartRange.collapse(true);
26920 var nodeEndRange = nodeRange.cloneRange();
26921 nodeEndRange.collapse(false);
26923 return rangeStartRange.compareBoundaryPoints(
26924 Range.START_TO_START, nodeEndRange) == -1 &&
26925 rangeEndRange.compareBoundaryPoints(
26926 Range.START_TO_START, nodeStartRange) == 1;
26930 rangeCompareNode : function(range, node)
26932 var nodeRange = node.ownerDocument.createRange();
26934 nodeRange.selectNode(node);
26936 nodeRange.selectNodeContents(node);
26940 range.collapse(true);
26942 nodeRange.collapse(true);
26944 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26945 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
26947 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26949 var nodeIsBefore = ss == 1;
26950 var nodeIsAfter = ee == -1;
26952 if (nodeIsBefore && nodeIsAfter) {
26955 if (!nodeIsBefore && nodeIsAfter) {
26956 return 1; //right trailed.
26959 if (nodeIsBefore && !nodeIsAfter) {
26960 return 2; // left trailed.
26966 // private? - in a new class?
26967 cleanUpPaste : function()
26969 // cleans up the whole document..
26970 Roo.log('cleanuppaste');
26972 this.cleanUpChildren(this.doc.body);
26973 var clean = this.cleanWordChars(this.doc.body.innerHTML);
26974 if (clean != this.doc.body.innerHTML) {
26975 this.doc.body.innerHTML = clean;
26980 cleanWordChars : function(input) {// change the chars to hex code
26981 var he = Roo.HtmlEditorCore;
26983 var output = input;
26984 Roo.each(he.swapCodes, function(sw) {
26985 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26987 output = output.replace(swapper, sw[1]);
26994 cleanUpChildren : function (n)
26996 if (!n.childNodes.length) {
26999 for (var i = n.childNodes.length-1; i > -1 ; i--) {
27000 this.cleanUpChild(n.childNodes[i]);
27007 cleanUpChild : function (node)
27010 //console.log(node);
27011 if (node.nodeName == "#text") {
27012 // clean up silly Windows -- stuff?
27015 if (node.nodeName == "#comment") {
27016 if (!this.allowComments) {
27017 node.parentNode.removeChild(node);
27019 // clean up silly Windows -- stuff?
27022 var lcname = node.tagName.toLowerCase();
27023 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
27024 // whitelist of tags..
27026 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
27028 node.parentNode.removeChild(node);
27033 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
27035 // spans with no attributes - just remove them..
27036 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
27037 remove_keep_children = true;
27040 // remove <a name=....> as rendering on yahoo mailer is borked with this.
27041 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
27043 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
27044 // remove_keep_children = true;
27047 if (remove_keep_children) {
27048 this.cleanUpChildren(node);
27049 // inserts everything just before this node...
27050 while (node.childNodes.length) {
27051 var cn = node.childNodes[0];
27052 node.removeChild(cn);
27053 node.parentNode.insertBefore(cn, node);
27055 node.parentNode.removeChild(node);
27059 if (!node.attributes || !node.attributes.length) {
27064 this.cleanUpChildren(node);
27068 function cleanAttr(n,v)
27071 if (v.match(/^\./) || v.match(/^\//)) {
27074 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
27077 if (v.match(/^#/)) {
27080 if (v.match(/^\{/)) { // allow template editing.
27083 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
27084 node.removeAttribute(n);
27088 var cwhite = this.cwhite;
27089 var cblack = this.cblack;
27091 function cleanStyle(n,v)
27093 if (v.match(/expression/)) { //XSS?? should we even bother..
27094 node.removeAttribute(n);
27098 var parts = v.split(/;/);
27101 Roo.each(parts, function(p) {
27102 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
27106 var l = p.split(':').shift().replace(/\s+/g,'');
27107 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
27109 if ( cwhite.length && cblack.indexOf(l) > -1) {
27110 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27111 //node.removeAttribute(n);
27115 // only allow 'c whitelisted system attributes'
27116 if ( cwhite.length && cwhite.indexOf(l) < 0) {
27117 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
27118 //node.removeAttribute(n);
27128 if (clean.length) {
27129 node.setAttribute(n, clean.join(';'));
27131 node.removeAttribute(n);
27137 for (var i = node.attributes.length-1; i > -1 ; i--) {
27138 var a = node.attributes[i];
27141 if (a.name.toLowerCase().substr(0,2)=='on') {
27142 node.removeAttribute(a.name);
27145 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
27146 node.removeAttribute(a.name);
27149 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
27150 cleanAttr(a.name,a.value); // fixme..
27153 if (a.name == 'style') {
27154 cleanStyle(a.name,a.value);
27157 /// clean up MS crap..
27158 // tecnically this should be a list of valid class'es..
27161 if (a.name == 'class') {
27162 if (a.value.match(/^Mso/)) {
27163 node.removeAttribute('class');
27166 if (a.value.match(/^body$/)) {
27167 node.removeAttribute('class');
27178 this.cleanUpChildren(node);
27184 * Clean up MS wordisms...
27186 cleanWord : function(node)
27189 this.cleanWord(this.doc.body);
27194 node.nodeName == 'SPAN' &&
27195 !node.hasAttributes() &&
27196 node.childNodes.length == 1 &&
27197 node.firstChild.nodeName == "#text"
27199 var textNode = node.firstChild;
27200 node.removeChild(textNode);
27201 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27202 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27204 node.parentNode.insertBefore(textNode, node);
27205 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27206 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27208 node.parentNode.removeChild(node);
27211 if (node.nodeName == "#text") {
27212 // clean up silly Windows -- stuff?
27215 if (node.nodeName == "#comment") {
27216 node.parentNode.removeChild(node);
27217 // clean up silly Windows -- stuff?
27221 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27222 node.parentNode.removeChild(node);
27225 //Roo.log(node.tagName);
27226 // remove - but keep children..
27227 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27228 //Roo.log('-- removed');
27229 while (node.childNodes.length) {
27230 var cn = node.childNodes[0];
27231 node.removeChild(cn);
27232 node.parentNode.insertBefore(cn, node);
27233 // move node to parent - and clean it..
27234 this.cleanWord(cn);
27236 node.parentNode.removeChild(node);
27237 /// no need to iterate chidlren = it's got none..
27238 //this.iterateChildren(node, this.cleanWord);
27242 if (node.className.length) {
27244 var cn = node.className.split(/\W+/);
27246 Roo.each(cn, function(cls) {
27247 if (cls.match(/Mso[a-zA-Z]+/)) {
27252 node.className = cna.length ? cna.join(' ') : '';
27254 node.removeAttribute("class");
27258 if (node.hasAttribute("lang")) {
27259 node.removeAttribute("lang");
27262 if (node.hasAttribute("style")) {
27264 var styles = node.getAttribute("style").split(";");
27266 Roo.each(styles, function(s) {
27267 if (!s.match(/:/)) {
27270 var kv = s.split(":");
27271 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27274 // what ever is left... we allow.
27277 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27278 if (!nstyle.length) {
27279 node.removeAttribute('style');
27282 this.iterateChildren(node, this.cleanWord);
27288 * iterateChildren of a Node, calling fn each time, using this as the scole..
27289 * @param {DomNode} node node to iterate children of.
27290 * @param {Function} fn method of this class to call on each item.
27292 iterateChildren : function(node, fn)
27294 if (!node.childNodes.length) {
27297 for (var i = node.childNodes.length-1; i > -1 ; i--) {
27298 fn.call(this, node.childNodes[i])
27304 * cleanTableWidths.
27306 * Quite often pasting from word etc.. results in tables with column and widths.
27307 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
27310 cleanTableWidths : function(node)
27315 this.cleanTableWidths(this.doc.body);
27320 if (node.nodeName == "#text" || node.nodeName == "#comment") {
27323 Roo.log(node.tagName);
27324 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
27325 this.iterateChildren(node, this.cleanTableWidths);
27328 if (node.hasAttribute('width')) {
27329 node.removeAttribute('width');
27333 if (node.hasAttribute("style")) {
27336 var styles = node.getAttribute("style").split(";");
27338 Roo.each(styles, function(s) {
27339 if (!s.match(/:/)) {
27342 var kv = s.split(":");
27343 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
27346 // what ever is left... we allow.
27349 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27350 if (!nstyle.length) {
27351 node.removeAttribute('style');
27355 this.iterateChildren(node, this.cleanTableWidths);
27363 domToHTML : function(currentElement, depth, nopadtext) {
27365 depth = depth || 0;
27366 nopadtext = nopadtext || false;
27368 if (!currentElement) {
27369 return this.domToHTML(this.doc.body);
27372 //Roo.log(currentElement);
27374 var allText = false;
27375 var nodeName = currentElement.nodeName;
27376 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
27378 if (nodeName == '#text') {
27380 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
27385 if (nodeName != 'BODY') {
27388 // Prints the node tagName, such as <A>, <IMG>, etc
27391 for(i = 0; i < currentElement.attributes.length;i++) {
27393 var aname = currentElement.attributes.item(i).name;
27394 if (!currentElement.attributes.item(i).value.length) {
27397 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
27400 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
27409 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
27412 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
27417 // Traverse the tree
27419 var currentElementChild = currentElement.childNodes.item(i);
27420 var allText = true;
27421 var innerHTML = '';
27423 while (currentElementChild) {
27424 // Formatting code (indent the tree so it looks nice on the screen)
27425 var nopad = nopadtext;
27426 if (lastnode == 'SPAN') {
27430 if (currentElementChild.nodeName == '#text') {
27431 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
27432 toadd = nopadtext ? toadd : toadd.trim();
27433 if (!nopad && toadd.length > 80) {
27434 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
27436 innerHTML += toadd;
27439 currentElementChild = currentElement.childNodes.item(i);
27445 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
27447 // Recursively traverse the tree structure of the child node
27448 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
27449 lastnode = currentElementChild.nodeName;
27451 currentElementChild=currentElement.childNodes.item(i);
27457 // The remaining code is mostly for formatting the tree
27458 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
27463 ret+= "</"+tagName+">";
27469 applyBlacklists : function()
27471 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
27472 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
27476 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
27477 if (b.indexOf(tag) > -1) {
27480 this.white.push(tag);
27484 Roo.each(w, function(tag) {
27485 if (b.indexOf(tag) > -1) {
27488 if (this.white.indexOf(tag) > -1) {
27491 this.white.push(tag);
27496 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27497 if (w.indexOf(tag) > -1) {
27500 this.black.push(tag);
27504 Roo.each(b, function(tag) {
27505 if (w.indexOf(tag) > -1) {
27508 if (this.black.indexOf(tag) > -1) {
27511 this.black.push(tag);
27516 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
27517 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
27521 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27522 if (b.indexOf(tag) > -1) {
27525 this.cwhite.push(tag);
27529 Roo.each(w, function(tag) {
27530 if (b.indexOf(tag) > -1) {
27533 if (this.cwhite.indexOf(tag) > -1) {
27536 this.cwhite.push(tag);
27541 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27542 if (w.indexOf(tag) > -1) {
27545 this.cblack.push(tag);
27549 Roo.each(b, function(tag) {
27550 if (w.indexOf(tag) > -1) {
27553 if (this.cblack.indexOf(tag) > -1) {
27556 this.cblack.push(tag);
27561 setStylesheets : function(stylesheets)
27563 if(typeof(stylesheets) == 'string'){
27564 Roo.get(this.iframe.contentDocument.head).createChild({
27566 rel : 'stylesheet',
27575 Roo.each(stylesheets, function(s) {
27580 Roo.get(_this.iframe.contentDocument.head).createChild({
27582 rel : 'stylesheet',
27591 removeStylesheets : function()
27595 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27600 setStyle : function(style)
27602 Roo.get(this.iframe.contentDocument.head).createChild({
27611 // hide stuff that is not compatible
27625 * @event specialkey
27629 * @cfg {String} fieldClass @hide
27632 * @cfg {String} focusClass @hide
27635 * @cfg {String} autoCreate @hide
27638 * @cfg {String} inputType @hide
27641 * @cfg {String} invalidClass @hide
27644 * @cfg {String} invalidText @hide
27647 * @cfg {String} msgFx @hide
27650 * @cfg {String} validateOnBlur @hide
27654 Roo.HtmlEditorCore.white = [
27655 'area', 'br', 'img', 'input', 'hr', 'wbr',
27657 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
27658 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
27659 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
27660 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
27661 'table', 'ul', 'xmp',
27663 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
27666 'dir', 'menu', 'ol', 'ul', 'dl',
27672 Roo.HtmlEditorCore.black = [
27673 // 'embed', 'object', // enable - backend responsiblity to clean thiese
27675 'base', 'basefont', 'bgsound', 'blink', 'body',
27676 'frame', 'frameset', 'head', 'html', 'ilayer',
27677 'iframe', 'layer', 'link', 'meta', 'object',
27678 'script', 'style' ,'title', 'xml' // clean later..
27680 Roo.HtmlEditorCore.clean = [
27681 'script', 'style', 'title', 'xml'
27683 Roo.HtmlEditorCore.remove = [
27688 Roo.HtmlEditorCore.ablack = [
27692 Roo.HtmlEditorCore.aclean = [
27693 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
27697 Roo.HtmlEditorCore.pwhite= [
27698 'http', 'https', 'mailto'
27701 // white listed style attributes.
27702 Roo.HtmlEditorCore.cwhite= [
27703 // 'text-align', /// default is to allow most things..
27709 // black listed style attributes.
27710 Roo.HtmlEditorCore.cblack= [
27711 // 'font-size' -- this can be set by the project
27715 Roo.HtmlEditorCore.swapCodes =[
27716 [ 8211, "–" ],
27717 [ 8212, "—" ],
27734 * @class Roo.bootstrap.form.HtmlEditor
27735 * @extends Roo.bootstrap.form.TextArea
27736 * Bootstrap HtmlEditor class
27739 * Create a new HtmlEditor
27740 * @param {Object} config The config object
27743 Roo.bootstrap.form.HtmlEditor = function(config){
27744 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
27745 if (!this.toolbars) {
27746 this.toolbars = [];
27749 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27752 * @event initialize
27753 * Fires when the editor is fully initialized (including the iframe)
27754 * @param {HtmlEditor} this
27759 * Fires when the editor is first receives the focus. Any insertion must wait
27760 * until after this event.
27761 * @param {HtmlEditor} this
27765 * @event beforesync
27766 * Fires before the textarea is updated with content from the editor iframe. Return false
27767 * to cancel the sync.
27768 * @param {HtmlEditor} this
27769 * @param {String} html
27773 * @event beforepush
27774 * Fires before the iframe editor is updated with content from the textarea. Return false
27775 * to cancel the push.
27776 * @param {HtmlEditor} this
27777 * @param {String} html
27782 * Fires when the textarea is updated with content from the editor iframe.
27783 * @param {HtmlEditor} this
27784 * @param {String} html
27789 * Fires when the iframe editor is updated with content from the textarea.
27790 * @param {HtmlEditor} this
27791 * @param {String} html
27795 * @event editmodechange
27796 * Fires when the editor switches edit modes
27797 * @param {HtmlEditor} this
27798 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27800 editmodechange: true,
27802 * @event editorevent
27803 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27804 * @param {HtmlEditor} this
27808 * @event firstfocus
27809 * Fires when on first focus - needed by toolbars..
27810 * @param {HtmlEditor} this
27815 * Auto save the htmlEditor value as a file into Events
27816 * @param {HtmlEditor} this
27820 * @event savedpreview
27821 * preview the saved version of htmlEditor
27822 * @param {HtmlEditor} this
27829 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
27833 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27838 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27843 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
27848 * @cfg {Number} height (in pixels)
27852 * @cfg {Number} width (in pixels)
27857 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27860 stylesheets: false,
27865 // private properties
27866 validationEvent : false,
27868 initialized : false,
27871 onFocus : Roo.emptyFn,
27873 hideMode:'offsets',
27875 tbContainer : false,
27879 toolbarContainer :function() {
27880 return this.wrap.select('.x-html-editor-tb',true).first();
27884 * Protected method that will not generally be called directly. It
27885 * is called when the editor creates its toolbar. Override this method if you need to
27886 * add custom toolbar buttons.
27887 * @param {HtmlEditor} editor
27889 createToolbar : function(){
27890 Roo.log('renewing');
27891 Roo.log("create toolbars");
27893 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
27894 this.toolbars[0].render(this.toolbarContainer());
27898 // if (!editor.toolbars || !editor.toolbars.length) {
27899 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
27902 // for (var i =0 ; i < editor.toolbars.length;i++) {
27903 // editor.toolbars[i] = Roo.factory(
27904 // typeof(editor.toolbars[i]) == 'string' ?
27905 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
27906 // Roo.bootstrap.form.HtmlEditor);
27907 // editor.toolbars[i].init(editor);
27913 onRender : function(ct, position)
27915 // Roo.log("Call onRender: " + this.xtype);
27917 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
27919 this.wrap = this.inputEl().wrap({
27920 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27923 this.editorcore.onRender(ct, position);
27925 if (this.resizable) {
27926 this.resizeEl = new Roo.Resizable(this.wrap, {
27930 minHeight : this.height,
27931 height: this.height,
27932 handles : this.resizable,
27935 resize : function(r, w, h) {
27936 _t.onResize(w,h); // -something
27942 this.createToolbar(this);
27945 if(!this.width && this.resizable){
27946 this.setSize(this.wrap.getSize());
27948 if (this.resizeEl) {
27949 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27950 // should trigger onReize..
27956 onResize : function(w, h)
27958 Roo.log('resize: ' +w + ',' + h );
27959 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
27963 if(this.inputEl() ){
27964 if(typeof w == 'number'){
27965 var aw = w - this.wrap.getFrameWidth('lr');
27966 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27969 if(typeof h == 'number'){
27970 var tbh = -11; // fixme it needs to tool bar size!
27971 for (var i =0; i < this.toolbars.length;i++) {
27972 // fixme - ask toolbars for heights?
27973 tbh += this.toolbars[i].el.getHeight();
27974 //if (this.toolbars[i].footer) {
27975 // tbh += this.toolbars[i].footer.el.getHeight();
27983 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27984 ah -= 5; // knock a few pixes off for look..
27985 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27989 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27990 this.editorcore.onResize(ew,eh);
27995 * Toggles the editor between standard and source edit mode.
27996 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27998 toggleSourceEdit : function(sourceEditMode)
28000 this.editorcore.toggleSourceEdit(sourceEditMode);
28002 if(this.editorcore.sourceEditMode){
28003 Roo.log('editor - showing textarea');
28006 // Roo.log(this.syncValue());
28008 this.inputEl().removeClass(['hide', 'x-hidden']);
28009 this.inputEl().dom.removeAttribute('tabIndex');
28010 this.inputEl().focus();
28012 Roo.log('editor - hiding textarea');
28014 // Roo.log(this.pushValue());
28017 this.inputEl().addClass(['hide', 'x-hidden']);
28018 this.inputEl().dom.setAttribute('tabIndex', -1);
28019 //this.deferFocus();
28022 if(this.resizable){
28023 this.setSize(this.wrap.getSize());
28026 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
28029 // private (for BoxComponent)
28030 adjustSize : Roo.BoxComponent.prototype.adjustSize,
28032 // private (for BoxComponent)
28033 getResizeEl : function(){
28037 // private (for BoxComponent)
28038 getPositionEl : function(){
28043 initEvents : function(){
28044 this.originalValue = this.getValue();
28048 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28051 // markInvalid : Roo.emptyFn,
28053 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
28056 // clearInvalid : Roo.emptyFn,
28058 setValue : function(v){
28059 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
28060 this.editorcore.pushValue();
28065 deferFocus : function(){
28066 this.focus.defer(10, this);
28070 focus : function(){
28071 this.editorcore.focus();
28077 onDestroy : function(){
28083 for (var i =0; i < this.toolbars.length;i++) {
28084 // fixme - ask toolbars for heights?
28085 this.toolbars[i].onDestroy();
28088 this.wrap.dom.innerHTML = '';
28089 this.wrap.remove();
28094 onFirstFocus : function(){
28095 //Roo.log("onFirstFocus");
28096 this.editorcore.onFirstFocus();
28097 for (var i =0; i < this.toolbars.length;i++) {
28098 this.toolbars[i].onFirstFocus();
28104 syncValue : function()
28106 this.editorcore.syncValue();
28109 pushValue : function()
28111 this.editorcore.pushValue();
28115 // hide stuff that is not compatible
28129 * @event specialkey
28133 * @cfg {String} fieldClass @hide
28136 * @cfg {String} focusClass @hide
28139 * @cfg {String} autoCreate @hide
28142 * @cfg {String} inputType @hide
28146 * @cfg {String} invalidText @hide
28149 * @cfg {String} msgFx @hide
28152 * @cfg {String} validateOnBlur @hide
28161 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
28163 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
28164 * @parent Roo.bootstrap.form.HtmlEditor
28165 * @extends Roo.bootstrap.nav.Simplebar
28171 new Roo.bootstrap.form.HtmlEditor({
28174 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
28175 disable : { fonts: 1 , format: 1, ..., ... , ...],
28181 * @cfg {Object} disable List of elements to disable..
28182 * @cfg {Array} btns List of additional buttons.
28186 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
28189 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
28192 Roo.apply(this, config);
28194 // default disabled, based on 'good practice'..
28195 this.disable = this.disable || {};
28196 Roo.applyIf(this.disable, {
28199 specialElements : true
28201 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
28203 this.editor = config.editor;
28204 this.editorcore = config.editor.editorcore;
28206 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
28208 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
28209 // dont call parent... till later.
28211 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
28216 editorcore : false,
28221 "h1","h2","h3","h4","h5","h6",
28223 "abbr", "acronym", "address", "cite", "samp", "var",
28227 onRender : function(ct, position)
28229 // Roo.log("Call onRender: " + this.xtype);
28231 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
28233 this.el.dom.style.marginBottom = '0';
28235 var editorcore = this.editorcore;
28236 var editor= this.editor;
28239 var btn = function(id,cmd , toggle, handler, html){
28241 var event = toggle ? 'toggle' : 'click';
28246 xns: Roo.bootstrap,
28250 enableToggle:toggle !== false,
28252 pressed : toggle ? false : null,
28255 a.listeners[toggle ? 'toggle' : 'click'] = function() {
28256 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
28262 // var cb_box = function...
28267 xns: Roo.bootstrap,
28272 xns: Roo.bootstrap,
28276 Roo.each(this.formats, function(f) {
28277 style.menu.items.push({
28279 xns: Roo.bootstrap,
28280 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
28285 editorcore.insertTag(this.tagname);
28292 children.push(style);
28294 btn('bold',false,true);
28295 btn('italic',false,true);
28296 btn('align-left', 'justifyleft',true);
28297 btn('align-center', 'justifycenter',true);
28298 btn('align-right' , 'justifyright',true);
28299 btn('link', false, false, function(btn) {
28300 //Roo.log("create link?");
28301 var url = prompt(this.createLinkText, this.defaultLinkValue);
28302 if(url && url != 'http:/'+'/'){
28303 this.editorcore.relayCmd('createlink', url);
28306 btn('list','insertunorderedlist',true);
28307 btn('pencil', false,true, function(btn){
28309 this.toggleSourceEdit(btn.pressed);
28312 if (this.editor.btns.length > 0) {
28313 for (var i = 0; i<this.editor.btns.length; i++) {
28314 children.push(this.editor.btns[i]);
28322 xns: Roo.bootstrap,
28327 xns: Roo.bootstrap,
28332 cog.menu.items.push({
28334 xns: Roo.bootstrap,
28335 html : Clean styles,
28340 editorcore.insertTag(this.tagname);
28349 this.xtype = 'NavSimplebar';
28351 for(var i=0;i< children.length;i++) {
28353 this.buttons.add(this.addxtypeChild(children[i]));
28357 editor.on('editorevent', this.updateToolbar, this);
28359 onBtnClick : function(id)
28361 this.editorcore.relayCmd(id);
28362 this.editorcore.focus();
28366 * Protected method that will not generally be called directly. It triggers
28367 * a toolbar update by reading the markup state of the current selection in the editor.
28369 updateToolbar: function(){
28371 if(!this.editorcore.activated){
28372 this.editor.onFirstFocus(); // is this neeed?
28376 var btns = this.buttons;
28377 var doc = this.editorcore.doc;
28378 btns.get('bold').setActive(doc.queryCommandState('bold'));
28379 btns.get('italic').setActive(doc.queryCommandState('italic'));
28380 //btns.get('underline').setActive(doc.queryCommandState('underline'));
28382 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
28383 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
28384 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
28386 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
28387 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
28390 var ans = this.editorcore.getAllAncestors();
28391 if (this.formatCombo) {
28394 var store = this.formatCombo.store;
28395 this.formatCombo.setValue("");
28396 for (var i =0; i < ans.length;i++) {
28397 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
28399 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
28407 // hides menus... - so this cant be on a menu...
28408 Roo.bootstrap.MenuMgr.hideAll();
28410 Roo.bootstrap.menu.Manager.hideAll();
28411 //this.editorsyncValue();
28413 onFirstFocus: function() {
28414 this.buttons.each(function(item){
28418 toggleSourceEdit : function(sourceEditMode){
28421 if(sourceEditMode){
28422 Roo.log("disabling buttons");
28423 this.buttons.each( function(item){
28424 if(item.cmd != 'pencil'){
28430 Roo.log("enabling buttons");
28431 if(this.editorcore.initialized){
28432 this.buttons.each( function(item){
28438 Roo.log("calling toggole on editor");
28439 // tell the editor that it's been pressed..
28440 this.editor.toggleSourceEdit(sourceEditMode);
28454 * @class Roo.bootstrap.form.Markdown
28455 * @extends Roo.bootstrap.form.TextArea
28456 * Bootstrap Showdown editable area
28457 * @cfg {string} content
28460 * Create a new Showdown
28463 Roo.bootstrap.form.Markdown = function(config){
28464 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
28468 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
28472 initEvents : function()
28475 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
28476 this.markdownEl = this.el.createChild({
28477 cls : 'roo-markdown-area'
28479 this.inputEl().addClass('d-none');
28480 if (this.getValue() == '') {
28481 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28484 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28486 this.markdownEl.on('click', this.toggleTextEdit, this);
28487 this.on('blur', this.toggleTextEdit, this);
28488 this.on('specialkey', this.resizeTextArea, this);
28491 toggleTextEdit : function()
28493 var sh = this.markdownEl.getHeight();
28494 this.inputEl().addClass('d-none');
28495 this.markdownEl.addClass('d-none');
28496 if (!this.editing) {
28498 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28499 this.inputEl().removeClass('d-none');
28500 this.inputEl().focus();
28501 this.editing = true;
28504 // show showdown...
28505 this.updateMarkdown();
28506 this.markdownEl.removeClass('d-none');
28507 this.editing = false;
28510 updateMarkdown : function()
28512 if (this.getValue() == '') {
28513 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28517 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28520 resizeTextArea: function () {
28523 Roo.log([sh, this.getValue().split("\n").length * 30]);
28524 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28526 setValue : function(val)
28528 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
28529 if (!this.editing) {
28530 this.updateMarkdown();
28536 if (!this.editing) {
28537 this.toggleTextEdit();
28545 * Ext JS Library 1.1.1
28546 * Copyright(c) 2006-2007, Ext JS, LLC.
28548 * Originally Released Under LGPL - original licence link has changed is not relivant.
28551 * <script type="text/javascript">
28555 * @class Roo.bootstrap.PagingToolbar
28556 * @extends Roo.bootstrap.nav.Simplebar
28557 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28559 * Create a new PagingToolbar
28560 * @param {Object} config The config object
28561 * @param {Roo.data.Store} store
28563 Roo.bootstrap.PagingToolbar = function(config)
28565 // old args format still supported... - xtype is prefered..
28566 // created from xtype...
28568 this.ds = config.dataSource;
28570 if (config.store && !this.ds) {
28571 this.store= Roo.factory(config.store, Roo.data);
28572 this.ds = this.store;
28573 this.ds.xmodule = this.xmodule || false;
28576 this.toolbarItems = [];
28577 if (config.items) {
28578 this.toolbarItems = config.items;
28581 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28586 this.bind(this.ds);
28589 if (Roo.bootstrap.version == 4) {
28590 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28592 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
28597 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
28599 * @cfg {Roo.bootstrap.Button} buttons[]
28600 * Buttons for the toolbar
28603 * @cfg {Roo.data.Store} store
28604 * The underlying data store providing the paged data
28607 * @cfg {String/HTMLElement/Element} container
28608 * container The id or element that will contain the toolbar
28611 * @cfg {Boolean} displayInfo
28612 * True to display the displayMsg (defaults to false)
28615 * @cfg {Number} pageSize
28616 * The number of records to display per page (defaults to 20)
28620 * @cfg {String} displayMsg
28621 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28623 displayMsg : 'Displaying {0} - {1} of {2}',
28625 * @cfg {String} emptyMsg
28626 * The message to display when no records are found (defaults to "No data to display")
28628 emptyMsg : 'No data to display',
28630 * Customizable piece of the default paging text (defaults to "Page")
28633 beforePageText : "Page",
28635 * Customizable piece of the default paging text (defaults to "of %0")
28638 afterPageText : "of {0}",
28640 * Customizable piece of the default paging text (defaults to "First Page")
28643 firstText : "First Page",
28645 * Customizable piece of the default paging text (defaults to "Previous Page")
28648 prevText : "Previous Page",
28650 * Customizable piece of the default paging text (defaults to "Next Page")
28653 nextText : "Next Page",
28655 * Customizable piece of the default paging text (defaults to "Last Page")
28658 lastText : "Last Page",
28660 * Customizable piece of the default paging text (defaults to "Refresh")
28663 refreshText : "Refresh",
28667 onRender : function(ct, position)
28669 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28670 this.navgroup.parentId = this.id;
28671 this.navgroup.onRender(this.el, null);
28672 // add the buttons to the navgroup
28674 if(this.displayInfo){
28675 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28676 this.displayEl = this.el.select('.x-paging-info', true).first();
28677 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28678 // this.displayEl = navel.el.select('span',true).first();
28684 Roo.each(_this.buttons, function(e){ // this might need to use render????
28685 Roo.factory(e).render(_this.el);
28689 Roo.each(_this.toolbarItems, function(e) {
28690 _this.navgroup.addItem(e);
28694 this.first = this.navgroup.addItem({
28695 tooltip: this.firstText,
28696 cls: "prev btn-outline-secondary",
28697 html : ' <i class="fa fa-step-backward"></i>',
28699 preventDefault: true,
28700 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28703 this.prev = this.navgroup.addItem({
28704 tooltip: this.prevText,
28705 cls: "prev btn-outline-secondary",
28706 html : ' <i class="fa fa-backward"></i>',
28708 preventDefault: true,
28709 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
28711 //this.addSeparator();
28714 var field = this.navgroup.addItem( {
28716 cls : 'x-paging-position btn-outline-secondary',
28718 html : this.beforePageText +
28719 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28720 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
28723 this.field = field.el.select('input', true).first();
28724 this.field.on("keydown", this.onPagingKeydown, this);
28725 this.field.on("focus", function(){this.dom.select();});
28728 this.afterTextEl = field.el.select('.x-paging-after',true).first();
28729 //this.field.setHeight(18);
28730 //this.addSeparator();
28731 this.next = this.navgroup.addItem({
28732 tooltip: this.nextText,
28733 cls: "next btn-outline-secondary",
28734 html : ' <i class="fa fa-forward"></i>',
28736 preventDefault: true,
28737 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
28739 this.last = this.navgroup.addItem({
28740 tooltip: this.lastText,
28741 html : ' <i class="fa fa-step-forward"></i>',
28742 cls: "next btn-outline-secondary",
28744 preventDefault: true,
28745 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
28747 //this.addSeparator();
28748 this.loading = this.navgroup.addItem({
28749 tooltip: this.refreshText,
28750 cls: "btn-outline-secondary",
28751 html : ' <i class="fa fa-refresh"></i>',
28752 preventDefault: true,
28753 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28759 updateInfo : function(){
28760 if(this.displayEl){
28761 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28762 var msg = count == 0 ?
28766 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
28768 this.displayEl.update(msg);
28773 onLoad : function(ds, r, o)
28775 this.cursor = o.params && o.params.start ? o.params.start : 0;
28777 var d = this.getPageData(),
28782 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28783 this.field.dom.value = ap;
28784 this.first.setDisabled(ap == 1);
28785 this.prev.setDisabled(ap == 1);
28786 this.next.setDisabled(ap == ps);
28787 this.last.setDisabled(ap == ps);
28788 this.loading.enable();
28793 getPageData : function(){
28794 var total = this.ds.getTotalCount();
28797 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28798 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28803 onLoadError : function(proxy, o){
28804 this.loading.enable();
28805 if (this.ds.events.loadexception.listeners.length < 2) {
28806 // nothing has been assigned to loadexception except this...
28808 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
28814 onPagingKeydown : function(e){
28815 var k = e.getKey();
28816 var d = this.getPageData();
28818 var v = this.field.dom.value, pageNum;
28819 if(!v || isNaN(pageNum = parseInt(v, 10))){
28820 this.field.dom.value = d.activePage;
28823 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28824 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28827 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))
28829 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28830 this.field.dom.value = pageNum;
28831 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28834 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28836 var v = this.field.dom.value, pageNum;
28837 var increment = (e.shiftKey) ? 10 : 1;
28838 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28841 if(!v || isNaN(pageNum = parseInt(v, 10))) {
28842 this.field.dom.value = d.activePage;
28845 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28847 this.field.dom.value = parseInt(v, 10) + increment;
28848 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28849 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28856 beforeLoad : function(){
28858 this.loading.disable();
28863 onClick : function(which){
28872 ds.load({params:{start: 0, limit: this.pageSize}});
28875 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28878 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28881 var total = ds.getTotalCount();
28882 var extra = total % this.pageSize;
28883 var lastStart = extra ? (total - extra) : total-this.pageSize;
28884 ds.load({params:{start: lastStart, limit: this.pageSize}});
28887 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28893 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28894 * @param {Roo.data.Store} store The data store to unbind
28896 unbind : function(ds){
28897 ds.un("beforeload", this.beforeLoad, this);
28898 ds.un("load", this.onLoad, this);
28899 ds.un("loadexception", this.onLoadError, this);
28900 ds.un("remove", this.updateInfo, this);
28901 ds.un("add", this.updateInfo, this);
28902 this.ds = undefined;
28906 * Binds the paging toolbar to the specified {@link Roo.data.Store}
28907 * @param {Roo.data.Store} store The data store to bind
28909 bind : function(ds){
28910 ds.on("beforeload", this.beforeLoad, this);
28911 ds.on("load", this.onLoad, this);
28912 ds.on("loadexception", this.onLoadError, this);
28913 ds.on("remove", this.updateInfo, this);
28914 ds.on("add", this.updateInfo, this);
28925 * @class Roo.bootstrap.MessageBar
28926 * @extends Roo.bootstrap.Component
28927 * Bootstrap MessageBar class
28928 * @cfg {String} html contents of the MessageBar
28929 * @cfg {String} weight (info | success | warning | danger) default info
28930 * @cfg {String} beforeClass insert the bar before the given class
28931 * @cfg {Boolean} closable (true | false) default false
28932 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28935 * Create a new Element
28936 * @param {Object} config The config object
28939 Roo.bootstrap.MessageBar = function(config){
28940 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28943 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
28949 beforeClass: 'bootstrap-sticky-wrap',
28951 getAutoCreate : function(){
28955 cls: 'alert alert-dismissable alert-' + this.weight,
28960 html: this.html || ''
28966 cfg.cls += ' alert-messages-fixed';
28980 onRender : function(ct, position)
28982 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28985 var cfg = Roo.apply({}, this.getAutoCreate());
28989 cfg.cls += ' ' + this.cls;
28992 cfg.style = this.style;
28994 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28996 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28999 this.el.select('>button.close').on('click', this.hide, this);
29005 if (!this.rendered) {
29011 this.fireEvent('show', this);
29017 if (!this.rendered) {
29023 this.fireEvent('hide', this);
29026 update : function()
29028 // var e = this.el.dom.firstChild;
29030 // if(this.closable){
29031 // e = e.nextSibling;
29034 // e.data = this.html || '';
29036 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
29052 * @class Roo.bootstrap.Graph
29053 * @extends Roo.bootstrap.Component
29054 * Bootstrap Graph class
29058 @cfg {String} graphtype bar | vbar | pie
29059 @cfg {number} g_x coodinator | centre x (pie)
29060 @cfg {number} g_y coodinator | centre y (pie)
29061 @cfg {number} g_r radius (pie)
29062 @cfg {number} g_height height of the chart (respected by all elements in the set)
29063 @cfg {number} g_width width of the chart (respected by all elements in the set)
29064 @cfg {Object} title The title of the chart
29067 -opts (object) options for the chart
29069 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
29070 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
29072 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.
29073 o stacked (boolean) whether or not to tread values as in a stacked bar chart
29075 o stretch (boolean)
29077 -opts (object) options for the pie
29080 o startAngle (number)
29081 o endAngle (number)
29085 * Create a new Input
29086 * @param {Object} config The config object
29089 Roo.bootstrap.Graph = function(config){
29090 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
29096 * The img click event for the img.
29097 * @param {Roo.EventObject} e
29103 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
29114 //g_colors: this.colors,
29121 getAutoCreate : function(){
29132 onRender : function(ct,position){
29135 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
29137 if (typeof(Raphael) == 'undefined') {
29138 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
29142 this.raphael = Raphael(this.el.dom);
29144 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29145 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29146 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
29147 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
29149 r.text(160, 10, "Single Series Chart").attr(txtattr);
29150 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
29151 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
29152 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
29154 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
29155 r.barchart(330, 10, 300, 220, data1);
29156 r.barchart(10, 250, 300, 220, data2, {stacked: true});
29157 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
29160 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29161 // r.barchart(30, 30, 560, 250, xdata, {
29162 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
29163 // axis : "0 0 1 1",
29164 // axisxlabels : xdata
29165 // //yvalues : cols,
29168 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
29170 // this.load(null,xdata,{
29171 // axis : "0 0 1 1",
29172 // axisxlabels : xdata
29177 load : function(graphtype,xdata,opts)
29179 this.raphael.clear();
29181 graphtype = this.graphtype;
29186 var r = this.raphael,
29187 fin = function () {
29188 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
29190 fout = function () {
29191 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
29193 pfin = function() {
29194 this.sector.stop();
29195 this.sector.scale(1.1, 1.1, this.cx, this.cy);
29198 this.label[0].stop();
29199 this.label[0].attr({ r: 7.5 });
29200 this.label[1].attr({ "font-weight": 800 });
29203 pfout = function() {
29204 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
29207 this.label[0].animate({ r: 5 }, 500, "bounce");
29208 this.label[1].attr({ "font-weight": 400 });
29214 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29217 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
29220 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
29221 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
29223 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
29230 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
29235 setTitle: function(o)
29240 initEvents: function() {
29243 this.el.on('click', this.onClick, this);
29247 onClick : function(e)
29249 Roo.log('img onclick');
29250 this.fireEvent('click', this, e);
29262 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29265 * @class Roo.bootstrap.dash.NumberBox
29266 * @extends Roo.bootstrap.Component
29267 * Bootstrap NumberBox class
29268 * @cfg {String} headline Box headline
29269 * @cfg {String} content Box content
29270 * @cfg {String} icon Box icon
29271 * @cfg {String} footer Footer text
29272 * @cfg {String} fhref Footer href
29275 * Create a new NumberBox
29276 * @param {Object} config The config object
29280 Roo.bootstrap.dash.NumberBox = function(config){
29281 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
29285 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
29294 getAutoCreate : function(){
29298 cls : 'small-box ',
29306 cls : 'roo-headline',
29307 html : this.headline
29311 cls : 'roo-content',
29312 html : this.content
29326 cls : 'ion ' + this.icon
29335 cls : 'small-box-footer',
29336 href : this.fhref || '#',
29340 cfg.cn.push(footer);
29347 onRender : function(ct,position){
29348 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
29355 setHeadline: function (value)
29357 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
29360 setFooter: function (value, href)
29362 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
29365 this.el.select('a.small-box-footer',true).first().attr('href', href);
29370 setContent: function (value)
29372 this.el.select('.roo-content',true).first().dom.innerHTML = value;
29375 initEvents: function()
29389 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29392 * @class Roo.bootstrap.dash.TabBox
29393 * @extends Roo.bootstrap.Component
29394 * @children Roo.bootstrap.dash.TabPane
29395 * Bootstrap TabBox class
29396 * @cfg {String} title Title of the TabBox
29397 * @cfg {String} icon Icon of the TabBox
29398 * @cfg {Boolean} showtabs (true|false) show the tabs default true
29399 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
29402 * Create a new TabBox
29403 * @param {Object} config The config object
29407 Roo.bootstrap.dash.TabBox = function(config){
29408 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
29413 * When a pane is added
29414 * @param {Roo.bootstrap.dash.TabPane} pane
29418 * @event activatepane
29419 * When a pane is activated
29420 * @param {Roo.bootstrap.dash.TabPane} pane
29422 "activatepane" : true
29430 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
29435 tabScrollable : false,
29437 getChildContainer : function()
29439 return this.el.select('.tab-content', true).first();
29442 getAutoCreate : function(){
29446 cls: 'pull-left header',
29454 cls: 'fa ' + this.icon
29460 cls: 'nav nav-tabs pull-right',
29466 if(this.tabScrollable){
29473 cls: 'nav nav-tabs pull-right',
29484 cls: 'nav-tabs-custom',
29489 cls: 'tab-content no-padding',
29497 initEvents : function()
29499 //Roo.log('add add pane handler');
29500 this.on('addpane', this.onAddPane, this);
29503 * Updates the box title
29504 * @param {String} html to set the title to.
29506 setTitle : function(value)
29508 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29510 onAddPane : function(pane)
29512 this.panes.push(pane);
29513 //Roo.log('addpane');
29515 // tabs are rendere left to right..
29516 if(!this.showtabs){
29520 var ctr = this.el.select('.nav-tabs', true).first();
29523 var existing = ctr.select('.nav-tab',true);
29524 var qty = existing.getCount();;
29527 var tab = ctr.createChild({
29529 cls : 'nav-tab' + (qty ? '' : ' active'),
29537 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29540 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29542 pane.el.addClass('active');
29547 onTabClick : function(ev,un,ob,pane)
29549 //Roo.log('tab - prev default');
29550 ev.preventDefault();
29553 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29554 pane.tab.addClass('active');
29555 //Roo.log(pane.title);
29556 this.getChildContainer().select('.tab-pane',true).removeClass('active');
29557 // technically we should have a deactivate event.. but maybe add later.
29558 // and it should not de-activate the selected tab...
29559 this.fireEvent('activatepane', pane);
29560 pane.el.addClass('active');
29561 pane.fireEvent('activate');
29566 getActivePane : function()
29569 Roo.each(this.panes, function(p) {
29570 if(p.el.hasClass('active')){
29591 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29593 * @class Roo.bootstrap.TabPane
29594 * @extends Roo.bootstrap.Component
29595 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
29596 * Bootstrap TabPane class
29597 * @cfg {Boolean} active (false | true) Default false
29598 * @cfg {String} title title of panel
29602 * Create a new TabPane
29603 * @param {Object} config The config object
29606 Roo.bootstrap.dash.TabPane = function(config){
29607 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29613 * When a pane is activated
29614 * @param {Roo.bootstrap.dash.TabPane} pane
29621 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
29626 // the tabBox that this is attached to.
29629 getAutoCreate : function()
29637 cfg.cls += ' active';
29642 initEvents : function()
29644 //Roo.log('trigger add pane handler');
29645 this.parent().fireEvent('addpane', this)
29649 * Updates the tab title
29650 * @param {String} html to set the title to.
29652 setTitle: function(str)
29658 this.tab.select('a', true).first().dom.innerHTML = str;
29677 * @class Roo.bootstrap.Tooltip
29678 * Bootstrap Tooltip class
29679 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29680 * to determine which dom element triggers the tooltip.
29682 * It needs to add support for additional attributes like tooltip-position
29685 * Create a new Toolti
29686 * @param {Object} config The config object
29689 Roo.bootstrap.Tooltip = function(config){
29690 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29692 this.alignment = Roo.bootstrap.Tooltip.alignment;
29694 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29695 this.alignment = config.alignment;
29700 Roo.apply(Roo.bootstrap.Tooltip, {
29702 * @function init initialize tooltip monitoring.
29706 currentTip : false,
29707 currentRegion : false,
29713 Roo.get(document).on('mouseover', this.enter ,this);
29714 Roo.get(document).on('mouseout', this.leave, this);
29717 this.currentTip = new Roo.bootstrap.Tooltip();
29720 enter : function(ev)
29722 var dom = ev.getTarget();
29724 //Roo.log(['enter',dom]);
29725 var el = Roo.fly(dom);
29726 if (this.currentEl) {
29728 //Roo.log(this.currentEl);
29729 //Roo.log(this.currentEl.contains(dom));
29730 if (this.currentEl == el) {
29733 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29739 if (this.currentTip.el) {
29740 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29744 if(!el || el.dom == document){
29750 if (!el.attr('tooltip')) {
29751 pel = el.findParent("[tooltip]");
29753 bindEl = Roo.get(pel);
29759 // you can not look for children, as if el is the body.. then everythign is the child..
29760 if (!pel && !el.attr('tooltip')) { //
29761 if (!el.select("[tooltip]").elements.length) {
29764 // is the mouse over this child...?
29765 bindEl = el.select("[tooltip]").first();
29766 var xy = ev.getXY();
29767 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29768 //Roo.log("not in region.");
29771 //Roo.log("child element over..");
29774 this.currentEl = el;
29775 this.currentTip.bind(bindEl);
29776 this.currentRegion = Roo.lib.Region.getRegion(dom);
29777 this.currentTip.enter();
29780 leave : function(ev)
29782 var dom = ev.getTarget();
29783 //Roo.log(['leave',dom]);
29784 if (!this.currentEl) {
29789 if (dom != this.currentEl.dom) {
29792 var xy = ev.getXY();
29793 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29796 // only activate leave if mouse cursor is outside... bounding box..
29801 if (this.currentTip) {
29802 this.currentTip.leave();
29804 //Roo.log('clear currentEl');
29805 this.currentEl = false;
29810 'left' : ['r-l', [-2,0], 'right'],
29811 'right' : ['l-r', [2,0], 'left'],
29812 'bottom' : ['t-b', [0,2], 'top'],
29813 'top' : [ 'b-t', [0,-2], 'bottom']
29819 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29824 delay : null, // can be { show : 300 , hide: 500}
29828 hoverState : null, //???
29830 placement : 'bottom',
29834 getAutoCreate : function(){
29841 cls : 'tooltip-arrow arrow'
29844 cls : 'tooltip-inner'
29851 bind : function(el)
29856 initEvents : function()
29858 this.arrowEl = this.el.select('.arrow', true).first();
29859 this.innerEl = this.el.select('.tooltip-inner', true).first();
29862 enter : function () {
29864 if (this.timeout != null) {
29865 clearTimeout(this.timeout);
29868 this.hoverState = 'in';
29869 //Roo.log("enter - show");
29870 if (!this.delay || !this.delay.show) {
29875 this.timeout = setTimeout(function () {
29876 if (_t.hoverState == 'in') {
29879 }, this.delay.show);
29883 clearTimeout(this.timeout);
29885 this.hoverState = 'out';
29886 if (!this.delay || !this.delay.hide) {
29892 this.timeout = setTimeout(function () {
29893 //Roo.log("leave - timeout");
29895 if (_t.hoverState == 'out') {
29897 Roo.bootstrap.Tooltip.currentEl = false;
29902 show : function (msg)
29905 this.render(document.body);
29908 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29910 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29912 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29914 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29915 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29917 var placement = typeof this.placement == 'function' ?
29918 this.placement.call(this, this.el, on_el) :
29921 var autoToken = /\s?auto?\s?/i;
29922 var autoPlace = autoToken.test(placement);
29924 placement = placement.replace(autoToken, '') || 'top';
29928 //this.el.setXY([0,0]);
29930 //this.el.dom.style.display='block';
29932 //this.el.appendTo(on_el);
29934 var p = this.getPosition();
29935 var box = this.el.getBox();
29941 var align = this.alignment[placement];
29943 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29945 if(placement == 'top' || placement == 'bottom'){
29947 placement = 'right';
29950 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29951 placement = 'left';
29954 var scroll = Roo.select('body', true).first().getScroll();
29956 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29960 align = this.alignment[placement];
29962 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29966 var elems = document.getElementsByTagName('div');
29967 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29968 for (var i = 0; i < elems.length; i++) {
29969 var zindex = Number.parseInt(
29970 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29973 if (zindex > highest) {
29980 this.el.dom.style.zIndex = highest;
29982 this.el.alignTo(this.bindEl, align[0],align[1]);
29983 //var arrow = this.el.select('.arrow',true).first();
29984 //arrow.set(align[2],
29986 this.el.addClass(placement);
29987 this.el.addClass("bs-tooltip-"+ placement);
29989 this.el.addClass('in fade show');
29991 this.hoverState = null;
29993 if (this.el.hasClass('fade')) {
30008 //this.el.setXY([0,0]);
30009 this.el.removeClass(['show', 'in']);
30025 * @class Roo.bootstrap.LocationPicker
30026 * @extends Roo.bootstrap.Component
30027 * Bootstrap LocationPicker class
30028 * @cfg {Number} latitude Position when init default 0
30029 * @cfg {Number} longitude Position when init default 0
30030 * @cfg {Number} zoom default 15
30031 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
30032 * @cfg {Boolean} mapTypeControl default false
30033 * @cfg {Boolean} disableDoubleClickZoom default false
30034 * @cfg {Boolean} scrollwheel default true
30035 * @cfg {Boolean} streetViewControl default false
30036 * @cfg {Number} radius default 0
30037 * @cfg {String} locationName
30038 * @cfg {Boolean} draggable default true
30039 * @cfg {Boolean} enableAutocomplete default false
30040 * @cfg {Boolean} enableReverseGeocode default true
30041 * @cfg {String} markerTitle
30044 * Create a new LocationPicker
30045 * @param {Object} config The config object
30049 Roo.bootstrap.LocationPicker = function(config){
30051 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
30056 * Fires when the picker initialized.
30057 * @param {Roo.bootstrap.LocationPicker} this
30058 * @param {Google Location} location
30062 * @event positionchanged
30063 * Fires when the picker position changed.
30064 * @param {Roo.bootstrap.LocationPicker} this
30065 * @param {Google Location} location
30067 positionchanged : true,
30070 * Fires when the map resize.
30071 * @param {Roo.bootstrap.LocationPicker} this
30076 * Fires when the map show.
30077 * @param {Roo.bootstrap.LocationPicker} this
30082 * Fires when the map hide.
30083 * @param {Roo.bootstrap.LocationPicker} this
30088 * Fires when click the map.
30089 * @param {Roo.bootstrap.LocationPicker} this
30090 * @param {Map event} e
30094 * @event mapRightClick
30095 * Fires when right click the map.
30096 * @param {Roo.bootstrap.LocationPicker} this
30097 * @param {Map event} e
30099 mapRightClick : true,
30101 * @event markerClick
30102 * Fires when click the marker.
30103 * @param {Roo.bootstrap.LocationPicker} this
30104 * @param {Map event} e
30106 markerClick : true,
30108 * @event markerRightClick
30109 * Fires when right click the marker.
30110 * @param {Roo.bootstrap.LocationPicker} this
30111 * @param {Map event} e
30113 markerRightClick : true,
30115 * @event OverlayViewDraw
30116 * Fires when OverlayView Draw
30117 * @param {Roo.bootstrap.LocationPicker} this
30119 OverlayViewDraw : true,
30121 * @event OverlayViewOnAdd
30122 * Fires when OverlayView Draw
30123 * @param {Roo.bootstrap.LocationPicker} this
30125 OverlayViewOnAdd : true,
30127 * @event OverlayViewOnRemove
30128 * Fires when OverlayView Draw
30129 * @param {Roo.bootstrap.LocationPicker} this
30131 OverlayViewOnRemove : true,
30133 * @event OverlayViewShow
30134 * Fires when OverlayView Draw
30135 * @param {Roo.bootstrap.LocationPicker} this
30136 * @param {Pixel} cpx
30138 OverlayViewShow : true,
30140 * @event OverlayViewHide
30141 * Fires when OverlayView Draw
30142 * @param {Roo.bootstrap.LocationPicker} this
30144 OverlayViewHide : true,
30146 * @event loadexception
30147 * Fires when load google lib failed.
30148 * @param {Roo.bootstrap.LocationPicker} this
30150 loadexception : true
30155 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
30157 gMapContext: false,
30163 mapTypeControl: false,
30164 disableDoubleClickZoom: false,
30166 streetViewControl: false,
30170 enableAutocomplete: false,
30171 enableReverseGeocode: true,
30174 getAutoCreate: function()
30179 cls: 'roo-location-picker'
30185 initEvents: function(ct, position)
30187 if(!this.el.getWidth() || this.isApplied()){
30191 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30196 initial: function()
30198 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30199 this.fireEvent('loadexception', this);
30203 if(!this.mapTypeId){
30204 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30207 this.gMapContext = this.GMapContext();
30209 this.initOverlayView();
30211 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30215 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30216 _this.setPosition(_this.gMapContext.marker.position);
30219 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30220 _this.fireEvent('mapClick', this, event);
30224 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30225 _this.fireEvent('mapRightClick', this, event);
30229 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30230 _this.fireEvent('markerClick', this, event);
30234 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30235 _this.fireEvent('markerRightClick', this, event);
30239 this.setPosition(this.gMapContext.location);
30241 this.fireEvent('initial', this, this.gMapContext.location);
30244 initOverlayView: function()
30248 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30252 _this.fireEvent('OverlayViewDraw', _this);
30257 _this.fireEvent('OverlayViewOnAdd', _this);
30260 onRemove: function()
30262 _this.fireEvent('OverlayViewOnRemove', _this);
30265 show: function(cpx)
30267 _this.fireEvent('OverlayViewShow', _this, cpx);
30272 _this.fireEvent('OverlayViewHide', _this);
30278 fromLatLngToContainerPixel: function(event)
30280 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30283 isApplied: function()
30285 return this.getGmapContext() == false ? false : true;
30288 getGmapContext: function()
30290 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30293 GMapContext: function()
30295 var position = new google.maps.LatLng(this.latitude, this.longitude);
30297 var _map = new google.maps.Map(this.el.dom, {
30300 mapTypeId: this.mapTypeId,
30301 mapTypeControl: this.mapTypeControl,
30302 disableDoubleClickZoom: this.disableDoubleClickZoom,
30303 scrollwheel: this.scrollwheel,
30304 streetViewControl: this.streetViewControl,
30305 locationName: this.locationName,
30306 draggable: this.draggable,
30307 enableAutocomplete: this.enableAutocomplete,
30308 enableReverseGeocode: this.enableReverseGeocode
30311 var _marker = new google.maps.Marker({
30312 position: position,
30314 title: this.markerTitle,
30315 draggable: this.draggable
30322 location: position,
30323 radius: this.radius,
30324 locationName: this.locationName,
30325 addressComponents: {
30326 formatted_address: null,
30327 addressLine1: null,
30328 addressLine2: null,
30330 streetNumber: null,
30334 stateOrProvince: null
30337 domContainer: this.el.dom,
30338 geodecoder: new google.maps.Geocoder()
30342 drawCircle: function(center, radius, options)
30344 if (this.gMapContext.circle != null) {
30345 this.gMapContext.circle.setMap(null);
30349 options = Roo.apply({}, options, {
30350 strokeColor: "#0000FF",
30351 strokeOpacity: .35,
30353 fillColor: "#0000FF",
30357 options.map = this.gMapContext.map;
30358 options.radius = radius;
30359 options.center = center;
30360 this.gMapContext.circle = new google.maps.Circle(options);
30361 return this.gMapContext.circle;
30367 setPosition: function(location)
30369 this.gMapContext.location = location;
30370 this.gMapContext.marker.setPosition(location);
30371 this.gMapContext.map.panTo(location);
30372 this.drawCircle(location, this.gMapContext.radius, {});
30376 if (this.gMapContext.settings.enableReverseGeocode) {
30377 this.gMapContext.geodecoder.geocode({
30378 latLng: this.gMapContext.location
30379 }, function(results, status) {
30381 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30382 _this.gMapContext.locationName = results[0].formatted_address;
30383 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30385 _this.fireEvent('positionchanged', this, location);
30392 this.fireEvent('positionchanged', this, location);
30397 google.maps.event.trigger(this.gMapContext.map, "resize");
30399 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30401 this.fireEvent('resize', this);
30404 setPositionByLatLng: function(latitude, longitude)
30406 this.setPosition(new google.maps.LatLng(latitude, longitude));
30409 getCurrentPosition: function()
30412 latitude: this.gMapContext.location.lat(),
30413 longitude: this.gMapContext.location.lng()
30417 getAddressName: function()
30419 return this.gMapContext.locationName;
30422 getAddressComponents: function()
30424 return this.gMapContext.addressComponents;
30427 address_component_from_google_geocode: function(address_components)
30431 for (var i = 0; i < address_components.length; i++) {
30432 var component = address_components[i];
30433 if (component.types.indexOf("postal_code") >= 0) {
30434 result.postalCode = component.short_name;
30435 } else if (component.types.indexOf("street_number") >= 0) {
30436 result.streetNumber = component.short_name;
30437 } else if (component.types.indexOf("route") >= 0) {
30438 result.streetName = component.short_name;
30439 } else if (component.types.indexOf("neighborhood") >= 0) {
30440 result.city = component.short_name;
30441 } else if (component.types.indexOf("locality") >= 0) {
30442 result.city = component.short_name;
30443 } else if (component.types.indexOf("sublocality") >= 0) {
30444 result.district = component.short_name;
30445 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30446 result.stateOrProvince = component.short_name;
30447 } else if (component.types.indexOf("country") >= 0) {
30448 result.country = component.short_name;
30452 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30453 result.addressLine2 = "";
30457 setZoomLevel: function(zoom)
30459 this.gMapContext.map.setZoom(zoom);
30472 this.fireEvent('show', this);
30483 this.fireEvent('hide', this);
30488 Roo.apply(Roo.bootstrap.LocationPicker, {
30490 OverlayView : function(map, options)
30492 options = options || {};
30499 * @class Roo.bootstrap.Alert
30500 * @extends Roo.bootstrap.Component
30501 * Bootstrap Alert class - shows an alert area box
30503 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30504 Enter a valid email address
30507 * @cfg {String} title The title of alert
30508 * @cfg {String} html The content of alert
30509 * @cfg {String} weight (success|info|warning|danger) Weight of the message
30510 * @cfg {String} fa font-awesomeicon
30511 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30512 * @cfg {Boolean} close true to show a x closer
30516 * Create a new alert
30517 * @param {Object} config The config object
30521 Roo.bootstrap.Alert = function(config){
30522 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30526 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
30532 faicon: false, // BC
30536 getAutoCreate : function()
30548 style : this.close ? '' : 'display:none'
30552 cls : 'roo-alert-icon'
30557 cls : 'roo-alert-title',
30562 cls : 'roo-alert-text',
30569 cfg.cn[0].cls += ' fa ' + this.faicon;
30572 cfg.cn[0].cls += ' fa ' + this.fa;
30576 cfg.cls += ' alert-' + this.weight;
30582 initEvents: function()
30584 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30585 this.titleEl = this.el.select('.roo-alert-title',true).first();
30586 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30587 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30588 if (this.seconds > 0) {
30589 this.hide.defer(this.seconds, this);
30593 * Set the Title Message HTML
30594 * @param {String} html
30596 setTitle : function(str)
30598 this.titleEl.dom.innerHTML = str;
30602 * Set the Body Message HTML
30603 * @param {String} html
30605 setHtml : function(str)
30607 this.htmlEl.dom.innerHTML = str;
30610 * Set the Weight of the alert
30611 * @param {String} (success|info|warning|danger) weight
30614 setWeight : function(weight)
30617 this.el.removeClass('alert-' + this.weight);
30620 this.weight = weight;
30622 this.el.addClass('alert-' + this.weight);
30625 * Set the Icon of the alert
30626 * @param {String} see fontawsome names (name without the 'fa-' bit)
30628 setIcon : function(icon)
30631 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30634 this.faicon = icon;
30636 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30661 * @class Roo.bootstrap.UploadCropbox
30662 * @extends Roo.bootstrap.Component
30663 * Bootstrap UploadCropbox class
30664 * @cfg {String} emptyText show when image has been loaded
30665 * @cfg {String} rotateNotify show when image too small to rotate
30666 * @cfg {Number} errorTimeout default 3000
30667 * @cfg {Number} minWidth default 300
30668 * @cfg {Number} minHeight default 300
30669 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30670 * @cfg {Boolean} isDocument (true|false) default false
30671 * @cfg {String} url action url
30672 * @cfg {String} paramName default 'imageUpload'
30673 * @cfg {String} method default POST
30674 * @cfg {Boolean} loadMask (true|false) default true
30675 * @cfg {Boolean} loadingText default 'Loading...'
30678 * Create a new UploadCropbox
30679 * @param {Object} config The config object
30682 Roo.bootstrap.UploadCropbox = function(config){
30683 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30687 * @event beforeselectfile
30688 * Fire before select file
30689 * @param {Roo.bootstrap.UploadCropbox} this
30691 "beforeselectfile" : true,
30694 * Fire after initEvent
30695 * @param {Roo.bootstrap.UploadCropbox} this
30700 * Fire after initEvent
30701 * @param {Roo.bootstrap.UploadCropbox} this
30702 * @param {String} data
30707 * Fire when preparing the file data
30708 * @param {Roo.bootstrap.UploadCropbox} this
30709 * @param {Object} file
30714 * Fire when get exception
30715 * @param {Roo.bootstrap.UploadCropbox} this
30716 * @param {XMLHttpRequest} xhr
30718 "exception" : true,
30720 * @event beforeloadcanvas
30721 * Fire before load the canvas
30722 * @param {Roo.bootstrap.UploadCropbox} this
30723 * @param {String} src
30725 "beforeloadcanvas" : true,
30728 * Fire when trash image
30729 * @param {Roo.bootstrap.UploadCropbox} this
30734 * Fire when download the image
30735 * @param {Roo.bootstrap.UploadCropbox} this
30739 * @event footerbuttonclick
30740 * Fire when footerbuttonclick
30741 * @param {Roo.bootstrap.UploadCropbox} this
30742 * @param {String} type
30744 "footerbuttonclick" : true,
30748 * @param {Roo.bootstrap.UploadCropbox} this
30753 * Fire when rotate the image
30754 * @param {Roo.bootstrap.UploadCropbox} this
30755 * @param {String} pos
30760 * Fire when inspect the file
30761 * @param {Roo.bootstrap.UploadCropbox} this
30762 * @param {Object} file
30767 * Fire when xhr upload the file
30768 * @param {Roo.bootstrap.UploadCropbox} this
30769 * @param {Object} data
30774 * Fire when arrange the file data
30775 * @param {Roo.bootstrap.UploadCropbox} this
30776 * @param {Object} formData
30781 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30784 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30786 emptyText : 'Click to upload image',
30787 rotateNotify : 'Image is too small to rotate',
30788 errorTimeout : 3000,
30802 cropType : 'image/jpeg',
30804 canvasLoaded : false,
30805 isDocument : false,
30807 paramName : 'imageUpload',
30809 loadingText : 'Loading...',
30812 getAutoCreate : function()
30816 cls : 'roo-upload-cropbox',
30820 cls : 'roo-upload-cropbox-selector',
30825 cls : 'roo-upload-cropbox-body',
30826 style : 'cursor:pointer',
30830 cls : 'roo-upload-cropbox-preview'
30834 cls : 'roo-upload-cropbox-thumb'
30838 cls : 'roo-upload-cropbox-empty-notify',
30839 html : this.emptyText
30843 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30844 html : this.rotateNotify
30850 cls : 'roo-upload-cropbox-footer',
30853 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30863 onRender : function(ct, position)
30865 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30867 if (this.buttons.length) {
30869 Roo.each(this.buttons, function(bb) {
30871 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30873 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30879 this.maskEl = this.el;
30883 initEvents : function()
30885 this.urlAPI = (window.createObjectURL && window) ||
30886 (window.URL && URL.revokeObjectURL && URL) ||
30887 (window.webkitURL && webkitURL);
30889 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30890 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30892 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30893 this.selectorEl.hide();
30895 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30896 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30898 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30899 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30900 this.thumbEl.hide();
30902 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30903 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30905 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30906 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30907 this.errorEl.hide();
30909 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30910 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30911 this.footerEl.hide();
30913 this.setThumbBoxSize();
30919 this.fireEvent('initial', this);
30926 window.addEventListener("resize", function() { _this.resize(); } );
30928 this.bodyEl.on('click', this.beforeSelectFile, this);
30931 this.bodyEl.on('touchstart', this.onTouchStart, this);
30932 this.bodyEl.on('touchmove', this.onTouchMove, this);
30933 this.bodyEl.on('touchend', this.onTouchEnd, this);
30937 this.bodyEl.on('mousedown', this.onMouseDown, this);
30938 this.bodyEl.on('mousemove', this.onMouseMove, this);
30939 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30940 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30941 Roo.get(document).on('mouseup', this.onMouseUp, this);
30944 this.selectorEl.on('change', this.onFileSelected, this);
30950 this.baseScale = 1;
30952 this.baseRotate = 1;
30953 this.dragable = false;
30954 this.pinching = false;
30957 this.cropData = false;
30958 this.notifyEl.dom.innerHTML = this.emptyText;
30960 this.selectorEl.dom.value = '';
30964 resize : function()
30966 if(this.fireEvent('resize', this) != false){
30967 this.setThumbBoxPosition();
30968 this.setCanvasPosition();
30972 onFooterButtonClick : function(e, el, o, type)
30975 case 'rotate-left' :
30976 this.onRotateLeft(e);
30978 case 'rotate-right' :
30979 this.onRotateRight(e);
30982 this.beforeSelectFile(e);
30997 this.fireEvent('footerbuttonclick', this, type);
31000 beforeSelectFile : function(e)
31002 e.preventDefault();
31004 if(this.fireEvent('beforeselectfile', this) != false){
31005 this.selectorEl.dom.click();
31009 onFileSelected : function(e)
31011 e.preventDefault();
31013 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31017 var file = this.selectorEl.dom.files[0];
31019 if(this.fireEvent('inspect', this, file) != false){
31020 this.prepare(file);
31025 trash : function(e)
31027 this.fireEvent('trash', this);
31030 download : function(e)
31032 this.fireEvent('download', this);
31035 loadCanvas : function(src)
31037 if(this.fireEvent('beforeloadcanvas', this, src) != false){
31041 this.imageEl = document.createElement('img');
31045 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
31047 this.imageEl.src = src;
31051 onLoadCanvas : function()
31053 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
31054 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
31056 this.bodyEl.un('click', this.beforeSelectFile, this);
31058 this.notifyEl.hide();
31059 this.thumbEl.show();
31060 this.footerEl.show();
31062 this.baseRotateLevel();
31064 if(this.isDocument){
31065 this.setThumbBoxSize();
31068 this.setThumbBoxPosition();
31070 this.baseScaleLevel();
31076 this.canvasLoaded = true;
31079 this.maskEl.unmask();
31084 setCanvasPosition : function()
31086 if(!this.canvasEl){
31090 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
31091 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31093 this.previewEl.setLeft(pw);
31094 this.previewEl.setTop(ph);
31098 onMouseDown : function(e)
31102 this.dragable = true;
31103 this.pinching = false;
31105 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31106 this.dragable = false;
31110 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31111 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31115 onMouseMove : function(e)
31119 if(!this.canvasLoaded){
31123 if (!this.dragable){
31127 var minX = Math.ceil(this.thumbEl.getLeft(true));
31128 var minY = Math.ceil(this.thumbEl.getTop(true));
31130 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31131 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31133 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31134 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31136 x = x - this.mouseX;
31137 y = y - this.mouseY;
31139 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31140 var bgY = Math.ceil(y + this.previewEl.getTop(true));
31142 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31143 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31145 this.previewEl.setLeft(bgX);
31146 this.previewEl.setTop(bgY);
31148 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31149 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31152 onMouseUp : function(e)
31156 this.dragable = false;
31159 onMouseWheel : function(e)
31163 this.startScale = this.scale;
31165 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31167 if(!this.zoomable()){
31168 this.scale = this.startScale;
31177 zoomable : function()
31179 var minScale = this.thumbEl.getWidth() / this.minWidth;
31181 if(this.minWidth < this.minHeight){
31182 minScale = this.thumbEl.getHeight() / this.minHeight;
31185 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31186 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31190 (this.rotate == 0 || this.rotate == 180) &&
31192 width > this.imageEl.OriginWidth ||
31193 height > this.imageEl.OriginHeight ||
31194 (width < this.minWidth && height < this.minHeight)
31202 (this.rotate == 90 || this.rotate == 270) &&
31204 width > this.imageEl.OriginWidth ||
31205 height > this.imageEl.OriginHeight ||
31206 (width < this.minHeight && height < this.minWidth)
31213 !this.isDocument &&
31214 (this.rotate == 0 || this.rotate == 180) &&
31216 width < this.minWidth ||
31217 width > this.imageEl.OriginWidth ||
31218 height < this.minHeight ||
31219 height > this.imageEl.OriginHeight
31226 !this.isDocument &&
31227 (this.rotate == 90 || this.rotate == 270) &&
31229 width < this.minHeight ||
31230 width > this.imageEl.OriginWidth ||
31231 height < this.minWidth ||
31232 height > this.imageEl.OriginHeight
31242 onRotateLeft : function(e)
31244 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31246 var minScale = this.thumbEl.getWidth() / this.minWidth;
31248 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31249 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31251 this.startScale = this.scale;
31253 while (this.getScaleLevel() < minScale){
31255 this.scale = this.scale + 1;
31257 if(!this.zoomable()){
31262 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31263 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31268 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31275 this.scale = this.startScale;
31277 this.onRotateFail();
31282 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31284 if(this.isDocument){
31285 this.setThumbBoxSize();
31286 this.setThumbBoxPosition();
31287 this.setCanvasPosition();
31292 this.fireEvent('rotate', this, 'left');
31296 onRotateRight : function(e)
31298 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31300 var minScale = this.thumbEl.getWidth() / this.minWidth;
31302 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31303 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31305 this.startScale = this.scale;
31307 while (this.getScaleLevel() < minScale){
31309 this.scale = this.scale + 1;
31311 if(!this.zoomable()){
31316 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31317 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31322 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31329 this.scale = this.startScale;
31331 this.onRotateFail();
31336 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31338 if(this.isDocument){
31339 this.setThumbBoxSize();
31340 this.setThumbBoxPosition();
31341 this.setCanvasPosition();
31346 this.fireEvent('rotate', this, 'right');
31349 onRotateFail : function()
31351 this.errorEl.show(true);
31355 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31360 this.previewEl.dom.innerHTML = '';
31362 var canvasEl = document.createElement("canvas");
31364 var contextEl = canvasEl.getContext("2d");
31366 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31367 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31368 var center = this.imageEl.OriginWidth / 2;
31370 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31371 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31372 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31373 center = this.imageEl.OriginHeight / 2;
31376 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31378 contextEl.translate(center, center);
31379 contextEl.rotate(this.rotate * Math.PI / 180);
31381 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31383 this.canvasEl = document.createElement("canvas");
31385 this.contextEl = this.canvasEl.getContext("2d");
31387 switch (this.rotate) {
31390 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31391 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31393 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31398 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31399 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31401 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31402 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);
31406 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31411 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31412 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31414 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31415 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);
31419 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);
31424 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31425 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31427 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31428 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31432 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);
31439 this.previewEl.appendChild(this.canvasEl);
31441 this.setCanvasPosition();
31446 if(!this.canvasLoaded){
31450 var imageCanvas = document.createElement("canvas");
31452 var imageContext = imageCanvas.getContext("2d");
31454 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31455 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31457 var center = imageCanvas.width / 2;
31459 imageContext.translate(center, center);
31461 imageContext.rotate(this.rotate * Math.PI / 180);
31463 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31465 var canvas = document.createElement("canvas");
31467 var context = canvas.getContext("2d");
31469 canvas.width = this.minWidth;
31470 canvas.height = this.minHeight;
31472 switch (this.rotate) {
31475 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31476 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31478 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31479 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31481 var targetWidth = this.minWidth - 2 * x;
31482 var targetHeight = this.minHeight - 2 * y;
31486 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31487 scale = targetWidth / width;
31490 if(x > 0 && y == 0){
31491 scale = targetHeight / height;
31494 if(x > 0 && y > 0){
31495 scale = targetWidth / width;
31497 if(width < height){
31498 scale = targetHeight / height;
31502 context.scale(scale, scale);
31504 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31505 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31507 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31508 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31510 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31515 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31516 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31518 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31519 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31521 var targetWidth = this.minWidth - 2 * x;
31522 var targetHeight = this.minHeight - 2 * y;
31526 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31527 scale = targetWidth / width;
31530 if(x > 0 && y == 0){
31531 scale = targetHeight / height;
31534 if(x > 0 && y > 0){
31535 scale = targetWidth / width;
31537 if(width < height){
31538 scale = targetHeight / height;
31542 context.scale(scale, scale);
31544 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31545 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31547 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31548 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31550 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31552 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31557 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31558 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31560 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31561 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31563 var targetWidth = this.minWidth - 2 * x;
31564 var targetHeight = this.minHeight - 2 * y;
31568 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31569 scale = targetWidth / width;
31572 if(x > 0 && y == 0){
31573 scale = targetHeight / height;
31576 if(x > 0 && y > 0){
31577 scale = targetWidth / width;
31579 if(width < height){
31580 scale = targetHeight / height;
31584 context.scale(scale, scale);
31586 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31587 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31589 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31590 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31592 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31593 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31595 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31600 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31601 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31603 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31604 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31606 var targetWidth = this.minWidth - 2 * x;
31607 var targetHeight = this.minHeight - 2 * y;
31611 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31612 scale = targetWidth / width;
31615 if(x > 0 && y == 0){
31616 scale = targetHeight / height;
31619 if(x > 0 && y > 0){
31620 scale = targetWidth / width;
31622 if(width < height){
31623 scale = targetHeight / height;
31627 context.scale(scale, scale);
31629 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31630 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31632 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31633 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31635 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31637 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31644 this.cropData = canvas.toDataURL(this.cropType);
31646 if(this.fireEvent('crop', this, this.cropData) !== false){
31647 this.process(this.file, this.cropData);
31654 setThumbBoxSize : function()
31658 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31659 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31660 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31662 this.minWidth = width;
31663 this.minHeight = height;
31665 if(this.rotate == 90 || this.rotate == 270){
31666 this.minWidth = height;
31667 this.minHeight = width;
31672 width = Math.ceil(this.minWidth * height / this.minHeight);
31674 if(this.minWidth > this.minHeight){
31676 height = Math.ceil(this.minHeight * width / this.minWidth);
31679 this.thumbEl.setStyle({
31680 width : width + 'px',
31681 height : height + 'px'
31688 setThumbBoxPosition : function()
31690 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31691 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31693 this.thumbEl.setLeft(x);
31694 this.thumbEl.setTop(y);
31698 baseRotateLevel : function()
31700 this.baseRotate = 1;
31703 typeof(this.exif) != 'undefined' &&
31704 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31705 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31707 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31710 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31714 baseScaleLevel : function()
31718 if(this.isDocument){
31720 if(this.baseRotate == 6 || this.baseRotate == 8){
31722 height = this.thumbEl.getHeight();
31723 this.baseScale = height / this.imageEl.OriginWidth;
31725 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31726 width = this.thumbEl.getWidth();
31727 this.baseScale = width / this.imageEl.OriginHeight;
31733 height = this.thumbEl.getHeight();
31734 this.baseScale = height / this.imageEl.OriginHeight;
31736 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31737 width = this.thumbEl.getWidth();
31738 this.baseScale = width / this.imageEl.OriginWidth;
31744 if(this.baseRotate == 6 || this.baseRotate == 8){
31746 width = this.thumbEl.getHeight();
31747 this.baseScale = width / this.imageEl.OriginHeight;
31749 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31750 height = this.thumbEl.getWidth();
31751 this.baseScale = height / this.imageEl.OriginHeight;
31754 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31755 height = this.thumbEl.getWidth();
31756 this.baseScale = height / this.imageEl.OriginHeight;
31758 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31759 width = this.thumbEl.getHeight();
31760 this.baseScale = width / this.imageEl.OriginWidth;
31767 width = this.thumbEl.getWidth();
31768 this.baseScale = width / this.imageEl.OriginWidth;
31770 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31771 height = this.thumbEl.getHeight();
31772 this.baseScale = height / this.imageEl.OriginHeight;
31775 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31777 height = this.thumbEl.getHeight();
31778 this.baseScale = height / this.imageEl.OriginHeight;
31780 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31781 width = this.thumbEl.getWidth();
31782 this.baseScale = width / this.imageEl.OriginWidth;
31790 getScaleLevel : function()
31792 return this.baseScale * Math.pow(1.1, this.scale);
31795 onTouchStart : function(e)
31797 if(!this.canvasLoaded){
31798 this.beforeSelectFile(e);
31802 var touches = e.browserEvent.touches;
31808 if(touches.length == 1){
31809 this.onMouseDown(e);
31813 if(touches.length != 2){
31819 for(var i = 0, finger; finger = touches[i]; i++){
31820 coords.push(finger.pageX, finger.pageY);
31823 var x = Math.pow(coords[0] - coords[2], 2);
31824 var y = Math.pow(coords[1] - coords[3], 2);
31826 this.startDistance = Math.sqrt(x + y);
31828 this.startScale = this.scale;
31830 this.pinching = true;
31831 this.dragable = false;
31835 onTouchMove : function(e)
31837 if(!this.pinching && !this.dragable){
31841 var touches = e.browserEvent.touches;
31848 this.onMouseMove(e);
31854 for(var i = 0, finger; finger = touches[i]; i++){
31855 coords.push(finger.pageX, finger.pageY);
31858 var x = Math.pow(coords[0] - coords[2], 2);
31859 var y = Math.pow(coords[1] - coords[3], 2);
31861 this.endDistance = Math.sqrt(x + y);
31863 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31865 if(!this.zoomable()){
31866 this.scale = this.startScale;
31874 onTouchEnd : function(e)
31876 this.pinching = false;
31877 this.dragable = false;
31881 process : function(file, crop)
31884 this.maskEl.mask(this.loadingText);
31887 this.xhr = new XMLHttpRequest();
31889 file.xhr = this.xhr;
31891 this.xhr.open(this.method, this.url, true);
31894 "Accept": "application/json",
31895 "Cache-Control": "no-cache",
31896 "X-Requested-With": "XMLHttpRequest"
31899 for (var headerName in headers) {
31900 var headerValue = headers[headerName];
31902 this.xhr.setRequestHeader(headerName, headerValue);
31908 this.xhr.onload = function()
31910 _this.xhrOnLoad(_this.xhr);
31913 this.xhr.onerror = function()
31915 _this.xhrOnError(_this.xhr);
31918 var formData = new FormData();
31920 formData.append('returnHTML', 'NO');
31923 formData.append('crop', crop);
31926 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31927 formData.append(this.paramName, file, file.name);
31930 if(typeof(file.filename) != 'undefined'){
31931 formData.append('filename', file.filename);
31934 if(typeof(file.mimetype) != 'undefined'){
31935 formData.append('mimetype', file.mimetype);
31938 if(this.fireEvent('arrange', this, formData) != false){
31939 this.xhr.send(formData);
31943 xhrOnLoad : function(xhr)
31946 this.maskEl.unmask();
31949 if (xhr.readyState !== 4) {
31950 this.fireEvent('exception', this, xhr);
31954 var response = Roo.decode(xhr.responseText);
31956 if(!response.success){
31957 this.fireEvent('exception', this, xhr);
31961 var response = Roo.decode(xhr.responseText);
31963 this.fireEvent('upload', this, response);
31967 xhrOnError : function()
31970 this.maskEl.unmask();
31973 Roo.log('xhr on error');
31975 var response = Roo.decode(xhr.responseText);
31981 prepare : function(file)
31984 this.maskEl.mask(this.loadingText);
31990 if(typeof(file) === 'string'){
31991 this.loadCanvas(file);
31995 if(!file || !this.urlAPI){
32000 this.cropType = file.type;
32004 if(this.fireEvent('prepare', this, this.file) != false){
32006 var reader = new FileReader();
32008 reader.onload = function (e) {
32009 if (e.target.error) {
32010 Roo.log(e.target.error);
32014 var buffer = e.target.result,
32015 dataView = new DataView(buffer),
32017 maxOffset = dataView.byteLength - 4,
32021 if (dataView.getUint16(0) === 0xffd8) {
32022 while (offset < maxOffset) {
32023 markerBytes = dataView.getUint16(offset);
32025 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
32026 markerLength = dataView.getUint16(offset + 2) + 2;
32027 if (offset + markerLength > dataView.byteLength) {
32028 Roo.log('Invalid meta data: Invalid segment size.');
32032 if(markerBytes == 0xffe1){
32033 _this.parseExifData(
32040 offset += markerLength;
32050 var url = _this.urlAPI.createObjectURL(_this.file);
32052 _this.loadCanvas(url);
32057 reader.readAsArrayBuffer(this.file);
32063 parseExifData : function(dataView, offset, length)
32065 var tiffOffset = offset + 10,
32069 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32070 // No Exif data, might be XMP data instead
32074 // Check for the ASCII code for "Exif" (0x45786966):
32075 if (dataView.getUint32(offset + 4) !== 0x45786966) {
32076 // No Exif data, might be XMP data instead
32079 if (tiffOffset + 8 > dataView.byteLength) {
32080 Roo.log('Invalid Exif data: Invalid segment size.');
32083 // Check for the two null bytes:
32084 if (dataView.getUint16(offset + 8) !== 0x0000) {
32085 Roo.log('Invalid Exif data: Missing byte alignment offset.');
32088 // Check the byte alignment:
32089 switch (dataView.getUint16(tiffOffset)) {
32091 littleEndian = true;
32094 littleEndian = false;
32097 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32100 // Check for the TIFF tag marker (0x002A):
32101 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32102 Roo.log('Invalid Exif data: Missing TIFF marker.');
32105 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32106 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32108 this.parseExifTags(
32111 tiffOffset + dirOffset,
32116 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32121 if (dirOffset + 6 > dataView.byteLength) {
32122 Roo.log('Invalid Exif data: Invalid directory offset.');
32125 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32126 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32127 if (dirEndOffset + 4 > dataView.byteLength) {
32128 Roo.log('Invalid Exif data: Invalid directory size.');
32131 for (i = 0; i < tagsNumber; i += 1) {
32135 dirOffset + 2 + 12 * i, // tag offset
32139 // Return the offset to the next directory:
32140 return dataView.getUint32(dirEndOffset, littleEndian);
32143 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
32145 var tag = dataView.getUint16(offset, littleEndian);
32147 this.exif[tag] = this.getExifValue(
32151 dataView.getUint16(offset + 2, littleEndian), // tag type
32152 dataView.getUint32(offset + 4, littleEndian), // tag length
32157 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32159 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32168 Roo.log('Invalid Exif data: Invalid tag type.');
32172 tagSize = tagType.size * length;
32173 // Determine if the value is contained in the dataOffset bytes,
32174 // or if the value at the dataOffset is a pointer to the actual data:
32175 dataOffset = tagSize > 4 ?
32176 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32177 if (dataOffset + tagSize > dataView.byteLength) {
32178 Roo.log('Invalid Exif data: Invalid data offset.');
32181 if (length === 1) {
32182 return tagType.getValue(dataView, dataOffset, littleEndian);
32185 for (i = 0; i < length; i += 1) {
32186 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32189 if (tagType.ascii) {
32191 // Concatenate the chars:
32192 for (i = 0; i < values.length; i += 1) {
32194 // Ignore the terminating NULL byte(s):
32195 if (c === '\u0000') {
32207 Roo.apply(Roo.bootstrap.UploadCropbox, {
32209 'Orientation': 0x0112
32213 1: 0, //'top-left',
32215 3: 180, //'bottom-right',
32216 // 4: 'bottom-left',
32218 6: 90, //'right-top',
32219 // 7: 'right-bottom',
32220 8: 270 //'left-bottom'
32224 // byte, 8-bit unsigned int:
32226 getValue: function (dataView, dataOffset) {
32227 return dataView.getUint8(dataOffset);
32231 // ascii, 8-bit byte:
32233 getValue: function (dataView, dataOffset) {
32234 return String.fromCharCode(dataView.getUint8(dataOffset));
32239 // short, 16 bit int:
32241 getValue: function (dataView, dataOffset, littleEndian) {
32242 return dataView.getUint16(dataOffset, littleEndian);
32246 // long, 32 bit int:
32248 getValue: function (dataView, dataOffset, littleEndian) {
32249 return dataView.getUint32(dataOffset, littleEndian);
32253 // rational = two long values, first is numerator, second is denominator:
32255 getValue: function (dataView, dataOffset, littleEndian) {
32256 return dataView.getUint32(dataOffset, littleEndian) /
32257 dataView.getUint32(dataOffset + 4, littleEndian);
32261 // slong, 32 bit signed int:
32263 getValue: function (dataView, dataOffset, littleEndian) {
32264 return dataView.getInt32(dataOffset, littleEndian);
32268 // srational, two slongs, first is numerator, second is denominator:
32270 getValue: function (dataView, dataOffset, littleEndian) {
32271 return dataView.getInt32(dataOffset, littleEndian) /
32272 dataView.getInt32(dataOffset + 4, littleEndian);
32282 cls : 'btn-group roo-upload-cropbox-rotate-left',
32283 action : 'rotate-left',
32287 cls : 'btn btn-default',
32288 html : '<i class="fa fa-undo"></i>'
32294 cls : 'btn-group roo-upload-cropbox-picture',
32295 action : 'picture',
32299 cls : 'btn btn-default',
32300 html : '<i class="fa fa-picture-o"></i>'
32306 cls : 'btn-group roo-upload-cropbox-rotate-right',
32307 action : 'rotate-right',
32311 cls : 'btn btn-default',
32312 html : '<i class="fa fa-repeat"></i>'
32320 cls : 'btn-group roo-upload-cropbox-rotate-left',
32321 action : 'rotate-left',
32325 cls : 'btn btn-default',
32326 html : '<i class="fa fa-undo"></i>'
32332 cls : 'btn-group roo-upload-cropbox-download',
32333 action : 'download',
32337 cls : 'btn btn-default',
32338 html : '<i class="fa fa-download"></i>'
32344 cls : 'btn-group roo-upload-cropbox-crop',
32349 cls : 'btn btn-default',
32350 html : '<i class="fa fa-crop"></i>'
32356 cls : 'btn-group roo-upload-cropbox-trash',
32361 cls : 'btn btn-default',
32362 html : '<i class="fa fa-trash"></i>'
32368 cls : 'btn-group roo-upload-cropbox-rotate-right',
32369 action : 'rotate-right',
32373 cls : 'btn btn-default',
32374 html : '<i class="fa fa-repeat"></i>'
32382 cls : 'btn-group roo-upload-cropbox-rotate-left',
32383 action : 'rotate-left',
32387 cls : 'btn btn-default',
32388 html : '<i class="fa fa-undo"></i>'
32394 cls : 'btn-group roo-upload-cropbox-rotate-right',
32395 action : 'rotate-right',
32399 cls : 'btn btn-default',
32400 html : '<i class="fa fa-repeat"></i>'
32413 * @class Roo.bootstrap.DocumentManager
32414 * @extends Roo.bootstrap.Component
32415 * Bootstrap DocumentManager class
32416 * @cfg {String} paramName default 'imageUpload'
32417 * @cfg {String} toolTipName default 'filename'
32418 * @cfg {String} method default POST
32419 * @cfg {String} url action url
32420 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32421 * @cfg {Boolean} multiple multiple upload default true
32422 * @cfg {Number} thumbSize default 300
32423 * @cfg {String} fieldLabel
32424 * @cfg {Number} labelWidth default 4
32425 * @cfg {String} labelAlign (left|top) default left
32426 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32427 * @cfg {Number} labellg set the width of label (1-12)
32428 * @cfg {Number} labelmd set the width of label (1-12)
32429 * @cfg {Number} labelsm set the width of label (1-12)
32430 * @cfg {Number} labelxs set the width of label (1-12)
32433 * Create a new DocumentManager
32434 * @param {Object} config The config object
32437 Roo.bootstrap.DocumentManager = function(config){
32438 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32441 this.delegates = [];
32446 * Fire when initial the DocumentManager
32447 * @param {Roo.bootstrap.DocumentManager} this
32452 * inspect selected file
32453 * @param {Roo.bootstrap.DocumentManager} this
32454 * @param {File} file
32459 * Fire when xhr load exception
32460 * @param {Roo.bootstrap.DocumentManager} this
32461 * @param {XMLHttpRequest} xhr
32463 "exception" : true,
32465 * @event afterupload
32466 * Fire when xhr load exception
32467 * @param {Roo.bootstrap.DocumentManager} this
32468 * @param {XMLHttpRequest} xhr
32470 "afterupload" : true,
32473 * prepare the form data
32474 * @param {Roo.bootstrap.DocumentManager} this
32475 * @param {Object} formData
32480 * Fire when remove the file
32481 * @param {Roo.bootstrap.DocumentManager} this
32482 * @param {Object} file
32487 * Fire after refresh the file
32488 * @param {Roo.bootstrap.DocumentManager} this
32493 * Fire after click the image
32494 * @param {Roo.bootstrap.DocumentManager} this
32495 * @param {Object} file
32500 * Fire when upload a image and editable set to true
32501 * @param {Roo.bootstrap.DocumentManager} this
32502 * @param {Object} file
32506 * @event beforeselectfile
32507 * Fire before select file
32508 * @param {Roo.bootstrap.DocumentManager} this
32510 "beforeselectfile" : true,
32513 * Fire before process file
32514 * @param {Roo.bootstrap.DocumentManager} this
32515 * @param {Object} file
32519 * @event previewrendered
32520 * Fire when preview rendered
32521 * @param {Roo.bootstrap.DocumentManager} this
32522 * @param {Object} file
32524 "previewrendered" : true,
32527 "previewResize" : true
32532 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
32541 paramName : 'imageUpload',
32542 toolTipName : 'filename',
32545 labelAlign : 'left',
32555 getAutoCreate : function()
32557 var managerWidget = {
32559 cls : 'roo-document-manager',
32563 cls : 'roo-document-manager-selector',
32568 cls : 'roo-document-manager-uploader',
32572 cls : 'roo-document-manager-upload-btn',
32573 html : '<i class="fa fa-plus"></i>'
32584 cls : 'column col-md-12',
32589 if(this.fieldLabel.length){
32594 cls : 'column col-md-12',
32595 html : this.fieldLabel
32599 cls : 'column col-md-12',
32604 if(this.labelAlign == 'left'){
32609 html : this.fieldLabel
32618 if(this.labelWidth > 12){
32619 content[0].style = "width: " + this.labelWidth + 'px';
32622 if(this.labelWidth < 13 && this.labelmd == 0){
32623 this.labelmd = this.labelWidth;
32626 if(this.labellg > 0){
32627 content[0].cls += ' col-lg-' + this.labellg;
32628 content[1].cls += ' col-lg-' + (12 - this.labellg);
32631 if(this.labelmd > 0){
32632 content[0].cls += ' col-md-' + this.labelmd;
32633 content[1].cls += ' col-md-' + (12 - this.labelmd);
32636 if(this.labelsm > 0){
32637 content[0].cls += ' col-sm-' + this.labelsm;
32638 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32641 if(this.labelxs > 0){
32642 content[0].cls += ' col-xs-' + this.labelxs;
32643 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32651 cls : 'row clearfix',
32659 initEvents : function()
32661 this.managerEl = this.el.select('.roo-document-manager', true).first();
32662 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32664 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32665 this.selectorEl.hide();
32668 this.selectorEl.attr('multiple', 'multiple');
32671 this.selectorEl.on('change', this.onFileSelected, this);
32673 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32674 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32676 this.uploader.on('click', this.onUploaderClick, this);
32678 this.renderProgressDialog();
32682 window.addEventListener("resize", function() { _this.refresh(); } );
32684 this.fireEvent('initial', this);
32687 renderProgressDialog : function()
32691 this.progressDialog = new Roo.bootstrap.Modal({
32692 cls : 'roo-document-manager-progress-dialog',
32693 allow_close : false,
32704 btnclick : function() {
32705 _this.uploadCancel();
32711 this.progressDialog.render(Roo.get(document.body));
32713 this.progress = new Roo.bootstrap.Progress({
32714 cls : 'roo-document-manager-progress',
32719 this.progress.render(this.progressDialog.getChildContainer());
32721 this.progressBar = new Roo.bootstrap.ProgressBar({
32722 cls : 'roo-document-manager-progress-bar',
32725 aria_valuemax : 12,
32729 this.progressBar.render(this.progress.getChildContainer());
32732 onUploaderClick : function(e)
32734 e.preventDefault();
32736 if(this.fireEvent('beforeselectfile', this) != false){
32737 this.selectorEl.dom.click();
32742 onFileSelected : function(e)
32744 e.preventDefault();
32746 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32750 Roo.each(this.selectorEl.dom.files, function(file){
32751 if(this.fireEvent('inspect', this, file) != false){
32752 this.files.push(file);
32762 this.selectorEl.dom.value = '';
32764 if(!this.files || !this.files.length){
32768 if(this.boxes > 0 && this.files.length > this.boxes){
32769 this.files = this.files.slice(0, this.boxes);
32772 this.uploader.show();
32774 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32775 this.uploader.hide();
32784 Roo.each(this.files, function(file){
32786 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32787 var f = this.renderPreview(file);
32792 if(file.type.indexOf('image') != -1){
32793 this.delegates.push(
32795 _this.process(file);
32796 }).createDelegate(this)
32804 _this.process(file);
32805 }).createDelegate(this)
32810 this.files = files;
32812 this.delegates = this.delegates.concat(docs);
32814 if(!this.delegates.length){
32819 this.progressBar.aria_valuemax = this.delegates.length;
32826 arrange : function()
32828 if(!this.delegates.length){
32829 this.progressDialog.hide();
32834 var delegate = this.delegates.shift();
32836 this.progressDialog.show();
32838 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32840 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32845 refresh : function()
32847 this.uploader.show();
32849 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32850 this.uploader.hide();
32853 Roo.isTouch ? this.closable(false) : this.closable(true);
32855 this.fireEvent('refresh', this);
32858 onRemove : function(e, el, o)
32860 e.preventDefault();
32862 this.fireEvent('remove', this, o);
32866 remove : function(o)
32870 Roo.each(this.files, function(file){
32871 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32880 this.files = files;
32887 Roo.each(this.files, function(file){
32892 file.target.remove();
32901 onClick : function(e, el, o)
32903 e.preventDefault();
32905 this.fireEvent('click', this, o);
32909 closable : function(closable)
32911 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32913 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32925 xhrOnLoad : function(xhr)
32927 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32931 if (xhr.readyState !== 4) {
32933 this.fireEvent('exception', this, xhr);
32937 var response = Roo.decode(xhr.responseText);
32939 if(!response.success){
32941 this.fireEvent('exception', this, xhr);
32945 var file = this.renderPreview(response.data);
32947 this.files.push(file);
32951 this.fireEvent('afterupload', this, xhr);
32955 xhrOnError : function(xhr)
32957 Roo.log('xhr on error');
32959 var response = Roo.decode(xhr.responseText);
32966 process : function(file)
32968 if(this.fireEvent('process', this, file) !== false){
32969 if(this.editable && file.type.indexOf('image') != -1){
32970 this.fireEvent('edit', this, file);
32974 this.uploadStart(file, false);
32981 uploadStart : function(file, crop)
32983 this.xhr = new XMLHttpRequest();
32985 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32990 file.xhr = this.xhr;
32992 this.managerEl.createChild({
32994 cls : 'roo-document-manager-loading',
32998 tooltip : file.name,
32999 cls : 'roo-document-manager-thumb',
33000 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33006 this.xhr.open(this.method, this.url, true);
33009 "Accept": "application/json",
33010 "Cache-Control": "no-cache",
33011 "X-Requested-With": "XMLHttpRequest"
33014 for (var headerName in headers) {
33015 var headerValue = headers[headerName];
33017 this.xhr.setRequestHeader(headerName, headerValue);
33023 this.xhr.onload = function()
33025 _this.xhrOnLoad(_this.xhr);
33028 this.xhr.onerror = function()
33030 _this.xhrOnError(_this.xhr);
33033 var formData = new FormData();
33035 formData.append('returnHTML', 'NO');
33038 formData.append('crop', crop);
33041 formData.append(this.paramName, file, file.name);
33048 if(this.fireEvent('prepare', this, formData, options) != false){
33050 if(options.manually){
33054 this.xhr.send(formData);
33058 this.uploadCancel();
33061 uploadCancel : function()
33067 this.delegates = [];
33069 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
33076 renderPreview : function(file)
33078 if(typeof(file.target) != 'undefined' && file.target){
33082 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
33084 var previewEl = this.managerEl.createChild({
33086 cls : 'roo-document-manager-preview',
33090 tooltip : file[this.toolTipName],
33091 cls : 'roo-document-manager-thumb',
33092 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33097 html : '<i class="fa fa-times-circle"></i>'
33102 var close = previewEl.select('button.close', true).first();
33104 close.on('click', this.onRemove, this, file);
33106 file.target = previewEl;
33108 var image = previewEl.select('img', true).first();
33112 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33114 image.on('click', this.onClick, this, file);
33116 this.fireEvent('previewrendered', this, file);
33122 onPreviewLoad : function(file, image)
33124 if(typeof(file.target) == 'undefined' || !file.target){
33128 var width = image.dom.naturalWidth || image.dom.width;
33129 var height = image.dom.naturalHeight || image.dom.height;
33131 if(!this.previewResize) {
33135 if(width > height){
33136 file.target.addClass('wide');
33140 file.target.addClass('tall');
33145 uploadFromSource : function(file, crop)
33147 this.xhr = new XMLHttpRequest();
33149 this.managerEl.createChild({
33151 cls : 'roo-document-manager-loading',
33155 tooltip : file.name,
33156 cls : 'roo-document-manager-thumb',
33157 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33163 this.xhr.open(this.method, this.url, true);
33166 "Accept": "application/json",
33167 "Cache-Control": "no-cache",
33168 "X-Requested-With": "XMLHttpRequest"
33171 for (var headerName in headers) {
33172 var headerValue = headers[headerName];
33174 this.xhr.setRequestHeader(headerName, headerValue);
33180 this.xhr.onload = function()
33182 _this.xhrOnLoad(_this.xhr);
33185 this.xhr.onerror = function()
33187 _this.xhrOnError(_this.xhr);
33190 var formData = new FormData();
33192 formData.append('returnHTML', 'NO');
33194 formData.append('crop', crop);
33196 if(typeof(file.filename) != 'undefined'){
33197 formData.append('filename', file.filename);
33200 if(typeof(file.mimetype) != 'undefined'){
33201 formData.append('mimetype', file.mimetype);
33206 if(this.fireEvent('prepare', this, formData) != false){
33207 this.xhr.send(formData);
33217 * @class Roo.bootstrap.DocumentViewer
33218 * @extends Roo.bootstrap.Component
33219 * Bootstrap DocumentViewer class
33220 * @cfg {Boolean} showDownload (true|false) show download button (default true)
33221 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33224 * Create a new DocumentViewer
33225 * @param {Object} config The config object
33228 Roo.bootstrap.DocumentViewer = function(config){
33229 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33234 * Fire after initEvent
33235 * @param {Roo.bootstrap.DocumentViewer} this
33241 * @param {Roo.bootstrap.DocumentViewer} this
33246 * Fire after download button
33247 * @param {Roo.bootstrap.DocumentViewer} this
33252 * Fire after trash button
33253 * @param {Roo.bootstrap.DocumentViewer} this
33260 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
33262 showDownload : true,
33266 getAutoCreate : function()
33270 cls : 'roo-document-viewer',
33274 cls : 'roo-document-viewer-body',
33278 cls : 'roo-document-viewer-thumb',
33282 cls : 'roo-document-viewer-image'
33290 cls : 'roo-document-viewer-footer',
33293 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33297 cls : 'btn-group roo-document-viewer-download',
33301 cls : 'btn btn-default',
33302 html : '<i class="fa fa-download"></i>'
33308 cls : 'btn-group roo-document-viewer-trash',
33312 cls : 'btn btn-default',
33313 html : '<i class="fa fa-trash"></i>'
33326 initEvents : function()
33328 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33329 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33331 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33332 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33334 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33335 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33337 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33338 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33340 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33341 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33343 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33344 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33346 this.bodyEl.on('click', this.onClick, this);
33347 this.downloadBtn.on('click', this.onDownload, this);
33348 this.trashBtn.on('click', this.onTrash, this);
33350 this.downloadBtn.hide();
33351 this.trashBtn.hide();
33353 if(this.showDownload){
33354 this.downloadBtn.show();
33357 if(this.showTrash){
33358 this.trashBtn.show();
33361 if(!this.showDownload && !this.showTrash) {
33362 this.footerEl.hide();
33367 initial : function()
33369 this.fireEvent('initial', this);
33373 onClick : function(e)
33375 e.preventDefault();
33377 this.fireEvent('click', this);
33380 onDownload : function(e)
33382 e.preventDefault();
33384 this.fireEvent('download', this);
33387 onTrash : function(e)
33389 e.preventDefault();
33391 this.fireEvent('trash', this);
33403 * @class Roo.bootstrap.form.FieldLabel
33404 * @extends Roo.bootstrap.Component
33405 * Bootstrap FieldLabel class
33406 * @cfg {String} html contents of the element
33407 * @cfg {String} tag tag of the element default label
33408 * @cfg {String} cls class of the element
33409 * @cfg {String} target label target
33410 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33411 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33412 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33413 * @cfg {String} iconTooltip default "This field is required"
33414 * @cfg {String} indicatorpos (left|right) default left
33417 * Create a new FieldLabel
33418 * @param {Object} config The config object
33421 Roo.bootstrap.form.FieldLabel = function(config){
33422 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33427 * Fires after the field has been marked as invalid.
33428 * @param {Roo.form.FieldLabel} this
33429 * @param {String} msg The validation message
33434 * Fires after the field has been validated with no errors.
33435 * @param {Roo.form.FieldLabel} this
33441 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
33448 invalidClass : 'has-warning',
33449 validClass : 'has-success',
33450 iconTooltip : 'This field is required',
33451 indicatorpos : 'left',
33453 getAutoCreate : function(){
33456 if (!this.allowBlank) {
33462 cls : 'roo-bootstrap-field-label ' + this.cls,
33467 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33468 tooltip : this.iconTooltip
33477 if(this.indicatorpos == 'right'){
33480 cls : 'roo-bootstrap-field-label ' + this.cls,
33489 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33490 tooltip : this.iconTooltip
33499 initEvents: function()
33501 Roo.bootstrap.Element.superclass.initEvents.call(this);
33503 this.indicator = this.indicatorEl();
33505 if(this.indicator){
33506 this.indicator.removeClass('visible');
33507 this.indicator.addClass('invisible');
33510 Roo.bootstrap.form.FieldLabel.register(this);
33513 indicatorEl : function()
33515 var indicator = this.el.select('i.roo-required-indicator',true).first();
33526 * Mark this field as valid
33528 markValid : function()
33530 if(this.indicator){
33531 this.indicator.removeClass('visible');
33532 this.indicator.addClass('invisible');
33534 if (Roo.bootstrap.version == 3) {
33535 this.el.removeClass(this.invalidClass);
33536 this.el.addClass(this.validClass);
33538 this.el.removeClass('is-invalid');
33539 this.el.addClass('is-valid');
33543 this.fireEvent('valid', this);
33547 * Mark this field as invalid
33548 * @param {String} msg The validation message
33550 markInvalid : function(msg)
33552 if(this.indicator){
33553 this.indicator.removeClass('invisible');
33554 this.indicator.addClass('visible');
33556 if (Roo.bootstrap.version == 3) {
33557 this.el.removeClass(this.validClass);
33558 this.el.addClass(this.invalidClass);
33560 this.el.removeClass('is-valid');
33561 this.el.addClass('is-invalid');
33565 this.fireEvent('invalid', this, msg);
33571 Roo.apply(Roo.bootstrap.form.FieldLabel, {
33576 * register a FieldLabel Group
33577 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
33579 register : function(label)
33581 if(this.groups.hasOwnProperty(label.target)){
33585 this.groups[label.target] = label;
33589 * fetch a FieldLabel Group based on the target
33590 * @param {string} target
33591 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
33593 get: function(target) {
33594 if (typeof(this.groups[target]) == 'undefined') {
33598 return this.groups[target] ;
33607 * page DateSplitField.
33613 * @class Roo.bootstrap.form.DateSplitField
33614 * @extends Roo.bootstrap.Component
33615 * Bootstrap DateSplitField class
33616 * @cfg {string} fieldLabel - the label associated
33617 * @cfg {Number} labelWidth set the width of label (0-12)
33618 * @cfg {String} labelAlign (top|left)
33619 * @cfg {Boolean} dayAllowBlank (true|false) default false
33620 * @cfg {Boolean} monthAllowBlank (true|false) default false
33621 * @cfg {Boolean} yearAllowBlank (true|false) default false
33622 * @cfg {string} dayPlaceholder
33623 * @cfg {string} monthPlaceholder
33624 * @cfg {string} yearPlaceholder
33625 * @cfg {string} dayFormat default 'd'
33626 * @cfg {string} monthFormat default 'm'
33627 * @cfg {string} yearFormat default 'Y'
33628 * @cfg {Number} labellg set the width of label (1-12)
33629 * @cfg {Number} labelmd set the width of label (1-12)
33630 * @cfg {Number} labelsm set the width of label (1-12)
33631 * @cfg {Number} labelxs set the width of label (1-12)
33635 * Create a new DateSplitField
33636 * @param {Object} config The config object
33639 Roo.bootstrap.form.DateSplitField = function(config){
33640 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
33646 * getting the data of years
33647 * @param {Roo.bootstrap.form.DateSplitField} this
33648 * @param {Object} years
33653 * getting the data of days
33654 * @param {Roo.bootstrap.form.DateSplitField} this
33655 * @param {Object} days
33660 * Fires after the field has been marked as invalid.
33661 * @param {Roo.form.Field} this
33662 * @param {String} msg The validation message
33667 * Fires after the field has been validated with no errors.
33668 * @param {Roo.form.Field} this
33674 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
33677 labelAlign : 'top',
33679 dayAllowBlank : false,
33680 monthAllowBlank : false,
33681 yearAllowBlank : false,
33682 dayPlaceholder : '',
33683 monthPlaceholder : '',
33684 yearPlaceholder : '',
33688 isFormField : true,
33694 getAutoCreate : function()
33698 cls : 'row roo-date-split-field-group',
33703 cls : 'form-hidden-field roo-date-split-field-group-value',
33709 var labelCls = 'col-md-12';
33710 var contentCls = 'col-md-4';
33712 if(this.fieldLabel){
33716 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33720 html : this.fieldLabel
33725 if(this.labelAlign == 'left'){
33727 if(this.labelWidth > 12){
33728 label.style = "width: " + this.labelWidth + 'px';
33731 if(this.labelWidth < 13 && this.labelmd == 0){
33732 this.labelmd = this.labelWidth;
33735 if(this.labellg > 0){
33736 labelCls = ' col-lg-' + this.labellg;
33737 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33740 if(this.labelmd > 0){
33741 labelCls = ' col-md-' + this.labelmd;
33742 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33745 if(this.labelsm > 0){
33746 labelCls = ' col-sm-' + this.labelsm;
33747 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33750 if(this.labelxs > 0){
33751 labelCls = ' col-xs-' + this.labelxs;
33752 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33756 label.cls += ' ' + labelCls;
33758 cfg.cn.push(label);
33761 Roo.each(['day', 'month', 'year'], function(t){
33764 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33771 inputEl: function ()
33773 return this.el.select('.roo-date-split-field-group-value', true).first();
33776 onRender : function(ct, position)
33780 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
33782 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33784 this.dayField = new Roo.bootstrap.form.ComboBox({
33785 allowBlank : this.dayAllowBlank,
33786 alwaysQuery : true,
33787 displayField : 'value',
33790 forceSelection : true,
33792 placeholder : this.dayPlaceholder,
33793 selectOnFocus : true,
33794 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33795 triggerAction : 'all',
33797 valueField : 'value',
33798 store : new Roo.data.SimpleStore({
33799 data : (function() {
33801 _this.fireEvent('days', _this, days);
33804 fields : [ 'value' ]
33807 select : function (_self, record, index)
33809 _this.setValue(_this.getValue());
33814 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33816 this.monthField = new Roo.bootstrap.form.MonthField({
33817 after : '<i class=\"fa fa-calendar\"></i>',
33818 allowBlank : this.monthAllowBlank,
33819 placeholder : this.monthPlaceholder,
33822 render : function (_self)
33824 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33825 e.preventDefault();
33829 select : function (_self, oldvalue, newvalue)
33831 _this.setValue(_this.getValue());
33836 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33838 this.yearField = new Roo.bootstrap.form.ComboBox({
33839 allowBlank : this.yearAllowBlank,
33840 alwaysQuery : true,
33841 displayField : 'value',
33844 forceSelection : true,
33846 placeholder : this.yearPlaceholder,
33847 selectOnFocus : true,
33848 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33849 triggerAction : 'all',
33851 valueField : 'value',
33852 store : new Roo.data.SimpleStore({
33853 data : (function() {
33855 _this.fireEvent('years', _this, years);
33858 fields : [ 'value' ]
33861 select : function (_self, record, index)
33863 _this.setValue(_this.getValue());
33868 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33871 setValue : function(v, format)
33873 this.inputEl.dom.value = v;
33875 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33877 var d = Date.parseDate(v, f);
33884 this.setDay(d.format(this.dayFormat));
33885 this.setMonth(d.format(this.monthFormat));
33886 this.setYear(d.format(this.yearFormat));
33893 setDay : function(v)
33895 this.dayField.setValue(v);
33896 this.inputEl.dom.value = this.getValue();
33901 setMonth : function(v)
33903 this.monthField.setValue(v, true);
33904 this.inputEl.dom.value = this.getValue();
33909 setYear : function(v)
33911 this.yearField.setValue(v);
33912 this.inputEl.dom.value = this.getValue();
33917 getDay : function()
33919 return this.dayField.getValue();
33922 getMonth : function()
33924 return this.monthField.getValue();
33927 getYear : function()
33929 return this.yearField.getValue();
33932 getValue : function()
33934 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33936 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33946 this.inputEl.dom.value = '';
33951 validate : function()
33953 var d = this.dayField.validate();
33954 var m = this.monthField.validate();
33955 var y = this.yearField.validate();
33960 (!this.dayAllowBlank && !d) ||
33961 (!this.monthAllowBlank && !m) ||
33962 (!this.yearAllowBlank && !y)
33967 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33976 this.markInvalid();
33981 markValid : function()
33984 var label = this.el.select('label', true).first();
33985 var icon = this.el.select('i.fa-star', true).first();
33991 this.fireEvent('valid', this);
33995 * Mark this field as invalid
33996 * @param {String} msg The validation message
33998 markInvalid : function(msg)
34001 var label = this.el.select('label', true).first();
34002 var icon = this.el.select('i.fa-star', true).first();
34004 if(label && !icon){
34005 this.el.select('.roo-date-split-field-label', true).createChild({
34007 cls : 'text-danger fa fa-lg fa-star',
34008 tooltip : 'This field is required',
34009 style : 'margin-right:5px;'
34013 this.fireEvent('invalid', this, msg);
34016 clearInvalid : function()
34018 var label = this.el.select('label', true).first();
34019 var icon = this.el.select('i.fa-star', true).first();
34025 this.fireEvent('valid', this);
34028 getName: function()
34038 * @class Roo.bootstrap.LayoutMasonry
34039 * @extends Roo.bootstrap.Component
34040 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
34041 * Bootstrap Layout Masonry class
34044 * http://masonry.desandro.com
34046 * The idea is to render all the bricks based on vertical width...
34048 * The original code extends 'outlayer' - we might need to use that....
34051 * Create a new Element
34052 * @param {Object} config The config object
34055 Roo.bootstrap.LayoutMasonry = function(config){
34057 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34061 Roo.bootstrap.LayoutMasonry.register(this);
34067 * Fire after layout the items
34068 * @param {Roo.bootstrap.LayoutMasonry} this
34069 * @param {Roo.EventObject} e
34076 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
34079 * @cfg {Boolean} isLayoutInstant = no animation?
34081 isLayoutInstant : false, // needed?
34084 * @cfg {Number} boxWidth width of the columns
34089 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
34094 * @cfg {Number} padWidth padding below box..
34099 * @cfg {Number} gutter gutter width..
34104 * @cfg {Number} maxCols maximum number of columns
34110 * @cfg {Boolean} isAutoInitial defalut true
34112 isAutoInitial : true,
34117 * @cfg {Boolean} isHorizontal defalut false
34119 isHorizontal : false,
34121 currentSize : null,
34127 bricks: null, //CompositeElement
34131 _isLayoutInited : false,
34133 // isAlternative : false, // only use for vertical layout...
34136 * @cfg {Number} alternativePadWidth padding below box..
34138 alternativePadWidth : 50,
34140 selectedBrick : [],
34142 getAutoCreate : function(){
34144 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34148 cls: 'blog-masonary-wrapper ' + this.cls,
34150 cls : 'mas-boxes masonary'
34157 getChildContainer: function( )
34159 if (this.boxesEl) {
34160 return this.boxesEl;
34163 this.boxesEl = this.el.select('.mas-boxes').first();
34165 return this.boxesEl;
34169 initEvents : function()
34173 if(this.isAutoInitial){
34174 Roo.log('hook children rendered');
34175 this.on('childrenrendered', function() {
34176 Roo.log('children rendered');
34182 initial : function()
34184 this.selectedBrick = [];
34186 this.currentSize = this.el.getBox(true);
34188 Roo.EventManager.onWindowResize(this.resize, this);
34190 if(!this.isAutoInitial){
34198 //this.layout.defer(500,this);
34202 resize : function()
34204 var cs = this.el.getBox(true);
34207 this.currentSize.width == cs.width &&
34208 this.currentSize.x == cs.x &&
34209 this.currentSize.height == cs.height &&
34210 this.currentSize.y == cs.y
34212 Roo.log("no change in with or X or Y");
34216 this.currentSize = cs;
34222 layout : function()
34224 this._resetLayout();
34226 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34228 this.layoutItems( isInstant );
34230 this._isLayoutInited = true;
34232 this.fireEvent('layout', this);
34236 _resetLayout : function()
34238 if(this.isHorizontal){
34239 this.horizontalMeasureColumns();
34243 this.verticalMeasureColumns();
34247 verticalMeasureColumns : function()
34249 this.getContainerWidth();
34251 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34252 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34256 var boxWidth = this.boxWidth + this.padWidth;
34258 if(this.containerWidth < this.boxWidth){
34259 boxWidth = this.containerWidth
34262 var containerWidth = this.containerWidth;
34264 var cols = Math.floor(containerWidth / boxWidth);
34266 this.cols = Math.max( cols, 1 );
34268 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34270 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34272 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34274 this.colWidth = boxWidth + avail - this.padWidth;
34276 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34277 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34280 horizontalMeasureColumns : function()
34282 this.getContainerWidth();
34284 var boxWidth = this.boxWidth;
34286 if(this.containerWidth < boxWidth){
34287 boxWidth = this.containerWidth;
34290 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34292 this.el.setHeight(boxWidth);
34296 getContainerWidth : function()
34298 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34301 layoutItems : function( isInstant )
34303 Roo.log(this.bricks);
34305 var items = Roo.apply([], this.bricks);
34307 if(this.isHorizontal){
34308 this._horizontalLayoutItems( items , isInstant );
34312 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34313 // this._verticalAlternativeLayoutItems( items , isInstant );
34317 this._verticalLayoutItems( items , isInstant );
34321 _verticalLayoutItems : function ( items , isInstant)
34323 if ( !items || !items.length ) {
34328 ['xs', 'xs', 'xs', 'tall'],
34329 ['xs', 'xs', 'tall'],
34330 ['xs', 'xs', 'sm'],
34331 ['xs', 'xs', 'xs'],
34337 ['sm', 'xs', 'xs'],
34341 ['tall', 'xs', 'xs', 'xs'],
34342 ['tall', 'xs', 'xs'],
34354 Roo.each(items, function(item, k){
34356 switch (item.size) {
34357 // these layouts take up a full box,
34368 boxes.push([item]);
34391 var filterPattern = function(box, length)
34399 var pattern = box.slice(0, length);
34403 Roo.each(pattern, function(i){
34404 format.push(i.size);
34407 Roo.each(standard, function(s){
34409 if(String(s) != String(format)){
34418 if(!match && length == 1){
34423 filterPattern(box, length - 1);
34427 queue.push(pattern);
34429 box = box.slice(length, box.length);
34431 filterPattern(box, 4);
34437 Roo.each(boxes, function(box, k){
34443 if(box.length == 1){
34448 filterPattern(box, 4);
34452 this._processVerticalLayoutQueue( queue, isInstant );
34456 // _verticalAlternativeLayoutItems : function( items , isInstant )
34458 // if ( !items || !items.length ) {
34462 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34466 _horizontalLayoutItems : function ( items , isInstant)
34468 if ( !items || !items.length || items.length < 3) {
34474 var eItems = items.slice(0, 3);
34476 items = items.slice(3, items.length);
34479 ['xs', 'xs', 'xs', 'wide'],
34480 ['xs', 'xs', 'wide'],
34481 ['xs', 'xs', 'sm'],
34482 ['xs', 'xs', 'xs'],
34488 ['sm', 'xs', 'xs'],
34492 ['wide', 'xs', 'xs', 'xs'],
34493 ['wide', 'xs', 'xs'],
34506 Roo.each(items, function(item, k){
34508 switch (item.size) {
34519 boxes.push([item]);
34543 var filterPattern = function(box, length)
34551 var pattern = box.slice(0, length);
34555 Roo.each(pattern, function(i){
34556 format.push(i.size);
34559 Roo.each(standard, function(s){
34561 if(String(s) != String(format)){
34570 if(!match && length == 1){
34575 filterPattern(box, length - 1);
34579 queue.push(pattern);
34581 box = box.slice(length, box.length);
34583 filterPattern(box, 4);
34589 Roo.each(boxes, function(box, k){
34595 if(box.length == 1){
34600 filterPattern(box, 4);
34607 var pos = this.el.getBox(true);
34611 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34613 var hit_end = false;
34615 Roo.each(queue, function(box){
34619 Roo.each(box, function(b){
34621 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34631 Roo.each(box, function(b){
34633 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34636 mx = Math.max(mx, b.x);
34640 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34644 Roo.each(box, function(b){
34646 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34660 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34663 /** Sets position of item in DOM
34664 * @param {Element} item
34665 * @param {Number} x - horizontal position
34666 * @param {Number} y - vertical position
34667 * @param {Boolean} isInstant - disables transitions
34669 _processVerticalLayoutQueue : function( queue, isInstant )
34671 var pos = this.el.getBox(true);
34676 for (var i = 0; i < this.cols; i++){
34680 Roo.each(queue, function(box, k){
34682 var col = k % this.cols;
34684 Roo.each(box, function(b,kk){
34686 b.el.position('absolute');
34688 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34689 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34691 if(b.size == 'md-left' || b.size == 'md-right'){
34692 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34693 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34696 b.el.setWidth(width);
34697 b.el.setHeight(height);
34699 b.el.select('iframe',true).setSize(width,height);
34703 for (var i = 0; i < this.cols; i++){
34705 if(maxY[i] < maxY[col]){
34710 col = Math.min(col, i);
34714 x = pos.x + col * (this.colWidth + this.padWidth);
34718 var positions = [];
34720 switch (box.length){
34722 positions = this.getVerticalOneBoxColPositions(x, y, box);
34725 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34728 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34731 positions = this.getVerticalFourBoxColPositions(x, y, box);
34737 Roo.each(box, function(b,kk){
34739 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34741 var sz = b.el.getSize();
34743 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34751 for (var i = 0; i < this.cols; i++){
34752 mY = Math.max(mY, maxY[i]);
34755 this.el.setHeight(mY - pos.y);
34759 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34761 // var pos = this.el.getBox(true);
34764 // var maxX = pos.right;
34766 // var maxHeight = 0;
34768 // Roo.each(items, function(item, k){
34772 // item.el.position('absolute');
34774 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34776 // item.el.setWidth(width);
34778 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34780 // item.el.setHeight(height);
34783 // item.el.setXY([x, y], isInstant ? false : true);
34785 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34788 // y = y + height + this.alternativePadWidth;
34790 // maxHeight = maxHeight + height + this.alternativePadWidth;
34794 // this.el.setHeight(maxHeight);
34798 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34800 var pos = this.el.getBox(true);
34805 var maxX = pos.right;
34807 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34809 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34811 Roo.each(queue, function(box, k){
34813 Roo.each(box, function(b, kk){
34815 b.el.position('absolute');
34817 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34818 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34820 if(b.size == 'md-left' || b.size == 'md-right'){
34821 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34822 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34825 b.el.setWidth(width);
34826 b.el.setHeight(height);
34834 var positions = [];
34836 switch (box.length){
34838 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34841 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34844 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34847 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34853 Roo.each(box, function(b,kk){
34855 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34857 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34865 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34867 Roo.each(eItems, function(b,k){
34869 b.size = (k == 0) ? 'sm' : 'xs';
34870 b.x = (k == 0) ? 2 : 1;
34871 b.y = (k == 0) ? 2 : 1;
34873 b.el.position('absolute');
34875 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34877 b.el.setWidth(width);
34879 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34881 b.el.setHeight(height);
34885 var positions = [];
34888 x : maxX - this.unitWidth * 2 - this.gutter,
34893 x : maxX - this.unitWidth,
34894 y : minY + (this.unitWidth + this.gutter) * 2
34898 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34902 Roo.each(eItems, function(b,k){
34904 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34910 getVerticalOneBoxColPositions : function(x, y, box)
34914 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34916 if(box[0].size == 'md-left'){
34920 if(box[0].size == 'md-right'){
34925 x : x + (this.unitWidth + this.gutter) * rand,
34932 getVerticalTwoBoxColPositions : function(x, y, box)
34936 if(box[0].size == 'xs'){
34940 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34944 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34958 x : x + (this.unitWidth + this.gutter) * 2,
34959 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34966 getVerticalThreeBoxColPositions : function(x, y, box)
34970 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34978 x : x + (this.unitWidth + this.gutter) * 1,
34983 x : x + (this.unitWidth + this.gutter) * 2,
34991 if(box[0].size == 'xs' && box[1].size == 'xs'){
35000 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35004 x : x + (this.unitWidth + this.gutter) * 1,
35018 x : x + (this.unitWidth + this.gutter) * 2,
35023 x : x + (this.unitWidth + this.gutter) * 2,
35024 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35031 getVerticalFourBoxColPositions : function(x, y, box)
35035 if(box[0].size == 'xs'){
35044 y : y + (this.unitHeight + this.gutter) * 1
35049 y : y + (this.unitHeight + this.gutter) * 2
35053 x : x + (this.unitWidth + this.gutter) * 1,
35067 x : x + (this.unitWidth + this.gutter) * 2,
35072 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35073 y : y + (this.unitHeight + this.gutter) * 1
35077 x : x + (this.unitWidth + this.gutter) * 2,
35078 y : y + (this.unitWidth + this.gutter) * 2
35085 getHorizontalOneBoxColPositions : function(maxX, minY, box)
35089 if(box[0].size == 'md-left'){
35091 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35098 if(box[0].size == 'md-right'){
35100 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35101 y : minY + (this.unitWidth + this.gutter) * 1
35107 var rand = Math.floor(Math.random() * (4 - box[0].y));
35110 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35111 y : minY + (this.unitWidth + this.gutter) * rand
35118 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35122 if(box[0].size == 'xs'){
35125 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35130 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35131 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35139 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35144 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35145 y : minY + (this.unitWidth + this.gutter) * 2
35152 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35156 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35159 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35164 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35165 y : minY + (this.unitWidth + this.gutter) * 1
35169 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35170 y : minY + (this.unitWidth + this.gutter) * 2
35177 if(box[0].size == 'xs' && box[1].size == 'xs'){
35180 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35185 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35190 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35191 y : minY + (this.unitWidth + this.gutter) * 1
35199 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35204 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35205 y : minY + (this.unitWidth + this.gutter) * 2
35209 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35210 y : minY + (this.unitWidth + this.gutter) * 2
35217 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35221 if(box[0].size == 'xs'){
35224 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35229 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35234 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),
35239 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35240 y : minY + (this.unitWidth + this.gutter) * 1
35248 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35253 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35254 y : minY + (this.unitWidth + this.gutter) * 2
35258 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35259 y : minY + (this.unitWidth + this.gutter) * 2
35263 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),
35264 y : minY + (this.unitWidth + this.gutter) * 2
35272 * remove a Masonry Brick
35273 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35275 removeBrick : function(brick_id)
35281 for (var i = 0; i<this.bricks.length; i++) {
35282 if (this.bricks[i].id == brick_id) {
35283 this.bricks.splice(i,1);
35284 this.el.dom.removeChild(Roo.get(brick_id).dom);
35291 * adds a Masonry Brick
35292 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35294 addBrick : function(cfg)
35296 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35297 //this.register(cn);
35298 cn.parentId = this.id;
35299 cn.render(this.el);
35304 * register a Masonry Brick
35305 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35308 register : function(brick)
35310 this.bricks.push(brick);
35311 brick.masonryId = this.id;
35315 * clear all the Masonry Brick
35317 clearAll : function()
35320 //this.getChildContainer().dom.innerHTML = "";
35321 this.el.dom.innerHTML = '';
35324 getSelected : function()
35326 if (!this.selectedBrick) {
35330 return this.selectedBrick;
35334 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35338 * register a Masonry Layout
35339 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35342 register : function(layout)
35344 this.groups[layout.id] = layout;
35347 * fetch a Masonry Layout based on the masonry layout ID
35348 * @param {string} the masonry layout to add
35349 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35352 get: function(layout_id) {
35353 if (typeof(this.groups[layout_id]) == 'undefined') {
35356 return this.groups[layout_id] ;
35368 * http://masonry.desandro.com
35370 * The idea is to render all the bricks based on vertical width...
35372 * The original code extends 'outlayer' - we might need to use that....
35378 * @class Roo.bootstrap.LayoutMasonryAuto
35379 * @extends Roo.bootstrap.Component
35380 * Bootstrap Layout Masonry class
35383 * Create a new Element
35384 * @param {Object} config The config object
35387 Roo.bootstrap.LayoutMasonryAuto = function(config){
35388 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35391 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35394 * @cfg {Boolean} isFitWidth - resize the width..
35396 isFitWidth : false, // options..
35398 * @cfg {Boolean} isOriginLeft = left align?
35400 isOriginLeft : true,
35402 * @cfg {Boolean} isOriginTop = top align?
35404 isOriginTop : false,
35406 * @cfg {Boolean} isLayoutInstant = no animation?
35408 isLayoutInstant : false, // needed?
35410 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35412 isResizingContainer : true,
35414 * @cfg {Number} columnWidth width of the columns
35420 * @cfg {Number} maxCols maximum number of columns
35425 * @cfg {Number} padHeight padding below box..
35431 * @cfg {Boolean} isAutoInitial defalut true
35434 isAutoInitial : true,
35440 initialColumnWidth : 0,
35441 currentSize : null,
35443 colYs : null, // array.
35450 bricks: null, //CompositeElement
35451 cols : 0, // array?
35452 // element : null, // wrapped now this.el
35453 _isLayoutInited : null,
35456 getAutoCreate : function(){
35460 cls: 'blog-masonary-wrapper ' + this.cls,
35462 cls : 'mas-boxes masonary'
35469 getChildContainer: function( )
35471 if (this.boxesEl) {
35472 return this.boxesEl;
35475 this.boxesEl = this.el.select('.mas-boxes').first();
35477 return this.boxesEl;
35481 initEvents : function()
35485 if(this.isAutoInitial){
35486 Roo.log('hook children rendered');
35487 this.on('childrenrendered', function() {
35488 Roo.log('children rendered');
35495 initial : function()
35497 this.reloadItems();
35499 this.currentSize = this.el.getBox(true);
35501 /// was window resize... - let's see if this works..
35502 Roo.EventManager.onWindowResize(this.resize, this);
35504 if(!this.isAutoInitial){
35509 this.layout.defer(500,this);
35512 reloadItems: function()
35514 this.bricks = this.el.select('.masonry-brick', true);
35516 this.bricks.each(function(b) {
35517 //Roo.log(b.getSize());
35518 if (!b.attr('originalwidth')) {
35519 b.attr('originalwidth', b.getSize().width);
35524 Roo.log(this.bricks.elements.length);
35527 resize : function()
35530 var cs = this.el.getBox(true);
35532 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35533 Roo.log("no change in with or X");
35536 this.currentSize = cs;
35540 layout : function()
35543 this._resetLayout();
35544 //this._manageStamps();
35546 // don't animate first layout
35547 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35548 this.layoutItems( isInstant );
35550 // flag for initalized
35551 this._isLayoutInited = true;
35554 layoutItems : function( isInstant )
35556 //var items = this._getItemsForLayout( this.items );
35557 // original code supports filtering layout items.. we just ignore it..
35559 this._layoutItems( this.bricks , isInstant );
35561 this._postLayout();
35563 _layoutItems : function ( items , isInstant)
35565 //this.fireEvent( 'layout', this, items );
35568 if ( !items || !items.elements.length ) {
35569 // no items, emit event with empty array
35574 items.each(function(item) {
35575 Roo.log("layout item");
35577 // get x/y object from method
35578 var position = this._getItemLayoutPosition( item );
35580 position.item = item;
35581 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35582 queue.push( position );
35585 this._processLayoutQueue( queue );
35587 /** Sets position of item in DOM
35588 * @param {Element} item
35589 * @param {Number} x - horizontal position
35590 * @param {Number} y - vertical position
35591 * @param {Boolean} isInstant - disables transitions
35593 _processLayoutQueue : function( queue )
35595 for ( var i=0, len = queue.length; i < len; i++ ) {
35596 var obj = queue[i];
35597 obj.item.position('absolute');
35598 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35604 * Any logic you want to do after each layout,
35605 * i.e. size the container
35607 _postLayout : function()
35609 this.resizeContainer();
35612 resizeContainer : function()
35614 if ( !this.isResizingContainer ) {
35617 var size = this._getContainerSize();
35619 this.el.setSize(size.width,size.height);
35620 this.boxesEl.setSize(size.width,size.height);
35626 _resetLayout : function()
35628 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35629 this.colWidth = this.el.getWidth();
35630 //this.gutter = this.el.getWidth();
35632 this.measureColumns();
35638 this.colYs.push( 0 );
35644 measureColumns : function()
35646 this.getContainerWidth();
35647 // if columnWidth is 0, default to outerWidth of first item
35648 if ( !this.columnWidth ) {
35649 var firstItem = this.bricks.first();
35650 Roo.log(firstItem);
35651 this.columnWidth = this.containerWidth;
35652 if (firstItem && firstItem.attr('originalwidth') ) {
35653 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35655 // columnWidth fall back to item of first element
35656 Roo.log("set column width?");
35657 this.initialColumnWidth = this.columnWidth ;
35659 // if first elem has no width, default to size of container
35664 if (this.initialColumnWidth) {
35665 this.columnWidth = this.initialColumnWidth;
35670 // column width is fixed at the top - however if container width get's smaller we should
35673 // this bit calcs how man columns..
35675 var columnWidth = this.columnWidth += this.gutter;
35677 // calculate columns
35678 var containerWidth = this.containerWidth + this.gutter;
35680 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35681 // fix rounding errors, typically with gutters
35682 var excess = columnWidth - containerWidth % columnWidth;
35685 // if overshoot is less than a pixel, round up, otherwise floor it
35686 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35687 cols = Math[ mathMethod ]( cols );
35688 this.cols = Math.max( cols, 1 );
35689 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35691 // padding positioning..
35692 var totalColWidth = this.cols * this.columnWidth;
35693 var padavail = this.containerWidth - totalColWidth;
35694 // so for 2 columns - we need 3 'pads'
35696 var padNeeded = (1+this.cols) * this.padWidth;
35698 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35700 this.columnWidth += padExtra
35701 //this.padWidth = Math.floor(padavail / ( this.cols));
35703 // adjust colum width so that padding is fixed??
35705 // we have 3 columns ... total = width * 3
35706 // we have X left over... that should be used by
35708 //if (this.expandC) {
35716 getContainerWidth : function()
35718 /* // container is parent if fit width
35719 var container = this.isFitWidth ? this.element.parentNode : this.element;
35720 // check that this.size and size are there
35721 // IE8 triggers resize on body size change, so they might not be
35723 var size = getSize( container ); //FIXME
35724 this.containerWidth = size && size.innerWidth; //FIXME
35727 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35731 _getItemLayoutPosition : function( item ) // what is item?
35733 // we resize the item to our columnWidth..
35735 item.setWidth(this.columnWidth);
35736 item.autoBoxAdjust = false;
35738 var sz = item.getSize();
35740 // how many columns does this brick span
35741 var remainder = this.containerWidth % this.columnWidth;
35743 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35744 // round if off by 1 pixel, otherwise use ceil
35745 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35746 colSpan = Math.min( colSpan, this.cols );
35748 // normally this should be '1' as we dont' currently allow multi width columns..
35750 var colGroup = this._getColGroup( colSpan );
35751 // get the minimum Y value from the columns
35752 var minimumY = Math.min.apply( Math, colGroup );
35753 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35755 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35757 // position the brick
35759 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35760 y: this.currentSize.y + minimumY + this.padHeight
35764 // apply setHeight to necessary columns
35765 var setHeight = minimumY + sz.height + this.padHeight;
35766 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35768 var setSpan = this.cols + 1 - colGroup.length;
35769 for ( var i = 0; i < setSpan; i++ ) {
35770 this.colYs[ shortColIndex + i ] = setHeight ;
35777 * @param {Number} colSpan - number of columns the element spans
35778 * @returns {Array} colGroup
35780 _getColGroup : function( colSpan )
35782 if ( colSpan < 2 ) {
35783 // if brick spans only one column, use all the column Ys
35788 // how many different places could this brick fit horizontally
35789 var groupCount = this.cols + 1 - colSpan;
35790 // for each group potential horizontal position
35791 for ( var i = 0; i < groupCount; i++ ) {
35792 // make an array of colY values for that one group
35793 var groupColYs = this.colYs.slice( i, i + colSpan );
35794 // and get the max value of the array
35795 colGroup[i] = Math.max.apply( Math, groupColYs );
35800 _manageStamp : function( stamp )
35802 var stampSize = stamp.getSize();
35803 var offset = stamp.getBox();
35804 // get the columns that this stamp affects
35805 var firstX = this.isOriginLeft ? offset.x : offset.right;
35806 var lastX = firstX + stampSize.width;
35807 var firstCol = Math.floor( firstX / this.columnWidth );
35808 firstCol = Math.max( 0, firstCol );
35810 var lastCol = Math.floor( lastX / this.columnWidth );
35811 // lastCol should not go over if multiple of columnWidth #425
35812 lastCol -= lastX % this.columnWidth ? 0 : 1;
35813 lastCol = Math.min( this.cols - 1, lastCol );
35815 // set colYs to bottom of the stamp
35816 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35819 for ( var i = firstCol; i <= lastCol; i++ ) {
35820 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35825 _getContainerSize : function()
35827 this.maxY = Math.max.apply( Math, this.colYs );
35832 if ( this.isFitWidth ) {
35833 size.width = this._getContainerFitWidth();
35839 _getContainerFitWidth : function()
35841 var unusedCols = 0;
35842 // count unused columns
35845 if ( this.colYs[i] !== 0 ) {
35850 // fit container to columns that have been used
35851 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35854 needsResizeLayout : function()
35856 var previousWidth = this.containerWidth;
35857 this.getContainerWidth();
35858 return previousWidth !== this.containerWidth;
35873 * @class Roo.bootstrap.MasonryBrick
35874 * @extends Roo.bootstrap.Component
35875 * Bootstrap MasonryBrick class
35878 * Create a new MasonryBrick
35879 * @param {Object} config The config object
35882 Roo.bootstrap.MasonryBrick = function(config){
35884 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35886 Roo.bootstrap.MasonryBrick.register(this);
35892 * When a MasonryBrick is clcik
35893 * @param {Roo.bootstrap.MasonryBrick} this
35894 * @param {Roo.EventObject} e
35900 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35903 * @cfg {String} title
35907 * @cfg {String} html
35911 * @cfg {String} bgimage
35915 * @cfg {String} videourl
35919 * @cfg {String} cls
35923 * @cfg {String} href
35927 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35932 * @cfg {String} placetitle (center|bottom)
35937 * @cfg {Boolean} isFitContainer defalut true
35939 isFitContainer : true,
35942 * @cfg {Boolean} preventDefault defalut false
35944 preventDefault : false,
35947 * @cfg {Boolean} inverse defalut false
35949 maskInverse : false,
35951 getAutoCreate : function()
35953 if(!this.isFitContainer){
35954 return this.getSplitAutoCreate();
35957 var cls = 'masonry-brick masonry-brick-full';
35959 if(this.href.length){
35960 cls += ' masonry-brick-link';
35963 if(this.bgimage.length){
35964 cls += ' masonry-brick-image';
35967 if(this.maskInverse){
35968 cls += ' mask-inverse';
35971 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35972 cls += ' enable-mask';
35976 cls += ' masonry-' + this.size + '-brick';
35979 if(this.placetitle.length){
35981 switch (this.placetitle) {
35983 cls += ' masonry-center-title';
35986 cls += ' masonry-bottom-title';
35993 if(!this.html.length && !this.bgimage.length){
35994 cls += ' masonry-center-title';
35997 if(!this.html.length && this.bgimage.length){
35998 cls += ' masonry-bottom-title';
36003 cls += ' ' + this.cls;
36007 tag: (this.href.length) ? 'a' : 'div',
36012 cls: 'masonry-brick-mask'
36016 cls: 'masonry-brick-paragraph',
36022 if(this.href.length){
36023 cfg.href = this.href;
36026 var cn = cfg.cn[1].cn;
36028 if(this.title.length){
36031 cls: 'masonry-brick-title',
36036 if(this.html.length){
36039 cls: 'masonry-brick-text',
36044 if (!this.title.length && !this.html.length) {
36045 cfg.cn[1].cls += ' hide';
36048 if(this.bgimage.length){
36051 cls: 'masonry-brick-image-view',
36056 if(this.videourl.length){
36057 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36058 // youtube support only?
36061 cls: 'masonry-brick-image-view',
36064 allowfullscreen : true
36072 getSplitAutoCreate : function()
36074 var cls = 'masonry-brick masonry-brick-split';
36076 if(this.href.length){
36077 cls += ' masonry-brick-link';
36080 if(this.bgimage.length){
36081 cls += ' masonry-brick-image';
36085 cls += ' masonry-' + this.size + '-brick';
36088 switch (this.placetitle) {
36090 cls += ' masonry-center-title';
36093 cls += ' masonry-bottom-title';
36096 if(!this.bgimage.length){
36097 cls += ' masonry-center-title';
36100 if(this.bgimage.length){
36101 cls += ' masonry-bottom-title';
36107 cls += ' ' + this.cls;
36111 tag: (this.href.length) ? 'a' : 'div',
36116 cls: 'masonry-brick-split-head',
36120 cls: 'masonry-brick-paragraph',
36127 cls: 'masonry-brick-split-body',
36133 if(this.href.length){
36134 cfg.href = this.href;
36137 if(this.title.length){
36138 cfg.cn[0].cn[0].cn.push({
36140 cls: 'masonry-brick-title',
36145 if(this.html.length){
36146 cfg.cn[1].cn.push({
36148 cls: 'masonry-brick-text',
36153 if(this.bgimage.length){
36154 cfg.cn[0].cn.push({
36156 cls: 'masonry-brick-image-view',
36161 if(this.videourl.length){
36162 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36163 // youtube support only?
36164 cfg.cn[0].cn.cn.push({
36166 cls: 'masonry-brick-image-view',
36169 allowfullscreen : true
36176 initEvents: function()
36178 switch (this.size) {
36211 this.el.on('touchstart', this.onTouchStart, this);
36212 this.el.on('touchmove', this.onTouchMove, this);
36213 this.el.on('touchend', this.onTouchEnd, this);
36214 this.el.on('contextmenu', this.onContextMenu, this);
36216 this.el.on('mouseenter' ,this.enter, this);
36217 this.el.on('mouseleave', this.leave, this);
36218 this.el.on('click', this.onClick, this);
36221 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36222 this.parent().bricks.push(this);
36227 onClick: function(e, el)
36229 var time = this.endTimer - this.startTimer;
36230 // Roo.log(e.preventDefault());
36233 e.preventDefault();
36238 if(!this.preventDefault){
36242 e.preventDefault();
36244 if (this.activeClass != '') {
36245 this.selectBrick();
36248 this.fireEvent('click', this, e);
36251 enter: function(e, el)
36253 e.preventDefault();
36255 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36259 if(this.bgimage.length && this.html.length){
36260 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36264 leave: function(e, el)
36266 e.preventDefault();
36268 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36272 if(this.bgimage.length && this.html.length){
36273 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36277 onTouchStart: function(e, el)
36279 // e.preventDefault();
36281 this.touchmoved = false;
36283 if(!this.isFitContainer){
36287 if(!this.bgimage.length || !this.html.length){
36291 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36293 this.timer = new Date().getTime();
36297 onTouchMove: function(e, el)
36299 this.touchmoved = true;
36302 onContextMenu : function(e,el)
36304 e.preventDefault();
36305 e.stopPropagation();
36309 onTouchEnd: function(e, el)
36311 // e.preventDefault();
36313 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36320 if(!this.bgimage.length || !this.html.length){
36322 if(this.href.length){
36323 window.location.href = this.href;
36329 if(!this.isFitContainer){
36333 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36335 window.location.href = this.href;
36338 //selection on single brick only
36339 selectBrick : function() {
36341 if (!this.parentId) {
36345 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36346 var index = m.selectedBrick.indexOf(this.id);
36349 m.selectedBrick.splice(index,1);
36350 this.el.removeClass(this.activeClass);
36354 for(var i = 0; i < m.selectedBrick.length; i++) {
36355 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36356 b.el.removeClass(b.activeClass);
36359 m.selectedBrick = [];
36361 m.selectedBrick.push(this.id);
36362 this.el.addClass(this.activeClass);
36366 isSelected : function(){
36367 return this.el.hasClass(this.activeClass);
36372 Roo.apply(Roo.bootstrap.MasonryBrick, {
36375 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36377 * register a Masonry Brick
36378 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36381 register : function(brick)
36383 //this.groups[brick.id] = brick;
36384 this.groups.add(brick.id, brick);
36387 * fetch a masonry brick based on the masonry brick ID
36388 * @param {string} the masonry brick to add
36389 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36392 get: function(brick_id)
36394 // if (typeof(this.groups[brick_id]) == 'undefined') {
36397 // return this.groups[brick_id] ;
36399 if(this.groups.key(brick_id)) {
36400 return this.groups.key(brick_id);
36418 * @class Roo.bootstrap.Brick
36419 * @extends Roo.bootstrap.Component
36420 * Bootstrap Brick class
36423 * Create a new Brick
36424 * @param {Object} config The config object
36427 Roo.bootstrap.Brick = function(config){
36428 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36434 * When a Brick is click
36435 * @param {Roo.bootstrap.Brick} this
36436 * @param {Roo.EventObject} e
36442 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36445 * @cfg {String} title
36449 * @cfg {String} html
36453 * @cfg {String} bgimage
36457 * @cfg {String} cls
36461 * @cfg {String} href
36465 * @cfg {String} video
36469 * @cfg {Boolean} square
36473 getAutoCreate : function()
36475 var cls = 'roo-brick';
36477 if(this.href.length){
36478 cls += ' roo-brick-link';
36481 if(this.bgimage.length){
36482 cls += ' roo-brick-image';
36485 if(!this.html.length && !this.bgimage.length){
36486 cls += ' roo-brick-center-title';
36489 if(!this.html.length && this.bgimage.length){
36490 cls += ' roo-brick-bottom-title';
36494 cls += ' ' + this.cls;
36498 tag: (this.href.length) ? 'a' : 'div',
36503 cls: 'roo-brick-paragraph',
36509 if(this.href.length){
36510 cfg.href = this.href;
36513 var cn = cfg.cn[0].cn;
36515 if(this.title.length){
36518 cls: 'roo-brick-title',
36523 if(this.html.length){
36526 cls: 'roo-brick-text',
36533 if(this.bgimage.length){
36536 cls: 'roo-brick-image-view',
36544 initEvents: function()
36546 if(this.title.length || this.html.length){
36547 this.el.on('mouseenter' ,this.enter, this);
36548 this.el.on('mouseleave', this.leave, this);
36551 Roo.EventManager.onWindowResize(this.resize, this);
36553 if(this.bgimage.length){
36554 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36555 this.imageEl.on('load', this.onImageLoad, this);
36562 onImageLoad : function()
36567 resize : function()
36569 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36571 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36573 if(this.bgimage.length){
36574 var image = this.el.select('.roo-brick-image-view', true).first();
36576 image.setWidth(paragraph.getWidth());
36579 image.setHeight(paragraph.getWidth());
36582 this.el.setHeight(image.getHeight());
36583 paragraph.setHeight(image.getHeight());
36589 enter: function(e, el)
36591 e.preventDefault();
36593 if(this.bgimage.length){
36594 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36595 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36599 leave: function(e, el)
36601 e.preventDefault();
36603 if(this.bgimage.length){
36604 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36605 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36620 * @class Roo.bootstrap.form.NumberField
36621 * @extends Roo.bootstrap.form.Input
36622 * Bootstrap NumberField class
36628 * Create a new NumberField
36629 * @param {Object} config The config object
36632 Roo.bootstrap.form.NumberField = function(config){
36633 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
36636 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
36639 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36641 allowDecimals : true,
36643 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36645 decimalSeparator : ".",
36647 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36649 decimalPrecision : 2,
36651 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36653 allowNegative : true,
36656 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36660 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36662 minValue : Number.NEGATIVE_INFINITY,
36664 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36666 maxValue : Number.MAX_VALUE,
36668 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36670 minText : "The minimum value for this field is {0}",
36672 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36674 maxText : "The maximum value for this field is {0}",
36676 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36677 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36679 nanText : "{0} is not a valid number",
36681 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36683 thousandsDelimiter : false,
36685 * @cfg {String} valueAlign alignment of value
36687 valueAlign : "left",
36689 getAutoCreate : function()
36691 var hiddenInput = {
36695 cls: 'hidden-number-input'
36699 hiddenInput.name = this.name;
36704 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
36706 this.name = hiddenInput.name;
36708 if(cfg.cn.length > 0) {
36709 cfg.cn.push(hiddenInput);
36716 initEvents : function()
36718 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
36720 var allowed = "0123456789";
36722 if(this.allowDecimals){
36723 allowed += this.decimalSeparator;
36726 if(this.allowNegative){
36730 if(this.thousandsDelimiter) {
36734 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36736 var keyPress = function(e){
36738 var k = e.getKey();
36740 var c = e.getCharCode();
36743 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36744 allowed.indexOf(String.fromCharCode(c)) === -1
36750 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36754 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36759 this.el.on("keypress", keyPress, this);
36762 validateValue : function(value)
36765 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
36769 var num = this.parseValue(value);
36772 this.markInvalid(String.format(this.nanText, value));
36776 if(num < this.minValue){
36777 this.markInvalid(String.format(this.minText, this.minValue));
36781 if(num > this.maxValue){
36782 this.markInvalid(String.format(this.maxText, this.maxValue));
36789 getValue : function()
36791 var v = this.hiddenEl().getValue();
36793 return this.fixPrecision(this.parseValue(v));
36796 parseValue : function(value)
36798 if(this.thousandsDelimiter) {
36800 r = new RegExp(",", "g");
36801 value = value.replace(r, "");
36804 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36805 return isNaN(value) ? '' : value;
36808 fixPrecision : function(value)
36810 if(this.thousandsDelimiter) {
36812 r = new RegExp(",", "g");
36813 value = value.replace(r, "");
36816 var nan = isNaN(value);
36818 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36819 return nan ? '' : value;
36821 return parseFloat(value).toFixed(this.decimalPrecision);
36824 setValue : function(v)
36826 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36832 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36834 this.inputEl().dom.value = (v == '') ? '' :
36835 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36837 if(!this.allowZero && v === '0') {
36838 this.hiddenEl().dom.value = '';
36839 this.inputEl().dom.value = '';
36846 decimalPrecisionFcn : function(v)
36848 return Math.floor(v);
36851 beforeBlur : function()
36853 var v = this.parseValue(this.getRawValue());
36855 if(v || v === 0 || v === ''){
36860 hiddenEl : function()
36862 return this.el.select('input.hidden-number-input',true).first();
36874 * @class Roo.bootstrap.DocumentSlider
36875 * @extends Roo.bootstrap.Component
36876 * Bootstrap DocumentSlider class
36879 * Create a new DocumentViewer
36880 * @param {Object} config The config object
36883 Roo.bootstrap.DocumentSlider = function(config){
36884 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36891 * Fire after initEvent
36892 * @param {Roo.bootstrap.DocumentSlider} this
36897 * Fire after update
36898 * @param {Roo.bootstrap.DocumentSlider} this
36904 * @param {Roo.bootstrap.DocumentSlider} this
36910 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36916 getAutoCreate : function()
36920 cls : 'roo-document-slider',
36924 cls : 'roo-document-slider-header',
36928 cls : 'roo-document-slider-header-title'
36934 cls : 'roo-document-slider-body',
36938 cls : 'roo-document-slider-prev',
36942 cls : 'fa fa-chevron-left'
36948 cls : 'roo-document-slider-thumb',
36952 cls : 'roo-document-slider-image'
36958 cls : 'roo-document-slider-next',
36962 cls : 'fa fa-chevron-right'
36974 initEvents : function()
36976 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36977 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36979 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36980 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36982 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36983 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36985 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36986 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36988 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36989 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36991 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36992 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36994 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36995 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36997 this.thumbEl.on('click', this.onClick, this);
36999 this.prevIndicator.on('click', this.prev, this);
37001 this.nextIndicator.on('click', this.next, this);
37005 initial : function()
37007 if(this.files.length){
37008 this.indicator = 1;
37012 this.fireEvent('initial', this);
37015 update : function()
37017 this.imageEl.attr('src', this.files[this.indicator - 1]);
37019 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37021 this.prevIndicator.show();
37023 if(this.indicator == 1){
37024 this.prevIndicator.hide();
37027 this.nextIndicator.show();
37029 if(this.indicator == this.files.length){
37030 this.nextIndicator.hide();
37033 this.thumbEl.scrollTo('top');
37035 this.fireEvent('update', this);
37038 onClick : function(e)
37040 e.preventDefault();
37042 this.fireEvent('click', this);
37047 e.preventDefault();
37049 this.indicator = Math.max(1, this.indicator - 1);
37056 e.preventDefault();
37058 this.indicator = Math.min(this.files.length, this.indicator + 1);
37072 * @class Roo.bootstrap.form.RadioSet
37073 * @extends Roo.bootstrap.form.Input
37074 * @children Roo.bootstrap.form.Radio
37075 * Bootstrap RadioSet class
37076 * @cfg {String} indicatorpos (left|right) default left
37077 * @cfg {Boolean} inline (true|false) inline the element (default true)
37078 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37080 * Create a new RadioSet
37081 * @param {Object} config The config object
37084 Roo.bootstrap.form.RadioSet = function(config){
37086 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
37090 Roo.bootstrap.form.RadioSet.register(this);
37095 * Fires when the element is checked or unchecked.
37096 * @param {Roo.bootstrap.form.RadioSet} this This radio
37097 * @param {Roo.bootstrap.form.Radio} item The checked item
37102 * Fires when the element is click.
37103 * @param {Roo.bootstrap.form.RadioSet} this This radio set
37104 * @param {Roo.bootstrap.form.Radio} item The checked item
37105 * @param {Roo.EventObject} e The event object
37112 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
37120 indicatorpos : 'left',
37122 getAutoCreate : function()
37126 cls : 'roo-radio-set-label',
37130 html : this.fieldLabel
37134 if (Roo.bootstrap.version == 3) {
37137 if(this.indicatorpos == 'left'){
37140 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37141 tooltip : 'This field is required'
37146 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37147 tooltip : 'This field is required'
37153 cls : 'roo-radio-set-items'
37156 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37158 if (align === 'left' && this.fieldLabel.length) {
37161 cls : "roo-radio-set-right",
37167 if(this.labelWidth > 12){
37168 label.style = "width: " + this.labelWidth + 'px';
37171 if(this.labelWidth < 13 && this.labelmd == 0){
37172 this.labelmd = this.labelWidth;
37175 if(this.labellg > 0){
37176 label.cls += ' col-lg-' + this.labellg;
37177 items.cls += ' col-lg-' + (12 - this.labellg);
37180 if(this.labelmd > 0){
37181 label.cls += ' col-md-' + this.labelmd;
37182 items.cls += ' col-md-' + (12 - this.labelmd);
37185 if(this.labelsm > 0){
37186 label.cls += ' col-sm-' + this.labelsm;
37187 items.cls += ' col-sm-' + (12 - this.labelsm);
37190 if(this.labelxs > 0){
37191 label.cls += ' col-xs-' + this.labelxs;
37192 items.cls += ' col-xs-' + (12 - this.labelxs);
37198 cls : 'roo-radio-set',
37202 cls : 'roo-radio-set-input',
37205 value : this.value ? this.value : ''
37212 if(this.weight.length){
37213 cfg.cls += ' roo-radio-' + this.weight;
37217 cfg.cls += ' roo-radio-set-inline';
37221 ['xs','sm','md','lg'].map(function(size){
37222 if (settings[size]) {
37223 cfg.cls += ' col-' + size + '-' + settings[size];
37231 initEvents : function()
37233 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37234 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37236 if(!this.fieldLabel.length){
37237 this.labelEl.hide();
37240 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37241 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37243 this.indicator = this.indicatorEl();
37245 if(this.indicator){
37246 this.indicator.addClass('invisible');
37249 this.originalValue = this.getValue();
37253 inputEl: function ()
37255 return this.el.select('.roo-radio-set-input', true).first();
37258 getChildContainer : function()
37260 return this.itemsEl;
37263 register : function(item)
37265 this.radioes.push(item);
37269 validate : function()
37271 if(this.getVisibilityEl().hasClass('hidden')){
37277 Roo.each(this.radioes, function(i){
37286 if(this.allowBlank) {
37290 if(this.disabled || valid){
37295 this.markInvalid();
37300 markValid : function()
37302 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37303 this.indicatorEl().removeClass('visible');
37304 this.indicatorEl().addClass('invisible');
37308 if (Roo.bootstrap.version == 3) {
37309 this.el.removeClass([this.invalidClass, this.validClass]);
37310 this.el.addClass(this.validClass);
37312 this.el.removeClass(['is-invalid','is-valid']);
37313 this.el.addClass(['is-valid']);
37315 this.fireEvent('valid', this);
37318 markInvalid : function(msg)
37320 if(this.allowBlank || this.disabled){
37324 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37325 this.indicatorEl().removeClass('invisible');
37326 this.indicatorEl().addClass('visible');
37328 if (Roo.bootstrap.version == 3) {
37329 this.el.removeClass([this.invalidClass, this.validClass]);
37330 this.el.addClass(this.invalidClass);
37332 this.el.removeClass(['is-invalid','is-valid']);
37333 this.el.addClass(['is-invalid']);
37336 this.fireEvent('invalid', this, msg);
37340 setValue : function(v, suppressEvent)
37342 if(this.value === v){
37349 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37352 Roo.each(this.radioes, function(i){
37354 i.el.removeClass('checked');
37357 Roo.each(this.radioes, function(i){
37359 if(i.value === v || i.value.toString() === v.toString()){
37361 i.el.addClass('checked');
37363 if(suppressEvent !== true){
37364 this.fireEvent('check', this, i);
37375 clearInvalid : function(){
37377 if(!this.el || this.preventMark){
37381 this.el.removeClass([this.invalidClass]);
37383 this.fireEvent('valid', this);
37388 Roo.apply(Roo.bootstrap.form.RadioSet, {
37392 register : function(set)
37394 this.groups[set.name] = set;
37397 get: function(name)
37399 if (typeof(this.groups[name]) == 'undefined') {
37403 return this.groups[name] ;
37409 * Ext JS Library 1.1.1
37410 * Copyright(c) 2006-2007, Ext JS, LLC.
37412 * Originally Released Under LGPL - original licence link has changed is not relivant.
37415 * <script type="text/javascript">
37420 * @class Roo.bootstrap.SplitBar
37421 * @extends Roo.util.Observable
37422 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37426 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37427 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37428 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37429 split.minSize = 100;
37430 split.maxSize = 600;
37431 split.animate = true;
37432 split.on('moved', splitterMoved);
37435 * Create a new SplitBar
37436 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37437 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37438 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37439 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37440 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37441 position of the SplitBar).
37443 Roo.bootstrap.SplitBar = function(cfg){
37448 // dragElement : elm
37449 // resizingElement: el,
37451 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37452 // placement : Roo.bootstrap.SplitBar.LEFT ,
37453 // existingProxy ???
37456 this.el = Roo.get(cfg.dragElement, true);
37457 this.el.dom.unselectable = "on";
37459 this.resizingEl = Roo.get(cfg.resizingElement, true);
37463 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37464 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37467 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37470 * The minimum size of the resizing element. (Defaults to 0)
37476 * The maximum size of the resizing element. (Defaults to 2000)
37479 this.maxSize = 2000;
37482 * Whether to animate the transition to the new size
37485 this.animate = false;
37488 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37491 this.useShim = false;
37496 if(!cfg.existingProxy){
37498 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37500 this.proxy = Roo.get(cfg.existingProxy).dom;
37503 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37506 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37509 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37512 this.dragSpecs = {};
37515 * @private The adapter to use to positon and resize elements
37517 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37518 this.adapter.init(this);
37520 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37522 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37523 this.el.addClass("roo-splitbar-h");
37526 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37527 this.el.addClass("roo-splitbar-v");
37533 * Fires when the splitter is moved (alias for {@link #event-moved})
37534 * @param {Roo.bootstrap.SplitBar} this
37535 * @param {Number} newSize the new width or height
37540 * Fires when the splitter is moved
37541 * @param {Roo.bootstrap.SplitBar} this
37542 * @param {Number} newSize the new width or height
37546 * @event beforeresize
37547 * Fires before the splitter is dragged
37548 * @param {Roo.bootstrap.SplitBar} this
37550 "beforeresize" : true,
37552 "beforeapply" : true
37555 Roo.util.Observable.call(this);
37558 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37559 onStartProxyDrag : function(x, y){
37560 this.fireEvent("beforeresize", this);
37562 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37564 o.enableDisplayMode("block");
37565 // all splitbars share the same overlay
37566 Roo.bootstrap.SplitBar.prototype.overlay = o;
37568 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37569 this.overlay.show();
37570 Roo.get(this.proxy).setDisplayed("block");
37571 var size = this.adapter.getElementSize(this);
37572 this.activeMinSize = this.getMinimumSize();;
37573 this.activeMaxSize = this.getMaximumSize();;
37574 var c1 = size - this.activeMinSize;
37575 var c2 = Math.max(this.activeMaxSize - size, 0);
37576 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37577 this.dd.resetConstraints();
37578 this.dd.setXConstraint(
37579 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37580 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37582 this.dd.setYConstraint(0, 0);
37584 this.dd.resetConstraints();
37585 this.dd.setXConstraint(0, 0);
37586 this.dd.setYConstraint(
37587 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37588 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37591 this.dragSpecs.startSize = size;
37592 this.dragSpecs.startPoint = [x, y];
37593 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37597 * @private Called after the drag operation by the DDProxy
37599 onEndProxyDrag : function(e){
37600 Roo.get(this.proxy).setDisplayed(false);
37601 var endPoint = Roo.lib.Event.getXY(e);
37603 this.overlay.hide();
37606 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37607 newSize = this.dragSpecs.startSize +
37608 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37609 endPoint[0] - this.dragSpecs.startPoint[0] :
37610 this.dragSpecs.startPoint[0] - endPoint[0]
37613 newSize = this.dragSpecs.startSize +
37614 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37615 endPoint[1] - this.dragSpecs.startPoint[1] :
37616 this.dragSpecs.startPoint[1] - endPoint[1]
37619 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37620 if(newSize != this.dragSpecs.startSize){
37621 if(this.fireEvent('beforeapply', this, newSize) !== false){
37622 this.adapter.setElementSize(this, newSize);
37623 this.fireEvent("moved", this, newSize);
37624 this.fireEvent("resize", this, newSize);
37630 * Get the adapter this SplitBar uses
37631 * @return The adapter object
37633 getAdapter : function(){
37634 return this.adapter;
37638 * Set the adapter this SplitBar uses
37639 * @param {Object} adapter A SplitBar adapter object
37641 setAdapter : function(adapter){
37642 this.adapter = adapter;
37643 this.adapter.init(this);
37647 * Gets the minimum size for the resizing element
37648 * @return {Number} The minimum size
37650 getMinimumSize : function(){
37651 return this.minSize;
37655 * Sets the minimum size for the resizing element
37656 * @param {Number} minSize The minimum size
37658 setMinimumSize : function(minSize){
37659 this.minSize = minSize;
37663 * Gets the maximum size for the resizing element
37664 * @return {Number} The maximum size
37666 getMaximumSize : function(){
37667 return this.maxSize;
37671 * Sets the maximum size for the resizing element
37672 * @param {Number} maxSize The maximum size
37674 setMaximumSize : function(maxSize){
37675 this.maxSize = maxSize;
37679 * Sets the initialize size for the resizing element
37680 * @param {Number} size The initial size
37682 setCurrentSize : function(size){
37683 var oldAnimate = this.animate;
37684 this.animate = false;
37685 this.adapter.setElementSize(this, size);
37686 this.animate = oldAnimate;
37690 * Destroy this splitbar.
37691 * @param {Boolean} removeEl True to remove the element
37693 destroy : function(removeEl){
37695 this.shim.remove();
37698 this.proxy.parentNode.removeChild(this.proxy);
37706 * @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.
37708 Roo.bootstrap.SplitBar.createProxy = function(dir){
37709 var proxy = new Roo.Element(document.createElement("div"));
37710 proxy.unselectable();
37711 var cls = 'roo-splitbar-proxy';
37712 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37713 document.body.appendChild(proxy.dom);
37718 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37719 * Default Adapter. It assumes the splitter and resizing element are not positioned
37720 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37722 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37725 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37726 // do nothing for now
37727 init : function(s){
37731 * Called before drag operations to get the current size of the resizing element.
37732 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37734 getElementSize : function(s){
37735 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37736 return s.resizingEl.getWidth();
37738 return s.resizingEl.getHeight();
37743 * Called after drag operations to set the size of the resizing element.
37744 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37745 * @param {Number} newSize The new size to set
37746 * @param {Function} onComplete A function to be invoked when resizing is complete
37748 setElementSize : function(s, newSize, onComplete){
37749 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37751 s.resizingEl.setWidth(newSize);
37753 onComplete(s, newSize);
37756 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37761 s.resizingEl.setHeight(newSize);
37763 onComplete(s, newSize);
37766 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37773 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37774 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37775 * Adapter that moves the splitter element to align with the resized sizing element.
37776 * Used with an absolute positioned SplitBar.
37777 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37778 * document.body, make sure you assign an id to the body element.
37780 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37781 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37782 this.container = Roo.get(container);
37785 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37786 init : function(s){
37787 this.basic.init(s);
37790 getElementSize : function(s){
37791 return this.basic.getElementSize(s);
37794 setElementSize : function(s, newSize, onComplete){
37795 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37798 moveSplitter : function(s){
37799 var yes = Roo.bootstrap.SplitBar;
37800 switch(s.placement){
37802 s.el.setX(s.resizingEl.getRight());
37805 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37808 s.el.setY(s.resizingEl.getBottom());
37811 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37818 * Orientation constant - Create a vertical SplitBar
37822 Roo.bootstrap.SplitBar.VERTICAL = 1;
37825 * Orientation constant - Create a horizontal SplitBar
37829 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37832 * Placement constant - The resizing element is to the left of the splitter element
37836 Roo.bootstrap.SplitBar.LEFT = 1;
37839 * Placement constant - The resizing element is to the right of the splitter element
37843 Roo.bootstrap.SplitBar.RIGHT = 2;
37846 * Placement constant - The resizing element is positioned above the splitter element
37850 Roo.bootstrap.SplitBar.TOP = 3;
37853 * Placement constant - The resizing element is positioned under splitter element
37857 Roo.bootstrap.SplitBar.BOTTOM = 4;
37860 * Ext JS Library 1.1.1
37861 * Copyright(c) 2006-2007, Ext JS, LLC.
37863 * Originally Released Under LGPL - original licence link has changed is not relivant.
37866 * <script type="text/javascript">
37870 * @class Roo.bootstrap.layout.Manager
37871 * @extends Roo.bootstrap.Component
37873 * Base class for layout managers.
37875 Roo.bootstrap.layout.Manager = function(config)
37877 this.monitorWindowResize = true; // do this before we apply configuration.
37879 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37885 /** false to disable window resize monitoring @type Boolean */
37891 * Fires when a layout is performed.
37892 * @param {Roo.LayoutManager} this
37896 * @event regionresized
37897 * Fires when the user resizes a region.
37898 * @param {Roo.LayoutRegion} region The resized region
37899 * @param {Number} newSize The new size (width for east/west, height for north/south)
37901 "regionresized" : true,
37903 * @event regioncollapsed
37904 * Fires when a region is collapsed.
37905 * @param {Roo.LayoutRegion} region The collapsed region
37907 "regioncollapsed" : true,
37909 * @event regionexpanded
37910 * Fires when a region is expanded.
37911 * @param {Roo.LayoutRegion} region The expanded region
37913 "regionexpanded" : true
37915 this.updating = false;
37918 this.el = Roo.get(config.el);
37924 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37929 monitorWindowResize : true,
37935 onRender : function(ct, position)
37938 this.el = Roo.get(ct);
37941 //this.fireEvent('render',this);
37945 initEvents: function()
37949 // ie scrollbar fix
37950 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37951 document.body.scroll = "no";
37952 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37953 this.el.position('relative');
37955 this.id = this.el.id;
37956 this.el.addClass("roo-layout-container");
37957 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37958 if(this.el.dom != document.body ) {
37959 this.el.on('resize', this.layout,this);
37960 this.el.on('show', this.layout,this);
37966 * Returns true if this layout is currently being updated
37967 * @return {Boolean}
37969 isUpdating : function(){
37970 return this.updating;
37974 * Suspend the LayoutManager from doing auto-layouts while
37975 * making multiple add or remove calls
37977 beginUpdate : function(){
37978 this.updating = true;
37982 * Restore auto-layouts and optionally disable the manager from performing a layout
37983 * @param {Boolean} noLayout true to disable a layout update
37985 endUpdate : function(noLayout){
37986 this.updating = false;
37992 layout: function(){
37996 onRegionResized : function(region, newSize){
37997 this.fireEvent("regionresized", region, newSize);
38001 onRegionCollapsed : function(region){
38002 this.fireEvent("regioncollapsed", region);
38005 onRegionExpanded : function(region){
38006 this.fireEvent("regionexpanded", region);
38010 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38011 * performs box-model adjustments.
38012 * @return {Object} The size as an object {width: (the width), height: (the height)}
38014 getViewSize : function()
38017 if(this.el.dom != document.body){
38018 size = this.el.getSize();
38020 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38022 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38023 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38028 * Returns the Element this layout is bound to.
38029 * @return {Roo.Element}
38031 getEl : function(){
38036 * Returns the specified region.
38037 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38038 * @return {Roo.LayoutRegion}
38040 getRegion : function(target){
38041 return this.regions[target.toLowerCase()];
38044 onWindowResize : function(){
38045 if(this.monitorWindowResize){
38052 * Ext JS Library 1.1.1
38053 * Copyright(c) 2006-2007, Ext JS, LLC.
38055 * Originally Released Under LGPL - original licence link has changed is not relivant.
38058 * <script type="text/javascript">
38061 * @class Roo.bootstrap.layout.Border
38062 * @extends Roo.bootstrap.layout.Manager
38063 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
38064 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
38065 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38066 * please see: examples/bootstrap/nested.html<br><br>
38068 <b>The container the layout is rendered into can be either the body element or any other element.
38069 If it is not the body element, the container needs to either be an absolute positioned element,
38070 or you will need to add "position:relative" to the css of the container. You will also need to specify
38071 the container size if it is not the body element.</b>
38074 * Create a new Border
38075 * @param {Object} config Configuration options
38077 Roo.bootstrap.layout.Border = function(config){
38078 config = config || {};
38079 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38083 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38084 if(config[region]){
38085 config[region].region = region;
38086 this.addRegion(config[region]);
38092 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
38094 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38097 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
38100 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
38103 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
38106 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
38109 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
38115 parent : false, // this might point to a 'nest' or a ???
38118 * Creates and adds a new region if it doesn't already exist.
38119 * @param {String} target The target region key (north, south, east, west or center).
38120 * @param {Object} config The regions config object
38121 * @return {BorderLayoutRegion} The new region
38123 addRegion : function(config)
38125 if(!this.regions[config.region]){
38126 var r = this.factory(config);
38127 this.bindRegion(r);
38129 return this.regions[config.region];
38133 bindRegion : function(r){
38134 this.regions[r.config.region] = r;
38136 r.on("visibilitychange", this.layout, this);
38137 r.on("paneladded", this.layout, this);
38138 r.on("panelremoved", this.layout, this);
38139 r.on("invalidated", this.layout, this);
38140 r.on("resized", this.onRegionResized, this);
38141 r.on("collapsed", this.onRegionCollapsed, this);
38142 r.on("expanded", this.onRegionExpanded, this);
38146 * Performs a layout update.
38148 layout : function()
38150 if(this.updating) {
38154 // render all the rebions if they have not been done alreayd?
38155 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38156 if(this.regions[region] && !this.regions[region].bodyEl){
38157 this.regions[region].onRender(this.el)
38161 var size = this.getViewSize();
38162 var w = size.width;
38163 var h = size.height;
38168 //var x = 0, y = 0;
38170 var rs = this.regions;
38171 var north = rs["north"];
38172 var south = rs["south"];
38173 var west = rs["west"];
38174 var east = rs["east"];
38175 var center = rs["center"];
38176 //if(this.hideOnLayout){ // not supported anymore
38177 //c.el.setStyle("display", "none");
38179 if(north && north.isVisible()){
38180 var b = north.getBox();
38181 var m = north.getMargins();
38182 b.width = w - (m.left+m.right);
38185 centerY = b.height + b.y + m.bottom;
38186 centerH -= centerY;
38187 north.updateBox(this.safeBox(b));
38189 if(south && south.isVisible()){
38190 var b = south.getBox();
38191 var m = south.getMargins();
38192 b.width = w - (m.left+m.right);
38194 var totalHeight = (b.height + m.top + m.bottom);
38195 b.y = h - totalHeight + m.top;
38196 centerH -= totalHeight;
38197 south.updateBox(this.safeBox(b));
38199 if(west && west.isVisible()){
38200 var b = west.getBox();
38201 var m = west.getMargins();
38202 b.height = centerH - (m.top+m.bottom);
38204 b.y = centerY + m.top;
38205 var totalWidth = (b.width + m.left + m.right);
38206 centerX += totalWidth;
38207 centerW -= totalWidth;
38208 west.updateBox(this.safeBox(b));
38210 if(east && east.isVisible()){
38211 var b = east.getBox();
38212 var m = east.getMargins();
38213 b.height = centerH - (m.top+m.bottom);
38214 var totalWidth = (b.width + m.left + m.right);
38215 b.x = w - totalWidth + m.left;
38216 b.y = centerY + m.top;
38217 centerW -= totalWidth;
38218 east.updateBox(this.safeBox(b));
38221 var m = center.getMargins();
38223 x: centerX + m.left,
38224 y: centerY + m.top,
38225 width: centerW - (m.left+m.right),
38226 height: centerH - (m.top+m.bottom)
38228 //if(this.hideOnLayout){
38229 //center.el.setStyle("display", "block");
38231 center.updateBox(this.safeBox(centerBox));
38234 this.fireEvent("layout", this);
38238 safeBox : function(box){
38239 box.width = Math.max(0, box.width);
38240 box.height = Math.max(0, box.height);
38245 * Adds a ContentPanel (or subclass) to this layout.
38246 * @param {String} target The target region key (north, south, east, west or center).
38247 * @param {Roo.ContentPanel} panel The panel to add
38248 * @return {Roo.ContentPanel} The added panel
38250 add : function(target, panel){
38252 target = target.toLowerCase();
38253 return this.regions[target].add(panel);
38257 * Remove a ContentPanel (or subclass) to this layout.
38258 * @param {String} target The target region key (north, south, east, west or center).
38259 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38260 * @return {Roo.ContentPanel} The removed panel
38262 remove : function(target, panel){
38263 target = target.toLowerCase();
38264 return this.regions[target].remove(panel);
38268 * Searches all regions for a panel with the specified id
38269 * @param {String} panelId
38270 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38272 findPanel : function(panelId){
38273 var rs = this.regions;
38274 for(var target in rs){
38275 if(typeof rs[target] != "function"){
38276 var p = rs[target].getPanel(panelId);
38286 * Searches all regions for a panel with the specified id and activates (shows) it.
38287 * @param {String/ContentPanel} panelId The panels id or the panel itself
38288 * @return {Roo.ContentPanel} The shown panel or null
38290 showPanel : function(panelId) {
38291 var rs = this.regions;
38292 for(var target in rs){
38293 var r = rs[target];
38294 if(typeof r != "function"){
38295 if(r.hasPanel(panelId)){
38296 return r.showPanel(panelId);
38304 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38305 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38308 restoreState : function(provider){
38310 provider = Roo.state.Manager;
38312 var sm = new Roo.LayoutStateManager();
38313 sm.init(this, provider);
38319 * Adds a xtype elements to the layout.
38323 xtype : 'ContentPanel',
38330 xtype : 'NestedLayoutPanel',
38336 items : [ ... list of content panels or nested layout panels.. ]
38340 * @param {Object} cfg Xtype definition of item to add.
38342 addxtype : function(cfg)
38344 // basically accepts a pannel...
38345 // can accept a layout region..!?!?
38346 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38349 // theory? children can only be panels??
38351 //if (!cfg.xtype.match(/Panel$/)) {
38356 if (typeof(cfg.region) == 'undefined') {
38357 Roo.log("Failed to add Panel, region was not set");
38361 var region = cfg.region;
38367 xitems = cfg.items;
38372 if ( region == 'center') {
38373 Roo.log("Center: " + cfg.title);
38379 case 'Content': // ContentPanel (el, cfg)
38380 case 'Scroll': // ContentPanel (el, cfg)
38382 cfg.autoCreate = cfg.autoCreate || true;
38383 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38385 // var el = this.el.createChild();
38386 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38389 this.add(region, ret);
38393 case 'TreePanel': // our new panel!
38394 cfg.el = this.el.createChild();
38395 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38396 this.add(region, ret);
38401 // create a new Layout (which is a Border Layout...
38403 var clayout = cfg.layout;
38404 clayout.el = this.el.createChild();
38405 clayout.items = clayout.items || [];
38409 // replace this exitems with the clayout ones..
38410 xitems = clayout.items;
38412 // force background off if it's in center...
38413 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38414 cfg.background = false;
38416 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38419 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38420 //console.log('adding nested layout panel ' + cfg.toSource());
38421 this.add(region, ret);
38422 nb = {}; /// find first...
38427 // needs grid and region
38429 //var el = this.getRegion(region).el.createChild();
38431 *var el = this.el.createChild();
38432 // create the grid first...
38433 cfg.grid.container = el;
38434 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38437 if (region == 'center' && this.active ) {
38438 cfg.background = false;
38441 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38443 this.add(region, ret);
38445 if (cfg.background) {
38446 // render grid on panel activation (if panel background)
38447 ret.on('activate', function(gp) {
38448 if (!gp.grid.rendered) {
38449 // gp.grid.render(el);
38453 // cfg.grid.render(el);
38459 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38460 // it was the old xcomponent building that caused this before.
38461 // espeically if border is the top element in the tree.
38471 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38473 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38474 this.add(region, ret);
38478 throw "Can not add '" + cfg.xtype + "' to Border";
38484 this.beginUpdate();
38488 Roo.each(xitems, function(i) {
38489 region = nb && i.region ? i.region : false;
38491 var add = ret.addxtype(i);
38494 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38495 if (!i.background) {
38496 abn[region] = nb[region] ;
38503 // make the last non-background panel active..
38504 //if (nb) { Roo.log(abn); }
38507 for(var r in abn) {
38508 region = this.getRegion(r);
38510 // tried using nb[r], but it does not work..
38512 region.showPanel(abn[r]);
38523 factory : function(cfg)
38526 var validRegions = Roo.bootstrap.layout.Border.regions;
38528 var target = cfg.region;
38531 var r = Roo.bootstrap.layout;
38535 return new r.North(cfg);
38537 return new r.South(cfg);
38539 return new r.East(cfg);
38541 return new r.West(cfg);
38543 return new r.Center(cfg);
38545 throw 'Layout region "'+target+'" not supported.';
38552 * Ext JS Library 1.1.1
38553 * Copyright(c) 2006-2007, Ext JS, LLC.
38555 * Originally Released Under LGPL - original licence link has changed is not relivant.
38558 * <script type="text/javascript">
38562 * @class Roo.bootstrap.layout.Basic
38563 * @extends Roo.util.Observable
38564 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38565 * and does not have a titlebar, tabs or any other features. All it does is size and position
38566 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38567 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38568 * @cfg {string} region the region that it inhabits..
38569 * @cfg {bool} skipConfig skip config?
38573 Roo.bootstrap.layout.Basic = function(config){
38575 this.mgr = config.mgr;
38577 this.position = config.region;
38579 var skipConfig = config.skipConfig;
38583 * @scope Roo.BasicLayoutRegion
38587 * @event beforeremove
38588 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38589 * @param {Roo.LayoutRegion} this
38590 * @param {Roo.ContentPanel} panel The panel
38591 * @param {Object} e The cancel event object
38593 "beforeremove" : true,
38595 * @event invalidated
38596 * Fires when the layout for this region is changed.
38597 * @param {Roo.LayoutRegion} this
38599 "invalidated" : true,
38601 * @event visibilitychange
38602 * Fires when this region is shown or hidden
38603 * @param {Roo.LayoutRegion} this
38604 * @param {Boolean} visibility true or false
38606 "visibilitychange" : true,
38608 * @event paneladded
38609 * Fires when a panel is added.
38610 * @param {Roo.LayoutRegion} this
38611 * @param {Roo.ContentPanel} panel The panel
38613 "paneladded" : true,
38615 * @event panelremoved
38616 * Fires when a panel is removed.
38617 * @param {Roo.LayoutRegion} this
38618 * @param {Roo.ContentPanel} panel The panel
38620 "panelremoved" : true,
38622 * @event beforecollapse
38623 * Fires when this region before collapse.
38624 * @param {Roo.LayoutRegion} this
38626 "beforecollapse" : true,
38629 * Fires when this region is collapsed.
38630 * @param {Roo.LayoutRegion} this
38632 "collapsed" : true,
38635 * Fires when this region is expanded.
38636 * @param {Roo.LayoutRegion} this
38641 * Fires when this region is slid into view.
38642 * @param {Roo.LayoutRegion} this
38644 "slideshow" : true,
38647 * Fires when this region slides out of view.
38648 * @param {Roo.LayoutRegion} this
38650 "slidehide" : true,
38652 * @event panelactivated
38653 * Fires when a panel is activated.
38654 * @param {Roo.LayoutRegion} this
38655 * @param {Roo.ContentPanel} panel The activated panel
38657 "panelactivated" : true,
38660 * Fires when the user resizes this region.
38661 * @param {Roo.LayoutRegion} this
38662 * @param {Number} newSize The new size (width for east/west, height for north/south)
38666 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38667 this.panels = new Roo.util.MixedCollection();
38668 this.panels.getKey = this.getPanelId.createDelegate(this);
38670 this.activePanel = null;
38671 // ensure listeners are added...
38673 if (config.listeners || config.events) {
38674 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38675 listeners : config.listeners || {},
38676 events : config.events || {}
38680 if(skipConfig !== true){
38681 this.applyConfig(config);
38685 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38687 getPanelId : function(p){
38691 applyConfig : function(config){
38692 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38693 this.config = config;
38698 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38699 * the width, for horizontal (north, south) the height.
38700 * @param {Number} newSize The new width or height
38702 resizeTo : function(newSize){
38703 var el = this.el ? this.el :
38704 (this.activePanel ? this.activePanel.getEl() : null);
38706 switch(this.position){
38709 el.setWidth(newSize);
38710 this.fireEvent("resized", this, newSize);
38714 el.setHeight(newSize);
38715 this.fireEvent("resized", this, newSize);
38721 getBox : function(){
38722 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38725 getMargins : function(){
38726 return this.margins;
38729 updateBox : function(box){
38731 var el = this.activePanel.getEl();
38732 el.dom.style.left = box.x + "px";
38733 el.dom.style.top = box.y + "px";
38734 this.activePanel.setSize(box.width, box.height);
38738 * Returns the container element for this region.
38739 * @return {Roo.Element}
38741 getEl : function(){
38742 return this.activePanel;
38746 * Returns true if this region is currently visible.
38747 * @return {Boolean}
38749 isVisible : function(){
38750 return this.activePanel ? true : false;
38753 setActivePanel : function(panel){
38754 panel = this.getPanel(panel);
38755 if(this.activePanel && this.activePanel != panel){
38756 this.activePanel.setActiveState(false);
38757 this.activePanel.getEl().setLeftTop(-10000,-10000);
38759 this.activePanel = panel;
38760 panel.setActiveState(true);
38762 panel.setSize(this.box.width, this.box.height);
38764 this.fireEvent("panelactivated", this, panel);
38765 this.fireEvent("invalidated");
38769 * Show the specified panel.
38770 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38771 * @return {Roo.ContentPanel} The shown panel or null
38773 showPanel : function(panel){
38774 panel = this.getPanel(panel);
38776 this.setActivePanel(panel);
38782 * Get the active panel for this region.
38783 * @return {Roo.ContentPanel} The active panel or null
38785 getActivePanel : function(){
38786 return this.activePanel;
38790 * Add the passed ContentPanel(s)
38791 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38792 * @return {Roo.ContentPanel} The panel added (if only one was added)
38794 add : function(panel){
38795 if(arguments.length > 1){
38796 for(var i = 0, len = arguments.length; i < len; i++) {
38797 this.add(arguments[i]);
38801 if(this.hasPanel(panel)){
38802 this.showPanel(panel);
38805 var el = panel.getEl();
38806 if(el.dom.parentNode != this.mgr.el.dom){
38807 this.mgr.el.dom.appendChild(el.dom);
38809 if(panel.setRegion){
38810 panel.setRegion(this);
38812 this.panels.add(panel);
38813 el.setStyle("position", "absolute");
38814 if(!panel.background){
38815 this.setActivePanel(panel);
38816 if(this.config.initialSize && this.panels.getCount()==1){
38817 this.resizeTo(this.config.initialSize);
38820 this.fireEvent("paneladded", this, panel);
38825 * Returns true if the panel is in this region.
38826 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38827 * @return {Boolean}
38829 hasPanel : function(panel){
38830 if(typeof panel == "object"){ // must be panel obj
38831 panel = panel.getId();
38833 return this.getPanel(panel) ? true : false;
38837 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38838 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38839 * @param {Boolean} preservePanel Overrides the config preservePanel option
38840 * @return {Roo.ContentPanel} The panel that was removed
38842 remove : function(panel, preservePanel){
38843 panel = this.getPanel(panel);
38848 this.fireEvent("beforeremove", this, panel, e);
38849 if(e.cancel === true){
38852 var panelId = panel.getId();
38853 this.panels.removeKey(panelId);
38858 * Returns the panel specified or null if it's not in this region.
38859 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38860 * @return {Roo.ContentPanel}
38862 getPanel : function(id){
38863 if(typeof id == "object"){ // must be panel obj
38866 return this.panels.get(id);
38870 * Returns this regions position (north/south/east/west/center).
38873 getPosition: function(){
38874 return this.position;
38878 * Ext JS Library 1.1.1
38879 * Copyright(c) 2006-2007, Ext JS, LLC.
38881 * Originally Released Under LGPL - original licence link has changed is not relivant.
38884 * <script type="text/javascript">
38888 * @class Roo.bootstrap.layout.Region
38889 * @extends Roo.bootstrap.layout.Basic
38890 * This class represents a region in a layout manager.
38892 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38893 * @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})
38894 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38895 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38896 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38897 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38898 * @cfg {String} title The title for the region (overrides panel titles)
38899 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38900 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38901 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38902 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38903 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38904 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38905 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38906 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38907 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38908 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38910 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38911 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38912 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38913 * @cfg {Number} width For East/West panels
38914 * @cfg {Number} height For North/South panels
38915 * @cfg {Boolean} split To show the splitter
38916 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38918 * @cfg {string} cls Extra CSS classes to add to region
38920 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38921 * @cfg {string} region the region that it inhabits..
38924 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38925 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38927 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38928 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38929 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38931 Roo.bootstrap.layout.Region = function(config)
38933 this.applyConfig(config);
38935 var mgr = config.mgr;
38936 var pos = config.region;
38937 config.skipConfig = true;
38938 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38941 this.onRender(mgr.el);
38944 this.visible = true;
38945 this.collapsed = false;
38946 this.unrendered_panels = [];
38949 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38951 position: '', // set by wrapper (eg. north/south etc..)
38952 unrendered_panels : null, // unrendered panels.
38954 tabPosition : false,
38956 mgr: false, // points to 'Border'
38959 createBody : function(){
38960 /** This region's body element
38961 * @type Roo.Element */
38962 this.bodyEl = this.el.createChild({
38964 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38968 onRender: function(ctr, pos)
38970 var dh = Roo.DomHelper;
38971 /** This region's container element
38972 * @type Roo.Element */
38973 this.el = dh.append(ctr.dom, {
38975 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38977 /** This region's title element
38978 * @type Roo.Element */
38980 this.titleEl = dh.append(this.el.dom, {
38982 unselectable: "on",
38983 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38985 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38986 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38990 this.titleEl.enableDisplayMode();
38991 /** This region's title text element
38992 * @type HTMLElement */
38993 this.titleTextEl = this.titleEl.dom.firstChild;
38994 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38996 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38997 this.closeBtn.enableDisplayMode();
38998 this.closeBtn.on("click", this.closeClicked, this);
38999 this.closeBtn.hide();
39001 this.createBody(this.config);
39002 if(this.config.hideWhenEmpty){
39004 this.on("paneladded", this.validateVisibility, this);
39005 this.on("panelremoved", this.validateVisibility, this);
39007 if(this.autoScroll){
39008 this.bodyEl.setStyle("overflow", "auto");
39010 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39012 //if(c.titlebar !== false){
39013 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39014 this.titleEl.hide();
39016 this.titleEl.show();
39017 if(this.config.title){
39018 this.titleTextEl.innerHTML = this.config.title;
39022 if(this.config.collapsed){
39023 this.collapse(true);
39025 if(this.config.hidden){
39029 if (this.unrendered_panels && this.unrendered_panels.length) {
39030 for (var i =0;i< this.unrendered_panels.length; i++) {
39031 this.add(this.unrendered_panels[i]);
39033 this.unrendered_panels = null;
39039 applyConfig : function(c)
39042 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39043 var dh = Roo.DomHelper;
39044 if(c.titlebar !== false){
39045 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39046 this.collapseBtn.on("click", this.collapse, this);
39047 this.collapseBtn.enableDisplayMode();
39049 if(c.showPin === true || this.showPin){
39050 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39051 this.stickBtn.enableDisplayMode();
39052 this.stickBtn.on("click", this.expand, this);
39053 this.stickBtn.hide();
39058 /** This region's collapsed element
39059 * @type Roo.Element */
39062 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39063 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39066 if(c.floatable !== false){
39067 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39068 this.collapsedEl.on("click", this.collapseClick, this);
39071 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39072 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39073 id: "message", unselectable: "on", style:{"float":"left"}});
39074 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39076 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39077 this.expandBtn.on("click", this.expand, this);
39081 if(this.collapseBtn){
39082 this.collapseBtn.setVisible(c.collapsible == true);
39085 this.cmargins = c.cmargins || this.cmargins ||
39086 (this.position == "west" || this.position == "east" ?
39087 {top: 0, left: 2, right:2, bottom: 0} :
39088 {top: 2, left: 0, right:0, bottom: 2});
39090 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39093 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39095 this.autoScroll = c.autoScroll || false;
39100 this.duration = c.duration || .30;
39101 this.slideDuration = c.slideDuration || .45;
39106 * Returns true if this region is currently visible.
39107 * @return {Boolean}
39109 isVisible : function(){
39110 return this.visible;
39114 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39115 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
39117 //setCollapsedTitle : function(title){
39118 // title = title || " ";
39119 // if(this.collapsedTitleTextEl){
39120 // this.collapsedTitleTextEl.innerHTML = title;
39124 getBox : function(){
39126 // if(!this.collapsed){
39127 b = this.el.getBox(false, true);
39129 // b = this.collapsedEl.getBox(false, true);
39134 getMargins : function(){
39135 return this.margins;
39136 //return this.collapsed ? this.cmargins : this.margins;
39139 highlight : function(){
39140 this.el.addClass("x-layout-panel-dragover");
39143 unhighlight : function(){
39144 this.el.removeClass("x-layout-panel-dragover");
39147 updateBox : function(box)
39149 if (!this.bodyEl) {
39150 return; // not rendered yet..
39154 if(!this.collapsed){
39155 this.el.dom.style.left = box.x + "px";
39156 this.el.dom.style.top = box.y + "px";
39157 this.updateBody(box.width, box.height);
39159 this.collapsedEl.dom.style.left = box.x + "px";
39160 this.collapsedEl.dom.style.top = box.y + "px";
39161 this.collapsedEl.setSize(box.width, box.height);
39164 this.tabs.autoSizeTabs();
39168 updateBody : function(w, h)
39171 this.el.setWidth(w);
39172 w -= this.el.getBorderWidth("rl");
39173 if(this.config.adjustments){
39174 w += this.config.adjustments[0];
39177 if(h !== null && h > 0){
39178 this.el.setHeight(h);
39179 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39180 h -= this.el.getBorderWidth("tb");
39181 if(this.config.adjustments){
39182 h += this.config.adjustments[1];
39184 this.bodyEl.setHeight(h);
39186 h = this.tabs.syncHeight(h);
39189 if(this.panelSize){
39190 w = w !== null ? w : this.panelSize.width;
39191 h = h !== null ? h : this.panelSize.height;
39193 if(this.activePanel){
39194 var el = this.activePanel.getEl();
39195 w = w !== null ? w : el.getWidth();
39196 h = h !== null ? h : el.getHeight();
39197 this.panelSize = {width: w, height: h};
39198 this.activePanel.setSize(w, h);
39200 if(Roo.isIE && this.tabs){
39201 this.tabs.el.repaint();
39206 * Returns the container element for this region.
39207 * @return {Roo.Element}
39209 getEl : function(){
39214 * Hides this region.
39217 //if(!this.collapsed){
39218 this.el.dom.style.left = "-2000px";
39221 // this.collapsedEl.dom.style.left = "-2000px";
39222 // this.collapsedEl.hide();
39224 this.visible = false;
39225 this.fireEvent("visibilitychange", this, false);
39229 * Shows this region if it was previously hidden.
39232 //if(!this.collapsed){
39235 // this.collapsedEl.show();
39237 this.visible = true;
39238 this.fireEvent("visibilitychange", this, true);
39241 closeClicked : function(){
39242 if(this.activePanel){
39243 this.remove(this.activePanel);
39247 collapseClick : function(e){
39249 e.stopPropagation();
39252 e.stopPropagation();
39258 * Collapses this region.
39259 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39262 collapse : function(skipAnim, skipCheck = false){
39263 if(this.collapsed) {
39267 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39269 this.collapsed = true;
39271 this.split.el.hide();
39273 if(this.config.animate && skipAnim !== true){
39274 this.fireEvent("invalidated", this);
39275 this.animateCollapse();
39277 this.el.setLocation(-20000,-20000);
39279 this.collapsedEl.show();
39280 this.fireEvent("collapsed", this);
39281 this.fireEvent("invalidated", this);
39287 animateCollapse : function(){
39292 * Expands this region if it was previously collapsed.
39293 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39294 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39297 expand : function(e, skipAnim){
39299 e.stopPropagation();
39301 if(!this.collapsed || this.el.hasActiveFx()) {
39305 this.afterSlideIn();
39308 this.collapsed = false;
39309 if(this.config.animate && skipAnim !== true){
39310 this.animateExpand();
39314 this.split.el.show();
39316 this.collapsedEl.setLocation(-2000,-2000);
39317 this.collapsedEl.hide();
39318 this.fireEvent("invalidated", this);
39319 this.fireEvent("expanded", this);
39323 animateExpand : function(){
39327 initTabs : function()
39329 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39331 var ts = new Roo.bootstrap.panel.Tabs({
39332 el: this.bodyEl.dom,
39334 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39335 disableTooltips: this.config.disableTabTips,
39336 toolbar : this.config.toolbar
39339 if(this.config.hideTabs){
39340 ts.stripWrap.setDisplayed(false);
39343 ts.resizeTabs = this.config.resizeTabs === true;
39344 ts.minTabWidth = this.config.minTabWidth || 40;
39345 ts.maxTabWidth = this.config.maxTabWidth || 250;
39346 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39347 ts.monitorResize = false;
39348 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39349 ts.bodyEl.addClass('roo-layout-tabs-body');
39350 this.panels.each(this.initPanelAsTab, this);
39353 initPanelAsTab : function(panel){
39354 var ti = this.tabs.addTab(
39358 this.config.closeOnTab && panel.isClosable(),
39361 if(panel.tabTip !== undefined){
39362 ti.setTooltip(panel.tabTip);
39364 ti.on("activate", function(){
39365 this.setActivePanel(panel);
39368 if(this.config.closeOnTab){
39369 ti.on("beforeclose", function(t, e){
39371 this.remove(panel);
39375 panel.tabItem = ti;
39380 updatePanelTitle : function(panel, title)
39382 if(this.activePanel == panel){
39383 this.updateTitle(title);
39386 var ti = this.tabs.getTab(panel.getEl().id);
39388 if(panel.tabTip !== undefined){
39389 ti.setTooltip(panel.tabTip);
39394 updateTitle : function(title){
39395 if(this.titleTextEl && !this.config.title){
39396 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39400 setActivePanel : function(panel)
39402 panel = this.getPanel(panel);
39403 if(this.activePanel && this.activePanel != panel){
39404 if(this.activePanel.setActiveState(false) === false){
39408 this.activePanel = panel;
39409 panel.setActiveState(true);
39410 if(this.panelSize){
39411 panel.setSize(this.panelSize.width, this.panelSize.height);
39414 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39416 this.updateTitle(panel.getTitle());
39418 this.fireEvent("invalidated", this);
39420 this.fireEvent("panelactivated", this, panel);
39424 * Shows the specified panel.
39425 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39426 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39428 showPanel : function(panel)
39430 panel = this.getPanel(panel);
39433 var tab = this.tabs.getTab(panel.getEl().id);
39434 if(tab.isHidden()){
39435 this.tabs.unhideTab(tab.id);
39439 this.setActivePanel(panel);
39446 * Get the active panel for this region.
39447 * @return {Roo.ContentPanel} The active panel or null
39449 getActivePanel : function(){
39450 return this.activePanel;
39453 validateVisibility : function(){
39454 if(this.panels.getCount() < 1){
39455 this.updateTitle(" ");
39456 this.closeBtn.hide();
39459 if(!this.isVisible()){
39466 * Adds the passed ContentPanel(s) to this region.
39467 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39468 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39470 add : function(panel)
39472 if(arguments.length > 1){
39473 for(var i = 0, len = arguments.length; i < len; i++) {
39474 this.add(arguments[i]);
39479 // if we have not been rendered yet, then we can not really do much of this..
39480 if (!this.bodyEl) {
39481 this.unrendered_panels.push(panel);
39488 if(this.hasPanel(panel)){
39489 this.showPanel(panel);
39492 panel.setRegion(this);
39493 this.panels.add(panel);
39494 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39495 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39496 // and hide them... ???
39497 this.bodyEl.dom.appendChild(panel.getEl().dom);
39498 if(panel.background !== true){
39499 this.setActivePanel(panel);
39501 this.fireEvent("paneladded", this, panel);
39508 this.initPanelAsTab(panel);
39512 if(panel.background !== true){
39513 this.tabs.activate(panel.getEl().id);
39515 this.fireEvent("paneladded", this, panel);
39520 * Hides the tab for the specified panel.
39521 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39523 hidePanel : function(panel){
39524 if(this.tabs && (panel = this.getPanel(panel))){
39525 this.tabs.hideTab(panel.getEl().id);
39530 * Unhides the tab for a previously hidden panel.
39531 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39533 unhidePanel : function(panel){
39534 if(this.tabs && (panel = this.getPanel(panel))){
39535 this.tabs.unhideTab(panel.getEl().id);
39539 clearPanels : function(){
39540 while(this.panels.getCount() > 0){
39541 this.remove(this.panels.first());
39546 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39547 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39548 * @param {Boolean} preservePanel Overrides the config preservePanel option
39549 * @return {Roo.ContentPanel} The panel that was removed
39551 remove : function(panel, preservePanel)
39553 panel = this.getPanel(panel);
39558 this.fireEvent("beforeremove", this, panel, e);
39559 if(e.cancel === true){
39562 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39563 var panelId = panel.getId();
39564 this.panels.removeKey(panelId);
39566 document.body.appendChild(panel.getEl().dom);
39569 this.tabs.removeTab(panel.getEl().id);
39570 }else if (!preservePanel){
39571 this.bodyEl.dom.removeChild(panel.getEl().dom);
39573 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39574 var p = this.panels.first();
39575 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39576 tempEl.appendChild(p.getEl().dom);
39577 this.bodyEl.update("");
39578 this.bodyEl.dom.appendChild(p.getEl().dom);
39580 this.updateTitle(p.getTitle());
39582 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39583 this.setActivePanel(p);
39585 panel.setRegion(null);
39586 if(this.activePanel == panel){
39587 this.activePanel = null;
39589 if(this.config.autoDestroy !== false && preservePanel !== true){
39590 try{panel.destroy();}catch(e){}
39592 this.fireEvent("panelremoved", this, panel);
39597 * Returns the TabPanel component used by this region
39598 * @return {Roo.TabPanel}
39600 getTabs : function(){
39604 createTool : function(parentEl, className){
39605 var btn = Roo.DomHelper.append(parentEl, {
39607 cls: "x-layout-tools-button",
39610 cls: "roo-layout-tools-button-inner " + className,
39614 btn.addClassOnOver("roo-layout-tools-button-over");
39619 * Ext JS Library 1.1.1
39620 * Copyright(c) 2006-2007, Ext JS, LLC.
39622 * Originally Released Under LGPL - original licence link has changed is not relivant.
39625 * <script type="text/javascript">
39631 * @class Roo.SplitLayoutRegion
39632 * @extends Roo.LayoutRegion
39633 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39635 Roo.bootstrap.layout.Split = function(config){
39636 this.cursor = config.cursor;
39637 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39640 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39642 splitTip : "Drag to resize.",
39643 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39644 useSplitTips : false,
39646 applyConfig : function(config){
39647 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39650 onRender : function(ctr,pos) {
39652 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39653 if(!this.config.split){
39658 var splitEl = Roo.DomHelper.append(ctr.dom, {
39660 id: this.el.id + "-split",
39661 cls: "roo-layout-split roo-layout-split-"+this.position,
39664 /** The SplitBar for this region
39665 * @type Roo.SplitBar */
39666 // does not exist yet...
39667 Roo.log([this.position, this.orientation]);
39669 this.split = new Roo.bootstrap.SplitBar({
39670 dragElement : splitEl,
39671 resizingElement: this.el,
39672 orientation : this.orientation
39675 this.split.on("moved", this.onSplitMove, this);
39676 this.split.useShim = this.config.useShim === true;
39677 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39678 if(this.useSplitTips){
39679 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39681 //if(config.collapsible){
39682 // this.split.el.on("dblclick", this.collapse, this);
39685 if(typeof this.config.minSize != "undefined"){
39686 this.split.minSize = this.config.minSize;
39688 if(typeof this.config.maxSize != "undefined"){
39689 this.split.maxSize = this.config.maxSize;
39691 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39692 this.hideSplitter();
39697 getHMaxSize : function(){
39698 var cmax = this.config.maxSize || 10000;
39699 var center = this.mgr.getRegion("center");
39700 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39703 getVMaxSize : function(){
39704 var cmax = this.config.maxSize || 10000;
39705 var center = this.mgr.getRegion("center");
39706 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39709 onSplitMove : function(split, newSize){
39710 this.fireEvent("resized", this, newSize);
39714 * Returns the {@link Roo.SplitBar} for this region.
39715 * @return {Roo.SplitBar}
39717 getSplitBar : function(){
39722 this.hideSplitter();
39723 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39726 hideSplitter : function(){
39728 this.split.el.setLocation(-2000,-2000);
39729 this.split.el.hide();
39735 this.split.el.show();
39737 Roo.bootstrap.layout.Split.superclass.show.call(this);
39740 beforeSlide: function(){
39741 if(Roo.isGecko){// firefox overflow auto bug workaround
39742 this.bodyEl.clip();
39744 this.tabs.bodyEl.clip();
39746 if(this.activePanel){
39747 this.activePanel.getEl().clip();
39749 if(this.activePanel.beforeSlide){
39750 this.activePanel.beforeSlide();
39756 afterSlide : function(){
39757 if(Roo.isGecko){// firefox overflow auto bug workaround
39758 this.bodyEl.unclip();
39760 this.tabs.bodyEl.unclip();
39762 if(this.activePanel){
39763 this.activePanel.getEl().unclip();
39764 if(this.activePanel.afterSlide){
39765 this.activePanel.afterSlide();
39771 initAutoHide : function(){
39772 if(this.autoHide !== false){
39773 if(!this.autoHideHd){
39774 var st = new Roo.util.DelayedTask(this.slideIn, this);
39775 this.autoHideHd = {
39776 "mouseout": function(e){
39777 if(!e.within(this.el, true)){
39781 "mouseover" : function(e){
39787 this.el.on(this.autoHideHd);
39791 clearAutoHide : function(){
39792 if(this.autoHide !== false){
39793 this.el.un("mouseout", this.autoHideHd.mouseout);
39794 this.el.un("mouseover", this.autoHideHd.mouseover);
39798 clearMonitor : function(){
39799 Roo.get(document).un("click", this.slideInIf, this);
39802 // these names are backwards but not changed for compat
39803 slideOut : function(){
39804 if(this.isSlid || this.el.hasActiveFx()){
39807 this.isSlid = true;
39808 if(this.collapseBtn){
39809 this.collapseBtn.hide();
39811 this.closeBtnState = this.closeBtn.getStyle('display');
39812 this.closeBtn.hide();
39814 this.stickBtn.show();
39817 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39818 this.beforeSlide();
39819 this.el.setStyle("z-index", 10001);
39820 this.el.slideIn(this.getSlideAnchor(), {
39821 callback: function(){
39823 this.initAutoHide();
39824 Roo.get(document).on("click", this.slideInIf, this);
39825 this.fireEvent("slideshow", this);
39832 afterSlideIn : function(){
39833 this.clearAutoHide();
39834 this.isSlid = false;
39835 this.clearMonitor();
39836 this.el.setStyle("z-index", "");
39837 if(this.collapseBtn){
39838 this.collapseBtn.show();
39840 this.closeBtn.setStyle('display', this.closeBtnState);
39842 this.stickBtn.hide();
39844 this.fireEvent("slidehide", this);
39847 slideIn : function(cb){
39848 if(!this.isSlid || this.el.hasActiveFx()){
39852 this.isSlid = false;
39853 this.beforeSlide();
39854 this.el.slideOut(this.getSlideAnchor(), {
39855 callback: function(){
39856 this.el.setLeftTop(-10000, -10000);
39858 this.afterSlideIn();
39866 slideInIf : function(e){
39867 if(!e.within(this.el)){
39872 animateCollapse : function(){
39873 this.beforeSlide();
39874 this.el.setStyle("z-index", 20000);
39875 var anchor = this.getSlideAnchor();
39876 this.el.slideOut(anchor, {
39877 callback : function(){
39878 this.el.setStyle("z-index", "");
39879 this.collapsedEl.slideIn(anchor, {duration:.3});
39881 this.el.setLocation(-10000,-10000);
39883 this.fireEvent("collapsed", this);
39890 animateExpand : function(){
39891 this.beforeSlide();
39892 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39893 this.el.setStyle("z-index", 20000);
39894 this.collapsedEl.hide({
39897 this.el.slideIn(this.getSlideAnchor(), {
39898 callback : function(){
39899 this.el.setStyle("z-index", "");
39902 this.split.el.show();
39904 this.fireEvent("invalidated", this);
39905 this.fireEvent("expanded", this);
39933 getAnchor : function(){
39934 return this.anchors[this.position];
39937 getCollapseAnchor : function(){
39938 return this.canchors[this.position];
39941 getSlideAnchor : function(){
39942 return this.sanchors[this.position];
39945 getAlignAdj : function(){
39946 var cm = this.cmargins;
39947 switch(this.position){
39963 getExpandAdj : function(){
39964 var c = this.collapsedEl, cm = this.cmargins;
39965 switch(this.position){
39967 return [-(cm.right+c.getWidth()+cm.left), 0];
39970 return [cm.right+c.getWidth()+cm.left, 0];
39973 return [0, -(cm.top+cm.bottom+c.getHeight())];
39976 return [0, cm.top+cm.bottom+c.getHeight()];
39982 * Ext JS Library 1.1.1
39983 * Copyright(c) 2006-2007, Ext JS, LLC.
39985 * Originally Released Under LGPL - original licence link has changed is not relivant.
39988 * <script type="text/javascript">
39991 * These classes are private internal classes
39993 Roo.bootstrap.layout.Center = function(config){
39994 config.region = "center";
39995 Roo.bootstrap.layout.Region.call(this, config);
39996 this.visible = true;
39997 this.minWidth = config.minWidth || 20;
39998 this.minHeight = config.minHeight || 20;
40001 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40003 // center panel can't be hidden
40007 // center panel can't be hidden
40010 getMinWidth: function(){
40011 return this.minWidth;
40014 getMinHeight: function(){
40015 return this.minHeight;
40029 Roo.bootstrap.layout.North = function(config)
40031 config.region = 'north';
40032 config.cursor = 'n-resize';
40034 Roo.bootstrap.layout.Split.call(this, config);
40038 this.split.placement = Roo.bootstrap.SplitBar.TOP;
40039 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40040 this.split.el.addClass("roo-layout-split-v");
40042 //var size = config.initialSize || config.height;
40043 //if(this.el && typeof size != "undefined"){
40044 // this.el.setHeight(size);
40047 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40049 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40052 onRender : function(ctr, pos)
40054 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40055 var size = this.config.initialSize || this.config.height;
40056 if(this.el && typeof size != "undefined"){
40057 this.el.setHeight(size);
40062 getBox : function(){
40063 if(this.collapsed){
40064 return this.collapsedEl.getBox();
40066 var box = this.el.getBox();
40068 box.height += this.split.el.getHeight();
40073 updateBox : function(box){
40074 if(this.split && !this.collapsed){
40075 box.height -= this.split.el.getHeight();
40076 this.split.el.setLeft(box.x);
40077 this.split.el.setTop(box.y+box.height);
40078 this.split.el.setWidth(box.width);
40080 if(this.collapsed){
40081 this.updateBody(box.width, null);
40083 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40091 Roo.bootstrap.layout.South = function(config){
40092 config.region = 'south';
40093 config.cursor = 's-resize';
40094 Roo.bootstrap.layout.Split.call(this, config);
40096 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40097 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40098 this.split.el.addClass("roo-layout-split-v");
40103 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40104 orientation: Roo.bootstrap.SplitBar.VERTICAL,
40106 onRender : function(ctr, pos)
40108 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40109 var size = this.config.initialSize || this.config.height;
40110 if(this.el && typeof size != "undefined"){
40111 this.el.setHeight(size);
40116 getBox : function(){
40117 if(this.collapsed){
40118 return this.collapsedEl.getBox();
40120 var box = this.el.getBox();
40122 var sh = this.split.el.getHeight();
40129 updateBox : function(box){
40130 if(this.split && !this.collapsed){
40131 var sh = this.split.el.getHeight();
40134 this.split.el.setLeft(box.x);
40135 this.split.el.setTop(box.y-sh);
40136 this.split.el.setWidth(box.width);
40138 if(this.collapsed){
40139 this.updateBody(box.width, null);
40141 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40145 Roo.bootstrap.layout.East = function(config){
40146 config.region = "east";
40147 config.cursor = "e-resize";
40148 Roo.bootstrap.layout.Split.call(this, config);
40150 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40151 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40152 this.split.el.addClass("roo-layout-split-h");
40156 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40157 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40159 onRender : function(ctr, pos)
40161 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40162 var size = this.config.initialSize || this.config.width;
40163 if(this.el && typeof size != "undefined"){
40164 this.el.setWidth(size);
40169 getBox : function(){
40170 if(this.collapsed){
40171 return this.collapsedEl.getBox();
40173 var box = this.el.getBox();
40175 var sw = this.split.el.getWidth();
40182 updateBox : function(box){
40183 if(this.split && !this.collapsed){
40184 var sw = this.split.el.getWidth();
40186 this.split.el.setLeft(box.x);
40187 this.split.el.setTop(box.y);
40188 this.split.el.setHeight(box.height);
40191 if(this.collapsed){
40192 this.updateBody(null, box.height);
40194 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40198 Roo.bootstrap.layout.West = function(config){
40199 config.region = "west";
40200 config.cursor = "w-resize";
40202 Roo.bootstrap.layout.Split.call(this, config);
40204 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40205 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40206 this.split.el.addClass("roo-layout-split-h");
40210 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40211 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40213 onRender: function(ctr, pos)
40215 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40216 var size = this.config.initialSize || this.config.width;
40217 if(typeof size != "undefined"){
40218 this.el.setWidth(size);
40222 getBox : function(){
40223 if(this.collapsed){
40224 return this.collapsedEl.getBox();
40226 var box = this.el.getBox();
40227 if (box.width == 0) {
40228 box.width = this.config.width; // kludge?
40231 box.width += this.split.el.getWidth();
40236 updateBox : function(box){
40237 if(this.split && !this.collapsed){
40238 var sw = this.split.el.getWidth();
40240 this.split.el.setLeft(box.x+box.width);
40241 this.split.el.setTop(box.y);
40242 this.split.el.setHeight(box.height);
40244 if(this.collapsed){
40245 this.updateBody(null, box.height);
40247 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40251 * Ext JS Library 1.1.1
40252 * Copyright(c) 2006-2007, Ext JS, LLC.
40254 * Originally Released Under LGPL - original licence link has changed is not relivant.
40257 * <script type="text/javascript">
40260 * @class Roo.bootstrap.paenl.Content
40261 * @extends Roo.util.Observable
40262 * @children Roo.bootstrap.Component
40263 * @parent builder Roo.bootstrap.layout.Border
40264 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
40265 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40266 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40267 * @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
40268 * @cfg {Boolean} closable True if the panel can be closed/removed
40269 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40270 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40271 * @cfg {Toolbar} toolbar A toolbar for this panel
40272 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40273 * @cfg {String} title The title for this panel
40274 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40275 * @cfg {String} url Calls {@link #setUrl} with this value
40276 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40277 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40278 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40279 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40280 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40281 * @cfg {Boolean} badges render the badges
40282 * @cfg {String} cls extra classes to use
40283 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40286 * Create a new ContentPanel.
40287 * @param {String/Object} config A string to set only the title or a config object
40290 Roo.bootstrap.panel.Content = function( config){
40292 this.tpl = config.tpl || false;
40294 var el = config.el;
40295 var content = config.content;
40297 if(config.autoCreate){ // xtype is available if this is called from factory
40300 this.el = Roo.get(el);
40301 if(!this.el && config && config.autoCreate){
40302 if(typeof config.autoCreate == "object"){
40303 if(!config.autoCreate.id){
40304 config.autoCreate.id = config.id||el;
40306 this.el = Roo.DomHelper.append(document.body,
40307 config.autoCreate, true);
40311 cls: (config.cls || '') +
40312 (config.background ? ' bg-' + config.background : '') +
40313 " roo-layout-inactive-content",
40316 if (config.iframe) {
40320 style : 'border: 0px',
40321 src : 'about:blank'
40327 elcfg.html = config.html;
40331 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40332 if (config.iframe) {
40333 this.iframeEl = this.el.select('iframe',true).first();
40338 this.closable = false;
40339 this.loaded = false;
40340 this.active = false;
40343 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40345 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40347 this.wrapEl = this.el; //this.el.wrap();
40349 if (config.toolbar.items) {
40350 ti = config.toolbar.items ;
40351 delete config.toolbar.items ;
40355 this.toolbar.render(this.wrapEl, 'before');
40356 for(var i =0;i < ti.length;i++) {
40357 // Roo.log(['add child', items[i]]);
40358 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40360 this.toolbar.items = nitems;
40361 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40362 delete config.toolbar;
40366 // xtype created footer. - not sure if will work as we normally have to render first..
40367 if (this.footer && !this.footer.el && this.footer.xtype) {
40368 if (!this.wrapEl) {
40369 this.wrapEl = this.el.wrap();
40372 this.footer.container = this.wrapEl.createChild();
40374 this.footer = Roo.factory(this.footer, Roo);
40379 if(typeof config == "string"){
40380 this.title = config;
40382 Roo.apply(this, config);
40386 this.resizeEl = Roo.get(this.resizeEl, true);
40388 this.resizeEl = this.el;
40390 // handle view.xtype
40398 * Fires when this panel is activated.
40399 * @param {Roo.ContentPanel} this
40403 * @event deactivate
40404 * Fires when this panel is activated.
40405 * @param {Roo.ContentPanel} this
40407 "deactivate" : true,
40411 * Fires when this panel is resized if fitToFrame is true.
40412 * @param {Roo.ContentPanel} this
40413 * @param {Number} width The width after any component adjustments
40414 * @param {Number} height The height after any component adjustments
40420 * Fires when this tab is created
40421 * @param {Roo.ContentPanel} this
40427 * Fires when this content is scrolled
40428 * @param {Roo.ContentPanel} this
40429 * @param {Event} scrollEvent
40440 if(this.autoScroll && !this.iframe){
40441 this.resizeEl.setStyle("overflow", "auto");
40442 this.resizeEl.on('scroll', this.onScroll, this);
40444 // fix randome scrolling
40445 //this.el.on('scroll', function() {
40446 // Roo.log('fix random scolling');
40447 // this.scrollTo('top',0);
40450 content = content || this.content;
40452 this.setContent(content);
40454 if(config && config.url){
40455 this.setUrl(this.url, this.params, this.loadOnce);
40460 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40462 if (this.view && typeof(this.view.xtype) != 'undefined') {
40463 this.view.el = this.el.appendChild(document.createElement("div"));
40464 this.view = Roo.factory(this.view);
40465 this.view.render && this.view.render(false, '');
40469 this.fireEvent('render', this);
40472 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40482 /* Resize Element - use this to work out scroll etc. */
40485 setRegion : function(region){
40486 this.region = region;
40487 this.setActiveClass(region && !this.background);
40491 setActiveClass: function(state)
40494 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40495 this.el.setStyle('position','relative');
40497 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40498 this.el.setStyle('position', 'absolute');
40503 * Returns the toolbar for this Panel if one was configured.
40504 * @return {Roo.Toolbar}
40506 getToolbar : function(){
40507 return this.toolbar;
40510 setActiveState : function(active)
40512 this.active = active;
40513 this.setActiveClass(active);
40515 if(this.fireEvent("deactivate", this) === false){
40520 this.fireEvent("activate", this);
40524 * Updates this panel's element (not for iframe)
40525 * @param {String} content The new content
40526 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40528 setContent : function(content, loadScripts){
40533 this.el.update(content, loadScripts);
40536 ignoreResize : function(w, h)
40538 //return false; // always resize?
40539 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40542 this.lastSize = {width: w, height: h};
40547 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40548 * @return {Roo.UpdateManager} The UpdateManager
40550 getUpdateManager : function(){
40554 return this.el.getUpdateManager();
40557 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40558 * Does not work with IFRAME contents
40559 * @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:
40562 url: "your-url.php",
40563 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40564 callback: yourFunction,
40565 scope: yourObject, //(optional scope)
40568 text: "Loading...",
40574 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40575 * 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.
40576 * @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}
40577 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40578 * @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.
40579 * @return {Roo.ContentPanel} this
40587 var um = this.el.getUpdateManager();
40588 um.update.apply(um, arguments);
40594 * 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.
40595 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40596 * @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)
40597 * @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)
40598 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40600 setUrl : function(url, params, loadOnce){
40602 this.iframeEl.dom.src = url;
40606 if(this.refreshDelegate){
40607 this.removeListener("activate", this.refreshDelegate);
40609 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40610 this.on("activate", this.refreshDelegate);
40611 return this.el.getUpdateManager();
40614 _handleRefresh : function(url, params, loadOnce){
40615 if(!loadOnce || !this.loaded){
40616 var updater = this.el.getUpdateManager();
40617 updater.update(url, params, this._setLoaded.createDelegate(this));
40621 _setLoaded : function(){
40622 this.loaded = true;
40626 * Returns this panel's id
40629 getId : function(){
40634 * Returns this panel's element - used by regiosn to add.
40635 * @return {Roo.Element}
40637 getEl : function(){
40638 return this.wrapEl || this.el;
40643 adjustForComponents : function(width, height)
40645 //Roo.log('adjustForComponents ');
40646 if(this.resizeEl != this.el){
40647 width -= this.el.getFrameWidth('lr');
40648 height -= this.el.getFrameWidth('tb');
40651 var te = this.toolbar.getEl();
40652 te.setWidth(width);
40653 height -= te.getHeight();
40656 var te = this.footer.getEl();
40657 te.setWidth(width);
40658 height -= te.getHeight();
40662 if(this.adjustments){
40663 width += this.adjustments[0];
40664 height += this.adjustments[1];
40666 return {"width": width, "height": height};
40669 setSize : function(width, height){
40670 if(this.fitToFrame && !this.ignoreResize(width, height)){
40671 if(this.fitContainer && this.resizeEl != this.el){
40672 this.el.setSize(width, height);
40674 var size = this.adjustForComponents(width, height);
40676 this.iframeEl.setSize(width,height);
40679 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40680 this.fireEvent('resize', this, size.width, size.height);
40687 * Returns this panel's title
40690 getTitle : function(){
40692 if (typeof(this.title) != 'object') {
40697 for (var k in this.title) {
40698 if (!this.title.hasOwnProperty(k)) {
40702 if (k.indexOf('-') >= 0) {
40703 var s = k.split('-');
40704 for (var i = 0; i<s.length; i++) {
40705 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40708 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40715 * Set this panel's title
40716 * @param {String} title
40718 setTitle : function(title){
40719 this.title = title;
40721 this.region.updatePanelTitle(this, title);
40726 * Returns true is this panel was configured to be closable
40727 * @return {Boolean}
40729 isClosable : function(){
40730 return this.closable;
40733 beforeSlide : function(){
40735 this.resizeEl.clip();
40738 afterSlide : function(){
40740 this.resizeEl.unclip();
40744 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40745 * Will fail silently if the {@link #setUrl} method has not been called.
40746 * This does not activate the panel, just updates its content.
40748 refresh : function(){
40749 if(this.refreshDelegate){
40750 this.loaded = false;
40751 this.refreshDelegate();
40756 * Destroys this panel
40758 destroy : function(){
40759 this.el.removeAllListeners();
40760 var tempEl = document.createElement("span");
40761 tempEl.appendChild(this.el.dom);
40762 tempEl.innerHTML = "";
40768 * form - if the content panel contains a form - this is a reference to it.
40769 * @type {Roo.form.Form}
40773 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40774 * This contains a reference to it.
40780 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40790 * @param {Object} cfg Xtype definition of item to add.
40794 getChildContainer: function () {
40795 return this.getEl();
40799 onScroll : function(e)
40801 this.fireEvent('scroll', this, e);
40806 var ret = new Roo.factory(cfg);
40811 if (cfg.xtype.match(/^Form$/)) {
40814 //if (this.footer) {
40815 // el = this.footer.container.insertSibling(false, 'before');
40817 el = this.el.createChild();
40820 this.form = new Roo.form.Form(cfg);
40823 if ( this.form.allItems.length) {
40824 this.form.render(el.dom);
40828 // should only have one of theses..
40829 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40830 // views.. should not be just added - used named prop 'view''
40832 cfg.el = this.el.appendChild(document.createElement("div"));
40835 var ret = new Roo.factory(cfg);
40837 ret.render && ret.render(false, ''); // render blank..
40847 * @class Roo.bootstrap.panel.Grid
40848 * @extends Roo.bootstrap.panel.Content
40850 * Create a new GridPanel.
40851 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40852 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
40853 * @param {Object} config A the config object
40859 Roo.bootstrap.panel.Grid = function(config)
40863 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40864 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40866 config.el = this.wrapper;
40867 //this.el = this.wrapper;
40869 if (config.container) {
40870 // ctor'ed from a Border/panel.grid
40873 this.wrapper.setStyle("overflow", "hidden");
40874 this.wrapper.addClass('roo-grid-container');
40879 if(config.toolbar){
40880 var tool_el = this.wrapper.createChild();
40881 this.toolbar = Roo.factory(config.toolbar);
40883 if (config.toolbar.items) {
40884 ti = config.toolbar.items ;
40885 delete config.toolbar.items ;
40889 this.toolbar.render(tool_el);
40890 for(var i =0;i < ti.length;i++) {
40891 // Roo.log(['add child', items[i]]);
40892 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40894 this.toolbar.items = nitems;
40896 delete config.toolbar;
40899 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40900 config.grid.scrollBody = true;;
40901 config.grid.monitorWindowResize = false; // turn off autosizing
40902 config.grid.autoHeight = false;
40903 config.grid.autoWidth = false;
40905 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40907 if (config.background) {
40908 // render grid on panel activation (if panel background)
40909 this.on('activate', function(gp) {
40910 if (!gp.grid.rendered) {
40911 gp.grid.render(this.wrapper);
40912 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40917 this.grid.render(this.wrapper);
40918 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40921 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40922 // ??? needed ??? config.el = this.wrapper;
40927 // xtype created footer. - not sure if will work as we normally have to render first..
40928 if (this.footer && !this.footer.el && this.footer.xtype) {
40930 var ctr = this.grid.getView().getFooterPanel(true);
40931 this.footer.dataSource = this.grid.dataSource;
40932 this.footer = Roo.factory(this.footer, Roo);
40933 this.footer.render(ctr);
40943 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
40946 getId : function(){
40947 return this.grid.id;
40951 * Returns the grid for this panel
40952 * @return {Roo.bootstrap.Table}
40954 getGrid : function(){
40958 setSize : function(width, height)
40961 //if(!this.ignoreResize(width, height)){
40962 var grid = this.grid;
40963 var size = this.adjustForComponents(width, height);
40964 // tfoot is not a footer?
40967 var gridel = grid.getGridEl();
40968 gridel.setSize(size.width, size.height);
40970 var tbd = grid.getGridEl().select('tbody', true).first();
40971 var thd = grid.getGridEl().select('thead',true).first();
40972 var tbf= grid.getGridEl().select('tfoot', true).first();
40975 size.height -= tbf.getHeight();
40978 size.height -= thd.getHeight();
40981 tbd.setSize(size.width, size.height );
40982 // this is for the account management tab -seems to work there.
40983 var thd = grid.getGridEl().select('thead',true).first();
40985 // tbd.setSize(size.width, size.height - thd.getHeight());
40995 beforeSlide : function(){
40996 this.grid.getView().scroller.clip();
40999 afterSlide : function(){
41000 this.grid.getView().scroller.unclip();
41003 destroy : function(){
41004 this.grid.destroy();
41006 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
41011 * @class Roo.bootstrap.panel.Nest
41012 * @extends Roo.bootstrap.panel.Content
41014 * Create a new Panel, that can contain a layout.Border.
41017 * @param {String/Object} config A string to set only the title or a config object
41019 Roo.bootstrap.panel.Nest = function(config)
41021 // construct with only one argument..
41022 /* FIXME - implement nicer consturctors
41023 if (layout.layout) {
41025 layout = config.layout;
41026 delete config.layout;
41028 if (layout.xtype && !layout.getEl) {
41029 // then layout needs constructing..
41030 layout = Roo.factory(layout, Roo);
41034 config.el = config.layout.getEl();
41036 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41038 config.layout.monitorWindowResize = false; // turn off autosizing
41039 this.layout = config.layout;
41040 this.layout.getEl().addClass("roo-layout-nested-layout");
41041 this.layout.parent = this;
41048 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41050 * @cfg {Roo.BorderLayout} layout The layout for this panel
41054 setSize : function(width, height){
41055 if(!this.ignoreResize(width, height)){
41056 var size = this.adjustForComponents(width, height);
41057 var el = this.layout.getEl();
41058 if (size.height < 1) {
41059 el.setWidth(size.width);
41061 el.setSize(size.width, size.height);
41063 var touch = el.dom.offsetWidth;
41064 this.layout.layout();
41065 // ie requires a double layout on the first pass
41066 if(Roo.isIE && !this.initialized){
41067 this.initialized = true;
41068 this.layout.layout();
41073 // activate all subpanels if not currently active..
41075 setActiveState : function(active){
41076 this.active = active;
41077 this.setActiveClass(active);
41080 this.fireEvent("deactivate", this);
41084 this.fireEvent("activate", this);
41085 // not sure if this should happen before or after..
41086 if (!this.layout) {
41087 return; // should not happen..
41090 for (var r in this.layout.regions) {
41091 reg = this.layout.getRegion(r);
41092 if (reg.getActivePanel()) {
41093 //reg.showPanel(reg.getActivePanel()); // force it to activate..
41094 reg.setActivePanel(reg.getActivePanel());
41097 if (!reg.panels.length) {
41100 reg.showPanel(reg.getPanel(0));
41109 * Returns the nested BorderLayout for this panel
41110 * @return {Roo.BorderLayout}
41112 getLayout : function(){
41113 return this.layout;
41117 * Adds a xtype elements to the layout of the nested panel
41121 xtype : 'ContentPanel',
41128 xtype : 'NestedLayoutPanel',
41134 items : [ ... list of content panels or nested layout panels.. ]
41138 * @param {Object} cfg Xtype definition of item to add.
41140 addxtype : function(cfg) {
41141 return this.layout.addxtype(cfg);
41146 * Ext JS Library 1.1.1
41147 * Copyright(c) 2006-2007, Ext JS, LLC.
41149 * Originally Released Under LGPL - original licence link has changed is not relivant.
41152 * <script type="text/javascript">
41155 * @class Roo.TabPanel
41156 * @extends Roo.util.Observable
41157 * A lightweight tab container.
41161 // basic tabs 1, built from existing content
41162 var tabs = new Roo.TabPanel("tabs1");
41163 tabs.addTab("script", "View Script");
41164 tabs.addTab("markup", "View Markup");
41165 tabs.activate("script");
41167 // more advanced tabs, built from javascript
41168 var jtabs = new Roo.TabPanel("jtabs");
41169 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41171 // set up the UpdateManager
41172 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41173 var updater = tab2.getUpdateManager();
41174 updater.setDefaultUrl("ajax1.htm");
41175 tab2.on('activate', updater.refresh, updater, true);
41177 // Use setUrl for Ajax loading
41178 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41179 tab3.setUrl("ajax2.htm", null, true);
41182 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41185 jtabs.activate("jtabs-1");
41188 * Create a new TabPanel.
41189 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41190 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41192 Roo.bootstrap.panel.Tabs = function(config){
41194 * The container element for this TabPanel.
41195 * @type Roo.Element
41197 this.el = Roo.get(config.el);
41200 if(typeof config == "boolean"){
41201 this.tabPosition = config ? "bottom" : "top";
41203 Roo.apply(this, config);
41207 if(this.tabPosition == "bottom"){
41208 // if tabs are at the bottom = create the body first.
41209 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41210 this.el.addClass("roo-tabs-bottom");
41212 // next create the tabs holders
41214 if (this.tabPosition == "west"){
41216 var reg = this.region; // fake it..
41218 if (!reg.mgr.parent) {
41221 reg = reg.mgr.parent.region;
41223 Roo.log("got nest?");
41225 if (reg.mgr.getRegion('west')) {
41226 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41227 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41228 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41229 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41230 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41238 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41239 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41240 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41241 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41246 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41249 // finally - if tabs are at the top, then create the body last..
41250 if(this.tabPosition != "bottom"){
41251 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41252 * @type Roo.Element
41254 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41255 this.el.addClass("roo-tabs-top");
41259 this.bodyEl.setStyle("position", "relative");
41261 this.active = null;
41262 this.activateDelegate = this.activate.createDelegate(this);
41267 * Fires when the active tab changes
41268 * @param {Roo.TabPanel} this
41269 * @param {Roo.TabPanelItem} activePanel The new active tab
41273 * @event beforetabchange
41274 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41275 * @param {Roo.TabPanel} this
41276 * @param {Object} e Set cancel to true on this object to cancel the tab change
41277 * @param {Roo.TabPanelItem} tab The tab being changed to
41279 "beforetabchange" : true
41282 Roo.EventManager.onWindowResize(this.onResize, this);
41283 this.cpad = this.el.getPadding("lr");
41284 this.hiddenCount = 0;
41287 // toolbar on the tabbar support...
41288 if (this.toolbar) {
41289 alert("no toolbar support yet");
41290 this.toolbar = false;
41292 var tcfg = this.toolbar;
41293 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41294 this.toolbar = new Roo.Toolbar(tcfg);
41295 if (Roo.isSafari) {
41296 var tbl = tcfg.container.child('table', true);
41297 tbl.setAttribute('width', '100%');
41305 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41308 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41310 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41312 tabPosition : "top",
41314 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41316 currentTabWidth : 0,
41318 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41322 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41326 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41328 preferredTabWidth : 175,
41330 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41332 resizeTabs : false,
41334 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41336 monitorResize : true,
41338 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41340 toolbar : false, // set by caller..
41342 region : false, /// set by caller
41344 disableTooltips : true, // not used yet...
41347 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41348 * @param {String} id The id of the div to use <b>or create</b>
41349 * @param {String} text The text for the tab
41350 * @param {String} content (optional) Content to put in the TabPanelItem body
41351 * @param {Boolean} closable (optional) True to create a close icon on the tab
41352 * @return {Roo.TabPanelItem} The created TabPanelItem
41354 addTab : function(id, text, content, closable, tpl)
41356 var item = new Roo.bootstrap.panel.TabItem({
41360 closable : closable,
41363 this.addTabItem(item);
41365 item.setContent(content);
41371 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41372 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41373 * @return {Roo.TabPanelItem}
41375 getTab : function(id){
41376 return this.items[id];
41380 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41381 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41383 hideTab : function(id){
41384 var t = this.items[id];
41387 this.hiddenCount++;
41388 this.autoSizeTabs();
41393 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41394 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41396 unhideTab : function(id){
41397 var t = this.items[id];
41399 t.setHidden(false);
41400 this.hiddenCount--;
41401 this.autoSizeTabs();
41406 * Adds an existing {@link Roo.TabPanelItem}.
41407 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41409 addTabItem : function(item)
41411 this.items[item.id] = item;
41412 this.items.push(item);
41413 this.autoSizeTabs();
41414 // if(this.resizeTabs){
41415 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41416 // this.autoSizeTabs();
41418 // item.autoSize();
41423 * Removes a {@link Roo.TabPanelItem}.
41424 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41426 removeTab : function(id){
41427 var items = this.items;
41428 var tab = items[id];
41429 if(!tab) { return; }
41430 var index = items.indexOf(tab);
41431 if(this.active == tab && items.length > 1){
41432 var newTab = this.getNextAvailable(index);
41437 this.stripEl.dom.removeChild(tab.pnode.dom);
41438 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41439 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41441 items.splice(index, 1);
41442 delete this.items[tab.id];
41443 tab.fireEvent("close", tab);
41444 tab.purgeListeners();
41445 this.autoSizeTabs();
41448 getNextAvailable : function(start){
41449 var items = this.items;
41451 // look for a next tab that will slide over to
41452 // replace the one being removed
41453 while(index < items.length){
41454 var item = items[++index];
41455 if(item && !item.isHidden()){
41459 // if one isn't found select the previous tab (on the left)
41462 var item = items[--index];
41463 if(item && !item.isHidden()){
41471 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41472 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41474 disableTab : function(id){
41475 var tab = this.items[id];
41476 if(tab && this.active != tab){
41482 * Enables a {@link Roo.TabPanelItem} that is disabled.
41483 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41485 enableTab : function(id){
41486 var tab = this.items[id];
41491 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41492 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41493 * @return {Roo.TabPanelItem} The TabPanelItem.
41495 activate : function(id)
41497 //Roo.log('activite:' + id);
41499 var tab = this.items[id];
41503 if(tab == this.active || tab.disabled){
41507 this.fireEvent("beforetabchange", this, e, tab);
41508 if(e.cancel !== true && !tab.disabled){
41510 this.active.hide();
41512 this.active = this.items[id];
41513 this.active.show();
41514 this.fireEvent("tabchange", this, this.active);
41520 * Gets the active {@link Roo.TabPanelItem}.
41521 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41523 getActiveTab : function(){
41524 return this.active;
41528 * Updates the tab body element to fit the height of the container element
41529 * for overflow scrolling
41530 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41532 syncHeight : function(targetHeight){
41533 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41534 var bm = this.bodyEl.getMargins();
41535 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41536 this.bodyEl.setHeight(newHeight);
41540 onResize : function(){
41541 if(this.monitorResize){
41542 this.autoSizeTabs();
41547 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41549 beginUpdate : function(){
41550 this.updating = true;
41554 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41556 endUpdate : function(){
41557 this.updating = false;
41558 this.autoSizeTabs();
41562 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41564 autoSizeTabs : function()
41566 var count = this.items.length;
41567 var vcount = count - this.hiddenCount;
41570 this.stripEl.hide();
41572 this.stripEl.show();
41575 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41580 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41581 var availWidth = Math.floor(w / vcount);
41582 var b = this.stripBody;
41583 if(b.getWidth() > w){
41584 var tabs = this.items;
41585 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41586 if(availWidth < this.minTabWidth){
41587 /*if(!this.sleft){ // incomplete scrolling code
41588 this.createScrollButtons();
41591 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41594 if(this.currentTabWidth < this.preferredTabWidth){
41595 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41601 * Returns the number of tabs in this TabPanel.
41604 getCount : function(){
41605 return this.items.length;
41609 * Resizes all the tabs to the passed width
41610 * @param {Number} The new width
41612 setTabWidth : function(width){
41613 this.currentTabWidth = width;
41614 for(var i = 0, len = this.items.length; i < len; i++) {
41615 if(!this.items[i].isHidden()) {
41616 this.items[i].setWidth(width);
41622 * Destroys this TabPanel
41623 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41625 destroy : function(removeEl){
41626 Roo.EventManager.removeResizeListener(this.onResize, this);
41627 for(var i = 0, len = this.items.length; i < len; i++){
41628 this.items[i].purgeListeners();
41630 if(removeEl === true){
41631 this.el.update("");
41636 createStrip : function(container)
41638 var strip = document.createElement("nav");
41639 strip.className = Roo.bootstrap.version == 4 ?
41640 "navbar-light bg-light" :
41641 "navbar navbar-default"; //"x-tabs-wrap";
41642 container.appendChild(strip);
41646 createStripList : function(strip)
41648 // div wrapper for retard IE
41649 // returns the "tr" element.
41650 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41651 //'<div class="x-tabs-strip-wrap">'+
41652 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41653 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41654 return strip.firstChild; //.firstChild.firstChild.firstChild;
41656 createBody : function(container)
41658 var body = document.createElement("div");
41659 Roo.id(body, "tab-body");
41660 //Roo.fly(body).addClass("x-tabs-body");
41661 Roo.fly(body).addClass("tab-content");
41662 container.appendChild(body);
41665 createItemBody :function(bodyEl, id){
41666 var body = Roo.getDom(id);
41668 body = document.createElement("div");
41671 //Roo.fly(body).addClass("x-tabs-item-body");
41672 Roo.fly(body).addClass("tab-pane");
41673 bodyEl.insertBefore(body, bodyEl.firstChild);
41677 createStripElements : function(stripEl, text, closable, tpl)
41679 var td = document.createElement("li"); // was td..
41680 td.className = 'nav-item';
41682 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41685 stripEl.appendChild(td);
41687 td.className = "x-tabs-closable";
41688 if(!this.closeTpl){
41689 this.closeTpl = new Roo.Template(
41690 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41691 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41692 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41695 var el = this.closeTpl.overwrite(td, {"text": text});
41696 var close = el.getElementsByTagName("div")[0];
41697 var inner = el.getElementsByTagName("em")[0];
41698 return {"el": el, "close": close, "inner": inner};
41701 // not sure what this is..
41702 // if(!this.tabTpl){
41703 //this.tabTpl = new Roo.Template(
41704 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41705 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41707 // this.tabTpl = new Roo.Template(
41708 // '<a href="#">' +
41709 // '<span unselectable="on"' +
41710 // (this.disableTooltips ? '' : ' title="{text}"') +
41711 // ' >{text}</span></a>'
41717 var template = tpl || this.tabTpl || false;
41720 template = new Roo.Template(
41721 Roo.bootstrap.version == 4 ?
41723 '<a class="nav-link" href="#" unselectable="on"' +
41724 (this.disableTooltips ? '' : ' title="{text}"') +
41727 '<a class="nav-link" href="#">' +
41728 '<span unselectable="on"' +
41729 (this.disableTooltips ? '' : ' title="{text}"') +
41730 ' >{text}</span></a>'
41735 switch (typeof(template)) {
41739 template = new Roo.Template(template);
41745 var el = template.overwrite(td, {"text": text});
41747 var inner = el.getElementsByTagName("span")[0];
41749 return {"el": el, "inner": inner};
41757 * @class Roo.TabPanelItem
41758 * @extends Roo.util.Observable
41759 * Represents an individual item (tab plus body) in a TabPanel.
41760 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41761 * @param {String} id The id of this TabPanelItem
41762 * @param {String} text The text for the tab of this TabPanelItem
41763 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41765 Roo.bootstrap.panel.TabItem = function(config){
41767 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41768 * @type Roo.TabPanel
41770 this.tabPanel = config.panel;
41772 * The id for this TabPanelItem
41775 this.id = config.id;
41777 this.disabled = false;
41779 this.text = config.text;
41781 this.loaded = false;
41782 this.closable = config.closable;
41785 * The body element for this TabPanelItem.
41786 * @type Roo.Element
41788 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41789 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41790 this.bodyEl.setStyle("display", "block");
41791 this.bodyEl.setStyle("zoom", "1");
41792 //this.hideAction();
41794 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41796 this.el = Roo.get(els.el);
41797 this.inner = Roo.get(els.inner, true);
41798 this.textEl = Roo.bootstrap.version == 4 ?
41799 this.el : Roo.get(this.el.dom.firstChild, true);
41801 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41802 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41805 // this.el.on("mousedown", this.onTabMouseDown, this);
41806 this.el.on("click", this.onTabClick, this);
41808 if(config.closable){
41809 var c = Roo.get(els.close, true);
41810 c.dom.title = this.closeText;
41811 c.addClassOnOver("close-over");
41812 c.on("click", this.closeClick, this);
41818 * Fires when this tab becomes the active tab.
41819 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41820 * @param {Roo.TabPanelItem} this
41824 * @event beforeclose
41825 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41826 * @param {Roo.TabPanelItem} this
41827 * @param {Object} e Set cancel to true on this object to cancel the close.
41829 "beforeclose": true,
41832 * Fires when this tab is closed.
41833 * @param {Roo.TabPanelItem} this
41837 * @event deactivate
41838 * Fires when this tab is no longer the active tab.
41839 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41840 * @param {Roo.TabPanelItem} this
41842 "deactivate" : true
41844 this.hidden = false;
41846 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41849 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41851 purgeListeners : function(){
41852 Roo.util.Observable.prototype.purgeListeners.call(this);
41853 this.el.removeAllListeners();
41856 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41859 this.status_node.addClass("active");
41862 this.tabPanel.stripWrap.repaint();
41864 this.fireEvent("activate", this.tabPanel, this);
41868 * Returns true if this tab is the active tab.
41869 * @return {Boolean}
41871 isActive : function(){
41872 return this.tabPanel.getActiveTab() == this;
41876 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41879 this.status_node.removeClass("active");
41881 this.fireEvent("deactivate", this.tabPanel, this);
41884 hideAction : function(){
41885 this.bodyEl.hide();
41886 this.bodyEl.setStyle("position", "absolute");
41887 this.bodyEl.setLeft("-20000px");
41888 this.bodyEl.setTop("-20000px");
41891 showAction : function(){
41892 this.bodyEl.setStyle("position", "relative");
41893 this.bodyEl.setTop("");
41894 this.bodyEl.setLeft("");
41895 this.bodyEl.show();
41899 * Set the tooltip for the tab.
41900 * @param {String} tooltip The tab's tooltip
41902 setTooltip : function(text){
41903 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41904 this.textEl.dom.qtip = text;
41905 this.textEl.dom.removeAttribute('title');
41907 this.textEl.dom.title = text;
41911 onTabClick : function(e){
41912 e.preventDefault();
41913 this.tabPanel.activate(this.id);
41916 onTabMouseDown : function(e){
41917 e.preventDefault();
41918 this.tabPanel.activate(this.id);
41921 getWidth : function(){
41922 return this.inner.getWidth();
41925 setWidth : function(width){
41926 var iwidth = width - this.linode.getPadding("lr");
41927 this.inner.setWidth(iwidth);
41928 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41929 this.linode.setWidth(width);
41933 * Show or hide the tab
41934 * @param {Boolean} hidden True to hide or false to show.
41936 setHidden : function(hidden){
41937 this.hidden = hidden;
41938 this.linode.setStyle("display", hidden ? "none" : "");
41942 * Returns true if this tab is "hidden"
41943 * @return {Boolean}
41945 isHidden : function(){
41946 return this.hidden;
41950 * Returns the text for this tab
41953 getText : function(){
41957 autoSize : function(){
41958 //this.el.beginMeasure();
41959 this.textEl.setWidth(1);
41961 * #2804 [new] Tabs in Roojs
41962 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41964 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41965 //this.el.endMeasure();
41969 * Sets the text for the tab (Note: this also sets the tooltip text)
41970 * @param {String} text The tab's text and tooltip
41972 setText : function(text){
41974 this.textEl.update(text);
41975 this.setTooltip(text);
41976 //if(!this.tabPanel.resizeTabs){
41977 // this.autoSize();
41981 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41983 activate : function(){
41984 this.tabPanel.activate(this.id);
41988 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41990 disable : function(){
41991 if(this.tabPanel.active != this){
41992 this.disabled = true;
41993 this.status_node.addClass("disabled");
41998 * Enables this TabPanelItem if it was previously disabled.
42000 enable : function(){
42001 this.disabled = false;
42002 this.status_node.removeClass("disabled");
42006 * Sets the content for this TabPanelItem.
42007 * @param {String} content The content
42008 * @param {Boolean} loadScripts true to look for and load scripts
42010 setContent : function(content, loadScripts){
42011 this.bodyEl.update(content, loadScripts);
42015 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42016 * @return {Roo.UpdateManager} The UpdateManager
42018 getUpdateManager : function(){
42019 return this.bodyEl.getUpdateManager();
42023 * Set a URL to be used to load the content for this TabPanelItem.
42024 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42025 * @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)
42026 * @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)
42027 * @return {Roo.UpdateManager} The UpdateManager
42029 setUrl : function(url, params, loadOnce){
42030 if(this.refreshDelegate){
42031 this.un('activate', this.refreshDelegate);
42033 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42034 this.on("activate", this.refreshDelegate);
42035 return this.bodyEl.getUpdateManager();
42039 _handleRefresh : function(url, params, loadOnce){
42040 if(!loadOnce || !this.loaded){
42041 var updater = this.bodyEl.getUpdateManager();
42042 updater.update(url, params, this._setLoaded.createDelegate(this));
42047 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
42048 * Will fail silently if the setUrl method has not been called.
42049 * This does not activate the panel, just updates its content.
42051 refresh : function(){
42052 if(this.refreshDelegate){
42053 this.loaded = false;
42054 this.refreshDelegate();
42059 _setLoaded : function(){
42060 this.loaded = true;
42064 closeClick : function(e){
42067 this.fireEvent("beforeclose", this, o);
42068 if(o.cancel !== true){
42069 this.tabPanel.removeTab(this.id);
42073 * The text displayed in the tooltip for the close icon.
42076 closeText : "Close this tab"
42079 * This script refer to:
42080 * Title: International Telephone Input
42081 * Author: Jack O'Connor
42082 * Code version: v12.1.12
42083 * Availability: https://github.com/jackocnr/intl-tel-input.git
42086 Roo.bootstrap.form.PhoneInputData = function() {
42089 "Afghanistan (افغانستان)",
42094 "Albania (Shqipëri)",
42099 "Algeria (الجزائر)",
42124 "Antigua and Barbuda",
42134 "Armenia (Հայաստան)",
42150 "Austria (Österreich)",
42155 "Azerbaijan (Azərbaycan)",
42165 "Bahrain (البحرين)",
42170 "Bangladesh (বাংলাদেশ)",
42180 "Belarus (Беларусь)",
42185 "Belgium (België)",
42215 "Bosnia and Herzegovina (Босна и Херцеговина)",
42230 "British Indian Ocean Territory",
42235 "British Virgin Islands",
42245 "Bulgaria (България)",
42255 "Burundi (Uburundi)",
42260 "Cambodia (កម្ពុជា)",
42265 "Cameroon (Cameroun)",
42274 ["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"]
42277 "Cape Verde (Kabu Verdi)",
42282 "Caribbean Netherlands",
42293 "Central African Republic (République centrafricaine)",
42313 "Christmas Island",
42319 "Cocos (Keeling) Islands",
42330 "Comoros (جزر القمر)",
42335 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42340 "Congo (Republic) (Congo-Brazzaville)",
42360 "Croatia (Hrvatska)",
42381 "Czech Republic (Česká republika)",
42386 "Denmark (Danmark)",
42401 "Dominican Republic (República Dominicana)",
42405 ["809", "829", "849"]
42423 "Equatorial Guinea (Guinea Ecuatorial)",
42443 "Falkland Islands (Islas Malvinas)",
42448 "Faroe Islands (Føroyar)",
42469 "French Guiana (Guyane française)",
42474 "French Polynesia (Polynésie française)",
42489 "Georgia (საქართველო)",
42494 "Germany (Deutschland)",
42514 "Greenland (Kalaallit Nunaat)",
42551 "Guinea-Bissau (Guiné Bissau)",
42576 "Hungary (Magyarország)",
42581 "Iceland (Ísland)",
42601 "Iraq (العراق)",
42617 "Israel (ישראל)",
42644 "Jordan (الأردن)",
42649 "Kazakhstan (Казахстан)",
42670 "Kuwait (الكويت)",
42675 "Kyrgyzstan (Кыргызстан)",
42685 "Latvia (Latvija)",
42690 "Lebanon (لبنان)",
42705 "Libya (ليبيا)",
42715 "Lithuania (Lietuva)",
42730 "Macedonia (FYROM) (Македонија)",
42735 "Madagascar (Madagasikara)",
42765 "Marshall Islands",
42775 "Mauritania (موريتانيا)",
42780 "Mauritius (Moris)",
42801 "Moldova (Republica Moldova)",
42811 "Mongolia (Монгол)",
42816 "Montenegro (Crna Gora)",
42826 "Morocco (المغرب)",
42832 "Mozambique (Moçambique)",
42837 "Myanmar (Burma) (မြန်မာ)",
42842 "Namibia (Namibië)",
42857 "Netherlands (Nederland)",
42862 "New Caledonia (Nouvelle-Calédonie)",
42897 "North Korea (조선 민주주의 인민 공화국)",
42902 "Northern Mariana Islands",
42918 "Pakistan (پاکستان)",
42928 "Palestine (فلسطين)",
42938 "Papua New Guinea",
42980 "Réunion (La Réunion)",
42986 "Romania (România)",
43002 "Saint Barthélemy",
43013 "Saint Kitts and Nevis",
43023 "Saint Martin (Saint-Martin (partie française))",
43029 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43034 "Saint Vincent and the Grenadines",
43049 "São Tomé and Príncipe (São Tomé e Príncipe)",
43054 "Saudi Arabia (المملكة العربية السعودية)",
43059 "Senegal (Sénégal)",
43089 "Slovakia (Slovensko)",
43094 "Slovenia (Slovenija)",
43104 "Somalia (Soomaaliya)",
43114 "South Korea (대한민국)",
43119 "South Sudan (جنوب السودان)",
43129 "Sri Lanka (ශ්රී ලංකාව)",
43134 "Sudan (السودان)",
43144 "Svalbard and Jan Mayen",
43155 "Sweden (Sverige)",
43160 "Switzerland (Schweiz)",
43165 "Syria (سوريا)",
43210 "Trinidad and Tobago",
43215 "Tunisia (تونس)",
43220 "Turkey (Türkiye)",
43230 "Turks and Caicos Islands",
43240 "U.S. Virgin Islands",
43250 "Ukraine (Україна)",
43255 "United Arab Emirates (الإمارات العربية المتحدة)",
43277 "Uzbekistan (Oʻzbekiston)",
43287 "Vatican City (Città del Vaticano)",
43298 "Vietnam (Việt Nam)",
43303 "Wallis and Futuna (Wallis-et-Futuna)",
43308 "Western Sahara (الصحراء الغربية)",
43314 "Yemen (اليمن)",
43338 * This script refer to:
43339 * Title: International Telephone Input
43340 * Author: Jack O'Connor
43341 * Code version: v12.1.12
43342 * Availability: https://github.com/jackocnr/intl-tel-input.git
43346 * @class Roo.bootstrap.form.PhoneInput
43347 * @extends Roo.bootstrap.form.TriggerField
43348 * An input with International dial-code selection
43350 * @cfg {String} defaultDialCode default '+852'
43351 * @cfg {Array} preferedCountries default []
43354 * Create a new PhoneInput.
43355 * @param {Object} config Configuration options
43358 Roo.bootstrap.form.PhoneInput = function(config) {
43359 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
43362 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
43364 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
43366 listWidth: undefined,
43368 selectedClass: 'active',
43370 invalidClass : "has-warning",
43372 validClass: 'has-success',
43374 allowed: '0123456789',
43379 * @cfg {String} defaultDialCode The default dial code when initializing the input
43381 defaultDialCode: '+852',
43384 * @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
43386 preferedCountries: false,
43388 getAutoCreate : function()
43390 var data = Roo.bootstrap.form.PhoneInputData();
43391 var align = this.labelAlign || this.parentLabelAlign();
43394 this.allCountries = [];
43395 this.dialCodeMapping = [];
43397 for (var i = 0; i < data.length; i++) {
43399 this.allCountries[i] = {
43403 priority: c[3] || 0,
43404 areaCodes: c[4] || null
43406 this.dialCodeMapping[c[2]] = {
43409 priority: c[3] || 0,
43410 areaCodes: c[4] || null
43422 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43423 maxlength: this.max_length,
43424 cls : 'form-control tel-input',
43425 autocomplete: 'new-password'
43428 var hiddenInput = {
43431 cls: 'hidden-tel-input'
43435 hiddenInput.name = this.name;
43438 if (this.disabled) {
43439 input.disabled = true;
43442 var flag_container = {
43459 cls: this.hasFeedback ? 'has-feedback' : '',
43465 cls: 'dial-code-holder',
43472 cls: 'roo-select2-container input-group',
43479 if (this.fieldLabel.length) {
43482 tooltip: 'This field is required'
43488 cls: 'control-label',
43494 html: this.fieldLabel
43497 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43503 if(this.indicatorpos == 'right') {
43504 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43511 if(align == 'left') {
43519 if(this.labelWidth > 12){
43520 label.style = "width: " + this.labelWidth + 'px';
43522 if(this.labelWidth < 13 && this.labelmd == 0){
43523 this.labelmd = this.labelWidth;
43525 if(this.labellg > 0){
43526 label.cls += ' col-lg-' + this.labellg;
43527 input.cls += ' col-lg-' + (12 - this.labellg);
43529 if(this.labelmd > 0){
43530 label.cls += ' col-md-' + this.labelmd;
43531 container.cls += ' col-md-' + (12 - this.labelmd);
43533 if(this.labelsm > 0){
43534 label.cls += ' col-sm-' + this.labelsm;
43535 container.cls += ' col-sm-' + (12 - this.labelsm);
43537 if(this.labelxs > 0){
43538 label.cls += ' col-xs-' + this.labelxs;
43539 container.cls += ' col-xs-' + (12 - this.labelxs);
43549 var settings = this;
43551 ['xs','sm','md','lg'].map(function(size){
43552 if (settings[size]) {
43553 cfg.cls += ' col-' + size + '-' + settings[size];
43557 this.store = new Roo.data.Store({
43558 proxy : new Roo.data.MemoryProxy({}),
43559 reader : new Roo.data.JsonReader({
43570 'name' : 'dialCode',
43574 'name' : 'priority',
43578 'name' : 'areaCodes',
43585 if(!this.preferedCountries) {
43586 this.preferedCountries = [
43593 var p = this.preferedCountries.reverse();
43596 for (var i = 0; i < p.length; i++) {
43597 for (var j = 0; j < this.allCountries.length; j++) {
43598 if(this.allCountries[j].iso2 == p[i]) {
43599 var t = this.allCountries[j];
43600 this.allCountries.splice(j,1);
43601 this.allCountries.unshift(t);
43607 this.store.proxy.data = {
43609 data: this.allCountries
43615 initEvents : function()
43618 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
43620 this.indicator = this.indicatorEl();
43621 this.flag = this.flagEl();
43622 this.dialCodeHolder = this.dialCodeHolderEl();
43624 this.trigger = this.el.select('div.flag-box',true).first();
43625 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43630 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43631 _this.list.setWidth(lw);
43634 this.list.on('mouseover', this.onViewOver, this);
43635 this.list.on('mousemove', this.onViewMove, this);
43636 this.inputEl().on("keyup", this.onKeyUp, this);
43637 this.inputEl().on("keypress", this.onKeyPress, this);
43639 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43641 this.view = new Roo.View(this.list, this.tpl, {
43642 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43645 this.view.on('click', this.onViewClick, this);
43646 this.setValue(this.defaultDialCode);
43649 onTriggerClick : function(e)
43651 Roo.log('trigger click');
43656 if(this.isExpanded()){
43658 this.hasFocus = false;
43660 this.store.load({});
43661 this.hasFocus = true;
43666 isExpanded : function()
43668 return this.list.isVisible();
43671 collapse : function()
43673 if(!this.isExpanded()){
43677 Roo.get(document).un('mousedown', this.collapseIf, this);
43678 Roo.get(document).un('mousewheel', this.collapseIf, this);
43679 this.fireEvent('collapse', this);
43683 expand : function()
43687 if(this.isExpanded() || !this.hasFocus){
43691 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43692 this.list.setWidth(lw);
43695 this.restrictHeight();
43697 Roo.get(document).on('mousedown', this.collapseIf, this);
43698 Roo.get(document).on('mousewheel', this.collapseIf, this);
43700 this.fireEvent('expand', this);
43703 restrictHeight : function()
43705 this.list.alignTo(this.inputEl(), this.listAlign);
43706 this.list.alignTo(this.inputEl(), this.listAlign);
43709 onViewOver : function(e, t)
43711 if(this.inKeyMode){
43714 var item = this.view.findItemFromChild(t);
43717 var index = this.view.indexOf(item);
43718 this.select(index, false);
43723 onViewClick : function(view, doFocus, el, e)
43725 var index = this.view.getSelectedIndexes()[0];
43727 var r = this.store.getAt(index);
43730 this.onSelect(r, index);
43732 if(doFocus !== false && !this.blockFocus){
43733 this.inputEl().focus();
43737 onViewMove : function(e, t)
43739 this.inKeyMode = false;
43742 select : function(index, scrollIntoView)
43744 this.selectedIndex = index;
43745 this.view.select(index);
43746 if(scrollIntoView !== false){
43747 var el = this.view.getNode(index);
43749 this.list.scrollChildIntoView(el, false);
43754 createList : function()
43756 this.list = Roo.get(document.body).createChild({
43758 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43759 style: 'display:none'
43762 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43765 collapseIf : function(e)
43767 var in_combo = e.within(this.el);
43768 var in_list = e.within(this.list);
43769 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43771 if (in_combo || in_list || is_list) {
43777 onSelect : function(record, index)
43779 if(this.fireEvent('beforeselect', this, record, index) !== false){
43781 this.setFlagClass(record.data.iso2);
43782 this.setDialCode(record.data.dialCode);
43783 this.hasFocus = false;
43785 this.fireEvent('select', this, record, index);
43789 flagEl : function()
43791 var flag = this.el.select('div.flag',true).first();
43798 dialCodeHolderEl : function()
43800 var d = this.el.select('input.dial-code-holder',true).first();
43807 setDialCode : function(v)
43809 this.dialCodeHolder.dom.value = '+'+v;
43812 setFlagClass : function(n)
43814 this.flag.dom.className = 'flag '+n;
43817 getValue : function()
43819 var v = this.inputEl().getValue();
43820 if(this.dialCodeHolder) {
43821 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43826 setValue : function(v)
43828 var d = this.getDialCode(v);
43830 //invalid dial code
43831 if(v.length == 0 || !d || d.length == 0) {
43833 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43834 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43840 this.setFlagClass(this.dialCodeMapping[d].iso2);
43841 this.setDialCode(d);
43842 this.inputEl().dom.value = v.replace('+'+d,'');
43843 this.hiddenEl().dom.value = this.getValue();
43848 getDialCode : function(v)
43852 if (v.length == 0) {
43853 return this.dialCodeHolder.dom.value;
43857 if (v.charAt(0) != "+") {
43860 var numericChars = "";
43861 for (var i = 1; i < v.length; i++) {
43862 var c = v.charAt(i);
43865 if (this.dialCodeMapping[numericChars]) {
43866 dialCode = v.substr(1, i);
43868 if (numericChars.length == 4) {
43878 this.setValue(this.defaultDialCode);
43882 hiddenEl : function()
43884 return this.el.select('input.hidden-tel-input',true).first();
43887 // after setting val
43888 onKeyUp : function(e){
43889 this.setValue(this.getValue());
43892 onKeyPress : function(e){
43893 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43900 * @class Roo.bootstrap.form.MoneyField
43901 * @extends Roo.bootstrap.form.ComboBox
43902 * Bootstrap MoneyField class
43905 * Create a new MoneyField.
43906 * @param {Object} config Configuration options
43909 Roo.bootstrap.form.MoneyField = function(config) {
43911 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
43915 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
43918 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43920 allowDecimals : true,
43922 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43924 decimalSeparator : ".",
43926 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43928 decimalPrecision : 0,
43930 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43932 allowNegative : true,
43934 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43938 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43940 minValue : Number.NEGATIVE_INFINITY,
43942 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43944 maxValue : Number.MAX_VALUE,
43946 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43948 minText : "The minimum value for this field is {0}",
43950 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43952 maxText : "The maximum value for this field is {0}",
43954 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43955 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43957 nanText : "{0} is not a valid number",
43959 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43963 * @cfg {String} defaults currency of the MoneyField
43964 * value should be in lkey
43966 defaultCurrency : false,
43968 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43970 thousandsDelimiter : false,
43972 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43981 * @cfg {Roo.data.Store} store Store to lookup currency??
43985 getAutoCreate : function()
43987 var align = this.labelAlign || this.parentLabelAlign();
43999 cls : 'form-control roo-money-amount-input',
44000 autocomplete: 'new-password'
44003 var hiddenInput = {
44007 cls: 'hidden-number-input'
44010 if(this.max_length) {
44011 input.maxlength = this.max_length;
44015 hiddenInput.name = this.name;
44018 if (this.disabled) {
44019 input.disabled = true;
44022 var clg = 12 - this.inputlg;
44023 var cmd = 12 - this.inputmd;
44024 var csm = 12 - this.inputsm;
44025 var cxs = 12 - this.inputxs;
44029 cls : 'row roo-money-field',
44033 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44037 cls: 'roo-select2-container input-group',
44041 cls : 'form-control roo-money-currency-input',
44042 autocomplete: 'new-password',
44044 name : this.currencyName
44048 cls : 'input-group-addon',
44062 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44066 cls: this.hasFeedback ? 'has-feedback' : '',
44077 if (this.fieldLabel.length) {
44080 tooltip: 'This field is required'
44086 cls: 'control-label',
44092 html: this.fieldLabel
44095 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44101 if(this.indicatorpos == 'right') {
44102 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44109 if(align == 'left') {
44117 if(this.labelWidth > 12){
44118 label.style = "width: " + this.labelWidth + 'px';
44120 if(this.labelWidth < 13 && this.labelmd == 0){
44121 this.labelmd = this.labelWidth;
44123 if(this.labellg > 0){
44124 label.cls += ' col-lg-' + this.labellg;
44125 input.cls += ' col-lg-' + (12 - this.labellg);
44127 if(this.labelmd > 0){
44128 label.cls += ' col-md-' + this.labelmd;
44129 container.cls += ' col-md-' + (12 - this.labelmd);
44131 if(this.labelsm > 0){
44132 label.cls += ' col-sm-' + this.labelsm;
44133 container.cls += ' col-sm-' + (12 - this.labelsm);
44135 if(this.labelxs > 0){
44136 label.cls += ' col-xs-' + this.labelxs;
44137 container.cls += ' col-xs-' + (12 - this.labelxs);
44148 var settings = this;
44150 ['xs','sm','md','lg'].map(function(size){
44151 if (settings[size]) {
44152 cfg.cls += ' col-' + size + '-' + settings[size];
44159 initEvents : function()
44161 this.indicator = this.indicatorEl();
44163 this.initCurrencyEvent();
44165 this.initNumberEvent();
44168 initCurrencyEvent : function()
44171 throw "can not find store for combo";
44174 this.store = Roo.factory(this.store, Roo.data);
44175 this.store.parent = this;
44179 this.triggerEl = this.el.select('.input-group-addon', true).first();
44181 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44186 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44187 _this.list.setWidth(lw);
44190 this.list.on('mouseover', this.onViewOver, this);
44191 this.list.on('mousemove', this.onViewMove, this);
44192 this.list.on('scroll', this.onViewScroll, this);
44195 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44198 this.view = new Roo.View(this.list, this.tpl, {
44199 singleSelect:true, store: this.store, selectedClass: this.selectedClass
44202 this.view.on('click', this.onViewClick, this);
44204 this.store.on('beforeload', this.onBeforeLoad, this);
44205 this.store.on('load', this.onLoad, this);
44206 this.store.on('loadexception', this.onLoadException, this);
44208 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44209 "up" : function(e){
44210 this.inKeyMode = true;
44214 "down" : function(e){
44215 if(!this.isExpanded()){
44216 this.onTriggerClick();
44218 this.inKeyMode = true;
44223 "enter" : function(e){
44226 if(this.fireEvent("specialkey", this, e)){
44227 this.onViewClick(false);
44233 "esc" : function(e){
44237 "tab" : function(e){
44240 if(this.fireEvent("specialkey", this, e)){
44241 this.onViewClick(false);
44249 doRelay : function(foo, bar, hname){
44250 if(hname == 'down' || this.scope.isExpanded()){
44251 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44259 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44263 initNumberEvent : function(e)
44265 this.inputEl().on("keydown" , this.fireKey, this);
44266 this.inputEl().on("focus", this.onFocus, this);
44267 this.inputEl().on("blur", this.onBlur, this);
44269 this.inputEl().relayEvent('keyup', this);
44271 if(this.indicator){
44272 this.indicator.addClass('invisible');
44275 this.originalValue = this.getValue();
44277 if(this.validationEvent == 'keyup'){
44278 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44279 this.inputEl().on('keyup', this.filterValidation, this);
44281 else if(this.validationEvent !== false){
44282 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44285 if(this.selectOnFocus){
44286 this.on("focus", this.preFocus, this);
44289 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44290 this.inputEl().on("keypress", this.filterKeys, this);
44292 this.inputEl().relayEvent('keypress', this);
44295 var allowed = "0123456789";
44297 if(this.allowDecimals){
44298 allowed += this.decimalSeparator;
44301 if(this.allowNegative){
44305 if(this.thousandsDelimiter) {
44309 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44311 var keyPress = function(e){
44313 var k = e.getKey();
44315 var c = e.getCharCode();
44318 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44319 allowed.indexOf(String.fromCharCode(c)) === -1
44325 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44329 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44334 this.inputEl().on("keypress", keyPress, this);
44338 onTriggerClick : function(e)
44345 this.loadNext = false;
44347 if(this.isExpanded()){
44352 this.hasFocus = true;
44354 if(this.triggerAction == 'all') {
44355 this.doQuery(this.allQuery, true);
44359 this.doQuery(this.getRawValue());
44362 getCurrency : function()
44364 var v = this.currencyEl().getValue();
44369 restrictHeight : function()
44371 this.list.alignTo(this.currencyEl(), this.listAlign);
44372 this.list.alignTo(this.currencyEl(), this.listAlign);
44375 onViewClick : function(view, doFocus, el, e)
44377 var index = this.view.getSelectedIndexes()[0];
44379 var r = this.store.getAt(index);
44382 this.onSelect(r, index);
44386 onSelect : function(record, index){
44388 if(this.fireEvent('beforeselect', this, record, index) !== false){
44390 this.setFromCurrencyData(index > -1 ? record.data : false);
44394 this.fireEvent('select', this, record, index);
44398 setFromCurrencyData : function(o)
44402 this.lastCurrency = o;
44404 if (this.currencyField) {
44405 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44407 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44410 this.lastSelectionText = currency;
44412 //setting default currency
44413 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44414 this.setCurrency(this.defaultCurrency);
44418 this.setCurrency(currency);
44421 setFromData : function(o)
44425 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44427 this.setFromCurrencyData(c);
44432 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44434 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44437 this.setValue(value);
44441 setCurrency : function(v)
44443 this.currencyValue = v;
44446 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44451 setValue : function(v)
44453 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44459 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44461 this.inputEl().dom.value = (v == '') ? '' :
44462 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44464 if(!this.allowZero && v === '0') {
44465 this.hiddenEl().dom.value = '';
44466 this.inputEl().dom.value = '';
44473 getRawValue : function()
44475 var v = this.inputEl().getValue();
44480 getValue : function()
44482 return this.fixPrecision(this.parseValue(this.getRawValue()));
44485 parseValue : function(value)
44487 if(this.thousandsDelimiter) {
44489 r = new RegExp(",", "g");
44490 value = value.replace(r, "");
44493 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44494 return isNaN(value) ? '' : value;
44498 fixPrecision : function(value)
44500 if(this.thousandsDelimiter) {
44502 r = new RegExp(",", "g");
44503 value = value.replace(r, "");
44506 var nan = isNaN(value);
44508 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44509 return nan ? '' : value;
44511 return parseFloat(value).toFixed(this.decimalPrecision);
44514 decimalPrecisionFcn : function(v)
44516 return Math.floor(v);
44519 validateValue : function(value)
44521 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
44525 var num = this.parseValue(value);
44528 this.markInvalid(String.format(this.nanText, value));
44532 if(num < this.minValue){
44533 this.markInvalid(String.format(this.minText, this.minValue));
44537 if(num > this.maxValue){
44538 this.markInvalid(String.format(this.maxText, this.maxValue));
44545 validate : function()
44547 if(this.disabled || this.allowBlank){
44552 var currency = this.getCurrency();
44554 if(this.validateValue(this.getRawValue()) && currency.length){
44559 this.markInvalid();
44563 getName: function()
44568 beforeBlur : function()
44574 var v = this.parseValue(this.getRawValue());
44581 onBlur : function()
44585 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44586 //this.el.removeClass(this.focusClass);
44589 this.hasFocus = false;
44591 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44595 var v = this.getValue();
44597 if(String(v) !== String(this.startValue)){
44598 this.fireEvent('change', this, v, this.startValue);
44601 this.fireEvent("blur", this);
44604 inputEl : function()
44606 return this.el.select('.roo-money-amount-input', true).first();
44609 currencyEl : function()
44611 return this.el.select('.roo-money-currency-input', true).first();
44614 hiddenEl : function()
44616 return this.el.select('input.hidden-number-input',true).first();
44620 * @class Roo.bootstrap.BezierSignature
44621 * @extends Roo.bootstrap.Component
44622 * Bootstrap BezierSignature class
44623 * This script refer to:
44624 * Title: Signature Pad
44626 * Availability: https://github.com/szimek/signature_pad
44629 * Create a new BezierSignature
44630 * @param {Object} config The config object
44633 Roo.bootstrap.BezierSignature = function(config){
44634 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44640 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44647 mouse_btn_down: true,
44650 * @cfg {int} canvas height
44652 canvas_height: '200px',
44655 * @cfg {float|function} Radius of a single dot.
44660 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44665 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44670 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44675 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44680 * @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.
44682 bg_color: 'rgba(0, 0, 0, 0)',
44685 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44687 dot_color: 'black',
44690 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44692 velocity_filter_weight: 0.7,
44695 * @cfg {function} Callback when stroke begin.
44700 * @cfg {function} Callback when stroke end.
44704 getAutoCreate : function()
44706 var cls = 'roo-signature column';
44709 cls += ' ' + this.cls;
44719 for(var i = 0; i < col_sizes.length; i++) {
44720 if(this[col_sizes[i]]) {
44721 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44731 cls: 'roo-signature-body',
44735 cls: 'roo-signature-body-canvas',
44736 height: this.canvas_height,
44737 width: this.canvas_width
44744 style: 'display: none'
44752 initEvents: function()
44754 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44756 var canvas = this.canvasEl();
44758 // mouse && touch event swapping...
44759 canvas.dom.style.touchAction = 'none';
44760 canvas.dom.style.msTouchAction = 'none';
44762 this.mouse_btn_down = false;
44763 canvas.on('mousedown', this._handleMouseDown, this);
44764 canvas.on('mousemove', this._handleMouseMove, this);
44765 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44767 if (window.PointerEvent) {
44768 canvas.on('pointerdown', this._handleMouseDown, this);
44769 canvas.on('pointermove', this._handleMouseMove, this);
44770 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44773 if ('ontouchstart' in window) {
44774 canvas.on('touchstart', this._handleTouchStart, this);
44775 canvas.on('touchmove', this._handleTouchMove, this);
44776 canvas.on('touchend', this._handleTouchEnd, this);
44779 Roo.EventManager.onWindowResize(this.resize, this, true);
44781 // file input event
44782 this.fileEl().on('change', this.uploadImage, this);
44789 resize: function(){
44791 var canvas = this.canvasEl().dom;
44792 var ctx = this.canvasElCtx();
44793 var img_data = false;
44795 if(canvas.width > 0) {
44796 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44798 // setting canvas width will clean img data
44801 var style = window.getComputedStyle ?
44802 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44804 var padding_left = parseInt(style.paddingLeft) || 0;
44805 var padding_right = parseInt(style.paddingRight) || 0;
44807 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44810 ctx.putImageData(img_data, 0, 0);
44814 _handleMouseDown: function(e)
44816 if (e.browserEvent.which === 1) {
44817 this.mouse_btn_down = true;
44818 this.strokeBegin(e);
44822 _handleMouseMove: function (e)
44824 if (this.mouse_btn_down) {
44825 this.strokeMoveUpdate(e);
44829 _handleMouseUp: function (e)
44831 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44832 this.mouse_btn_down = false;
44837 _handleTouchStart: function (e) {
44839 e.preventDefault();
44840 if (e.browserEvent.targetTouches.length === 1) {
44841 // var touch = e.browserEvent.changedTouches[0];
44842 // this.strokeBegin(touch);
44844 this.strokeBegin(e); // assume e catching the correct xy...
44848 _handleTouchMove: function (e) {
44849 e.preventDefault();
44850 // var touch = event.targetTouches[0];
44851 // _this._strokeMoveUpdate(touch);
44852 this.strokeMoveUpdate(e);
44855 _handleTouchEnd: function (e) {
44856 var wasCanvasTouched = e.target === this.canvasEl().dom;
44857 if (wasCanvasTouched) {
44858 e.preventDefault();
44859 // var touch = event.changedTouches[0];
44860 // _this._strokeEnd(touch);
44865 reset: function () {
44866 this._lastPoints = [];
44867 this._lastVelocity = 0;
44868 this._lastWidth = (this.min_width + this.max_width) / 2;
44869 this.canvasElCtx().fillStyle = this.dot_color;
44872 strokeMoveUpdate: function(e)
44874 this.strokeUpdate(e);
44876 if (this.throttle) {
44877 this.throttleStroke(this.strokeUpdate, this.throttle);
44880 this.strokeUpdate(e);
44884 strokeBegin: function(e)
44886 var newPointGroup = {
44887 color: this.dot_color,
44891 if (typeof this.onBegin === 'function') {
44895 this.curve_data.push(newPointGroup);
44897 this.strokeUpdate(e);
44900 strokeUpdate: function(e)
44902 var rect = this.canvasEl().dom.getBoundingClientRect();
44903 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44904 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44905 var lastPoints = lastPointGroup.points;
44906 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44907 var isLastPointTooClose = lastPoint
44908 ? point.distanceTo(lastPoint) <= this.min_distance
44910 var color = lastPointGroup.color;
44911 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44912 var curve = this.addPoint(point);
44914 this.drawDot({color: color, point: point});
44917 this.drawCurve({color: color, curve: curve});
44927 strokeEnd: function(e)
44929 this.strokeUpdate(e);
44930 if (typeof this.onEnd === 'function') {
44935 addPoint: function (point) {
44936 var _lastPoints = this._lastPoints;
44937 _lastPoints.push(point);
44938 if (_lastPoints.length > 2) {
44939 if (_lastPoints.length === 3) {
44940 _lastPoints.unshift(_lastPoints[0]);
44942 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44943 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44944 _lastPoints.shift();
44950 calculateCurveWidths: function (startPoint, endPoint) {
44951 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44952 (1 - this.velocity_filter_weight) * this._lastVelocity;
44954 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44957 start: this._lastWidth
44960 this._lastVelocity = velocity;
44961 this._lastWidth = newWidth;
44965 drawDot: function (_a) {
44966 var color = _a.color, point = _a.point;
44967 var ctx = this.canvasElCtx();
44968 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44970 this.drawCurveSegment(point.x, point.y, width);
44972 ctx.fillStyle = color;
44976 drawCurve: function (_a) {
44977 var color = _a.color, curve = _a.curve;
44978 var ctx = this.canvasElCtx();
44979 var widthDelta = curve.endWidth - curve.startWidth;
44980 var drawSteps = Math.floor(curve.length()) * 2;
44982 ctx.fillStyle = color;
44983 for (var i = 0; i < drawSteps; i += 1) {
44984 var t = i / drawSteps;
44990 var x = uuu * curve.startPoint.x;
44991 x += 3 * uu * t * curve.control1.x;
44992 x += 3 * u * tt * curve.control2.x;
44993 x += ttt * curve.endPoint.x;
44994 var y = uuu * curve.startPoint.y;
44995 y += 3 * uu * t * curve.control1.y;
44996 y += 3 * u * tt * curve.control2.y;
44997 y += ttt * curve.endPoint.y;
44998 var width = curve.startWidth + ttt * widthDelta;
44999 this.drawCurveSegment(x, y, width);
45005 drawCurveSegment: function (x, y, width) {
45006 var ctx = this.canvasElCtx();
45008 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45009 this.is_empty = false;
45014 var ctx = this.canvasElCtx();
45015 var canvas = this.canvasEl().dom;
45016 ctx.fillStyle = this.bg_color;
45017 ctx.clearRect(0, 0, canvas.width, canvas.height);
45018 ctx.fillRect(0, 0, canvas.width, canvas.height);
45019 this.curve_data = [];
45021 this.is_empty = true;
45026 return this.el.select('input',true).first();
45029 canvasEl: function()
45031 return this.el.select('canvas',true).first();
45034 canvasElCtx: function()
45036 return this.el.select('canvas',true).first().dom.getContext('2d');
45039 getImage: function(type)
45041 if(this.is_empty) {
45046 return this.canvasEl().dom.toDataURL('image/'+type, 1);
45049 drawFromImage: function(img_src)
45051 var img = new Image();
45053 img.onload = function(){
45054 this.canvasElCtx().drawImage(img, 0, 0);
45059 this.is_empty = false;
45062 selectImage: function()
45064 this.fileEl().dom.click();
45067 uploadImage: function(e)
45069 var reader = new FileReader();
45071 reader.onload = function(e){
45072 var img = new Image();
45073 img.onload = function(){
45075 this.canvasElCtx().drawImage(img, 0, 0);
45077 img.src = e.target.result;
45080 reader.readAsDataURL(e.target.files[0]);
45083 // Bezier Point Constructor
45084 Point: (function () {
45085 function Point(x, y, time) {
45088 this.time = time || Date.now();
45090 Point.prototype.distanceTo = function (start) {
45091 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45093 Point.prototype.equals = function (other) {
45094 return this.x === other.x && this.y === other.y && this.time === other.time;
45096 Point.prototype.velocityFrom = function (start) {
45097 return this.time !== start.time
45098 ? this.distanceTo(start) / (this.time - start.time)
45105 // Bezier Constructor
45106 Bezier: (function () {
45107 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45108 this.startPoint = startPoint;
45109 this.control2 = control2;
45110 this.control1 = control1;
45111 this.endPoint = endPoint;
45112 this.startWidth = startWidth;
45113 this.endWidth = endWidth;
45115 Bezier.fromPoints = function (points, widths, scope) {
45116 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45117 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45118 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45120 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45121 var dx1 = s1.x - s2.x;
45122 var dy1 = s1.y - s2.y;
45123 var dx2 = s2.x - s3.x;
45124 var dy2 = s2.y - s3.y;
45125 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45126 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45127 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45128 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45129 var dxm = m1.x - m2.x;
45130 var dym = m1.y - m2.y;
45131 var k = l2 / (l1 + l2);
45132 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45133 var tx = s2.x - cm.x;
45134 var ty = s2.y - cm.y;
45136 c1: new scope.Point(m1.x + tx, m1.y + ty),
45137 c2: new scope.Point(m2.x + tx, m2.y + ty)
45140 Bezier.prototype.length = function () {
45145 for (var i = 0; i <= steps; i += 1) {
45147 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45148 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45150 var xdiff = cx - px;
45151 var ydiff = cy - py;
45152 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45159 Bezier.prototype.point = function (t, start, c1, c2, end) {
45160 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45161 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45162 + (3.0 * c2 * (1.0 - t) * t * t)
45163 + (end * t * t * t);
45168 throttleStroke: function(fn, wait) {
45169 if (wait === void 0) { wait = 250; }
45171 var timeout = null;
45175 var later = function () {
45176 previous = Date.now();
45178 result = fn.apply(storedContext, storedArgs);
45180 storedContext = null;
45184 return function wrapper() {
45186 for (var _i = 0; _i < arguments.length; _i++) {
45187 args[_i] = arguments[_i];
45189 var now = Date.now();
45190 var remaining = wait - (now - previous);
45191 storedContext = this;
45193 if (remaining <= 0 || remaining > wait) {
45195 clearTimeout(timeout);
45199 result = fn.apply(storedContext, storedArgs);
45201 storedContext = null;
45205 else if (!timeout) {
45206 timeout = window.setTimeout(later, remaining);
45216 // old names for form elements
45217 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
45218 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
45219 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
45220 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
45221 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
45222 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
45223 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
45224 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
45225 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
45226 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
45227 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
45228 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
45229 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
45230 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
45231 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
45232 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
45233 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
45234 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
45235 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
45236 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
45237 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
45238 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
45239 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
45240 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
45241 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
45242 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
45244 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
45245 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
45247 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
45248 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
45250 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
45251 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
45252 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
45253 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator